async def join(client, message, **kwargs): """Join the current tournament No Arguments """ account = kwargs.get('account') try: await account.participants.create( kwargs.get('tournament_id'), message.author.name, challonge_username=kwargs.get('participant_username')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.add_roles(message.author, kwargs.get('tournament_role')) await client.send_message( message.channel, '✅ You have successfully joined the tournament') try: t = await account.tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await update_channel_topic(account, t, client, message.channel) await modules.on_event(message.server.id, Events.on_join, p1_name=message.author.name, t_name=t['name'], me=message.server.me)
async def get_open_match_dependancy(account, t_id, m, p_id): if m['player1-id'] == p_id: if 'player2-prereq-match-id' in m: waiting_on_match_id = m['player2-prereq-match-id'] waiting_for_loser = m['player2-is-prereq-match-loser'] else: return None, '✅ You have a pending match with no dependancy!?' elif m['player2-id'] == p_id: if 'player1-prereq-match-id' in m: waiting_on_match_id = m['player1-prereq-match-id'] waiting_for_loser = m['player1-is-prereq-match-loser'] else: return None, '✅ You have a pending match with no dependancy!?' else: return None, '✅ Couldn\'t find participant' try: waiting_on_m = await account.matches.show(t_id, waiting_on_match_id) except ChallongeException as e: log_challonge.error('failed waiting_on_m %s - %s' % (waiting_on_match_id, e)) return None, T_OnChallongeException.format(e) if not waiting_on_m['player1-id'] or not waiting_on_m['player2-id']: return 'you are waiting for more than one match', None else: try: p1 = await account.participants.show(t_id, waiting_on_m['player1-id']) p2 = await account.participants.show(t_id, waiting_on_m['player2-id']) except ChallongeException as e: log_challonge.error('failed p1 (%s) or p2 (%s) - %s' % (waiting_on_m['player1-id'], waiting_on_m['player2-id'], e)) return None, T_OnChallongeException.format(e) else: loser_txt = '`Loser`' if waiting_for_loser else '`Winner`' return 'you are waiting on the %s of %s 🆚 %s' % (loser_txt, p1['name'], p2['name']), None
async def forfeitx(client, message, **kwargs): """Forfeit for the current tournament If the tournament is pending, you will be removed from the participants list If the tournament is in progress, Challonge will forfeit your potential remaining games and you won't be able to write in this channel anymore No Arguments """ account, t_id, opponent = kwargs.get('account'), kwargs.get('tournament_id'), kwargs.get('opponent') author_id, exc = await get_participant(account, t_id, opponent) if exc: await client.send_message(message.channel, exc) return elif author_id: try: await account.participants.destroy(t_id, author_id) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) await client.send_message(message.channel, '✅ {0} has been removed from this tournament'.format(opponent)) member = get_member(opponent, message.server) if member: await client.remove_roles(member, kwargs.get('tournament_role')) try: t = await account.tournaments.show(t_id, include_participants=1, include_matches=1) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await update_channel_topic(account, t, client, message.channel) await modules.on_event(message.server.id, Events.on_forfeit, p1_name=opponent, t_name=t['name'], me=message.server.me)
async def get_next_match(account, t_id, name): participant, exc = await get_participant(account, t_id, name) if exc: return None, exc if not participant: return None, '❌ Participant \'%s\' not found' % name if participant['final-rank'] is not None: return '✅ %s, tournament is over and you endend up at rank #%s' % ( name, participant['final-rank']), None p_id = participant['id'] try: openMatches = await account.matches.index(t_id, state='open', participant_id=p_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) if len(openMatches) > 0: if openMatches[0]['player1-id'] == p_id: opponent_id = openMatches[0]['player2-id'] else: opponent_id = openMatches[0]['player1-id'] try: opponent = await account.participants.show(t_id, opponent_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) return '✅ %s, you have an open match 🆚 %s' % (name, opponent['name']), None try: pendingMatches = await account.matches.index(t_id, state='pending', participant_id=p_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) if len(pendingMatches) > 0: msg, exc = await get_open_match_dependancy(account, t_id, pendingMatches[0], p_id) if exc: log_challonge.error(exc) return '✅ %s, you have a pending match. Please wait for it to open' % name, None return '✅ %s, %s' % (name, msg), None return '✅ %s, you have no pending nor open match. It seems you\'re out of the tournament' % name, None
async def finalize(client, message, **kwargs): """Finalize a tournament Tournament will be closed and no further modifications will be possible Be sure to upload attachements before finalizing This Channel will be locked for writing except for organizers No Arguments """ try: t = await kwargs.get('account').tournaments.finalize( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: # let's remove the role associated to this tournament. # only the Challonge role will be able to write in it await client.delete_role(message.server, kwargs.get('tournament_role')) await client.send_message(message.channel, '✅ Tournament has been finalized!') try: t = await kwargs.get('account').tournaments.show( kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel) await modules.on_state_change(message.server.id, TournamentState.complete, t_name=t['name'], me=message.server.me)
async def reset(client, message, **kwargs): """Reset a tournament All scores and attachments will be cleared. You will be able to edit participants then start again No Arguments """ try: await kwargs.get('account').tournaments.reset( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Tournament has been reset!') try: t = await kwargs.get('account').tournaments.show( kwargs.get('tournament_id')) except ChallongeException as e: log_commands_def.error('reset exc=: %s' % e) else: await update_channel_topic(kwargs.get('account'), t, client, message.channel) await modules.on_state_change(message.server.id, TournamentState.pending, t_name=t['name'], me=message.server.me)
async def start(client, message, **kwargs): """Start a tournament This channel will be locked for writing except for participants / organizers No Arguments """ try: t = await kwargs.get('account').tournaments.start(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: overwrite = discord.PermissionOverwrite() overwrite.send_messages = True await client.edit_channel_permissions(message.channel, kwargs.get('tournament_role'), overwrite) for r in message.server.me.roles: if r.name == C_RoleName: await client.edit_channel_permissions(message.channel, r, overwrite) overwrite = discord.PermissionOverwrite() overwrite.send_messages = False await client.edit_channel_permissions(message.channel, message.server.default_role, overwrite) await client.send_message(message.channel, '✅ Tournament is now started!') await update_channel_topic(kwargs.get('account'), t, client, message.channel) await modules.on_state_change(message.server.id, TournamentState.underway, t_name=t['name'], me=message.server.me) # TODO real text (with games to play...) """
async def status(client, message, **kwargs): """Get the tournament status No Arguments """ account = kwargs.get('account') try: t = await account.tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: if t['state'] == 'underway': matchesRepr, exc = await get_current_matches_repr(account, t) if exc: await client.send_message(message.channel, exc) else: await client.send_message(message.channel, '✅ Open matches for tournament `{0}` ({1})\n{2}'.format(t['name'], t['full-challonge-url'], matchesRepr)) elif t['state'] == 'pending': info = [] info.append('✅ Tournament: {0} ({1}) is pending.'.format(t['name'], t['full-challonge-url'])) info.append('%d participants have registered right now. More can still join until tournament is started' % t['participants-count']) await client.send_message(message.channel, '\n'.join(info)) elif t['state'] == 'awaiting_review': await client.send_message(message.channel, '✅ Tournament: {0} ({1}) has been completed and is waiting for final review (finalize)'.format(t['name'], t['full-challonge-url'])) elif t['state'] == 'complete': rankingRepr, exc = get_final_ranking_repr(account, t) if exc: await client.send_message(message.channel, exc) else: await client.send_message(message.channel, '✅ Tournament: {0} ({1}) has been completed\n{2}'.format(t['name'], t['full-challonge-url'], rankingRepr)) else: log_commands_def.error('[status] Unknown state: ' + t['state'])
async def destroy(client, message, **kwargs): """Delete a tournament on Challonge and cleanup Discord bindings Use with caution! This action can't be reversed! No Arguments """ try: t = await kwargs.get('account').tournaments.show( kwargs.get('tournament_id')) await kwargs.get('account').tournaments.destroy( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: if kwargs.get( 'tournament_role' ): # tournament role may have been deleted by finalize before await client.delete_role(message.server, kwargs.get('tournament_role')) await client.delete_channel(message.channel) channelId = db.get_server(message.server).management_channel_id await client.send_message( discord.Channel(server=message.server, id=channelId), '✅ Tournament {0} has been destroyed by {1}!'.format( t['name'], message.author.mention)) db.remove_tournament(kwargs.get('tournament_id'))
async def update_score(account, t_id, m_id, score, winner_id): try: await account.matches.update(t_id, m_id, scores_csv=score, winner_id=winner_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) return '✅ These results have been uploaded to Challonge', None
async def get_next_match(account, t_id, name): participant, exc = await get_participant(account, t_id, name) if exc: return None, exc if not participant: return None, '❌ Participant \'%s\' not found' % name if participant['final-rank'] is not None: return '✅ %s, tournament is over and you endend up at rank #%s' % (name, participant['final-rank']), None p_id = participant['id'] try: openMatches = await account.matches.index(t_id, state='open', participant_id=p_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) if len(openMatches) > 0: if openMatches[0]['player1-id'] == p_id: opponent_id = openMatches[0]['player2-id'] else: opponent_id = openMatches[0]['player1-id'] try: opponent = await account.participants.show(t_id, opponent_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) return '✅ %s, you have an open match 🆚 %s' % (name, opponent['name']), None try: pendingMatches = await account.matches.index(t_id, state='pending', participant_id=p_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) if len(pendingMatches) > 0: msg, exc = await get_open_match_dependancy(account, t_id, pendingMatches[0], p_id) if exc: log_challonge.error(exc) return '✅ %s, you have a pending match. Please wait for it to open' % name, None return '✅ %s, %s' % (name, msg), None return '✅ %s, you have no pending nor open match. It seems you\'re out of the tournament' % name, None
async def get_participants(account, t): if 'participants' in t: participants = t['participants'] else: try: participants = await account.participants.index(t['id']) except ChallongeException as e: return None, T_OnChallongeException.format(e) return participants, None
async def get_matches(account, t, state): if 'matches' in t: matches = [m for m in t['matches'] if m['state'] == state] else: try: matches = await account.matches.index(t['id'], state=state) except ChallongeException as e: return None, T_OnChallongeException.format(e) return matches, None
async def get_participant(account, t_id, name): try: participants = await account.participants.index(t_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) else: for x in participants: if x['name'] == name: return x, None return None, None
async def join(client, message, **kwargs): """Join the current tournament No Arguments """ account = kwargs.get('account') try: await account.participants.create(kwargs.get('tournament_id'), message.author.name, challonge_username=kwargs.get('participant_username')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.add_roles(message.author, kwargs.get('tournament_role')) await client.send_message(message.channel, '✅ You have successfully joined the tournament') try: t = await account.tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await update_channel_topic(account, t, client, message.channel) await modules.on_event(message.server.id, Events.on_join, p1_name=message.author.name, t_name=t['name'], me=message.server.me)
async def get_matches(account, t, state): if 'matches' in t: matches = [m for m in t['matches'] if m['state'] == state] else: try: matches = await account.matches.index(t['id'], state=state) except ChallongeException as e: return None, T_OnChallongeException.format(e) return matches, None
async def get_participants(account, t): if 'participants' in t: participants = t['participants'] else: try: participants = await account.participants.index(t['id']) except ChallongeException as e: return None, T_OnChallongeException.format(e) return participants, None
async def get_participant(account, t_id, name): try: participants = await account.participants.index(t_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) else: for x in participants: if x['name'] == name: return x, None return None, None
async def dump(client, message, **kwargs): def decorate(s): return '```ruby\n' + s + '```' name_id_fmt = '{0.name} ({0.id})' maxChars = 1800 what = kwargs.get('what') if what is None or what == 'commands': for page in paginate(cmds.dump(), maxChars): await client.send_message(message.author, decorate(page)) if what is None or what == 'profile': pass if what is None or what == 'servers': a = ArrayFormater('Servers', 3) a.add('Server Name (ID)', 'Owner Name (ID)', 'Trigger') for s in db.get_servers(): server = message.server if s.server_id: server = client.get_server(s.server_id) a.add(name_id_fmt.format(server), name_id_fmt.format(server.owner), str(s.trigger)) for page in paginate(a.get(), maxChars): await client.send_message(message.author, decorate(page)) if what is None or what == 'users': a = ArrayFormater('Users', 2) a.add('User Name (ID)', 'Challonge username') for u in db.get_users(): if u.discord_id: user = None for server in client.servers: user = discord.utils.get(server.members, id=u.discord_id) if user: break a.add(name_id_fmt.format(user), str(u.challonge_user_name)) for page in paginate(a.get(), maxChars): await client.send_message(message.author, decorate(page)) if what is None or what == 'tournaments': acc, exc = await get_account(app_config['devid']) if not acc: return a = ArrayFormater('Tournaments', 3) a.add('Server Name (ID)', 'Host Name (ID)', 'Tournament Url') for server in client.servers: for t in db.get_tournaments(server.id): host = discord.utils.get(server.members, id=t.host_id) url = 'Not Found' try: t = await acc.tournaments.show(t.challonge_id) url = t['full-challonge-url'] except ChallongeException as e: url = T_OnChallongeException.format(e) a.add(name_id_fmt.format(server), name_id_fmt.format(host), url) for page in paginate(a.get(), maxChars): await client.send_message(message.author, decorate(page))
async def shuffleseeds(client, message, **kwargs): """Shuffle tournament seeds The tournament MUST NOT have been started yet! No Arguments """ try: await kwargs.get('account').participants.randomize(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Seeds for this tournament have been shuffled!')
async def get_match(account, t_id, p1_id, p2_id): try: openMatches = await account.matches.index(t_id, state='open', participant_id=p1_id) except ChallongeException as e: return None, None, T_OnChallongeException.format(e) else: for m in openMatches: if m['player1-id'] == p1_id and m['player2-id'] == p2_id or m['player2-id'] == p1_id and m['player1-id'] == p2_id: is_reversed = m['player1-id'] == p2_id return m, is_reversed, None return None, None, None
async def get_open_match_dependancy(account, t_id, m, p_id): if m['player1-id'] == p_id: if 'player2-prereq-match-id' in m: waiting_on_match_id = m['player2-prereq-match-id'] waiting_for_loser = m['player2-is-prereq-match-loser'] else: return None, '✅ You have a pending match with no dependancy!?' elif m['player2-id'] == p_id: if 'player1-prereq-match-id' in m: waiting_on_match_id = m['player1-prereq-match-id'] waiting_for_loser = m['player1-is-prereq-match-loser'] else: return None, '✅ You have a pending match with no dependancy!?' else: return None, '✅ Couldn\'t find participant' try: waiting_on_m = await account.matches.show(t_id, waiting_on_match_id) except ChallongeException as e: log_challonge.error('failed waiting_on_m %s - %s' % (waiting_on_match_id, e)) return None, T_OnChallongeException.format(e) if not waiting_on_m['player1-id'] or not waiting_on_m['player2-id']: return 'you are waiting for more than one match', None else: try: p1 = await account.participants.show(t_id, waiting_on_m['player1-id']) p2 = await account.participants.show(t_id, waiting_on_m['player2-id']) except ChallongeException as e: log_challonge.error( 'failed p1 (%s) or p2 (%s) - %s' % (waiting_on_m['player1-id'], waiting_on_m['player2-id'], e)) return None, T_OnChallongeException.format(e) else: loser_txt = '`Loser`' if waiting_for_loser else '`Winner`' return 'you are waiting on the %s of %s 🆚 %s' % ( loser_txt, p1['name'], p2['name']), None
async def forfeitx(client, message, **kwargs): """Forfeit for the current tournament If the tournament is pending, you will be removed from the participants list If the tournament is in progress, Challonge will forfeit your potential remaining games and you won't be able to write in this channel anymore No Arguments """ account, t_id, opponent = kwargs.get('account'), kwargs.get( 'tournament_id'), kwargs.get('opponent') author_id, exc = await get_participant(account, t_id, opponent) if exc: await client.send_message(message.channel, exc) return elif author_id: try: await account.participants.destroy(t_id, author_id) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) await client.send_message( message.channel, '✅ {0} has been removed from this tournament'.format(opponent)) member = get_member(opponent, message.server) if member: await client.remove_roles(member, kwargs.get('tournament_role')) try: t = await account.tournaments.show(t_id, include_participants=1, include_matches=1) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await update_channel_topic(account, t, client, message.channel) await modules.on_event(message.server.id, Events.on_forfeit, p1_name=opponent, t_name=t['name'], me=message.server.me)
async def status(client, message, **kwargs): """Get the tournament status No Arguments """ account = kwargs.get('account') try: t = await account.tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: if t['state'] == 'underway': matchesRepr, exc = await get_current_matches_repr(account, t) if exc: await client.send_message(message.channel, exc) else: await client.send_message( message.channel, '✅ Open matches for tournament `{0}` ({1})\n{2}'.format( t['name'], t['full-challonge-url'], matchesRepr)) elif t['state'] == 'pending': info = [] info.append('✅ Tournament: {0} ({1}) is pending.'.format( t['name'], t['full-challonge-url'])) info.append( '%d participants have registered right now. More can still join until tournament is started' % t['participants-count']) await client.send_message(message.channel, '\n'.join(info)) elif t['state'] == 'awaiting_review': await client.send_message( message.channel, '✅ Tournament: {0} ({1}) has been completed and is waiting for final review (finalize)' .format(t['name'], t['full-challonge-url'])) elif t['state'] == 'complete': rankingRepr, exc = get_final_ranking_repr(account, t) if exc: await client.send_message(message.channel, exc) else: await client.send_message( message.channel, '✅ Tournament: {0} ({1}) has been completed\n{2}'.format( t['name'], t['full-challonge-url'], rankingRepr)) else: log_commands_def.error('[status] Unknown state: ' + t['state'])
async def get_match(account, t_id, p1_id, p2_id): try: openMatches = await account.matches.index(t_id, state='open', participant_id=p1_id) except ChallongeException as e: return None, None, T_OnChallongeException.format(e) else: for m in openMatches: if m['player1-id'] == p1_id and m['player2-id'] == p2_id or m[ 'player2-id'] == p1_id and m['player1-id'] == p2_id: is_reversed = m['player1-id'] == p2_id return m, is_reversed, None return None, None, None
async def undocheckin(client, message, **kwargs): """Undo check-in for the current tournament No Arguments """ try: participants = await kwargs.get('account').participants.index(kwargs.get('tournament_id')) for x in participants: if x['name'] == message.author.name: await kwargs.get('account').participants.undo_check_in(kwargs.get('tournament_id'), x['id']) break except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Your checked in has been successfully reverted')
async def shuffleseeds(client, message, **kwargs): """Shuffle tournament seeds The tournament MUST NOT have been started yet! No Arguments """ try: await kwargs.get('account').participants.randomize( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message( message.channel, '✅ Seeds for this tournament have been shuffled!')
async def create(client, message, **kwargs): """Create a new tournament Required Arguments: name -- will be used as the tournament name (Max: 60 characters, no spaces) url -- http://challonge.com/url (letters, numbers, and underscores only) type -- can be [singleelim, doubleelim, roundrobin, swiss] Optional Arguments: subdomain -- a valid Challonge organization http://subdomain.challonge.com/url """ # Validate name if len(kwargs.get('name')) > 60: await client.send_message(message.channel, '❌ Invalid name. Please use less than 60 characters and no spaces') return # Validate url diff = set(kwargs.get('url')) - set(string.ascii_letters + string.digits + '_') if diff: await client.send_message(message.channel, '❌ Invalid url {}. Please use only letters, numbers and underscores'.format(kwargs.get('url'))) return # Validate type if kwargs.get('type') not in ['singleelim', 'doubleelim', 'roundrobin', 'swiss']: await client.send_message(message.channel, '❌ Invalid tournament type {}. Please choose from singleelim, doubleelim, roundrobin or swiss'.format(kwargs.get('type'))) return if kwargs.get('type') == 'singleelim': tournament_type = 'single elimination' elif kwargs.get('type') == 'doubleelim': tournament_type = 'double elimination' elif kwargs.get('type') == 'roundrobin': tournament_type = 'round robin' else: tournament_type = 'swiss' params = {} if kwargs.get('subdomain', None): params['subdomain'] = kwargs.get('subdomain') try: t = await kwargs.get('account').tournaments.create(kwargs.get('name'), kwargs.get('url'), tournament_type, **params) except ChallongeException as e: await client.send_message(message.channel, T_OnChallongeException.format(e)) else: role = await client.create_role(message.server, name='Participant_' + kwargs.get('name'), mentionable=True) chChannel = await client.create_channel(message.server, 'T_' + kwargs.get('name')) db.add_tournament(t['id'], chChannel, role.id, message.author.id) await client.send_message(message.channel, T_TournamentCreated.format(kwargs.get('name'), t['full-challonge-url'], role.mention, chChannel.mention)) await update_channel_topic(kwargs.get('account'), t, client, chChannel) await modules.on_state_change(message.server.id, TournamentState.pending, t_name=kwargs.get('name'), me=message.server.me)
async def checkin(client, message, **kwargs): """Check-in for the current tournament No Arguments """ author_id, exc = await get_participant(kwargs.get('account'), kwargs.get('tournament_id'), message.author.name) if exc: await client.send_message(message.channel, exc) return elif author_id: try: await kwargs.get('account').participants.check_in(kwargs.get('tournament_id'), author_id) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ You have successfully checked in. Please wait for the organizers to start the tournament')
async def get_players(account, t_id, p1_name, p2_name): try: participants = await account.participants.index(t_id) except ChallongeException as e: return None, None, T_OnChallongeException.format(e) else: p1_id = None p2_id = None for x in participants: if x['name'] == p1_name: p1_id = x['id'] elif x['name'] == p2_name: p2_id = x['id'] if p1_id and p2_id: break return p1_id, p2_id, None
async def checkin_abort(client, message, **kwargs): """Stop or reset the check-in process No arguments """ try: await kwargs.get('account').tournaments.abort_check_in(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Check-ins have been aborted') try: t = await kwargs.get('account').tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel)
async def get_players(account, t_id, p1_name, p2_name): try: participants = await account.participants.index(t_id) except ChallongeException as e: return None, None, T_OnChallongeException.format(e) else: p1_id = None p2_id = None for x in participants: if x['name'] == p1_name: p1_id = x['id'] elif x['name'] == p2_name: p2_id = x['id'] if p1_id and p2_id: break return p1_id, p2_id, None
async def destroy(client, message, **kwargs): """Delete a tournament on Challonge and cleanup Discord bindings Use with caution! This action can't be reversed! No Arguments """ try: t = await kwargs.get('account').tournaments.show(kwargs.get('tournament_id')) await kwargs.get('account').tournaments.destroy(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: if kwargs.get('tournament_role'): # tournament role may have been deleted by finalize before await client.delete_role(message.server, kwargs.get('tournament_role')) await client.delete_channel(message.channel) channelId = db.get_server(message.server).management_channel_id await client.send_message(discord.Channel(server=message.server, id=channelId), '✅ Tournament {0} has been destroyed by {1}!'.format(t['name'], message.author.mention)) db.remove_tournament(kwargs.get('tournament_id'))
async def checkin_setup(client, message, **kwargs): """Setup the checkin process for participants Required Arguments: date -- date of the tournament: YYYY/MM/DD time -- time of the tournament: HH:MM (24h format) duration -- length of the participant check-in window in minutes. """ # verify date date = get_date(kwargs.get('date')) if not date: await client.send_message(message.channel, '❌ Wrong date format. Must be `YYYY/MM/DD`.') return # verify time time = get_time(kwargs.get('time')) if not time: await client.send_message(message.channel, '❌ Wrong time format. Must be `HH:MM` (24h format).') return # verify duration try: duration = int(kwargs.get('duration')) except ValueError: await client.send_message(message.channel, '❌ Duration must be an integer') return else: if duration <= 0: await client.send_message(message.channel, '❌ Duration must be a positive integer') return # combime date & time full_date_time = datetime.strptime(kwargs.get('date') + ' ' + kwargs.get('time'), '%Y/%m/%d %H:%M') try: await kwargs.get('account').tournaments.update(kwargs.get('tournament_id'), start_at=full_date_time, check_in_duration=duration) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Start date and check-in duration have been processed') try: t = await kwargs.get('account').tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel)
async def checkin_validate(client, message, **kwargs): """Finalize the check-in process once check-in time is done Participants that didn't check-in will be moved to bottom seeds No arguments """ try: await kwargs.get('account').tournaments.process_check_ins(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Check-ins have been processed') try: t = await kwargs.get('account').tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel)
async def reset(client, message, **kwargs): """Reset a tournament All scores and attachments will be cleared. You will be able to edit participants then start again No Arguments """ try: await kwargs.get('account').tournaments.reset(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Tournament has been reset!') try: t = await kwargs.get('account').tournaments.show(kwargs.get('tournament_id')) except ChallongeException as e: log_commands_def.error('reset exc=: %s' % e) else: await update_channel_topic(kwargs.get('account'), t, client, message.channel) await modules.on_state_change(message.server.id, TournamentState.pending, t_name=t['name'], me=message.server.me)
async def undocheckin(client, message, **kwargs): """Undo check-in for the current tournament No Arguments """ try: participants = await kwargs.get('account').participants.index( kwargs.get('tournament_id')) for x in participants: if x['name'] == message.author.name: await kwargs.get('account').participants.undo_check_in( kwargs.get('tournament_id'), x['id']) break except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message( message.channel, '✅ Your checked in has been successfully reverted')
async def checkin(client, message, **kwargs): """Check-in for the current tournament No Arguments """ author_id, exc = await get_participant(kwargs.get('account'), kwargs.get('tournament_id'), message.author.name) if exc: await client.send_message(message.channel, exc) return elif author_id: try: await kwargs.get('account').participants.check_in( kwargs.get('tournament_id'), author_id) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message( message.channel, '✅ You have successfully checked in. Please wait for the organizers to start the tournament' )
async def start(client, message, **kwargs): """Start a tournament This channel will be locked for writing except for participants / organizers No Arguments """ try: t = await kwargs.get('account').tournaments.start( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: overwrite = discord.PermissionOverwrite() overwrite.send_messages = True await client.edit_channel_permissions(message.channel, kwargs.get('tournament_role'), overwrite) for r in message.server.me.roles: if r.name == C_RoleName: await client.edit_channel_permissions(message.channel, r, overwrite) overwrite = discord.PermissionOverwrite() overwrite.send_messages = False await client.edit_channel_permissions(message.channel, message.server.default_role, overwrite) await client.send_message(message.channel, '✅ Tournament is now started!') await update_channel_topic(kwargs.get('account'), t, client, message.channel) await modules.on_state_change(message.server.id, TournamentState.underway, t_name=t['name'], me=message.server.me) # TODO real text (with games to play...) """
async def finalize(client, message, **kwargs): """Finalize a tournament Tournament will be closed and no further modifications will be possible Be sure to upload attachements before finalizing This Channel will be locked for writing except for organizers No Arguments """ try: t = await kwargs.get('account').tournaments.finalize(kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: # let's remove the role associated to this tournament. # only the Challonge role will be able to write in it await client.delete_role(message.server, kwargs.get('tournament_role')) await client.send_message(message.channel, '✅ Tournament has been finalized!') try: t = await kwargs.get('account').tournaments.show(kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel) await modules.on_state_change(message.server.id, TournamentState.complete, t_name=t['name'], me=message.server.me)
async def checkin_abort(client, message, **kwargs): """Stop or reset the check-in process No arguments """ try: await kwargs.get('account').tournaments.abort_check_in( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Check-ins have been aborted') try: t = await kwargs.get('account').tournaments.show( kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel)
async def checkin_validate(client, message, **kwargs): """Finalize the check-in process once check-in time is done Participants that didn't check-in will be moved to bottom seeds No arguments """ try: await kwargs.get('account').tournaments.process_check_ins( kwargs.get('tournament_id')) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message(message.channel, '✅ Check-ins have been processed') try: t = await kwargs.get('account').tournaments.show( kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel)
async def update_score(account, t_id, m_id, score, winner_id): try: await account.matches.update(t_id, m_id, scores_csv=score, winner_id=winner_id) except ChallongeException as e: return None, T_OnChallongeException.format(e) return '✅ These results have been uploaded to Challonge', None
async def get_blocking_matches(account, t_id): try: t = await account.tournaments.show(t_id, include_participants=1, include_matches=1) except ChallongeException as e: return None, T_OnChallongeException.format(e) if len(t['matches']) == 0: return '✅ No blocking matches!', None matches = t['matches'] participants = t['participants'] blocking = {} def process_prereq_match(m, player, blocked): key = '%s-prereq-match-id' % player if key in m and m[key]: found = False for k, v in blocking.items(): if m[key] in v: log_challonge.debug('%s is already in blocked matches of %s - adding %s' % (m[key], k, m['id'])) blocking[k].append(m['id']) found = True if not found: log_challonge.debug('Adding %s to the blocked list' % m[key]) blocked.append(m['id']) return m[key] return None def check_match(m_id, blocked): for k, v in blocking.items(): if m_id in v: log_challonge.debug('%s is already in blocked matches of %s - adding %s' % (m_id, k, blocked)) blocking[k].extend(blocked) return m = find(matches, 'id', m_id) if not m: log_challonge.debug('no match with id #%s' % m_id) return debug_p1Name = 'None' if not m['player1-id'] else find(participants, 'id', m['player1-id'])['name'] debug_p2Name = 'None' if not m['player2-id'] else find(participants, 'id', m['player2-id'])['name'] log_challonge.debug('check_match %s: %s Vs %s (%s)' % (m_id, debug_p1Name, debug_p2Name, m['state'])) if m['state'] == 'pending': processed = process_prereq_match(m, 'player1', blocked) if processed: log_challonge.debug('%s needs to dive deeper' % processed) check_match(processed, blocked) processed = process_prereq_match(m, 'player2', blocked) if processed: blocked.append(processed) log_challonge.debug('%s needs to dive deeper' % processed) check_match(processed, blocked) elif m['state'] == 'open': if m_id in blocking: blocking[m_id].extend(blocked) else: blocking.update({m_id: blocked}) log_challonge.debug(blocking) for m in matches: if m['state'] == 'pending' and (m['player1-id'] or m['player2-id']): found = False for k, v in blocking.items(): if m['id'] in v: log_challonge.debug('%s is already in blocked matches of %s' % (m['id'], k)) found = True if not found: log_challonge.debug('Checking blockers for %s' % m['id']) blocked = [m['id']] if not m['player1-id'] and 'player1-prereq-match-id' in m and m['player1-prereq-match-id']: check_match(m['player1-prereq-match-id'], blocked) if not m['player2-id'] and 'player2-prereq-match-id' in m and m['player2-prereq-match-id']: check_match(m['player2-prereq-match-id'], blocked) sorted_m = sorted(blocking.items(), key=lambda x: len(x[1]), reverse=True) log_challonge.debug(sorted_m) msg = ['✅ Blocking matches:'] for tup_m in sorted_m: m = find(matches, 'id', tup_m[0]) if m: p1 = find(participants, 'id', m['player1-id']) p2 = find(participants, 'id', m['player2-id']) msg.append('`%s game%s blocked` by: %s 🆚 %s' % (len(tup_m[1]), 's' if len(tup_m[1]) > 1 else '', p1['name'], p2['name'])) return '\n '.join(msg), None
async def checkin_setup(client, message, **kwargs): """Setup the checkin process for participants Required Arguments: date -- date of the tournament: YYYY/MM/DD time -- time of the tournament: HH:MM (24h format) duration -- length of the participant check-in window in minutes. """ # verify date date = get_date(kwargs.get('date')) if not date: await client.send_message( message.channel, '❌ Wrong date format. Must be `YYYY/MM/DD`.') return # verify time time = get_time(kwargs.get('time')) if not time: await client.send_message( message.channel, '❌ Wrong time format. Must be `HH:MM` (24h format).') return # verify duration try: duration = int(kwargs.get('duration')) except ValueError: await client.send_message(message.channel, '❌ Duration must be an integer') return else: if duration <= 0: await client.send_message(message.channel, '❌ Duration must be a positive integer') return # combime date & time full_date_time = datetime.strptime( kwargs.get('date') + ' ' + kwargs.get('time'), '%Y/%m/%d %H:%M') try: await kwargs.get('account').tournaments.update( kwargs.get('tournament_id'), start_at=full_date_time, check_in_duration=duration) except ChallongeException as e: await client.send_message(message.author, T_OnChallongeException.format(e)) else: await client.send_message( message.channel, '✅ Start date and check-in duration have been processed') try: t = await kwargs.get('account').tournaments.show( kwargs.get('tournament_id'), include_participants=1, include_matches=1) except ChallongeException: log_commands_def.exception('') else: await update_channel_topic(kwargs.get('account'), t, client, message.channel)
async def create(client, message, **kwargs): """Create a new tournament Required Arguments: name -- will be used as the tournament name (Max: 60 characters, no spaces) url -- http://challonge.com/url (letters, numbers, and underscores only) type -- can be [singleelim, doubleelim, roundrobin, swiss] Optional Arguments: subdomain -- a valid Challonge organization http://subdomain.challonge.com/url """ # Validate name if len(kwargs.get('name')) > 60: await client.send_message( message.channel, '❌ Invalid name. Please use less than 60 characters and no spaces') return # Validate url diff = set( kwargs.get('url')) - set(string.ascii_letters + string.digits + '_') if diff: await client.send_message( message.channel, '❌ Invalid url {}. Please use only letters, numbers and underscores' .format(kwargs.get('url'))) return # Validate type if kwargs.get('type') not in [ 'singleelim', 'doubleelim', 'roundrobin', 'swiss' ]: await client.send_message( message.channel, '❌ Invalid tournament type {}. Please choose from singleelim, doubleelim, roundrobin or swiss' .format(kwargs.get('type'))) return if kwargs.get('type') == 'singleelim': tournament_type = 'single elimination' elif kwargs.get('type') == 'doubleelim': tournament_type = 'double elimination' elif kwargs.get('type') == 'roundrobin': tournament_type = 'round robin' else: tournament_type = 'swiss' params = {} if kwargs.get('subdomain', None): params['subdomain'] = kwargs.get('subdomain') try: t = await kwargs.get('account').tournaments.create( kwargs.get('name'), kwargs.get('url'), tournament_type, **params) except ChallongeException as e: await client.send_message(message.channel, T_OnChallongeException.format(e)) else: role = await client.create_role(message.server, name='Participant_' + kwargs.get('name'), mentionable=True) chChannel = await client.create_channel(message.server, 'T_' + kwargs.get('name')) db.add_tournament(t['id'], chChannel, role.id, message.author.id) await client.send_message( message.channel, T_TournamentCreated.format(kwargs.get('name'), t['full-challonge-url'], role.mention, chChannel.mention)) await update_channel_topic(kwargs.get('account'), t, client, chChannel) await modules.on_state_change(message.server.id, TournamentState.pending, t_name=kwargs.get('name'), me=message.server.me)
async def get_blocking_matches(account, t_id): try: t = await account.tournaments.show(t_id, include_participants=1, include_matches=1) except ChallongeException as e: return None, T_OnChallongeException.format(e) if len(t['matches']) == 0: return '✅ No blocking matches!', None matches = t['matches'] participants = t['participants'] blocking = {} def process_prereq_match(m, player, blocked): key = '%s-prereq-match-id' % player if key in m and m[key]: found = False for k, v in blocking.items(): if m[key] in v: log_challonge.debug( '%s is already in blocked matches of %s - adding %s' % (m[key], k, m['id'])) blocking[k].append(m['id']) found = True if not found: log_challonge.debug('Adding %s to the blocked list' % m[key]) blocked.append(m['id']) return m[key] return None def check_match(m_id, blocked): for k, v in blocking.items(): if m_id in v: log_challonge.debug( '%s is already in blocked matches of %s - adding %s' % (m_id, k, blocked)) blocking[k].extend(blocked) return m = find(matches, 'id', m_id) if not m: log_challonge.debug('no match with id #%s' % m_id) return debug_p1Name = 'None' if not m['player1-id'] else find( participants, 'id', m['player1-id'])['name'] debug_p2Name = 'None' if not m['player2-id'] else find( participants, 'id', m['player2-id'])['name'] log_challonge.debug('check_match %s: %s Vs %s (%s)' % (m_id, debug_p1Name, debug_p2Name, m['state'])) if m['state'] == 'pending': processed = process_prereq_match(m, 'player1', blocked) if processed: log_challonge.debug('%s needs to dive deeper' % processed) check_match(processed, blocked) processed = process_prereq_match(m, 'player2', blocked) if processed: blocked.append(processed) log_challonge.debug('%s needs to dive deeper' % processed) check_match(processed, blocked) elif m['state'] == 'open': if m_id in blocking: blocking[m_id].extend(blocked) else: blocking.update({m_id: blocked}) log_challonge.debug(blocking) for m in matches: if m['state'] == 'pending' and (m['player1-id'] or m['player2-id']): found = False for k, v in blocking.items(): if m['id'] in v: log_challonge.debug( '%s is already in blocked matches of %s' % (m['id'], k)) found = True if not found: log_challonge.debug('Checking blockers for %s' % m['id']) blocked = [m['id']] if not m['player1-id'] and 'player1-prereq-match-id' in m and m[ 'player1-prereq-match-id']: check_match(m['player1-prereq-match-id'], blocked) if not m['player2-id'] and 'player2-prereq-match-id' in m and m[ 'player2-prereq-match-id']: check_match(m['player2-prereq-match-id'], blocked) sorted_m = sorted(blocking.items(), key=lambda x: len(x[1]), reverse=True) log_challonge.debug(sorted_m) msg = ['✅ Blocking matches:'] for tup_m in sorted_m: m = find(matches, 'id', tup_m[0]) if m: p1 = find(participants, 'id', m['player1-id']) p2 = find(participants, 'id', m['player2-id']) msg.append('`%s game%s blocked` by: %s 🆚 %s' % (len(tup_m[1]), 's' if len(tup_m[1]) > 1 else '', p1['name'], p2['name'])) return '\n '.join(msg), None