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 Exception as e: print(f'[shard {self.shard}] Error in displayMid() at', time.asctime()) print(e) 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.')
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 ch.send( '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 ch.send(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 ch.send('Not enough players, game has been reset.')
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) if config.C[ch]['win'] in config.C[ch]['score']: await self.displayWinners(ch) await config.reset(ch) else: await self.pass_(ch) except Exception as e: print('Error with answer selection at', time.asctime()) print(e)
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 # ignore czar/players who have already played card(s) 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][ 'abcdefghijklmnopqrstuvwxyz'.index(x)] if card in newMid: return # check for blank if card == '': await p.send( "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 ch.send(p.display_name + ' has played!') else: await p.send( f'This prompt requires {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 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']: # reset timer config.C[ch]['time'] = time.time() 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 ch.send(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 in timer_check 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: try: await ch.send( "**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 except Exception as e: print('Error in timer_check 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) # 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.guilds))+'.')) # 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 isinstance(ch, discord.abc.PrivateChannel): # check for c!p or c!play if msg.startswith(c + '!p'): await au.send( 'Please play your card(s) in the corresponding channel and not as a private message.' ) return # iterate through all users with blank cards for p in config.P: if p.id == au.id: for c in config.P[p]: 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 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 return # ignore irrelevant messages if not msg.startswith(c + '!'): return if ch not in config.C: config.C[ch] = {} await config.initChannel(ch) # warning if msg.startswith(c + '!warning') and au.id == 252249185112293376: for x in config.C: if config.C[x]['started']: await x.send(message.content[9:]) # check number of ongoing games if msg == c + '!ongoing' and (au.id == 252249185112293376 or au.id == 413516816137322506): nC = 0 for x in config.C: if config.C[x]['started']: nC += 1 await ch.send(str(nC)) # save state if (msg == c + '!save' or msg == c + '!savestate') and au.id == 252249185112293376: self.save_state() # number of servers if msg == c + '!servers' and (au.id == 252249185112293376 or au.id == 413516816137322506): await ch.send(str(len(self.client.guilds))) # # eval # if message.content.startswith(c+'!eval') and au.id == 252249185112293376: # try: # print(eval(message.content[6:].strip())) # await ch.send(str(eval(message.content[6:].strip()))) # except: # pass # changelog if msg == c + '!whatsnew' or msg == c + '!update' or msg == c + '!updates': s = info.changelog await ch.send(s[:s.index('**9/27')]) # commands list if msg == c + '!commands' or msg == c + '!command': await ch.send(info.commands) # support server if msg == c + '!support' or msg == c + '!server' or msg == c + '!supp': await ch.send('https://discord.gg/qGjRSYQ') # FAQ if msg == c + '!faq': await ch.send('https://goo.gl/p9j2ve') # invite link if msg == c + '!invite': await ch.send( '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 ch.send( 'Use this link to vote for the bot on discordbots.org:\n<https://discordbots.org/bot/429024440060215296/vote>' ) # donation links if msg == c + '!donate': await ch.send( 'Help support the bot:\n<http://buymeacoffee.com/sourmongoose>\n<https://paypal.me/sourmongoose>' ) # custom prefix setting if len(msg ) == 10 and msg[:9] == c + '!prefix ' and 'a' <= msg[9] <= 'z': await ch.send('Command prefix set to `' + msg[9] + '`!') config.pre[ch.id] = msg[9] with open('prefix.txt', 'a') as f: f.write(str(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 await ch.send('Language changed to ' + l + '.') await self.edit_start_msg(ch) if msg == c + '!help': await ch.send(( "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' await ch.send('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 ch.send(s) output = str(len( config.C[ch]['players'])) + '/20 Players: ' output += ', '.join(usr.display_name for usr in config.C[ch]['players']) await ch.send(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 ch.send('Game is starting!\n' + ' '.join(usr.mention for usr in config.C[ch]['players'])) config.C[ch]['msg'] = None await self.displayMid(ch) elif msg.startswith(c + '!setwin'): try: n = int(msg[8:].strip()) if n > 0: config.C[ch]['win'] = n await ch.send( f'Number of points needed to win has been set to {n}.' ) await self.edit_start_msg(ch) except: pass elif msg.startswith(c + '!timer') or msg.startswith(c + '!settimer'): #await ch.send('Idle timer is currently disabled.') try: n = int(msg[msg.index('timer') + 5:].strip()) if n >= 15: config.C[ch]['timer'] = n await ch.send(f'Idle timer set to {n} seconds.') await self.edit_start_msg(ch) elif n == 0: config.C[ch]['timer'] = n await ch.send('Idle timer is now disabled.') await self.edit_start_msg(ch) else: await ch.send('Please choose a minimum of 15 seconds.') except: pass elif msg.startswith(c + '!blank') or msg.startswith(c + '!setblank'): #await ch.send('Blank cards are currently disabled.') try: n = int(msg[msg.index('blank') + 5:].strip()) if 0 <= n <= 30: config.C[ch]['blanks'] = n await ch.send(f'Number of blanks has been set to {n}.') await self.edit_start_msg(ch) elif n > 30: await ch.send('Please choose a maximum of 30 cards.') 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: cnt = await config.getCount(p) output += f'**{p}** - {config.packs[p]} ({cnt[0]}/{cnt[1]})\n' await ch.send(output) output = '\nThird party packs:\n' for p in config.thirdparty: cnt = await config.getCount(p) output += f'**{p}** - {config.thirdparty[p]} ({cnt[0]}/{cnt[1]})\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 ch.send(output) elif msg.startswith(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 ch.send(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 ch.send(output.replace('_', '\_' * 3)) output = '' await ch.send(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' cnt = await config.getCount(pk) cards = await config.getPack(pk) output += f'**Black cards:** ({cnt[0]})\n' for card in cards[0]: output += '- ' + card[0] + '\n' if len(output) > 1500: await ch.send(output.replace('_', '\_' * 3)) output = '' output += f'\n**White cards:** ({cnt[1]})\n' for card in cards[1]: output += '- ' + card[0] + '\n' if len(output) > 1500: await ch.send(output.replace('_', '\_' * 3)) output = '' await ch.send(output.replace('_', '\_' * 3)) elif msg == c + '!cancel': if config.C[ch]['playerMenu']: config.C[ch]['playerMenu'] = False config.C[ch]['players'] = [] await ch.send('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 ch.send(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 + '!join' or (len(msg) > 6 and msg[:6] == c + '!add '): await ch.send( 'Use `c!start` to start a game before joining or adding packs.' ) else: if msg == c + '!help': await ch.send(( "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>`.\n" "To re-display the scoreboard, use `{0}!display`." ).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 ch.send( 'Please wait for the czar to pick a card before leaving.' ) elif msg.startswith(c + '!kick'): mt = message.content[6:].strip() # player to kick if mt == au.mention: await ch.send( '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 ch.send(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 ch.send( p.mention + ' has been kicked from the game.') await self.removePlayer(ch, p, kick=True) else: await ch.send( 'This player has already played and cannot be kicked.' ) break elif msg == c + '!reset': await config.reset(ch) await ch.send('Game reset!') return else: if msg == c + '!join': if not config.done(ch): await self.addPlayer(ch, au) else: await ch.send( 'Please wait for the czar to pick an answer before joining.' ) # playing cards if msg.startswith('c!play'): await self.play(ch, au, msg[6:]) try: await message.delete() except: pass elif msg.startswith(c + '!p '): await self.play(ch, au, msg[4:]) try: await message.delete() 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 ch.send('Game reset!')