async def on_reaction_add(self, reaction, user): ch = reaction.message.channel if (ch not in config.C) or (not config.C[ch]['started']): return czar = config.C[ch]['players'][config.C[ch]['pov']] letters = [ '\U0001F1E6', '\U0001F1E7', '\U0001F1E8', '\U0001F1E9', '\U0001F1EA', '\U0001F1EB', '\U0001F1EC', '\U0001F1ED', '\U0001F1EE', '\U0001F1EF' ][:config.C[ch]['nPlayers'] - 1] if config.done(ch) and config.C[ch][ 'msg'] != None and reaction.message.content == config.C[ch][ 'msg'].content and czar == user: if reaction.emoji in letters: try: p = config.C[ch]['mid'][letters.index(reaction.emoji)][1] config.C[ch]['score'][p] += 1 msg = czar.display_name + ' selected ' + 'ABCDEFGHIJ'[ letters.index(reaction.emoji)] + '.\n' msg += config.C[ch]['players'][ p].display_name + ' wins the round!' await self.client.send_message(ch, msg) await self.pass_(ch) if config.C[ch]['win'] in config.C[ch]['score']: await self.displayWinners(ch) await config.reset(ch) except: print('\nError with answer selection\n' + time.asctime() + '\n')
async def play(self, ch, p, s): s = s.strip().replace(' ', '').replace(',', '').replace('<', '').replace('>', '') # check that player is in current game try: player = config.C[ch]['players'].index(p) except: return if config.C[ch]['played'][player] or player == config.C[ch]['pov']: return try: newMid = [] for x in s: card = config.C[ch]['hands'][player]['abcdefghijkl'.index(x)] if card in newMid: return # check for blank if card == '': await self.client.send_message( p, "You tried to send a blank card without filling it in first!\nMessage me what you'd like to put in the blank." ) return newMid.append(card) if len(newMid) == config.nCards(ch): config.C[ch]['mid'].append([newMid, player]) for c in newMid: #config.C[ch]['white'].append(c) config.C[ch]['hands'][player].remove(c) config.C[ch]['played'][player] = True await self.client.send_message(ch, p.display_name + ' has played!') else: await self.client.send_message( p, 'This prompt requires ' + str(config.nCards(ch)) + ' white card(s).') return # update kicks for k in range(len(config.C[ch]['kick'])): if config.C[ch]['kick'][k] == p.mention: config.C[ch]['kick'][k] = '' # all players are done if config.done(ch): random.shuffle(config.C[ch]['mid']) config.C[ch]['time'] = time.time() await self.displayMid(ch) except: pass
async def removePlayer(self, ch, p, kick=False): if p in config.C[ch]['players']: i = config.C[ch]['players'].index(p) if config.C[ch]['played'][i]: await self.client.send_message( ch, 'You have already played your cards and may not leave.') return for s in ['players', 'played', 'hands', 'score', 'kick']: config.C[ch][s].pop(i) if i < config.C[ch]['pov']: config.C[ch]['pov'] -= 1 config.C[ch]['nPlayers'] = len(config.C[ch]['players']) config.C[ch]['pov'] %= config.C[ch]['nPlayers'] # update cards already in the middle for j in range(len(config.C[ch]['mid'])): if config.C[ch]['mid'][j][1] > i: config.C[ch]['mid'][j][1] -= 1 # all players are done if config.done(ch): random.shuffle(config.C[ch]['mid']) if not kick: await self.client.send_message( ch, p.display_name + ' has left the game.') await self.displayMid(ch) if config.C[ch][ 'nPlayers'] < 2: # number of players has been reduced to 1 await config.reset(ch) await self.client.send_message( ch, 'Not enough players, game has been reset.')
async def timer_check(self): await self.client.wait_until_ready() while not self.client.is_closed: start_time = time.time() channels = list(config.C.keys()) for ch in channels: if config.C[ch]['started'] and 'time' in config.C[ch]: if config.C[ch]['timer'] != 0 and time.time( ) - config.C[ch]['time'] >= config.C[ch]['timer']: if config.done(ch): try: # pick random letter l = len(config.C[ch]['mid']) if l == 0: return else: letter = random.randint(0, l - 1) p = config.C[ch]['mid'][letter][1] config.C[ch]['score'][p] += 1 msg = "**TIME'S UP!**\nThe bot randomly selected " + 'ABCDEFGHIJ'[ letter] + '.\n' msg += config.C[ch]['players'][ p].display_name + ' wins the round!' await self.client.send_message(ch, msg) await self.pass_(ch) if config.C[ch]['win'] in config.C[ch][ 'score']: await self.displayWinners(ch) await config.reset(ch) except Exception as e: print('Error at', time.asctime()) print(e) # unknown channel/missing access if 'unknown' in str(e).lower( ) or 'missing' in str(e).lower(): config.C.pop(ch) else: await self.client.send_message( ch, "**TIME'S UP!**\nFor those who haven't played, cards will be automatically selected." ) N = config.nCards(ch) for p in range(len(config.C[ch]['players'])): if not config.C[ch]['played'][ p] and config.C[ch]['pov'] != p: cards = '' for c in range( len(config.C[ch]['hands'][p])): if config.C[ch]['hands'][p][c]: cards += 'abcdefghijkl'[c] if len(cards) == N: await self.play( ch, config.C[ch]['players'][p], cards) break # debug elapsed_time = time.time() - start_time if elapsed_time > 2: print('timer_check: {0}'.format(time.time() - start_time)) #print('number of channels: {0}'.format(len(channels))) await asyncio.sleep(2)
async def on_message(self, message): #if (time.time() / 3600) - last_update > 1: # await self.client.change_presence(game=discord.Game(name='on '+'_'*4+' servers. ' + str(len(client.servers))+'.')) # config.last_update = time.time() / 3600 msg = message.content.lower() ch = message.channel au = message.author c = config.pre[ch.id] if ch.id in config.pre else 'c' # ignore own messages if au.id == '429024440060215296': return # fill in blank cards if self.shard == 0: if ch.is_private: # check for c!p or c!play if len(msg) > 3 and msg[:3] == c + '!p': await self.client.send_message( au, 'Please play your card(s) in the corresponding channel and not as a private message.' ) return found = False if au in config.P: # check that user has a blank card for c in config.P[ au]: # check all channels that user is in if config.C[c]['started'] and au in config.C[c][ 'players']: # check that user is currently playing i = config.C[c]['players'].index(au) if '' in config.C[c]['hands'][ i]: # check that player has a blank found = True j = config.C[c]['hands'][i].index('') config.C[c][ 'hands'][i][j] = message.content.replace( '*', '\*').replace('_', '\_').replace( '~', '\~').replace('`', '\`') await self.sendHand(c, i) break if not found: edited_msg = message.content.replace('*', '\*').replace( '_', '\_').replace('~', '\~').replace('`', '\`') for i in range(1, self.num_shards): async with aiosqlite.connect( 'messages{0}.db'.format(i)) as conn: C = await conn.cursor() await C.execute( """insert into Messages values (?, ?)""", (au.id, edited_msg)) await conn.commit() return # ignore irrelevant messages if not (len(msg) > 2 and msg[:2] == c + '!'): return if ch not in config.C: config.C[ch] = {} await config.initChannel(ch) # warning if len( msg ) > 9 and msg[:9] == c + '!warning' and au.id == '252249185112293376': for x in config.C: if config.C[x]['started']: await self.client.send_message(x, message.content[9:]) # check number of ongoing games if msg == c + '!ongoing' and au.id == '252249185112293376': nC = 0 for x in config.C: if config.C[x]['started']: nC += 1 await self.client.send_message(ch, str(nC)) # save state if (msg == c + '!save' or msg == c + '!savestate') and au.id == '252249185112293376': self.save_state() # changelog if msg == c + '!whatsnew' or msg == c + '!update' or msg == c + '!updates': s = info.changelog await self.client.send_message(ch, s[:s.index('**9/27')]) # commands list if msg == c + '!commands' or msg == c + '!command': await self.client.send_message(ch, info.commands) # support server if msg == c + '!support' or msg == c + '!server' or msg == c + '!supp': await self.client.send_message(ch, 'https://discord.gg/qGjRSYQ') # FAQ if msg == c + '!faq': await self.client.send_message(ch, 'https://goo.gl/p9j2ve') # invite link if msg == c + '!invite': await self.client.send_message( ch, 'Use this link to invite the bot to your own server:\n<https://discordapp.com/api/oauth2/authorize?client_id=429024440060215296&permissions=134294592&scope=bot>' ) # vote link if msg == c + '!vote': await self.client.send_message( ch, 'Use this link to vote for the bot on discordbots.org:\n<https://discordbots.org/bot/429024440060215296/vote>' ) # shard if msg == c + '!shard': await self.client.send_message(ch, str(self.shard)) # custom prefix setting if len(msg ) == 10 and msg[:9] == c + '!prefix ' and 'a' <= msg[9] <= 'z': await self.client.send_message( ch, 'Command prefix set to `' + msg[9] + '`!') config.pre[ch.id] = msg[9] with open('prefix.txt', 'a') as f: f.write(ch.id + ' ' + msg[9] + '\n') if not config.C[ch]['started']: # language change for l in config.languages: if msg == c + '!language ' + l.lower(): if config.C[ch]['lang'] != l: config.C[ch]['lang'] = l config.C[ch]['black'] = list( eval('config.black_' + config.languages[l])) config.C[ch]['white'] = list( eval('config.white_' + config.languages[l])) await self.client.send_message( ch, 'Language changed to ' + l + '.') await self.edit_start_msg(ch) if msg == c + '!help': await self.client.send_message(ch, ( "Use `{0}!start` to start a game of Cards Against Humanity, or `{0}!cancel` to cancel an existing one.\n" "Use `{0}!commands` to bring up a list of available commands.\n" "For a list of frequently asked questions and general directions, use `{0}!faq`." ).format(c)) elif msg == c + '!language english': if config.C[ch]['lang'] != 'English': config.C[ch]['lang'] = 'English' config.C[ch]['black'] = config.black config.C[ch]['white'] = config.white await self.client.send_message( ch, 'Language changed to English.') await self.edit_start_msg(ch) elif msg == c + '!start': if not config.C[ch]['playerMenu']: config.C[ch]['playerMenu'] = True config.C[ch]['players'].append(au) s = await self.get_start_msg(ch) config.C[ch]['msg'] = await self.client.send_message(ch, s) output = str(len( config.C[ch]['players'])) + '/20 Players: ' output += ', '.join(usr.display_name for usr in config.C[ch]['players']) await self.client.send_message(ch, output) elif 2 <= len(config.C[ch]['players']) <= 20: config.C[ch]['playerMenu'] = False config.C[ch]['nPlayers'] = len(config.C[ch]['players']) await self.start_(ch) await self.client.send_message( ch, 'Game is starting!\n' + ' '.join(usr.mention for usr in config.C[ch]['players'])) config.C[ch]['msg'] = None await self.displayMid(ch) elif len(msg) > 8 and msg[:8] == c + '!setwin': try: n = int(msg[8:].strip()) if n > 0: config.C[ch]['win'] = n await self.client.send_message( ch, 'Number of points needed to win has been set to ' + str(n) + '.') await self.edit_start_msg(ch) except: pass elif len(msg) > 7 and msg[:7] == c + '!timer': try: n = int(msg[7:].strip()) if n >= 15: config.C[ch]['timer'] = n await self.client.send_message( ch, 'Idle timer set to ' + str(n) + ' seconds.') await self.edit_start_msg(ch) elif n == 0: config.C[ch]['timer'] = n await self.client.send_message( ch, 'Idle timer is now disabled.') await self.edit_start_msg(ch) else: await self.client.send_message( ch, 'Please choose a minimum of 15 seconds.') except: pass elif len(msg) > 10 and msg[:10] == c + '!settimer': try: n = int(msg[10:].strip()) if n >= 15: config.C[ch]['timer'] = n await self.client.send_message( ch, 'Idle timer set to " + str(n) + " seconds.') await self.edit_start_msg(ch) elif n == 0: config.C[ch]['timer'] = n await self.client.send_message( ch, 'Idle timer is now disabled.') await self.edit_start_msg(ch) else: await self.client.send_message( ch, 'Please choose a minimum of 15 seconds.') except: pass elif len(msg) > 10 and msg[:10] == c + '!setblank': try: n = int(msg[10:].strip()) if 0 <= n <= 30: config.C[ch]['blanks'] = n await self.client.send_message( ch, 'Number of blanks has been set to ' + str(n) + '.') await self.edit_start_msg(ch) except: pass elif msg == c + '!packs': output = ( "**List of available packs:**\n" "(pack code followed by name of pack, then number of black and white cards)\n" "----------\n") for p in config.packs: output += '**'+p+'** - ' + config.packs[p] \ + ' (' + str(len(eval('config.black_'+p))) + '/' + str(len(eval('config.white_'+p))) + ')\n' await self.client.send_message(ch, output) output = '\nThird party packs:\n' for p in config.thirdparty: output += '**'+p+'** - ' + config.thirdparty[p] \ + ' (' + str(len(eval('config.black_'+p))) + '/' + str(len(eval('config.white_'+p))) + ')\n' output += ( "\nUse `{0}!add <code>` to add a pack, or use `{0}!add all` to add all available packs.\n" "(Note: this will only add official CAH packs; use `{0}!add thirdparty` to add all third party packs.)\n" "Use `{0}!contents <code>` to see what cards are in a specific pack." ).format(c) await self.client.send_message(ch, output) elif len(msg) > 10 and msg[:10] == c + '!contents': pk = message.content[10:].strip() # check for CardCast packs try: b, w = api.get_deck_blacks_json( pk), api.get_deck_whites_json(pk) deck_b = ['_'.join(c['text']) for c in b] deck_w = [''.join(c['text']) for c in w] output = '**Cards in ' + api.get_deck_info_json( pk)['name'] + '** (code: ' + pk + ')**:**\n\n' output += '**Black cards:** (' + str(len(deck_b)) + ')\n' for c in deck_b: output += '- ' + c + '\n' if len(output) > 1500: await self.client.send_message( ch, output.replace('_', '\_' * 3)) output = '' output += '\n**White cards:** (' + str(len(deck_w)) + ')\n' for c in deck_w: output += '- ' + c + '\n' if len(output) > 1500: await self.client.send_message( ch, output.replace('_', '\_' * 3)) output = '' await self.client.send_message( ch, output.replace('_', '\_' * 3)) return except: pass # check built-in packs pk = pk.lower() if pk in config.packs or pk in config.thirdparty: output = '' if pk in config.packs: output = '**Cards in ' + config.packs[pk] + ':**\n\n' elif pk in config.thirdparty: output = '**Cards in ' + config.thirdparty[ pk] + ':**\n\n' output += '**Black cards:** (' + str( len(eval('config.black_' + pk))) + ')\n' for c in eval('config.black_' + pk): output += '- ' + c + '\n' if len(output) > 1500: await self.client.send_message( ch, output.replace('_', '\_' * 3)) output = '' output += '\n**White cards:** (' + str( len(eval('config.white_' + pk))) + ')\n' for c in eval('config.white_' + pk): output += '- ' + c + '\n' if len(output) > 1500: await self.client.send_message( ch, output.replace('_', '\_' * 3)) output = '' await self.client.send_message( ch, output.replace('_', '\_' * 3)) elif msg == c + '!cancel': if config.C[ch]['playerMenu']: config.C[ch]['playerMenu'] = False config.C[ch]['players'] = [] await self.client.send_message(ch, 'Game cancelled!') if config.C[ch]['playerMenu']: curr = len(config.C[ch]['players']) if msg == c + '!join': if au not in config.C[ch]['players']: config.C[ch]['players'].append(au) elif msg == c + '!leave': if au in config.C[ch]['players']: config.C[ch]['players'].remove(au) if curr != len(config.C[ch]['players']): output = str(len( config.C[ch]['players'])) + '/20 Players: ' output += ', '.join(usr.display_name for usr in config.C[ch]['players']) await self.client.send_message(ch, output) if len(msg) > 6 and msg[:6] == c + '!add ' and config.C[ch][ 'lang'] == 'English': await self.addPack(ch, message.content[6:]) if len(msg) > 9 and msg[:9] == c + '!remove ' and config.C[ch][ 'lang'] == 'English': await self.removePack(ch, message.content[9:]) elif len(msg) > 5 and msg[:5] == c + '!rm ' and config.C[ch][ 'lang'] == 'English': await self.removePack(ch, message.content[5:]) else: if msg == c + '!help': await self.client.send_message(ch, ( "To play white cards, use `{0}!play` followed by the letters next to the cards you want to play. " "For example, `{0}!play b` would play card B, and `{0}!play df` would play cards D and F.\n" "If you're the czar, react with the letter of your choice once everyone has played their cards.\n" "To reset an ongoing game, use `{0}!reset`.\n" "To leave an ongoing game, use `{0}!leave` or `{0}!quit`.\n" "To join an ongoing game, use `{0}!join`.\n" "To kick an AFK player, use `{0}!kick <player>`." ).format(c)) # player commands if au in config.C[ch]['players']: if msg == c + '!display': config.C[ch]['msg'] = None await self.displayMid(ch) return elif msg == c + '!leave' or msg == c + '!quit': if not config.done(ch): await self.removePlayer(ch, au) else: await self.client.send_message( ch, 'Please wait for the czar to pick a card before leaving.' ) elif len(msg) > 7 and msg[:7] == c + '!kick ': mt = message.content[7:].strip() # player to kick if mt == au.mention: await self.client.send_message( ch, 'You cannot kick yourself. To leave the game, use `' + c + '!leave`.') else: for i in range(len(config.C[ch]['players'])): p = config.C[ch]['players'][i] if mt == p.mention: if not config.C[ch]['played'][i]: config.C[ch]['kick'][config.C[ch][ 'players'].index(au)] = p.mention cnt = config.C[ch]['kick'].count(p.mention) await self.client.send_message(ch, au.mention + ' has voted to kick ' + p.mention + '. ' \ + str(cnt) + '/' + str(config.C[ch]['nPlayers']-1) + ' votes needed') if cnt == config.C[ch]['nPlayers'] - 1: await self.client.send_message( ch, p.mention + ' has been kicked from the game.') await self.removePlayer(ch, p, kick=True) else: await self.client.send_message( ch, 'This player has already played and cannot be kicked.' ) break elif msg == c + '!reset': await config.reset(ch) await self.client.send_message(ch, 'Game reset!') return else: if msg == c + '!join': if not config.done(ch): await self.addPlayer(ch, au) else: await self.client.send_message( ch, 'Please wait for the czar to pick an answer before joining.' ) # playing cards if len(msg) > 6 and msg[:6] == c + '!play': await self.play(ch, au, msg[6:]) try: await self.client.delete_message(message) except: pass elif len(msg) > 4 and msg[:4] == c + '!p ': await self.play(ch, au, msg[4:]) try: await self.client.delete_message(message) except: pass # admin override if au.id == '252249185112293376' or au.permissions_in( ch).administrator: if msg == c + '!display': config.C[ch]['msg'] = None await self.displayMid(ch) elif msg == c + '!reset': await config.reset(ch) await self.client.send_message(ch, 'Game reset!')
async def displayMid(self, ch): # don't display if not enough players if config.C[ch]['nPlayers'] < 2: return msg = '─' * 20 + '\n' for i in range(config.C[ch]['nPlayers']): msg += config.C[ch]['players'][i].display_name + ' - ' + str( config.C[ch]['score'][i]) if config.C[ch]['played'][i] and not config.done(ch): msg += ' **Played!**' elif config.C[ch]['pov'] == i: msg += ' **Czar**' msg += '\n' if config.C[ch]['win'] not in config.C[ch]['score']: msg += '\nCurrent Czar: ' + config.C[ch]['players'][ config.C[ch]['pov']].mention + '\n\n' msg += 'Black card:\n' + config.C[ch]['curr'].replace( '_', '\_' * 5) + '\n' if config.done(ch): msg += '\n' for m in range(len(config.C[ch]['mid'])): msg += '**' + 'ABCDEFGHIJKL'[m] + ')** ' for card in config.C[ch]['mid'][m][0]: msg += card + '\n' msg += '─' * 20 try: #em = discord.Embed(description=msg, colour=0xBBBBBB) if config.C[ch]['msg'] == None or config.done(ch): #config.C[ch]['msg'] = await self.client.send_message(ch, embed=em) config.C[ch]['msg'] = await self.client.send_message(ch, msg) config.C[ch]['time'] = time.time() else: #await self.client.edit_message(config.C[ch]['msg'], embed=em) await self.client.edit_message(config.C[ch]['msg'], msg) except: print('Error in displayMid() at\n' + time.asctime() + '\n') c = config.pre[ch.id] if ch.id in config.pre else 'c' await self.client.send_message( ch, 'Encountered error while displaying - answer selection may not function normally. If the czar is unable to select, try using `' + c + '!display`.') return if config.done(ch): letters = [ '\U0001F1E6', '\U0001F1E7', '\U0001F1E8', '\U0001F1E9', '\U0001F1EA', '\U0001F1EB', '\U0001F1EC', '\U0001F1ED', '\U0001F1EE', '\U0001F1EF' ] error_occured = False for i in range(config.C[ch]['nPlayers'] - 1): try: await self.client.add_reaction(config.C[ch]['msg'], letters[i]) except: if not error_occured: error_occured = True await self.client.send_message( ch, 'An error occurred while adding a letter; if the letter of your choice is not shown, please choose it by adding the letter manually.' )