async def _unregister(self, ctx, *, handle: str): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return tournament_info = self.db.get_tournament_info(ctx.guild.id) if not tournament_info: await discord_.send_message( ctx, "There is no ongoing tournament in the server currently") return if tournament_info.status != 0: await discord_.send_message(ctx, "The tournament has already begun") return registrants = self.db.get_registrants(ctx.guild.id) res = self.db.remove_registrant_by_handle(ctx.guild.id, handle) if not res: await discord_.send_message( ctx, f"The user with handle `{handle}` has not registered for the tournament" ) return await ctx.send(embed=discord.Embed( description= f"Successfully unregistered from the tournament. `{MAX_REGISTRANTS - len(registrants) + 1}` slots left.", color=discord.Color.green()))
async def match_invalidate(self, ctx, idx: int): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return tournament_info = self.db.get_tournament_info(ctx.guild.id) if not tournament_info: await discord_.send_message( ctx, "There is no ongoing tournament in the server currently") return if tournament_info.status != 2: await discord_.send_message( ctx, "The tournament has not begun yet, type `.tournament begin` to start the tournament" ) return matches_resp = await self.api.get_tournament_matches(tournament_info.id ) if not matches_resp or 'errors' in matches_resp: await discord_.send_message( ctx, "Some error occurred, try again later") if matches_resp and 'errors' in matches_resp: logging_channel = await self.client.fetch_channel( os.environ.get("LOGGING_CHANNEL")) await logging_channel.send( f"Error in match_invalidate: {ctx.guild.id} {matches_resp['errors']}" ) return match_id = None for match in matches_resp: if match['match']['state'] == 'complete' and match['match'][ 'suggested_play_order'] == idx: match_id = match['match']['id'] if match_id: await self.api.invalidate_match(tournament_info.id, match_id) await discord_.send_message( ctx, f"Invalidated match number `{idx}`. All future matches whose results was dependent on this match have also been reset" ) else: await discord_.send_message(ctx, f"Couldn't find match number `{idx}`")
async def _invalidate(self, ctx, member: discord.Member): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return if not self.db.in_a_round(ctx.guild.id, member.id): await discord_.send_message(ctx, f"{member.mention} is not in a round") return self.db.delete_round(ctx.guild.id, member.id) await discord_.send_message(ctx, f"Round deleted")
async def _invalidate(self, ctx, member: discord.Member): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return if not self.db.in_a_match(ctx.guild.id, member.id): await discord_.send_message( ctx, f"User {member.mention} is not in a match.") return self.db.delete_match(ctx.guild.id, member.id) await ctx.send( embed=discord.Embed(description="Match has been invalidated", color=discord.Color.green()))
async def setup(self, ctx, tournament_type: int, *, tournament_name: str): """ **tournament_name:** Alpha-numeric string (Max 50 characters) **tournament_type:** Integer (0: single elimination, 1: double elimination, 2: swiss) """ if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return if len(tournament_name) not in range(1, 51): await discord_.send_message( ctx, "The tournament name should be 50 character max") return if any([not ch.isalnum() and ch != ' ' for ch in tournament_name]): await discord_.send_message( ctx, "The tournament name should contain only alpha-numeric characters" ) return if tournament_type not in range(0, 3): await discord_.send_message( ctx, "Tournament type should be either 0, 1 or 2. (0: single elimination, 1: double elimination, 2: swiss)" ) return if self.db.get_tournament_info(ctx.guild.id): await discord_.send_message( ctx, "A tournament is already in progress in this server!") return self.db.add_tournament(ctx.guild.id, tournament_name, tournament_type, 0, "-", 0) types = ["Single Elimination", "Double Elimination", "Swiss"] desc = f""" Initialised a {types[tournament_type]} tournament. To register, type `.tournament register` (Max registrations: **{MAX_REGISTRANTS}**) To unregister, type `.tournament unregister` To start the tournament, type `.tournament begin` """ embed = discord.Embed(description=desc, color=discord.Color.green()) embed.set_author(name=tournament_name) await ctx.send(embed=embed)
async def remove(self, ctx, member: discord.Member): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return if not self.db.get_handle(ctx.guild.id, member.id): await discord_.send_message( ctx, f"Handle for {member.mention} not set") return self.db.remove_handle(ctx.guild.id, member.id) await ctx.send(embed=Embed( description=f"Handle for {member.mention} removed successfully", color=Color.green()))
async def set(self, ctx, member: discord.Member, handle: str): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return data = await self.cf.check_handle(handle) if not data[0]: await discord_.send_message(ctx, data[1]) return handle = data[1]['handle'] if self.db.get_handle(ctx.guild.id, member.id): await discord_.send_message( ctx, f"Handle for user {member.mention} already set to {self.db.get_handle(ctx.guild.id, member.id)}" ) return # all conditions met data = data[1] if "rating" not in data: rating = 0 rank = "unrated" else: rating = data['rating'] rank = data['rank'] self.db.add_handle(ctx.guild.id, member.id, handle, rating) self.db.add_rated_user(ctx.guild.id, member.id) embed = discord.Embed( description= f'Handle for user {member.mention} successfully set to [{handle}](https://codeforces.com/profile/{handle})', color=Color(cf_colors[rank.lower()])) embed.add_field(name='Rank', value=f'{rank}', inline=True) embed.add_field(name='Rating', value=f'{rating}', inline=True) embed.set_thumbnail(url=f"{data['titlePhoto']}") await ctx.send(embed=embed)
async def delete_(self, ctx): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return tournament_info = self.db.get_tournament_info(ctx.guild.id) if not tournament_info: await discord_.send_message( ctx, "There is no ongoing tournament in the server currently") return resp = await discord_.get_time_response( self.client, ctx, "Are you sure you want to delete the tournament? This action is irreversable. Type `1` for yes and `0` for no", 30, ctx.author, [0, 1]) if resp[0] and resp[1] == 1: if tournament_info.status != 0: await self.api.delete_tournament(tournament_info.id) self.db.delete_tournament(ctx.guild.id) await discord_.send_message(ctx, "Tournament has been deleted")
async def forcedraw(self, ctx, *, handle: str): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return tournament_info = self.db.get_tournament_info(ctx.guild.id) if not tournament_info: await discord_.send_message( ctx, "There is no ongoing tournament in the server currently") return if tournament_info.status != 2: await discord_.send_message( ctx, "The tournament has not begun yet, type `.tournament begin` to start the tournament" ) return if tournament_info.type != 2: await discord_.send_message( ctx, "This command can only be used for swiss tournaments") return registrants = self.db.get_registrants(ctx.guild.id) challonge_id = None for user in registrants: if user.handle.lower() == handle.lower(): challonge_id = user.challonge_id if not challonge_id: await discord_.send_message( ctx, f"User with handle `{handle}` has not registered for the tournament" ) return matches_resp = await self.api.get_tournament_matches(tournament_info.id ) if not matches_resp or 'errors' in matches_resp: await discord_.send_message( ctx, "Some error occurred, try again later") if matches_resp and 'errors' in matches_resp: logging_channel = await self.client.fetch_channel( os.environ.get("LOGGING_CHANNEL")) await logging_channel.send( f"Error in forcewin match fetching: {ctx.guild.id} {matches_resp['errors']}" ) return match_id = None player1_id = None for match in matches_resp: if match['match']['state'] != 'open': continue if match['match']['player1_id'] == challonge_id or match['match'][ 'player2_id'] == challonge_id: match_id = match['match']['id'] player1_id = match['match']['player1_id'] break if not match_id: await discord_.send_message( ctx, f"Couldn't find a match for handle `{handle}`") return scores = await discord_.get_seq_response( self.client, ctx, f"{ctx.author.mention} enter 2 space seperated integers denoting the scores of the players", 30, 2, ctx.author, [0, 10000]) if not scores[0]: return if challonge_id != player1_id: scores[1][0], scores[1][1] = scores[1][1], scores[1][0] resp = await self.api.post_match_results( tournament_info.id, match_id, f"{scores[1][0]}-{scores[1][1]}", "tie") if not resp or 'errors' in resp: await discord_.send_message( ctx, "Some error occurred, try again later") if resp and 'errors' in resp: logging_channel = await self.client.fetch_channel( os.environ.get("LOGGING_CHANNEL")) await logging_channel.send( f"Error in forcedraw match score reporting: {ctx.guild.id} {resp['errors']}" ) else: await discord_.send_message( ctx, f"Match involving `{handle}` has been drawn") if await tournament_helper.validate_tournament_completion( tournament_info.guild, self.api, self.db): await self.api.finish_tournament(tournament_info.id) winner_handle = await tournament_helper.get_winner( tournament_info.id, self.api) await ctx.send(embed=tournament_helper.tournament_over_embed( tournament_info.guild, winner_handle, self.db)) self.db.delete_tournament(tournament_info.guild) self.db.add_to_finished_tournaments(tournament_info, winner_handle)
async def begin(self, ctx): if not discord_.has_admin_privilege(ctx): await discord_.send_message( ctx, f"{ctx.author.mention} you require 'manage server' permission or one of the " f"following roles: {', '.join(ADMIN_PRIVILEGE_ROLES)} to use this command" ) return tournament_info = self.db.get_tournament_info(ctx.guild.id) if not tournament_info: await discord_.send_message( ctx, "There is no ongoing tournament in the server currently") return if tournament_info.status == 2: await discord_.send_message( ctx, f"The tournament has already begun! Type `.tournament matches` or `.tournament info` to view details about the tournament" ) return registrants = self.db.get_registrants(ctx.guild.id) if not registrants or len(registrants) < 2: await discord_.send_message( ctx, "Not enough registrants for the tournament yet") return logging_channel = await self.client.fetch_channel( os.environ.get("LOGGING_CHANNEL")) if tournament_info.status == 0: resp = await discord_.get_time_response( self.client, ctx, "Are you sure you want to start the tournament? No new registrations will be allowed once the tournament has started. Type `1` for yes and `0` for no", 30, ctx.author, [0, 1]) if not resp[0] or resp[1] == 0: ctx.command.reset_cooldown(ctx) return await ctx.send(f"Setting up tournament...") tournament_resp = await self.api.add_tournament(tournament_info) if not tournament_resp or 'errors' in tournament_resp: ctx.command.reset_cooldown(ctx) await discord_.send_message( ctx, "Some error occurred, try again later") if tournament_resp and 'errors' in tournament_resp: await logging_channel.send( f"Error in tournament setup: {ctx.guild.id} {tournament_resp['errors']}" ) return # api takes some time to register tournament id await asyncio.sleep(5) await ctx.send(f"Adding participants...") participants_resp = await self.api.bulk_add_participants( tournament_resp['tournament']['id'], [{ "name": f"{registrants[i].handle} ({registrants[i].rating})", "seed": i + 1 } for i in range(len(registrants))]) if not participants_resp or 'errors' in participants_resp: ctx.command.reset_cooldown(ctx) await discord_.send_message( ctx, "Some error occurred, try again later") if participants_resp and 'errors' in participants_resp: await logging_channel.send( f"Error in bulk adding participants: {ctx.guild.id} {participants_resp['errors']}" ) await self.api.delete_tournament( tournament_resp['tournament']['id']) return await asyncio.sleep(5) await ctx.send("Enabling brackets predictions...") predictions_resp = await self.api.open_for_predictions( tournament_resp['tournament']['id']) if not predictions_resp or 'errors' in predictions_resp: ctx.command.reset_cooldown(ctx) await discord_.send_message( ctx, "Some error occurred, try again later") if predictions_resp and 'errors' in predictions_resp: await logging_channel.send( f"Error in enabling predictions: {ctx.guild.id} {predictions_resp['errors']}" ) await self.api.delete_tournament( tournament_resp['tournament']['id']) return self.db.update_tournament_params( tournament_resp['tournament']['id'], tournament_resp['tournament']['url'], 1, ctx.guild.id) for data in participants_resp: self.db.map_user_to_challongeid( ctx.guild.id, registrants[data['participant']['seed'] - 1].discord_id, data['participant']['id']) desc = "" desc += f"The tournament has been setup. You can find the brackets [here](https://challonge.com/{tournament_resp['tournament']['url']})\n" desc += f"You can now make predictions on each of the brackets and climb up the predictions leaderboard. Make new predictions [here](https://challonge.com/tournaments/{tournament_resp['tournament']['id']}/predictions/new)\n\n" desc += f"Note that the tournament has **not** started yet. Once everyone has made their predictions, type `.tournament begin` for the tournament to officially begin\n\n" desc += f"**Tournament type**: {['Single Elimination', 'Double Elimination', 'Swiss'][tournament_info.type]}\n" desc += f"**Number of registrations**: {len(registrants)}" embed = discord.Embed(description=desc, color=discord.Color.green()) embed.set_author(name=tournament_info.name) await ctx.send(embed=embed) ctx.command.reset_cooldown(ctx) else: await ctx.send(f"Starting the tournament...") tournament_resp = await self.api.start_tournament( tournament_info.id) if not tournament_resp or 'errors' in tournament_resp: ctx.command.reset_cooldown(ctx) await discord_.send_message( ctx, "Some error occurred, try again later") if tournament_resp and 'errors' in tournament_resp: await logging_channel.send( f"Error in tournament setup: {ctx.guild.id} {tournament_resp['errors']}" ) return self.db.update_tournament_params(tournament_info.id, tournament_info.url, 2, ctx.guild.id) desc = f"The tournament has officially begun! View brackets on this [link](https://challonge.com/{tournament_info.url})\n\n" desc += f"To play tournament matches just use the `.round` command of the bot and challenge someone to a round. \n" \ f"If the round is part of the tournament, then the bot will ask whether you want the result of the round " \ f"to be counted in the tournament. \nIn case of a draw, you will have to play the round again. GLHF!\n\n" desc += f"**Some useful commands**:\n\n" desc += f"`.tournament matches`: View a list of future matches of the tournament\n" desc += f"`.tournament info`: View general info about the tournament\n" desc += f"`.tournament forcewin <handle>`: Grant victory to a user without conducting the match\n" desc += f"`.tournament invalidate`: Invalidate the tournament\n" embed = discord.Embed(description=desc, color=discord.Color.green()) embed.set_author(name=tournament_info.name) await ctx.send(embed=embed)