archive- Random tools & helpful resources for IRC |
git clone git://git.acid.vegas/archive.git |
Log | Files | Refs | Archive |
iex.py (11213B)
1 #!/usr/bin/env python 2 # IExTrading IRC Bot - Developed by acidvegas in Python (https://acid.vegas/iex) 3 4 import http.client 5 import json 6 import random 7 import ssl 8 import socket 9 import time 10 11 # Connection 12 server = 'irc.supernets.org' 13 channel = '#dev' 14 15 # Identity 16 nickname = 'StockMarket' 17 username = 'iex' 18 realname = 'acid.vegas/iex' 19 20 # Login 21 nickserv_password = None 22 network_password = None 23 24 # Settings 25 throttle_cmd = 3 26 throttle_msg = 0.5 27 user_modes = None 28 29 # Formatting Control Characters / Color Codes 30 bold = '\x02' 31 italic = '\x1D' 32 underline = '\x1F' 33 reverse = '\x16' 34 reset = '\x0f' 35 white = '00' 36 black = '01' 37 blue = '02' 38 green = '03' 39 red = '04' 40 brown = '05' 41 purple = '06' 42 orange = '07' 43 yellow = '08' 44 light_green = '09' 45 cyan = '10' 46 light_cyan = '11' 47 light_blue = '12' 48 pink = '13' 49 grey = '14' 50 light_grey = '15' 51 52 def condense_value(value): 53 value = float(value) 54 if value < 0.01: 55 return '${0:,.8f}'.format(value) 56 elif value < 24.99: 57 return '${0:,.2f}'.format(value) 58 else: 59 return '${:,}'.format(int(value)) 60 61 def debug(msg): 62 print(f'{get_time()} | [~] - {msg}') 63 64 def error(msg, reason=None): 65 if reason: 66 print(f'{get_time()} | [!] - {msg} ({reason})') 67 else: 68 print(f'{get_time()} | [!] - {msg}') 69 70 def error_exit(msg): 71 raise SystemExit(f'{get_time()} | [!] - {msg}') 72 73 def get_float(data): 74 try: 75 float(data) 76 return True 77 except ValueError: 78 return False 79 80 def get_time(): 81 return time.strftime('%I:%M:%S') 82 83 def percent_color(percent): 84 percent = float(percent) 85 if percent == 0.0: 86 return grey 87 elif percent < 0.0: 88 if percent > -10.0: 89 return brown 90 else: 91 return red 92 else: 93 if percent < 10.0: 94 return green 95 else: 96 return light_green 97 98 def random_int(min, max): 99 return random.randint(min, max) 100 101 class IEX: 102 def api(api_data): 103 conn = http.client.HTTPSConnection('api.iextrading.com', timeout=15) 104 conn.request('GET', '/1.0/' + api_data) 105 response = conn.getresponse().read().decode('utf-8') 106 data = json.loads(response) 107 conn.close() 108 return data 109 110 def company(symbol): 111 return IEX.api(f'stock/{symbol}/company') 112 113 def lists(list_type): 114 return IEX.api('stock/market/list/' + list_type) 115 116 def news(symbol): 117 return IEX.api(f'stock/{symbol}/news') 118 119 def quote(symbols): 120 data = IEX.api(f'stock/market/batch?symbols={symbols}&types=quote') 121 if len(data) == 1: 122 return data[next(iter(data))]['quote'] 123 else: 124 return [data[item]['quote'] for item in data] 125 126 def stats(symbol): 127 return IEX.api(f'stock/{symbol}/stats') 128 129 def symbols(): 130 return IEX.api('ref-data/symbols') 131 132 class IRC(object): 133 def __init__(self): 134 self.last = 0 135 self.slow = False 136 self.sock = None 137 138 def stock_info(self, data): 139 sep = self.color('|', grey) 140 sep2 = self.color('/', grey) 141 name = '{0} ({1})'.format(self.color(data['companyName'], white), data['symbol']) 142 value = condense_value(data['latestPrice']) 143 percent = self.color('{:,.2f}%'.format(float(data['change'])), percent_color(data['change'])) 144 volume = '{0} {1}'.format(self.color('Volume:', white), '${:,}'.format(data['avgTotalVolume'])) 145 cap = '{0} {1}'.format(self.color('Market Cap:', white), '${:,}'.format(data['marketCap'])) 146 return f'{name} {sep} {value} ({percent}) {sep} {volume} {sep} {cap}' 147 148 def stock_matrix(self, data): # very retarded way of calculating the longest strings per-column 149 results = {'symbol':list(),'value':list(),'percent':list(),'volume':list(),'cap':list()} 150 for item in data: 151 results['symbol'].append(item['symbol']) 152 results['value'].append(condense_value(item['latestPrice'])) 153 results['percent'].append('{:,.2f}%'.format(float(item['change']))) 154 results['volume'].append('${:,}'.format(item['avgTotalVolume'])) 155 results['cap'].append('${:,}'.format(item['marketCap'])) 156 for item in results: 157 results[item] = len(max(results[item], key=len)) 158 if results['symbol'] < len('Symbol'): 159 results['symbol'] = len('Symbol') 160 if results['value'] < len('Value'): 161 results['value'] = len('Value') 162 if results['percent'] < len('Change'): 163 results['percent'] = len('Change') 164 if results['volume'] < len('Volume'): 165 results['volume'] = len('Volume') 166 if results['cap'] < len('Market Cap'): 167 results['cap'] = len('Market Cap') 168 return results 169 170 def stock_table(self, data): 171 matrix = self.stock_matrix(data) 172 header = self.color(' {0} {1} {2} {3} {4}'.format('Symbol'.center(matrix['symbol']), 'Value'.center(matrix['value']), 'Percent'.center(matrix['percent']), 'Volume'.center(matrix['volume']), 'Market Cap'.center(matrix['cap'])), black, light_grey) 173 lines = [header,] 174 for item in data: 175 symbol = item['symbol'].ljust(matrix['symbol']) 176 value = condense_value(item['latestPrice']).rjust(matrix['value']) 177 percent = self.color('{:,.2f}%'.format(float(item['change'])).rjust(matrix['percent']), percent_color(item['change'])) 178 volume = '${:,}'.format(item['avgTotalVolume']).rjust(matrix['volume']) 179 cap = '${:,}'.format(item['marketCap']).rjust(matrix['cap']) 180 lines.append(' {0} | {1} | {2} | {3} | {4} '.format(symbol,value,percent,volume,cap)) 181 return lines 182 183 def color(self, msg, foreground, background=None): 184 if background: 185 return f'\x03{foreground},{background}{msg}{reset}' 186 else: 187 return f'\x03{foreground}{msg}{reset}' 188 189 def connect(self): 190 try: 191 self.sock = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) 192 self.sock.connect((server, 6697)) 193 self.raw(f'USER {username} 0 * :{realname}') 194 self.nick(nickname) 195 except socket.error as ex: 196 error('Failed to connect to IRC server.', ex) 197 self.event_disconnect() 198 else: 199 self.listen() 200 201 def error(self, chan, msg, reason=None): 202 if reason: 203 self.sendmsg(chan, '[{0}] {1} {2}'.format(self.color('!', red), msg, self.color('({0})'.format(reason), grey))) 204 else: 205 self.sendmsg(chan, '[{0}] {1}'.format(self.color('!', red), msg)) 206 207 def event_connect(self): 208 if user_modes: 209 self.mode(nickname, '+' + user_modes) 210 if nickserv_password: 211 self.identify(nickname, nickserv_password) 212 self.join_channel(channel) 213 214 def event_disconnect(self): 215 self.sock.close() 216 time.sleep(10) 217 self.connect() 218 219 def event_kick(self, chan, kicked): 220 if chan == channel and kicked == nickname: 221 time.sleep(3) 222 self.join_channel(channel, key) 223 224 def event_message(self, nick, chan, msg): 225 #try: 226 if msg[:1] in '@!': 227 if time.time() - self.last < throttle_cmd: 228 if not self.slow: 229 self.error(chan, 'Slow down nerd!') 230 self.slow = True 231 else: 232 args = msg.split() 233 if msg == '@iex': 234 self.sendmsg(chan, bold + 'IExTrading IRC Bot - Developed by acidvegas in Python - https://acid.vegas/iex') 235 elif args[0] == '!stock': 236 if len(args) == 2: 237 symbols = args[1].upper() 238 if ',' in symbols: 239 symbols = ','.join(list(symbols.split(','))[:10]) 240 data = IEX.quote(symbols) 241 if type(data) == dict: 242 self.sendmsg(chan, self.stock_info(data)) 243 elif type(data) == list: 244 for line in self.stock_table(data): 245 self.sendmsg(chan, line) 246 time.sleep(throttle_msg) 247 else: 248 self.error(chan, 'Invalid stock names!') 249 else: 250 symbol = args[1] 251 data = IEX.quote(symbol) 252 if data: 253 self.sendmsg(chan, self.stock_info(data)) 254 else: 255 self.error(chan, 'Invalid stock name!') 256 elif len(args) == 3: 257 if args[1] == 'company': 258 symbol = args[2] 259 data = IEX.company(symbol) 260 if data: 261 self.sendmsg(chan, '{0} {1} ({2}) {3} {4} {5} {6} {7} {8}'.format(self.color('Company:', white), data['companyName'], data['symbol'], self.color('|', grey), data['website'], self.color('|', grey), data['industry'], self.color('|', grey), data['CEO'])) 262 self.sendmsg(chan, '{0} {1}'.format(self.color('Description:', white), data['description'])) 263 else: 264 self.error('Invalid stock name!') 265 elif args[1] == 'search': 266 query = args[2].lower() 267 data = [{'symbol':item['symbol'],'name':item['name']} for item in IEX.symbols() if query in item['name'].lower()] 268 if data: 269 count = 1 270 max_length = len(max([item['name'] for item in data], key=len)) 271 for item in data[:10]: 272 self.sendmsg(chan, '[{0}] {1} {2} {3}'.format(self.color(str(count), pink), item['name'].ljust(max_length), self.color('|', grey), item['symbol'])) 273 count += 1 274 time.sleep(throttle_msg) 275 else: 276 self.error(chan, 'No results found.') 277 elif args[1] == 'list': 278 options = {'active':'mostactive','gainers':'gainers','losers':'losers','volume':'iexvolume','percent':'iexpercent'} 279 option = args[2] 280 try: 281 option = options[option] 282 except KeyError: 283 self.error(chan, 'Invalid option!', 'Valid options are active, gainers, losers, volume, & percent') 284 else: 285 data = IEX.lists(option) 286 for line in self.stock_table(data): 287 self.sendmsg(chan, line) 288 time.sleep(throttle_msg) 289 elif args[1] == 'news': 290 symbol = args[2] 291 data = IEX.news(symbol) 292 if data: 293 count = 1 294 for item in data: 295 self.sendmsg(chan, '[{0}] {1}'.format(self.color(str(count), pink), item['headline'])) 296 self.sendmsg(chan, ' - ' + self.color(item['url'], grey)) 297 count += 1 298 time.sleep(throttle_msg) 299 else: 300 self.error(chan, 'Invalid stock name!') 301 self.last = time.time() 302 #except Exception as ex: 303 # self.error(chan, 'Unknown error occured!', ex) 304 305 def event_nick_in_use(self): 306 self.nick('IEX_' + str(random_int(10,99))) 307 308 def handle_events(self, data): 309 args = data.split() 310 if data.startswith('ERROR :Closing Link:'): 311 raise Exception('Connection has closed.') 312 elif args[0] == 'PING': 313 self.raw('PONG ' + args[1][1:]) 314 elif args[1] == '001': 315 self.event_connect() 316 elif args[1] == '433': 317 self.event_nick_in_use() 318 elif args[1] == 'KICK': 319 chan = args[2] 320 kicked = args[3] 321 self.event_kick(nick, chan, kicked) 322 elif args[1] == 'PRIVMSG': 323 nick = args[0].split('!')[0][1:] 324 chan = args[2] 325 msg = ' '.join(args[3:])[1:] 326 if chan == channel: 327 self.event_message(nick, chan, msg) 328 329 def identify(self, nick, passwd): 330 self.sendmsg('nickserv', f'identify {nick} {passwd}') 331 332 def join_channel(self, chan, key=None): 333 self.raw(f'JOIN {chan} {key}') if key else self.raw('JOIN ' + chan) 334 335 def listen(self): 336 while True: 337 try: 338 data = self.sock.recv(1024).decode('utf-8') 339 for line in (line for line in data.split('\r\n') if line): 340 debug(line) 341 if len(line.split()) >= 2: 342 self.handle_events(line) 343 except (UnicodeDecodeError,UnicodeEncodeError): 344 pass 345 #except Exception as ex: 346 # error('Unexpected error occured.', ex) 347 # break 348 self.event_disconnect() 349 350 def mode(self, target, mode): 351 self.raw(f'MODE {target} {mode}') 352 353 def nick(self, nick): 354 self.raw('NICK ' + nick) 355 356 def raw(self, msg): 357 self.sock.send(bytes(msg + '\r\n', 'utf-8')) 358 359 def sendmsg(self, target, msg): 360 self.raw(f'PRIVMSG {target} :{msg}') 361 362 IRC().connect()