cancer

- bad habits for internet relay chat
git clone git://git.acid.vegas/cancer.git
Log | Files | Refs | Archive | README | LICENSE

commit ade6244c9df3d8b1935e0093f3d0aa7ee52c129c
Author: acidvegas <acid.vegas@acid.vegas>
Date: Tue, 4 Jul 2023 12:44:56 -0400

Initial commit

Diffstat:
A.screens/preview.png | 0
ALICENSE | 15+++++++++++++++
AREADME.md | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Acancer.py | 462+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

4 files changed, 526 insertions(+), 0 deletions(-)

diff --git a/.screens/preview.png b/.screens/preview.png
Binary files differ.
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2023, acidvegas <acid.vegas@acid.vegas>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,49 @@
+# CANCER
+> bad habits for internet relay chat
+
+![](.screens/preview.png)
+
+## Information
+A fun bot that brings some bad habits on to IRC. Smoke cigarettes, smoke weed, and drink beer in your channels!
+
+Blow `!smoke` in the face of the opers, get bloody buck from !chug and vomit over the backlog.
+
+The bot includes colored ASCII art cigarettes, joints, and beers, that change everytime you drink or smoke.
+
+There is a various games and other fun things that generally promote user interactions in the channel.
+
+Try it out, join **#superbowl** on *irc.supernets.org* now, we have beer!
+
+**Warning:** This bot highly encourages *unthrottled* flooding! This may or may not be suited for your IRC network and/or channel(s). 
+
+**Optional**: Network operator privledges can be given to the bot for a fun feature where if someone does `!smoke` and it is the last hit of a cigarette, they will have a 1 in 100 chance of being `/KILL`'d from the network, aka **KILLED BY CANCER**
+
+## Commands
+| Command         | Description                                                |
+| --------------- | ---------------------------------------------------------- |
+| `@cancer`       | Information about the bot                                  |
+| `@cancer stats` | Return bot statistics for the channel                      |
+| `!100`          | 1 in 100 chance to get a 100 *(big `!smoke`)*              |
+| `!beer [nick]`  | Grab a beer or toss one to someone                         |
+| `!chainsmoke`   | Start a game of Chain Smoke                                |
+| `!chug`         | Sip beer                                                   |
+| `!dragrace`     | Start a game of Drag Race                                  |
+| `!extendo`      | 1 in 100 chance to get an EXTENDO *(big `!toke`)*          |
+| `!fatfuck`      | 1 in 100 chance to get a  FATFUCK *(fat `!smoke`/`!toke`)* |
+| `!letschug`     | LET'S FUCKING CHUG!                                        |
+| `!letstoke`     | LET'S FUCKING TOKE!                                        |
+| `!toke`         | Hit joint                                                  |
+| `!smoke`        | Hit cigarette                                              |
+| `!nosmoking`    | Disable the bot for 30 seconds                             |
+
+## Todo
+- Too drunk/Vomit features trigged from `!chug`
+- `!cigar` smoking and `!pizza` eating
+- ASCII art for cigarette packs, lighter, nosmoking, vomit
+- Fastest to respond w/ command timers for somthing.
+- Keep track of how many chugged, smoked, & toked per-nick for `!top` command.
+
+___
+
+###### Mirrors
+[acid.vegas](https://git.acid.vegas/cancer) • [GitHub](https://github.com/acidvegas/cancer) • [GitLab](https://gitlab.com/acidvegas/cancer) • [SourceHut](https://git.sr.ht/~acidvegas/cancer) • [SuperNETs](https://git.supernets.org/acidvegas/cancer)
diff --git a/cancer.py b/cancer.py
@@ -0,0 +1,461 @@
+#!/usr/bin/env python
+# Cancer IRC Bot - Developed by acidvegas in Python (https://git.acid.vegas/cancer)
+
+'''
+WARNING: This bot highly encourages flooding!
+
+Commands:
+	@cancer       | Information about the bot
+	@cancer stats | Return bot statistics for the channel
+	!100          | 1 in 100 chance to get a 100 (big !smoke)
+	!beer [nick]  | Grab a beer or toss one to someone
+	!chainsmoke   | Start a game of Chain Smoke
+	!chug         | Sip beer
+	!dragrace     | Start a game of Drag Race
+	!extendo      | 1 in 100 chance to get an EXTENDO (big !toke)
+	!fatfuck      | 1 in 100 chance to get a  FATFUCK (fat !smoke/!toke)
+	!letschug     | LET'S FUCKING CHUG!
+	!letstoke     | LET'S FUCKING TOKE!
+	!toke         | Hit joint
+	!smoke        | Hit cigarette
+	!nosmoking    | Disable the bot for 30 seconds
+'''
+
+import asyncio
+import json
+import os
+import random
+import ssl
+import time
+
+# Connection
+server     = 'irc.server.com'
+port       = 6697
+use_ipv6   = False
+use_ssl    = True
+vhost      = None
+channel    = '#chats'
+key        = None
+
+# Identity
+nickname = 'CANCER'
+username = 'smokesome'
+realname = 'git.acid.vegas/cancer'
+
+# Login
+nickserv_password = None
+network_password  = None
+operator_password = None
+
+# Settings
+user_modes = 'BdDg' # +d requires additional ! and @ to be in your set::channel-command-prefix on UnrealIRCd
+
+# Formatting Control Characters / Color Codes
+bold        = '\x02'
+italic      = '\x1D'
+underline   = '\x1F'
+reverse     = '\x16'
+reset       = '\x0f'
+white       = '00'
+black       = '01'
+blue        = '02'
+green       = '03'
+red         = '04'
+brown       = '05'
+purple      = '06'
+orange      = '07'
+yellow      = '08'
+light_green = '09'
+cyan        = '10'
+light_cyan  = '11'
+light_blue  = '12'
+pink        = '13'
+grey        = '14'
+light_grey  = '15'
+
+def color(msg, foreground, background=None):
+	return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}'
+
+def debug(data):
+	print('{0} | [~] - {1}'.format(time.strftime('%I:%M:%S'), data))
+
+def error(data, reason=None):
+	print('{0} | [!] - {1} ({2})'.format(time.strftime('%I:%M:%S'), data, str(reason))) if reason else print('{0} | [!] - {1}'.format(time.strftime('%I:%M:%S'), data))
+
+def luck(odds):
+	return True if random.randint(1,odds) == 1 else False
+
+def ssl_ctx():
+	ctx = ssl.create_default_context()
+	ctx.check_hostname = False
+	ctx.verify_mode = ssl.CERT_NONE
+	return ctx
+
+class Generate: # degenerate *
+	def beer():
+		glass = color(' ', light_grey, light_grey)
+		return glass + color(''.join(random.choice(('       :.')) for _ in range(9)), orange, yellow) + glass
+
+	def cigarette(size):
+		filter    = color(';.`-,:.`;', yellow, orange)+color(' ', yellow, yellow)
+		cigarette = color('|'*size, light_grey, white)
+		cherry    = color('\u259A', random.choice((red,yellow,orange)), black)+color('\u259A', random.choice((red,yellow,orange)), grey)
+		smoke     = color('-' + ''.join(random.choice((';:-.,_`~\'')) for _ in range(random.randint(5,8))), grey)
+		return filter + cigarette + cherry + smoke
+
+	def joint(size):
+		joint    = color('/'*size, light_grey, white)
+		cherry   = color('\u259A', random.choice((red,yellow,orange)), black)+color('\u259A', random.choice((red,yellow,orange)), grey)
+		smoke    = color('-' + ''.join(random.choice((';:-.,_`~\'')) for _ in range(random.randint(5,8))), grey)
+		return joint + cherry + smoke
+
+	def mug(size):
+		glass  = color(' ', light_grey, light_grey)
+		empty  = f'{glass}         {glass}'
+		foam   = glass + color(':::::::::', light_grey, white) + glass
+		bottom = color('           ', light_grey, light_grey)
+		mug   = [foam,Generate.beer(),Generate.beer(),Generate.beer(),Generate.beer(),Generate.beer(),Generate.beer(),Generate.beer()]
+		for i in range(8-size):
+			mug.pop()
+			mug.insert(0, empty)
+		for i in range(len(mug)):
+			if i == 2 or i == 7:
+				mug[i] += glass + glass
+			elif i > 2 and i < 7:
+				mug[i] += '  ' + glass
+		mug.append(bottom)
+		return mug
+
+class Bot():
+	def __init__(self):
+		self.fat             = False
+		self.event           = None
+		self.nicks           = list()
+		self.stats           = {'hits':25,'sips':8,'chugged':0,'smoked':0,'toked':0,'chain':0,'drag':0}
+		self.loops           = {'chainsmoke':None,'dragrace':None,'letschug':None,'letstoke':None,'nosmoking':None,'timers':None}
+		self.status          = True
+		self.reader          = None
+		self.writer          = None
+
+	async def raw(self, data):
+		self.writer.write(data[:510].encode('utf-8') + b'\r\n')
+		await self.writer.drain()
+
+	async def action(self, chan, msg):
+		await self.sendmsg(chan, f'\x01ACTION {msg}\x01')
+
+	async def sendmsg(self, target, msg):
+		await self.raw(f'PRIVMSG {target} :{msg}')
+
+	async def notice(self, target, msg):
+		await self.raw(f'NOTICE {target} :{msg}')
+
+	async def connect(self):
+		while True:
+			try:
+				options = {
+					'host'       : server,
+					'port'       : port,
+					'limit'      : 1024,
+					'ssl'        : ssl_ctx() if use_ssl else None,
+					'family'     : 10 if use_ipv6 else 2,
+					'local_addr' : vhost
+				}
+				self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**options), 15)
+				await self.raw(f'USER {username} 0 * :{realname}')
+				await self.raw('NICK ' + nickname)
+			except Exception as ex:
+				error('error: failed to connect to ' + server, ex)
+			else:
+				if os.path.isfile('stats.json'):
+					with open('stats.json') as stats_file:
+						self.stats = json.loads(stats_file.read())
+						debug('reloaded stats')
+				await self.listen()
+				for loop in self.loops:
+					if self.loops[loop]:
+						self.loops[loop].cancel()
+				self.stats['chain'] = 0
+				self.stats['drag']  = 0
+				self.event  = None
+				self.nicks  = list()
+				self.status = True
+			finally:
+				await asyncio.sleep(30)
+
+	async def loop_nosmoking(self):
+		await asyncio.sleep(30)
+		self.status = True
+
+	async def loop_timers(self):
+		while True:
+			try:
+				if time.strftime('%I:%M') == '04:20':
+					await self.sendmsg(channel, color('S M O K E W E E D E R R D A Y', light_green))
+					await self.sendmsg(channel, color('ITZ DAT MUTHA FUCKN 420 BITCH', yellow))
+					await self.sendmsg(channel, color('LIGHT UP A NICE GOOD FAT FUCK', red))
+					await asyncio.sleep(120)
+				elif time.strftime('%I:%M %p') == '02:00 AM':
+					await self.sendmsg(channel, '.ascii phish')
+					await asyncio.sleep(120)
+				elif time.strftime('%I:%M') == '12:00': # the biscuit hour..
+					with open('stats.json', 'w') as stats_file:
+						json.dump(self.stats, stats_file)
+					await asyncio.sleep(120)
+			except Exception as ex:
+				error('error: loop_timers failed', ex)
+			finally:
+				await asyncio.sleep(20)
+
+	async def loop_chainsmoke(self):
+		self.nicks = dict()
+		try:
+			await self.notice(channel, 'Starting a round of {0} in {1} seconds!'.format(color('ChainSmoke', red), color('10', white)))
+			await self.notice(channel, '[{0}] {1} {2} {3}'.format(color('How To Play', light_blue), color('Type', yellow), color('!smoke', light_green), color('to hit a cigarette. The cigarette goes down a little after each hit. Once you finish a cigarette, a new one will be lit for you. You will have 60 seconds to chain smoke as many cigarettes as possible.', yellow)))
+			await asyncio.sleep(10)
+			await self.action(channel, 'Round starts in 3...')
+			await asyncio.sleep(1)
+			await self.action(channel, '2...')
+			await asyncio.sleep(1)
+			await self.action(channel, '1...')
+			await asyncio.sleep(1)
+			await self.action(channel, color('GO', light_green))
+			self.status = True
+			await asyncio.sleep(60)
+			self.status = False
+			await self.sendmsg(channel, color('          CHAINSMOKE ROUND IS OVER          ', red, yellow))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('          CHAINSMOKE ROUND IS OVER          ', red, yellow))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('          CHAINSMOKE ROUND IS OVER          ', red, yellow))
+			await self.sendmsg(channel, color('Counting cigarette butts...', yellow))
+			await asyncio.sleep(10)
+			await self.sendmsg(channel, '{0} smoked {1} cigarettes!'.format(channel, color(str(self.stats['chain']), light_blue)))
+			if self.nicks:
+				guy = max(self.nicks, key=self.nicks.get)
+				await self.sendmsg(channel, '{0} smoked the most cigarettes... {1}'.format(guy, self.nicks[guy]))
+		except Exception as ex:
+			error('error: loop_chainsmoke failed', ex)
+		finally:
+			self.stats['chain'] = 0
+			self.nicks  = list()
+			self.event  = None
+			self.status = True
+
+	async def loop_dragrace(self):
+		self.hits   = 25
+		try:
+			await self.notice(channel, 'Starting a round of {0} in {1} seconds!'.format(color('DragRace', red), color('10', white)))
+			await self.notice(channel, '[{0}] {1} {2} {3}'.format(color('How To Play', light_blue), color('Type', yellow), color('!smoke', light_green), color('to hit a cigarette. The cigarette goes down a little after each hit. You will have 10 seconds to smoke as quickly as possible.', yellow)))
+			await asyncio.sleep(10)
+			await self.action(channel, 'Round starts in 3...')
+			await asyncio.sleep(1)
+			await self.action(channel, '2...')
+			await asyncio.sleep(1)
+			await self.action(channel, '1...')
+			await asyncio.sleep(1)
+			await self.action(channel, color('GO', light_green))
+			self.stats['drag'] = time.time()
+		except Exception as ex:
+			error('error: loop_dragrace failed', ex)
+		finally:
+			self.status = True
+
+	async def loop_letschug(self, nick):
+		self.nicks.append(nick)
+		try:
+			await self.sendmsg(channel, color(f'OH SHIT {nick} is drunk', light_green))
+			await self.notice(channel, color(f'Time to TOTALLY CHUG in {channel.upper()} in 30 seconds, type !chug to join', light_green))
+			await asyncio.sleep(10)
+			await self.sendmsg(channel, color('LOL we CHUG in 20 get ready ' + ' '.join(self.nicks), light_green))
+			await asyncio.sleep(10)
+			await self.sendmsg(channel, color('YO we CHUG in 10 get ready ' + ' '.join(self.nicks), light_green))
+			await asyncio.sleep(5)
+			await self.sendmsg(channel, color('alright CHUG in 5', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('4..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('3..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('2..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('1..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color(' '.join(self.nicks) + ' .. CHUG!', light_green))
+		except Exception as ex:
+			error('error: loop_letschug failed', ex)
+		finally:
+			self.event = None
+			self.nicks = list()
+
+	async def loop_letstoke(self, nick):
+		self.nicks.append(nick)
+		try:
+			await self.sendmsg(channel, color(f'YO {nick} is high', light_green))
+			await self.notice(channel, color(f'Time to FUCKING toke in {channel.upper()}, type !toke to join', light_green))
+			await asyncio.sleep(10)
+			await self.sendmsg(channel, color('OH SHIT we toke in 20 get ready ' + ' '.join(self.nicks), light_green))
+			await asyncio.sleep(10)
+			await self.sendmsg(channel, color('OH SHIT we toke in 10 get ready ' + ' '.join(self.nicks), light_green))
+			await asyncio.sleep(5)
+			await self.sendmsg(channel, color('alright toke in 5', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('4..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('3..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('2..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color('1..', light_green))
+			await asyncio.sleep(1)
+			await self.sendmsg(channel, color(' '.join(self.nicks) + ' .. toke!', light_green))
+		except Exception as ex:
+			error('error: loop_letstoke failed', ex)
+		finally:
+			self.event = None
+			self.nicks = list()
+
+	async def listen(self):
+		while True:
+			try:
+				if self.reader.at_eof():
+					break
+				data = await asyncio.wait_for(self.reader.readuntil(b'\r\n'), 500)
+				line = data.decode('utf-8').strip()
+				args = line.split()
+				debug(line)
+				if line.startswith('ERROR :Closing Link:'):
+					raise Exception('Connection has closed.')
+				elif args[0] == 'PING':
+					await self.raw('PONG '+args[1][1:])
+				elif args[1] == '001':
+					if user_modes:
+						await self.raw(f'MODE {nickname} +{user_modes}')
+					if nickserv_password:
+						await self.sendmsg('NickServ', f'IDENTIFY {nickname} {nickserv_password}')
+					if operator_password:
+						await self.raw(f'OPER {username} {operator_password}')
+					await self.raw(f'JOIN {channel} {key}') if key else await self.raw('JOIN ' + channel)
+					self.loops['timers'] = asyncio.create_task(self.loop_timers())
+				elif args[1] == '433':
+					error('The bot is already running or nick is in use.') # nick change
+				elif args[1] == 'INVITE' and len(args) == 4:
+					invited = args[2]
+					chan    = args[3][1:]
+					if invited == nickname and chan == channel:
+						await self.raw(f'JOIN {channel} {key}') if key else await self.raw('JOIN ' + channel)
+				elif args[1] == 'KICK' and len(args) >= 4:
+					chan   = args[2]
+					kicked = args[3]
+					if kicked == nickname and chan == channel:
+						await asyncio.sleep(3)
+						await self.raw(f'JOIN {channel} {key}') if key else await self.raw('JOIN ' + channel)
+				elif args[1] == 'PART' and len(args) >= 3:
+					chan = args[2]
+					if chan == channel:
+						nick = args[0].split('!')[0][1:]
+						await self.action(nick, f'blows smoke in {nick}\'s face...')
+				elif args[1] == 'PRIVMSG' and len(args) >= 4:
+					nick = args[0].split('!')[0][1:]
+					chan = args[2]
+					msg  = ' '.join(args[3:])[1:]
+					if chan ==  channel:
+						if self.status:
+							args = msg.split()
+							if msg == '@cancer':
+								await self.sendmsg(chan, bold + 'CANCER IRC Bot - Developed by acidvegas in Python - https://git.acid.vegas/cancer')
+							elif msg == '@cancer stats':
+								await self.sendmsg(chan, 'Chugged : {0} beers      {1}'.format(color(self.stats['chugged'], light_blue), color('({0:,} cases)'.format(int(self.stats['chugged']/24)), grey)))
+								await self.sendmsg(chan, 'Smoked  : {0} cigarettes {1}'.format(color(self.stats['smoked'],  light_blue), color('({0:,} packs)'.format(int(self.stats['smoked']/24)),  grey)))
+								await self.sendmsg(chan, 'Toked   : {0} joints     {1}'.format(color(self.stats['toked'],   light_blue), color('({0:,} grams)'.format(int(self.stats['toked']/3)),    grey)))
+							elif msg in ('!100','!extendo','!fatfuck') and luck(100):
+								if msg == '!fatfuck':
+									self.fat = True
+									await self.sendmsg(chan, '{0}{1}{2}'.format(color(' !!! ', red, green), color('AWWW SHIT, IT\'S TIME FOR THAT MARLBORO FATFUCK', black, green), color(' !!! ', red, green)))
+								else:
+									self.stats['hits'] = 100
+									if msg == '!100':
+										await self.sendmsg(chan, '{0}{1}{2}'.format(color(' !!! ', white, red), color('AWWW SHIT, IT\'S TIME FOR THAT NEWPORT 100', red, white), color(' !!! ', white, red)))
+									else:
+										await self.sendmsg(chan, '{0}{1}{2}'.format(color(' !!! ', red, green), color('OHHH FUCK, IT\'S TIME FOR THAT 420 EXTENDO', yellow, green), color(' !!! ', red, green)))
+							elif args[0] == '!beer':
+								if len(args) == 1:
+									target = nick
+								elif len(args) == 2:
+									target = args[1]
+								beer = '{0}{1}{2}'.format(color(' ', white, white), color(' BUD ', white, random.choice((blue,brown))), color('c', grey, white))
+								await self.action(chan, f'throws {color(target, white)} an ice cold {beer} =)')
+								if luck(100):
+									await asyncio.sleep(2)
+									await self.action(chan, 'suddenly feels more gay...')
+							elif msg == '!chainsmoke' and not self.event:
+								self.status = False
+								self.event  = 'chainsmoke'
+								self.loops['chainsmoke'] = asyncio.create_task(self.loop_chainsmoke())
+							elif msg == '!chug':
+								if self.event == 'letschug':
+									if nick in self.nicks:
+										await self.sendmsg(chan, color(nick + ' you are already chuggin u wastoid!', light_green))
+									else:
+										self.nicks.append(nick)
+										await self.sendmsg(chan, color(nick + ' joined the CHUG session!', light_green))
+								else:
+									if self.stats['sips'] <= 0:
+										self.stats['sips'] = 8
+										self.stats['chugged'] += 1
+									for line in Generate.mug(self.stats['sips']):
+										await self.sendmsg(chan, line)
+									self.stats['sips'] -= random.choice((1,2))
+							elif msg == '!dragrace' and not self.event:
+								self.status = False
+								self.event  = 'dragrace'
+								self.loops['dragrace'] = asyncio.create_task(self.loop_dragrace())
+							elif msg == '!letschug' and not self.event:
+								self.event = 'letschug'
+								self.loops['letschug'] = asyncio.create_task(self.loop_letschug(nick))
+							elif msg == '!letstoke' and not self.event:
+								self.event = 'letstoke'
+								self.loops['letstoke'] = asyncio.create_task(self.loop_letstoke(nick))
+							elif msg == '!nosmoking':
+								self.status = False
+								self.loops['nosmoking'] = asyncio.create_task(self.loop_nosmoking())
+							elif msg in ('!smoke','!toke'):
+								option = 'smoked' if msg == '!smoke' else 'toked'
+								if self.event == 'letstoke' and msg == '!toke':
+									if nick in self.nicks:
+										await self.sendmsg(chan, color(nick + ' you are already toking u stoner!', light_green))
+									else:
+										self.nicks.append(nick)
+										await self.sendmsg(chan, color(nick + ' joined the TOKE session!', light_green))
+								else:
+									if self.stats['hits'] <= 0:
+										self.stats['hits'] = 25
+										self.stats[option] += 1
+										if self.fat:
+											self.fat = False
+										if self.event == 'chainsmoke' and msg == '!smoke':
+											self.nicks[nick] = self.nicks[nick]+1 if nick in self.nicks else 1
+											self.stats['chain'] += 1
+										elif self.event == 'dragrace' and msg == '!smoke':
+											await self.sendmsg(chan, 'It took {0} seconds for {1} to smoke a cigarette!'.format(color('{:.2f}'.format(time.time()-self.stats['drag']), light_blue), color(chan, white)))
+											self.event = None
+											self.stats['drag'] = 0
+										elif luck(25) and msg == '!smoke':
+											await self.raw(f'KILL {nick} CANCER KILLED {nick.upper()} - QUIT SMOKING TODAY! +1 800-QUIT-NOW')
+									else:
+										object = Generate.cigarette(self.stats['hits']) if msg == '!smoke' else Generate.joint(self.stats['hits'])
+										cigarette = Generate.cigarette(self.stats['hits'])
+										if self.fat:
+											for i in range(3):
+												await self.sendmsg(chan, object)
+										else:
+											await self.sendmsg(chan, object)
+										self.stats['hits'] -= random.choice((1,2))
+			except (UnicodeDecodeError, UnicodeEncodeError):
+				pass
+			except Exception as ex:
+				error(self.display + 'fatal error occured', ex)
+				break
+
+# Main
+asyncio.run(Bot().connect())
+\ No newline at end of file