blackjack- irc bot to play blackjack |
git clone git://git.acid.vegas/blackjack.git |
Log | Files | Refs | Archive | README | LICENSE |
irc.py (13519B)
1 #!/usr/bin/env python 2 # BlackJack IRC Bot - Developed by acidvegas in Python (https://acid.vegas/blackjack) 3 # irc.py 4 5 import inspect 6 import os 7 import random 8 import socket 9 import ssl 10 import threading 11 import time 12 13 import config 14 import debug 15 16 # Data Directories & Files (DO NOT EDIT) 17 data_dir = os.path.join(os.path.dirname(os.path.realpath(inspect.stack()[-1][1])), 'data') 18 cheat_file = os.path.join(data_dir, 'cheat.txt') 19 help_file = os.path.join(data_dir, 'help.txt') 20 21 # Card Types 22 club = ('♣','clubs') 23 diamond = ('♦','diamonds') 24 heart = ('♥','hearts') 25 spade = ('♠','spades') 26 27 # Deck Table (Name, ASCII, Value, Remaining Suits) 28 deck = { 29 'ace' : [None, 11, [club,diamond,heart,spade]], 30 'two' : [None, 2, [club,diamond,heart,spade]], 31 'three' : [None, 3, [club,diamond,heart,spade]], 32 'four' : [None, 4, [club,diamond,heart,spade]], 33 'five' : [None, 5, [club,diamond,heart,spade]], 34 'six' : [None, 6, [club,diamond,heart,spade]], 35 'seven' : [None, 7, [club,diamond,heart,spade]], 36 'eight' : [None, 8, [club,diamond,heart,spade]], 37 'nine' : [None, 9, [club,diamond,heart,spade]], 38 'ten' : [None, 10, [club,diamond,heart,spade]], 39 'jack' : [None, 10, [club,diamond,heart,spade]], 40 'queen' : [None, 10, [club,diamond,heart,spade]], 41 'king' : [None, 10, [club,diamond,heart,spade]] 42 } 43 44 # Formatting Control Characters / Color Codes 45 bold = '\x02' 46 italic = '\x1D' 47 underline = '\x1F' 48 reverse = '\x16' 49 reset = '\x0f' 50 white = '00' 51 black = '01' 52 blue = '02' 53 green = '03' 54 red = '04' 55 brown = '05' 56 purple = '06' 57 orange = '07' 58 yellow = '08' 59 light_green = '09' 60 cyan = '10' 61 light_cyan = '11' 62 light_blue = '12' 63 pink = '13' 64 grey = '14' 65 light_grey = '15' 66 67 def color(msg, foreground, background=None): 68 if background: 69 return '\x03{0},{1}{2}{3}'.format(foreground, background, msg, reset) 70 else: 71 return '\x03{0}{1}{2}'.format(foreground, msg, reset) 72 73 class IRC(object): 74 def __init__(self): 75 self.ace_minus = False 76 self.hand = None 77 self.last_move = 0 78 self.last_time = 0 79 self.player = None 80 self.total = 0 81 self.mini_deck = False 82 self.sock = None 83 84 def action(self, chan, msg): 85 self.sendmsg(chan, '\x01ACTION {0}\x01'.format(msg)) 86 87 def connect(self): 88 try: 89 self.create_socket() 90 self.sock.connect((config.connection.server, config.connection.port)) 91 if config.login.network: 92 self.raw('PASS ' + config.login.network) 93 self.raw('USER {0} 0 * :{1}'.format(config.ident.username, config.ident.realname)) 94 self.raw('NICK ' + config.ident.nickname) 95 except socket.error as ex: 96 debug.error('Failed to connect to IRC server.', ex) 97 self.event_disconnect() 98 else: 99 self.listen() 100 101 def create_socket(self): 102 family = socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET 103 self.sock = socket.socket(family, socket.SOCK_STREAM) 104 if config.connection.vhost: 105 self.sock.bind((config.connection.vhost, 0)) 106 if config.connection.ssl: 107 self.sock = ssl.wrap_socket(self.sock) 108 109 def draw(self): 110 card_type = random.choice(list(deck.keys())) 111 remaining = deck[card_type][2] 112 while not remaining: 113 card_type = random.choice(list(deck.keys())) 114 remaining = deck[card_type][2] 115 card_suit = random.choice(remaining) 116 if card_suit in (heart,diamond): 117 card_color = red 118 else: 119 card_color = black 120 card_value = deck[card_type][1] 121 if self.mini_deck: 122 card = deck[card_type][0].replace('X', card_suit[0]) 123 card = color(card, card_color, white) 124 self.hand.append(card) 125 else: 126 for i in range(5): 127 card = deck[card_type][0][i].replace('X', card_suit[0]) 128 card = color(card, card_color, white) 129 self.hand[i].append(card) 130 deck[card_type][2].remove(card_suit) 131 self.total += card_value 132 if card_type == 'ace' and deck['ace'][1] != 1: 133 deck['ace'][1] = 1 134 return (card_type, card_suit) 135 136 def error(self, chan, msg, reason=None): 137 if reason: 138 self.sendmsg(chan, '[{0}] {1} {2}'.format(color('ERROR', red), msg, color('({0})'.format(str(reason)), grey))) 139 else: 140 self.sendmsg(chan, '[{0}] {1}'.format(color('ERROR', red), msg)) 141 142 def event_connect(self): 143 self.setup_deck('normal') 144 if config.login.nickserv: 145 self.identify(self.username, config.login.nickserv) 146 if config.login.operator: 147 self.oper(config.ident.username, config.login.operator) 148 self.join(config.connection.channel, config.connection.key) 149 150 def event_disconnect(self): 151 self.sock.close() 152 self.reset() 153 time.sleep(10) 154 self.connect() 155 156 def event_kick(self, nick, chan, kicked): 157 if kicked == config.ident.nickname and chan == config.connection.channel: 158 time.sleep(3) 159 self.join(config.connection.channel, config.connection.key) 160 161 def event_message(self, nick, chan, msg): 162 if chan == config.connection.channel: 163 if not msg.startswith('.'): 164 if msg == '@help': 165 self.action(chan, 'Sending help in a private message...') 166 help = [line.strip() for line in open(help_file).readlines() if line] 167 for line in help: 168 self.sendmsg(chan, line) 169 elif msg == '@cheat': 170 self.action(chan, 'Sending cheat sheet in a private message...') 171 cheat_sheet = [line.strip() for line in open(cheat_file).readlines() if line] 172 for line in cheat_sheet: 173 self.sendmsg(chan, line) 174 else: 175 cmd = msg.split()[0][1:] 176 args = msg[len(cmd)+2:] 177 if time.time() - self.last_time < 2: 178 self.sendmsg(chan, color('Slow down nerd!', red)) 179 elif cmd == 'hit': 180 if self.player: 181 if self.player == nick: 182 card_type, card_suit = self.draw() 183 if self.mini_deck: 184 msg_str = '' 185 for i in self.hand: 186 msg_str += ' ' + i 187 self.sendmsg(chan, msg_str) 188 else: 189 for i in range(5): 190 msg_str = '' 191 for i in self.hand[i]: 192 msg_str += ' ' + i 193 self.sendmsg(chan, msg_str) 194 if self.total > 21: 195 if deck['ace'][1] == 1 and not self.ace_minus: 196 self.total = self.total - 10 197 self.ace_minus = True 198 if self.total > 21: 199 self.sendmsg(chan, '{0} {1}'.format(color('BUST!', red), color('You went over 21 and lost!', grey))) 200 self.reset() 201 else: 202 self.sendmsg(chan, '{0} {1}'.format(color('You drew a {0} of {1}! Your total is now:'.format(card_type, card_suit[1]), yellow), color(str(self.total), light_blue))) 203 self.last_move = time.time() 204 else: 205 self.sendmsg(chan, '{0} {1}'.format(color('BUST!', red), color('You went over 21 and lost!', grey))) 206 self.reset() 207 else: 208 self.sendmsg(chan, '{0} {1}'.format(color('You drew a {0} of {1}! Your total is now:'.format(card_type, card_suit[1]), yellow), color(str(self.total), light_blue))) 209 self.last_move = time.time() 210 else: 211 self.error(chan, 'You are not currently playing!', '{0} is playing still'.format(self.player)) 212 else: 213 self.error(chan, 'You are not currently playing!') 214 elif cmd == 'mini': 215 if not self.player: 216 if self.mini_deck: 217 self.setup_deck('normal') 218 self.sendmsg(chan, '{0} {1}'.format(color('Mini deck has been', yellow), color('DISABLED', red))) 219 else: 220 self.setup_deck('mini') 221 self.sendmsg(chan, '{0} {1}'.format(color('Mini deck has been', yellow), color('ENABLED', green))) 222 else: 223 self.error(chan, 'You can not change the deck in game!') 224 elif cmd == 'play': 225 if not self.player: 226 self.player = nick 227 self.action(chan, 'Starting a game of blackjack with {0}!'.format(nick)) 228 for i in range(2): 229 self.draw() 230 if self.mini_deck: 231 msg_str = '' 232 for i in self.hand: 233 msg_str += ' ' + i 234 self.sendmsg(chan, msg_str) 235 else: 236 for i in range(5): 237 msg_str = '' 238 for i in self.hand[i]: 239 msg_str += ' ' + i 240 self.sendmsg(chan, msg_str) 241 self.sendmsg(chan, '{0} {1}'.format(color('Your total is now:', yellow), color(str(self.total), light_blue))) 242 self.last_move = time.time() 243 threading.Thread(target=self.timer).start() 244 elif self.player == nick: 245 self.error(chan, 'You have already started a game, please finish or stop the game!'.format(self.player)) 246 else: 247 self.error(chan, '{0} is currently playing a game, please wait!'.format(self.player)) 248 elif cmd == 'stand': 249 if self.player: 250 if self.player == nick: 251 self.sendmsg(chan, 'You have chosen to stand with {0} as your total.'.format(self.total)) 252 else: 253 self.error(chan, 'You are not currently playing!', '{0} is playing still'.format(self.player)) 254 else: 255 self.error(chan, 'You are not currently playing!') 256 elif cmd == 'stop': 257 if self.player: 258 if self.player == nick: 259 self.action(chan, 'Ending current game with {0}!'.format(nick)) 260 self.reset() 261 else: 262 self.error(chan, 'You are not currently playing!', '{0} is playing still'.format(self.player)) 263 else: 264 self.error(chan, 'You are not currently playing!') 265 self.last_time = time.time() 266 267 def event_nick_in_use(self): 268 debug.error_exit('BlackJack is already running.') 269 270 def event_part(self, nick, chan): 271 if self.player == nick: 272 self.sendmsg(chan, 'The game with {0} has ended.'.format(color(self.nick, light_blue))) 273 self.reset() 274 275 def event_quit(self, nick): 276 if self.player == nick: 277 self.sendmsg(chan, 'The game with {0} has ended.'.format(color(self.nick, light_blue))) 278 self.reset() 279 280 def handle_events(self, data): 281 args = data.split() 282 if args[0] == 'PING': 283 self.raw('PONG ' + args[1][1:]) 284 elif args[1] == '001': # Use 002 or 003 if you run into issues. 285 self.event_connect() 286 elif args[1] == '433': 287 self.event_nick_in_use() 288 elif args[1] in ('KICK','PART','PRIVMSG','QUIT'): 289 nick = args[0].split('!')[0][1:] 290 if nick != config.ident.nickname: 291 if args[1] == 'KICK': 292 chan = args[2] 293 kicked = args[3] 294 self.event_kick(nick, chan, kicked) 295 elif args[1] == 'PART': 296 chan = args[2] 297 self.event_part(nick, chan) 298 elif args[1] == 'PRIVMSG': 299 chan = args[2] 300 msg = data.split('{0} PRIVMSG {1} :'.format(args[0], chan))[1] 301 if chan != config.ident.nickname: 302 self.event_message(nick, chan, msg) 303 elif args[1] == 'QUIT': 304 self.event_quit(nick) 305 306 def identify(self, username, password): 307 self.sendmsg('nickserv', f'identify {username} {password}') 308 309 def join(self, chan, key=None): 310 self.raw(f'JOIN {chan} {key}') if key else self.raw('JOIN ' + chan) 311 312 def listen(self): 313 while True: 314 try: 315 data = self.sock.recv(1024).decode('utf-8') 316 if data: 317 for line in (line for line in data.split('\r\n') if line): 318 debug.irc(line) 319 if line.startswith('ERROR :Closing Link:') and config.ident.nickname in data: 320 raise Exception('Connection has closed.') 321 elif len(line.split()) >= 2: 322 self.handle_events(line) 323 else: 324 debug.error('No data recieved from server.') 325 break 326 except (UnicodeDecodeError,UnicodeEncodeError): 327 debug.error('Unicode error has occured.') 328 except Exception as ex: 329 debug.error('Unexpected error occured.', ex) 330 break 331 self.event_disconnect() 332 333 def mode(self, target, mode): 334 self.raw(f'MODE {target} {mode}') 335 336 def raw(self, msg): 337 self.sock.send(bytes(msg + '\r\n', 'utf-8')) 338 339 def reset(self): 340 self.ace = [False,False] 341 self.last_move = 0 342 self.player = None 343 self.total = 0 344 if self.mini_deck: 345 self.hand = [] 346 else: 347 self.hand = {0:[],1:[],2:[],3:[],4:[]} 348 deck['ace'][1] = 11 349 for card in deck: 350 deck[card][2] = [club,diamond,heart,spade] 351 352 def sendmsg(self, target, msg): 353 self.raw(f'PRIVMSG {target} :{msg}') 354 355 def setup_deck(self, deck_type): 356 if deck_type == 'mini': 357 self.hand = [] 358 self.mini_deck = True 359 deck['ace'][0] = 'A X' 360 deck['two'][0] = '2 X' 361 deck['three'][0] = '3 X' 362 deck['four'][0] = '4 X' 363 deck['five'][0] = '5 X' 364 deck['six'][0] = '6 X' 365 deck['seven'][0] = '7 X' 366 deck['eight'][0] = '8 X' 367 deck['nine'][0] = '9 X' 368 deck['ten'][0] = '10X' 369 deck['jack'][0] = 'J X' 370 deck['queen'][0] = 'Q X' 371 deck['king'][0] = 'K X' 372 elif deck_type == 'normal': 373 self.hand = {0:[],1:[],2:[],3:[],4:[]} 374 self.mini_deck = False 375 deck['ace'][0] = ('A ',' ',' X ',' ',' A') 376 deck['two'][0] = ('2 ',' X ',' ',' X ',' 2') 377 deck['three'][0] = ('3 ',' X ',' X ',' X ',' 3') 378 deck['four'][0] = ('4 ',' X X ',' ',' X X ',' 4') 379 deck['five'][0] = ('5 ',' X X ',' X ',' X X ',' 5') 380 deck['six'][0] = ('6 ',' X X ',' X X ',' X X ',' 6') 381 deck['seven'][0] = ('7 ',' X X ',' XXX ',' X X ',' 7') 382 deck['eight'][0] = ('8 ',' XXX ',' X X ',' XXX ',' 8') 383 deck['nine'][0] = ('9 ',' XXX ',' XXX ',' XXX ',' 9') 384 deck['ten'][0] = ('10 ',' XXX ',' XX XX ',' XXX ',' 10') 385 deck['jack'][0] = ('J ',' ',' X ',' ',' J') 386 deck['queen'][0] = ('Q ',' ',' X ',' ',' Q') 387 deck['king'][0] = ('K ',' ',' X ',' ',' K') 388 389 def timer(self): 390 while self.player: 391 if time.time() - self.last_move > self.game_timeout: 392 self.sendmsg(config.connection.channel, '{0}, you took too long! The game has ended.'.format(self.player)) 393 self.reset() 394 break 395 else: 396 time.sleep(1) 397 398 BlackJack = IRC()