irccex

- fantasy cryptocurrency exchange for irc
git clone git://git.acid.vegas/irccex.git
Log | Files | Refs | Archive | README | LICENSE

irc.py (29785B)

      1 #!/usr/bin/env python
      2 # IRC Cryptocurrency Exchange (IRCCEX) - Developed by acidvegas in Python (https://git.acid.vegas/irccex)
      3 # irc.py
      4 
      5 '''
      6 if using_too_many_if_statements == True:
      7 	use_more = True
      8 else:
      9 	use_alot_more = True
     10 '''
     11 
     12 import datetime
     13 import os
     14 import pickle
     15 import random
     16 import socket
     17 import threading
     18 import time
     19 
     20 import config
     21 import constants
     22 import functions
     23 from coinmarketcap import CoinMarketCap
     24 
     25 if config.connection.ssl:
     26 	import ssl
     27 
     28 def color(msg, foreground, background=None):
     29 	if background:
     30 		return f'{constants.color}{foreground},{background}{msg}{constants.reset}'
     31 	else:
     32 		return f'{constants.color}{foreground}{msg}{constants.reset}'
     33 
     34 class IRC(object):
     35 	def __init__(self):
     36 		self.db          = {'bank':dict(),'pool':0.0,'round':1,'score':dict(),'start':datetime.date.today(),'verify':dict(),'wallet':dict()}
     37 		self.last        = 0
     38 		self.last_backup = time.strftime('%I:%M')
     39 		self.maintenance = False
     40 		self.reward      = {'reward':0,'rewards':0,'status':False}
     41 		self.slow        = False
     42 		self.sock        = None
     43 		self.start       = time.time()
     44 
     45 	def run(self):
     46 		if os.path.isfile('db.pkl'):
     47 			with open('db.pkl', 'rb') as db_file:
     48 				self.db = pickle.load(db_file)
     49 			print('[+] - Restored database!')
     50 		Loops.start_loops()
     51 		self.connect()
     52 
     53 	def connect(self):
     54 		try:
     55 			self.create_socket()
     56 			self.sock.connect((config.connection.server, config.connection.port))
     57 			self.register()
     58 		except socket.error as ex:
     59 			print('[!] - Failed to connect to IRC server! (' + str(ex) + ')')
     60 			Events.disconnect()
     61 		else:
     62 			self.listen()
     63 
     64 	def create_socket(self):
     65 		self.sock = socket.socket(AF_INET6) if config.connection.ipv6 else socket.socket()
     66 		if config.connection.vhost:
     67 			self.sock.bind((config.connection.vhost, 0))
     68 		if config.connection.ssl:
     69 			ctx = ssl.SSLContext()
     70 			if config.cert.file:
     71 				ctx.load_cert_chain(config.cert.file, password=config.cert.password)
     72 			if config.connection.ssl_verify:
     73 				ctx.check_hostname = True
     74 				ctx.load_default_certs()
     75 				self.sock = ctx.wrap_socket(self.sock, server_hostname=config.connection.server)
     76 			else:
     77 				self.sock = ctx.wrap_socket(self.sock)
     78 
     79 	def listen(self):
     80 		buffer = str()
     81 		last   = time.time()
     82 		while True:
     83 			try:
     84 				data = self.sock.recv(1024).decode('utf-8')
     85 				buffer += data
     86 				if data:
     87 					last = time.time()
     88 				else:
     89 					if time.time() - last > 120:
     90 						break
     91 				while '\r\n' in buffer:
     92 					line    = buffer.split('\r\n')[0]
     93 					buffer  = buffer.split('\r\n', 1)[1]
     94 					print('[~] - ' + line)
     95 					if len(line.split()) >= 2:
     96 						Events.handle(line)
     97 			except (UnicodeDecodeError,UnicodeEncodeError):
     98 				pass
     99 			except Exception as ex:
    100 				print('[!] - Unexpected error occured. (' + str(ex) + ')')
    101 				break
    102 		Events.disconnect()
    103 
    104 	def register(self):
    105 		if config.login.network:
    106 			Commands.raw('PASS ' + config.login.network)
    107 		Commands.raw(f'USER {config.ident.username} 0 * :{config.ident.realname}')
    108 		Commands.nick(config.ident.nickname)
    109 
    110 class Commands:
    111 	def action(chan, msg):
    112 		Commands.sendmsg(chan, f'\x01ACTION {msg}\x01')
    113 
    114 	def check_nick(nick, chan):
    115 		if nick in Bot.db['wallet']:
    116 			return True
    117 		else:
    118 			if nick in Bot.db['verify']:
    119 				Commands.error(chan, 'Your account is not verified!', 'try again later')
    120 			else:
    121 				Commands.error(chan, 'You don\'t have an account!', 'use !register to make an account')
    122 			return False
    123 
    124 	def cleanup(nick):
    125 		for symbol in [symbol for symbol in Bot.db['wallet'][nick] if not Bot.db['wallet'][nick][symbol]]:
    126 			del Bot.db['wallet'][nick][symbol]
    127 		if not Bot.db['wallet'][nick]:
    128 			del Bot.db['wallet'][nick]
    129 
    130 	def error(target, data, reason=None):
    131 		if reason:
    132 			Commands.sendmsg(target, '[{0}] {1} {2}'.format(color('!', constants.red), data, color('({0})'.format(reason), constants.grey)))
    133 		else:
    134 			Commands.sendmsg(target, '[{0}] {1}'.format(color('!', constants.red), data))
    135 
    136 	def identify(nick, password):
    137 		Commands.sendmsg('NickServ', f'identify {nick} {password}')
    138 
    139 	def join_channel(chan, key=None):
    140 		Commands.raw(f'JOIN {chan} {key}') if key else Commands.raw('JOIN ' + chan)
    141 
    142 	def mode(target, mode):
    143 		Commands.raw(f'MODE {target} {mode}')
    144 
    145 	def nick(nick):
    146 		Commands.raw('NICK ' + nick)
    147 
    148 	def notice(target, msg):
    149 		Commands.raw(f'NOTICE {target} :{msg}')
    150 
    151 	def oper(user, password):
    152 		Commands.raw(f'OPER {user} {password}')
    153 
    154 	def raw(msg):
    155 		Bot.sock.send(bytes(msg + '\r\n', 'utf-8'))
    156 
    157 	def sendmsg(target, msg):
    158 		Commands.raw(f'PRIVMSG {target} :{msg}')
    159 		time.sleep(config.throttle.msg)
    160 
    161 class Events:
    162 	def connect():
    163 		if config.connection.modes:
    164 			Commands.mode(config.ident.nickname, '+' + config.connection.modes)
    165 		if config.login.nickserv:
    166 			Commands.identify(config.ident.nickname, config.login.nickserv)
    167 		if config.login.operator:
    168 			Commands.oper(config.ident.username, config.login.operator)
    169 		Commands.join_channel(config.connection.channel, config.connection.key)
    170 
    171 	def disconnect():
    172 		Bot.sock.close()
    173 		time.sleep(config.throttle.reconnect)
    174 		Bot.connect()
    175 
    176 	def invite(chan):
    177 		if chan == config.connection.channel:
    178 			Commands.join_channel(config.connection.channel, config.connection.key)
    179 
    180 	def kick(chan, kicked):
    181 		if kicked == config.ident.nickname and chan == config.connection.channel:
    182 			time.sleep(config.throttle.rejoin)
    183 			Commands.join_channel(chan, config.connection.key)
    184 
    185 	def message(nick, chan, msg):
    186 		if chan == config.connection.channel:
    187 			try:
    188 				if msg[:1] in '!@$':
    189 					if time.time() - Bot.last < config.throttle.cmd:
    190 						if not Bot.slow:
    191 							Bot.slow = True
    192 							Commands.sendmsg(chan, color('Slow down nerd!', constants.red))
    193 					else:
    194 						Bot.slow = False
    195 						args = msg.split()
    196 						if Bot.maintenance and args[0] in ('!cashout','!send','!trade'):
    197 							Commands.error(chan, 'Exchange is down for scheduled maintenance!', 'try again later')
    198 						else:
    199 							if args[0] == '!cashout':
    200 								if Commands.check_nick(nick, chan):
    201 									if 'USD' not in Bot.db['wallet'][nick]:
    202 										Commands.error(chan, 'Insufficent funds.', 'you have no USD in your account')
    203 									elif Bot.db['wallet'][nick]['USD'] < config.limits.cashout:
    204 										Commands.error(chan, 'Insufficent funds.', f'${config.limits.cashout:,} minimum')
    205 									else:
    206 										profit = Bot.db['wallet'][nick]['USD']-config.limits.init
    207 										amount = functions.fee(profit, config.fees.cashout)
    208 										cashout_msg = msg[9:][:100] if len(args) > 1 else Bot.db['bank'][nick][1] if nick in Bot.db['bank'] else 'IM RICH BITCH !!!'
    209 										Bot.db['bank'][nick] = (Bot.db['bank'][nick][0]+amount, cashout_msg) if nick in Bot.db['bank'] else (amount, cashout_msg)
    210 										Bot.db['pool'] += profit-amount
    211 										Bot.db['wallet'][nick]['USD'] = config.limits.init
    212 										Commands.sendmsg(chan, 'Cashed out {0} to your bank account! {1}'.format(color('${:,}'.format(int(amount)), constants.green), color('(current balance: ${:,})'.format(int(Bot.db['bank'][nick][0])), constants.grey)))
    213 							elif len(args) == 1:
    214 								if msg == '@irccex':
    215 									Commands.sendmsg(chan, constants.bold + 'IRC Cryptocurrency Exchange (IRCCEX) - Developed by acidvegas in Python - https://github.com/acidvegas/irccex')
    216 								elif msg == '@stats':
    217 									bank_total = 0
    218 									global_data = CMC._global()
    219 									ticker_data = CMC._ticker()
    220 									wallet_total = 0
    221 									for item in Bot.db['bank']:
    222 										bank_total += Bot.db['bank'][item][0]
    223 									for user in Bot.db['wallet']:
    224 										for symbol in Bot.db['wallet'][user]:
    225 											value = Bot.db['wallet'][user][symbol] if symbol == 'USD' else ticker_data[symbol]['price']*Bot.db['wallet'][user][symbol]
    226 											wallet_total += value
    227 									Commands.sendmsg(chan, '[{0}]'.format(color('Bot', constants.cyan)))
    228 									Commands.sendmsg(chan, '  {0} {1}'.format(color('Backup :', constants.white), Bot.last_backup))
    229 									Commands.sendmsg(chan, '  {0} {1}'.format(color('Round  :', constants.white), Bot.db['round']))
    230 									Commands.sendmsg(chan, '  {0} {1}'.format(color('Uptime :', constants.white), functions.uptime(Bot.start)))
    231 									Commands.sendmsg(chan, '[{0}]'.format(color('Market', constants.cyan)))
    232 									Commands.sendmsg(chan, '  {0} {1}%'.format(color('BTC Dominance    :', constants.white), global_data['btc_dominance']))
    233 									Commands.sendmsg(chan, '  {0} {1}%'.format(color('ETH Dominance    :', constants.white), global_data['eth_dominance']))
    234 									Commands.sendmsg(chan, '  {0} {1:,}'.format(color('Cryptocurrencies :', constants.white), global_data['cryptocurrencies']))
    235 									Commands.sendmsg(chan, '  {0} {1:,}'.format(color('Exchanges        :', constants.white), global_data['exchanges']))
    236 									Commands.sendmsg(chan, '  {0} ${1:,}'.format(color('Market Cap       :', constants.white), global_data['market_cap']))
    237 									Commands.sendmsg(chan, '  {0} ${1:,}'.format(color('Volume           :', constants.white), global_data['volume']))
    238 									Commands.sendmsg(chan, '[{0}]'.format(color('Round', constants.cyan)))
    239 									Commands.sendmsg(chan, '  {0} {1} {2}'.format(color('Accounts    :', constants.white), '{:,}'.format(len(Bot.db['wallet'])), color('(${:,})'.format(int(wallet_total)), constants.grey)))
    240 									Commands.sendmsg(chan, '  {0} {1} {2}'.format(color('Bank        :', constants.white), '{:,}'.format(len(Bot.db['bank'])), color('(${:,})'.format(int(bank_total)), constants.grey)))
    241 									Commands.sendmsg(chan, '  {0} ${1:,}'.format(color('Reward Pool :', constants.white), int(Bot.db['pool'])))
    242 									Commands.sendmsg(chan, '  {0} {1:,}'.format(color('Unverified  :', constants.white), len(Bot.db['verify'])))
    243 								elif msg.startswith('$'):
    244 									msg = msg.upper()
    245 									if ',' in msg:
    246 										seen  = set()
    247 										coins = [x for x in list(msg[1:].split(','))[:10] if not (x in seen or seen.add(x))]
    248 										data  = [CMC._ticker()[coin] for coin in coins if coin in CMC._ticker()]
    249 										if data:
    250 											if len(data) == 1:
    251 												Commands.sendmsg(chan, functions.coin_info(data[0]))
    252 											else:
    253 												for line in functions.coin_table(data):
    254 													Commands.sendmsg(chan, line)
    255 										else:
    256 											Commands.error(chan, 'Invalid cryptocurrency names!')
    257 									else:
    258 										coin = msg[1:]
    259 										if not coin.split('.')[0].isdigit():
    260 											if coin in CMC._ticker():
    261 												Commands.sendmsg(chan, functions.coin_info(CMC._ticker()[coin]))
    262 											else:
    263 												Commands.error(chan, 'Invalid cryptocurrency name!')
    264 								elif msg == '!bang' and Bot.reward['status']:
    265 									if Commands.check_nick(nick, chan):
    266 										amount = functions.fee(Bot.reward['reward'], float('0.{0:02}'.format(functions.random_int(5,15)))) if Bot.reward['rewards'] else Bot.db['pool']
    267 										Bot.db['wallet'][nick]['USD'] = Bot.db['wallet'][nick]['USD']+amount if 'USD' in Bot.db['wallet'][nick] else amount
    268 										Bot.db['pool'] -= amount
    269 										if Bot.db['pool']:
    270 											Commands.sendmsg(chan, 'You won {0}!'.format(color('${:,}'.format(amount), constants.green)))
    271 											Bot.reward['rewards'] -= 1
    272 										else:
    273 											Commands.sendmsg(chan, 'You won the big {0}!'.format(color('${:,}'.format(amount), constants.green)))
    274 											Bot.reward = {'reward':0,'rewards':0,'status':False}
    275 								elif msg == '!bank':
    276 									if nick in Bot.db['bank']:
    277 										clean_bank = dict()
    278 										for item in Bot.db['bank']:
    279 											clean_bank[item] = Bot.db['bank'][item][0]
    280 										richest = sorted(clean_bank, key=clean_bank.get, reverse=True)
    281 										Commands.sendmsg(chan, '[{0}] {1} {2} {3}'.format(color('{:02}'.format(richest.index(nick)+1), constants.pink), nick, color('${:,}'.format(int(Bot.db['bank'][nick][0])), constants.green), Bot.db['bank'][nick][1]))
    282 									else:
    283 										Commands.error(chan, 'You don\'t have any money in the bank!', 'use !cashout to put money in the bank')
    284 								elif msg == '!portfolio':
    285 									if Commands.check_nick(nick, chan):
    286 										total = 0
    287 										for symbol in Bot.db['wallet'][nick]:
    288 											value = Bot.db['wallet'][nick][symbol] if symbol == 'USD' else CMC._ticker()[symbol]['price']*Bot.db['wallet'][nick][symbol]
    289 											total += value
    290 										Commands.sendmsg(chan, color('${:,}'.format(int(total)), constants.green))
    291 								elif msg == '!register':
    292 									if nick not in Bot.db['verify'] and nick not in Bot.db['wallet']:
    293 										Bot.db['verify'][nick] = time.time()
    294 										Commands.sendmsg(chan, 'Welcome to the IRC Cryptocurrency Exchange! ' + color('(please wait 24 hours while we verify your documents)', constants.grey))
    295 									else:
    296 										Commands.error(chan, 'Failed to register an account!', 'you already have an account')
    297 								elif msg == '!rich':
    298 									if Bot.db['bank']:
    299 										clean_bank = dict()
    300 										for item in Bot.db['bank']:
    301 											clean_bank[item] = Bot.db['bank'][item][0]
    302 										richest = sorted(clean_bank, key=clean_bank.get, reverse=True)[:10]
    303 										for user in richest:
    304 											_user = f'{user[:1]}{constants.reset}{user[1:]}'
    305 											Commands.sendmsg(chan, '[{0}] {1} {2} {3}'.format(color('{:02}'.format(richest.index(user)+1), constants.pink), _user.ljust(15), color('${:,}'.format(int(Bot.db['bank'][user][0])).ljust(13), constants.green), Bot.db['bank'][user][1]))
    306 										Commands.sendmsg(chan, '^ this could be u but u playin...')
    307 									else:
    308 										Commands.error(chan, 'Yall broke...')
    309 								elif msg == '!score':
    310 									if nick in Bot.db['score']:
    311 										clean_bank = dict()
    312 										for item in Bot.db['score']:
    313 											clean_bank[item] = Bot.db['score'][item][0]
    314 										top = sorted(clean_bank, key=clean_bank.get, reverse=True)
    315 										Commands.sendmsg(chan, '[{0}] {1} {2} {3}'.format(color('{:02}'.format(top.index(nick)+1), constants.pink), nick, color(str(Bot.db['score'][nick][0]), constants.cyan), color('${:,}'.format(int(Bot.db['score'][nick][1])), constants.green)))
    316 									else:
    317 										Commands.error(chan, 'You don\'t have any points!', 'be in the top 10 at the end of the month when the current round ends to get points')
    318 								elif msg == '!scores':
    319 									if Bot.db['score']:
    320 										clean_score = dict()
    321 										for item in Bot.db['score']:
    322 											clean_score[item] = Bot.db['score'][item][0]
    323 										top = sorted(clean_score, key=clean_score.get, reverse=True)[:10]
    324 										for user in top:
    325 											Commands.sendmsg(chan, '[{0}] {1} {2} {3}'.format(color('{:02}'.format(top.index(user)+1), constants.pink), user.ljust(15), color(str(Bot.db['score'][user][0]).ljust(5), constants.cyan), color('${:,}'.format(int(Bot.db['score'][nick][1])), constants.green)))
    326 										Commands.sendmsg(chan, '^ this could be u but u playin...')
    327 									else:
    328 										Commands.error(chan, 'Yall broke...')
    329 								elif msg == '!top':
    330 									data = list(CMC._ticker().values())[:10]
    331 									for line in functions.coin_table(data):
    332 										Commands.sendmsg(chan, line)
    333 								elif msg == '!wallet':
    334 									if Commands.check_nick(nick, chan):
    335 										Commands.sendmsg(chan, color('  Symbol          Amount                  Value        ', constants.black, constants.light_grey))
    336 										total = 0
    337 										for symbol in Bot.db['wallet'][nick]:
    338 											amount = Bot.db['wallet'][nick][symbol]
    339 											if symbol == 'USD':
    340 												value = amount
    341 											elif symbol in CMC._ticker():
    342 												value = CMC._ticker()[symbol]['price']*amount
    343 											else: # CLEAN THIS UP - TEMP FIX FOR ERRORS ON !wallet reported by sht, ji, and others...
    344 												value = 'JACKED'
    345 											if value == 'JACKED':
    346 												Commands.sendmsg(chan, symbol + ' was JACKED sucka!')
    347 												del Bot.db['wallet'][nick][symbol]
    348 											else:
    349 												Commands.sendmsg(chan, f' {symbol.ljust(8)} | {str(functions.clean_float(amount)).rjust(20)} | {str(functions.clean_value(value)).rjust(20)}')
    350 												total += float(value)
    351 										Commands.sendmsg(chan, color(f'                      Total: {str(functions.clean_value(total)).rjust(27)}', constants.black, constants.light_grey))
    352 							elif len(args) == 2:
    353 								if args[0] in ('!bottom','!top'):
    354 									option  = args[1].lower()
    355 									if option not in ('1h','24h','7d','value','volume'):
    356 										Commands.error(chan, 'Invalid option!', 'valid options are 1h, 24h, 7d, value & volume')
    357 									else:
    358 										data = dict()
    359 										for item in CMC._ticker():
    360 											data[item] = float(CMC._ticker()[item]['percent'][option])
    361 										data = sorted(data, key=data.get, reverse=True)
    362 										data = data[-10:] if args[0] == '!bottom' else data[:10]
    363 										data = [CMC._ticker()[coin] for coin in data]
    364 										for line in functions.coin_table(data):
    365 											Commands.sendmsg(chan, line)
    366 							elif len(args) == 3:
    367 								if args[0] == '!trade':
    368 									if Commands.check_nick(nick, chan):
    369 										pair = args[1].upper()
    370 										if len(pair.split('/')) == 2:
    371 											from_symbol, to_symbol = pair.split('/')
    372 											if from_symbol in Bot.db['wallet'][nick]:
    373 												amount = args[2].replace(',','')
    374 												if functions.is_amount(amount):
    375 													if amount == '*':
    376 														amount = Bot.db['wallet'][nick][from_symbol]
    377 													elif amount.startswith('$'):
    378 														amount = float(amount[1:]) if from_symbol == 'USD' else float(amount[1:])/CMC._ticker()[from_symbol]['price']
    379 													else:
    380 														amount = float(amount)
    381 													usd_value = amount if from_symbol == 'USD' else CMC._ticker()[from_symbol]['price']*amount
    382 													if Bot.db['wallet'][nick][from_symbol] >= amount:
    383 														if usd_value >= config.limits.trade:
    384 															recv_amount = functions.fee(amount, config.fees.trade)
    385 															if functions.check_pair(from_symbol, to_symbol):
    386 																from_value  = 1 if from_symbol == 'USD' else CMC._ticker()[from_symbol]['price']
    387 																to_value    = 1 if to_symbol == 'USD' else CMC._ticker()[to_symbol]['price']
    388 																recv_amount = (recv_amount*from_value)/to_value
    389 																if to_symbol in Bot.db['wallet'][nick]:
    390 																	Bot.db['wallet'][nick][from_symbol] -= amount
    391 																	Bot.db['wallet'][nick][to_symbol] += recv_amount
    392 																	Commands.cleanup(nick)
    393 																	Bot.db['pool'] += usd_value-functions.fee(usd_value, config.fees.trade)
    394 																	Commands.sendmsg(chan, 'Trade successful!')
    395 																else:
    396 																	if len(Bot.db['wallet'][nick]) < config.limits.assets:
    397 																		Bot.db['wallet'][nick][from_symbol] -= amount
    398 																		Bot.db['wallet'][nick][to_symbol] = recv_amount
    399 																		Commands.cleanup(nick)
    400 																		Bot.db['pool'] += usd_value-functions.fee(usd_value, config.fees.trade)
    401 																		Commands.sendmsg(chan, 'Trade successful!')
    402 																	else:
    403 																		Commands.error(chan, f'You can\'t hold more than {config.limits.assets} assets!')
    404 															else:
    405 																Commands.error(chan, 'Invalid trade pair!')
    406 														else:
    407 															Commands.error(chan, 'Invalid amount.', f'${config.limits.trade} minimum')
    408 													else:
    409 														Commands.error(chan, 'Insufficient funds.')
    410 												else:
    411 													Commands.error(chan, 'Invalid amount argument.')
    412 											else:
    413 												Commands.error(chan, 'Insufficient funds.')
    414 										else:
    415 											Commands.error(chan, 'Invalid trade pair.')
    416 								elif args[0] == '!value':
    417 									amount = args[1]
    418 									if functions.is_amount(amount, False):
    419 										amount = amount.replace(',','')
    420 										symbol = args[2].upper()
    421 										if symbol in CMC._ticker():
    422 											value = CMC._ticker()[symbol]['price']*float(amount)
    423 											if value < 0.01:
    424 												Commands.sendmsg(chan, '{0} is worth {1}'.format(color(f'{amount} {symbol}', constants.white), color('${0:,.8f}'.format(value), constants.light_blue)))
    425 											else:
    426 												Commands.sendmsg(chan, '{0} is worth {1}'.format(color(f'{amount} {symbol}', constants.white), color('${0:,.2f}'.format(value), constants.light_blue)))
    427 										else:
    428 											Commands.error(chan, 'Invalid cryptocurrency name!')
    429 									else:
    430 										Commands.error(chan, 'Invalid amount!')
    431 							elif len(args) == 4:
    432 								if args[0] == '!send':
    433 									if Commands.check_nick(nick, chan):
    434 										total = 0
    435 										for symbol in Bot.db['wallet'][nick]:
    436 											total += Bot.db['wallet'][nick]['USD'] if symbol == 'USD' else CMC._ticker()[symbol]['price']*Bot.db['wallet'][nick][symbol]
    437 										if total >= config.limits.send:
    438 											receiver = args[1].lower()
    439 											if receiver in Bot.db['wallet']:
    440 												if nick != receiver:
    441 													amount = args[2].replace(',','')
    442 													symbol = args[3].upper()
    443 													if symbol in Bot.db['wallet'][nick]:
    444 														if functions.is_amount(amount):
    445 															amount = amount.replace(',','')
    446 															if amount == '*':
    447 																amount = Bot.db['wallet'][nick][symbol]
    448 															elif amount.startswith('$'):
    449 																amount = float(amount[1:]) if symbol == 'USD' else float(amount[1:])/CMC._ticker()[symbol]['price']
    450 															else:
    451 																amount = float(amount)
    452 															usd_value = amount if symbol == 'USD' else CMC._ticker()[symbol]['price']*amount
    453 															if Bot.db['wallet'][nick][symbol] >= amount:
    454 																if usd_value >= config.limits.trade:
    455 																	recv_amount = functions.fee(amount, config.fees.send)
    456 																	if symbol in Bot.db['wallet'][receiver] or len(Bot.db['wallet'][receiver]) < config.limits.assets:
    457 																		Bot.db['wallet'][receiver][symbol] = Bot.db['wallet'][receiver][symbol]+recv_amount if symbol in Bot.db['wallet'][receiver] else recv_amount
    458 																		Bot.db['wallet'][nick][symbol] -= amount
    459 																		Commands.cleanup(nick)
    460 																		Bot.db['pool'] += usd_value-functions.fee(usd_value, config.fees.send)
    461 																		Commands.sendmsg(receiver, '{0} just sent you {1} {2}! {3}'.format(color(nick, constants.light_blue), functions.clean_float(recv_amount), symbol, color('({0})'.format(functions.clean_value(usd_value)), constants.grey)))
    462 																		Commands.sendmsg(chan, 'Sent!')
    463 																	else:
    464 																		Commands.error(chan, f'User can\'t hold more than {config.limits.assets} assets!')
    465 																else:
    466 																	Commands.error(chan, 'Invalid send amount.', f'${config.limits.trade} minimum')
    467 															else:
    468 																Commands.error(chan, 'Insufficient funds.')
    469 														else:
    470 															Commands.error(chan, 'Invalid send amount.')
    471 													else:
    472 														Commands.error(chan, 'Insufficient funds.')
    473 												else:
    474 													Commands.error(chan, '...Really?')
    475 											elif receiver in Bot.db['verify']:
    476 												Commands.error(chan, 'User is not verified yet!')
    477 											else:
    478 												Commands.error(chan, 'User is not in the database.')
    479 										else:
    480 											Commands.error(chan, 'Insufficent funds!', f'${config.limits.send} minium')
    481 					Bot.last = time.time()
    482 			except Exception as ex:
    483 				if time.time() - Bot.last < config.throttle.cmd:
    484 					if not Bot.slow:
    485 						Commands.sendmsg(chan, color('Slow down nerd!', constants.red))
    486 						Bot.slow = True
    487 				else:
    488 					Commands.error(chan, 'Command threw an exception.', ex)
    489 				Bot.last = time.time()
    490 
    491 	def nick_in_use():
    492 		config.ident.nickname = 'IRCCEX_' + str(functions.random_int(10,99))
    493 		Commands.nick(config.ident.nickname)
    494 
    495 	def handle(data):
    496 		args = data.split()
    497 		if data.startswith('ERROR :Closing Link:'):
    498 			raise Exception('Connection has closed.')
    499 		elif args[0] == 'PING':
    500 			Commands.raw('PONG ' + args[1][1:])
    501 		elif args[1] == constants.RPL_WELCOME:
    502 			Events.connect()
    503 		elif args[1] == constants.ERR_NICKNAMEINUSE:
    504 			Events.nick_in_use()
    505 		elif args[1] == constants.INVITE and len(args) == 4:
    506 			chan = args[3][1:]
    507 			Events.invite(chan)
    508 		elif args[1] == constants.KICK and len(args) >= 4:
    509 			chan   = args[2]
    510 			kicked = args[3]
    511 			Events.kick(chan, kicked)
    512 		elif args[1] == constants.PRIVMSG and len(args) >= 4:
    513 			chan = args[2]
    514 			nick = args[0].split('!')[0][1:].lower()
    515 			msg  = ' '.join(args[3:])[1:]
    516 			Events.message(nick, chan, msg)
    517 
    518 class Loops:
    519 	def start_loops():
    520 		threading.Thread(target=Loops.backup).start()
    521 		threading.Thread(target=Loops.double_fees).start()
    522 		threading.Thread(target=Loops.maintenance).start()
    523 		threading.Thread(target=Loops.remind).start()
    524 		threading.Thread(target=Loops.reward).start()
    525 		threading.Thread(target=Loops.round).start()
    526 		threading.Thread(target=Loops.verify).start()
    527 
    528 	def backup():
    529 		while True:
    530 			time.sleep(3600) # 1H
    531 			with open('db.pkl', 'wb') as db_file:
    532 				pickle.dump(Bot.db, db_file, pickle.HIGHEST_PROTOCOL)
    533 			Bot.last_backup = time.strftime('%I:%M')
    534 			print('[+] - Database backed up!')
    535 
    536 	def double_fees():
    537 		original_fees = {'cashout':config.fees.cashout,'send':config.fees.send,'trade':config.fees.trade}
    538 		while True:
    539 			try:
    540 				time.sleep(functions.random_int(604800,864000)) # 7D - 10D
    541 				config.fees.cashout = original_fees['cashout']*2
    542 				config.fees.send    = original_fees['send']*2
    543 				config.fees.trade   = original_fees['trade']*2
    544 				Commands.action(config.connection.channel, color('Double fees have been activated!', constants.red))
    545 				time.sleep(functions.random_int(86400, 259200)) # 1D - 3D
    546 				config.fees.cashout = original_fees['cashout']/2
    547 				config.fees.send    = original_fees['send']/2
    548 				config.fees.trade   = original_fees['trade']/2
    549 				Commands.notice(config.connection.channel, color('Double fees have been deactivated!', constants.red))
    550 			except Exception as ex:
    551 				config.fees.cashout = original_fees['cashout']
    552 				config.fees.send    = original_fees['send']
    553 				config.fees.trade   = original_fees['trade']
    554 				print('[!] - Error occured in the double fees loop! (' + str(ex) + ')')
    555 
    556 	def maintenance():
    557 		while True:
    558 			try:
    559 				time.sleep(functions.random_int(604800,1209600)) # 7D - 14D
    560 				Bot.maintenance = True
    561 				Commands.action(config.connection.channel, color('Exchange is going down for scheduled maintenance!', constants.red))
    562 				time.sleep(functions.random_int(3600, 86400)) # 1H - 1D
    563 				Bot.maintenance = False
    564 				Commands.notice(config.connection.channel, color('Maintenance complete! Exchange is back online!', constants.green))
    565 			except Exception as ex:
    566 				Bot.maintenance = False
    567 				print('[!] - Error occured in the maintenance loop! (' + str(ex) + ')')
    568 
    569 	def remind():
    570 		time.sleep(10)
    571 		while True:
    572 			try:
    573 				days = functions.month_days()
    574 				now = int(time.strftime('%-d'))
    575 				for dayz in (7,14,21):
    576 					if days-now == dayz:
    577 						Commands.notice(config.connection.channel, 'There is only {0} week(s) left until round {1} ends!'.format(color(str(int(dayz/7)), constants.cyan), color(str(Bot.db['round']), constants.cyan)))
    578 						break
    579 			except Exception as ex:
    580 				print('[!] - Error occured in the reminder loop! (' + str(ex) + ')')
    581 			finally:
    582 				time.sleep(86400) # 24H
    583 
    584 	def reward():
    585 		while True:
    586 			try:
    587 				time.sleep(functions.random_int(86400, 259200)) # 1D - 3D
    588 				if not Bot.reward['status'] and not Bot.maintenance:
    589 					option = functions.random_int(25,50)
    590 					Bot.reward = {'reward':Bot.db['pool']/(option*2),'rewards':option,'status':True}
    591 					Commands.notice(config.connection.channel, 'There is {1} in the reward pool. Type {2} to grab some cash stacks!'.format(color('${:,}'.format(int(Bot.db['pool'])), constants.green), color('!bang', constants.light_blue)))
    592 			except Exception as ex:
    593 				print('[!] - Error occured in the reward loop! (' + str(ex) + ')')
    594 
    595 	def round():
    596 		time.sleep(10)
    597 		while True:
    598 			try:
    599 				if time.strftime('%d') == '01':
    600 					Bot.maintenance = True
    601 					amount = 10 if len(Bot.db['bank']) >= 10 else len(Bot.db['bank'])
    602 					if amount:
    603 						clean_bank = dict()
    604 						for item in Bot.db['bank']:
    605 							clean_bank[item] = Bot.db['bank'][item][0]
    606 						richest = sorted(clean_bank, key=clean_bank.get, reverse=True)[:amount]
    607 						for nick in richest:
    608 							if nick in Bot.db['score']:
    609 								Bot.db['score'][nick] = (Bot.db['score'][nick][0]+(amount-richest.index(nick)), Bot.db['score'][nick][1]+Bot.db['bank'][nick][0])
    610 							else:
    611 								Bot.db['score'][nick] = (amount-richest.index(nick), Bot.db['bank'][nick][0])
    612 						Commands.notice(config.connection.channel, 'Round {0} is now over! Winners: {1}'.format(color(Bot.db['round'], constants.light_blue), color(', '.join(richest), constants.yellow)))
    613 					else:
    614 						Commands.notice(config.connection.channel, 'Round {0} is now over! Winners: {1}'.format(color(Bot.db['round'], constants.light_blue), color('None', constants.grey)))
    615 					Bot.db = {'bank':dict(),'pool':0.0,'round':Bot.db['round']+1,'score':Bot.db['score'],'verify':dict(),'wallet':dict()}
    616 					Commands.action(config.connection.channel, 'Round {0} starts NOW!'.format(color(Bot.db['round'], constants.light_blue)))
    617 					Bot.maintenance = False
    618 			except Exception as ex:
    619 				print('[!] - Error occured in the round loop! (' + str(ex) + ')')
    620 			finally:
    621 				time.sleep(86400) # 24H
    622 
    623 	def verify():
    624 		time.sleep(10)
    625 		while True:
    626 			try:
    627 				if Bot.db['verify']:
    628 					for nick in [nick for nick in Bot.db['verify'] if time.time() - Bot.db['verify'][nick] >= 86400]: # 1D
    629 						Bot.db['wallet'][nick] = {'USD':config.limits.init}
    630 						del Bot.db['verify'][nick]
    631 						Commands.sendmsg(nick, f'Your account is now verified! Here is ${config.limits.init:,} to start trading!')
    632 			except Exception as ex:
    633 				print('[!] - Error occured in the verify loop! (' + str(ex) + ')')
    634 			finally:
    635 				time.sleep(3600) # 1H
    636 
    637 Bot = IRC()
    638 CMC = CoinMarketCap(config.CMC_API_KEY)