예제 #1
0
    async def problems(self, ctx, member: discord.Member = None):
        if not member:
            member = ctx.author
        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

        round_info = self.db.get_round_info(ctx.guild.id, member.id)
        await ctx.send(embed=discord_.round_problems_embed(round_info))
예제 #2
0
async def update_rounds(client):
    rounds = db.get_all_rounds()
    global api
    if api is None:
        api = challonge_api.ChallongeAPI(client)
    for round in rounds:
        try:
            guild = client.get_guild(round.guild)
            resp = await updation.update_round(round)
            if not resp[0]:
                logging_channel = await client.fetch_channel(
                    os.environ.get("LOGGING_CHANNEL"))
                await logging_channel.send(
                    f"Error while updating rounds: {resp[1]}")
                continue
            resp = resp[1]
            channel = client.get_channel(round.channel)

            if resp[2] or resp[1]:
                await channel.send(
                    f"{' '.join([(await guild.fetch_member(int(m))).mention for m in round.users.split()])} there is an update in standings"
                )

            for i in range(len(resp[0])):
                if len(resp[0][i]):
                    await channel.send(embed=discord.Embed(
                        description=
                        f"{' '.join([(await guild.fetch_member(m)).mention for m in resp[0][i]])} has solved problem worth **{round.points.split()[i]}** points",
                        color=discord.Color.blue()))

            if not resp[1] and resp[2]:
                new_info = db.get_round_info(round.guild, round.users)
                await channel.send(
                    embed=discord_.round_problems_embed(new_info))

            if resp[1]:
                round_info = db.get_round_info(round.guild, round.users)
                ranklist = updation.round_score(
                    list(map(int, round_info.users.split())),
                    list(map(int, round_info.status.split())),
                    list(map(int, round_info.times.split())))
                eloChanges = elo.calculateChanges([[
                    (await guild.fetch_member(user.id)), user.rank,
                    db.get_match_rating(round_info.guild, user.id)[-1]
                ] for user in ranklist])

                for id in list(map(int, round_info.users.split())):
                    db.add_rating_update(round_info.guild, id,
                                         eloChanges[id][0])

                db.delete_round(round_info.guild, round_info.users)
                db.add_to_finished_rounds(round_info)

                embed = discord.Embed(color=discord.Color.dark_magenta())
                pos, name, ratingChange = '', '', ''
                for user in ranklist:
                    handle = db.get_handle(round_info.guild, user.id)
                    emojis = [
                        ":first_place:", ":second_place:", ":third_place:"
                    ]
                    pos += f"{emojis[user.rank - 1] if user.rank <= len(emojis) else str(user.rank)} **{user.points}**\n"
                    name += f"[{handle}](https://codeforces.com/profile/{handle})\n"
                    ratingChange += f"{eloChanges[user.id][0]} (**{'+' if eloChanges[user.id][1] >= 0 else ''}{eloChanges[user.id][1]}**)\n"
                embed.add_field(name="Position", value=pos)
                embed.add_field(name="User", value=name)
                embed.add_field(name="Rating changes", value=ratingChange)
                embed.set_author(name=f"Round over! Final standings")
                await channel.send(embed=embed)

                if round_info.tournament == 1:
                    tournament_info = db.get_tournament_info(round_info.guild)
                    if not tournament_info or tournament_info.status != 2:
                        continue
                    if ranklist[1].rank == 1 and tournament_info.type != 2:
                        await discord_.send_message(
                            channel,
                            "Since the round ended in a draw, you will have to compete again for it to be counted in the tournament"
                        )
                    else:
                        res = await tournament_helper.validate_match(
                            round_info.guild, ranklist[0].id, ranklist[1].id,
                            api, db)
                        if not res[0]:
                            await discord_.send_message(
                                channel, res[1] +
                                "\n\nIf you think this is a mistake, type `.tournament forcewin <handle>` to grant victory to a user"
                            )
                        else:
                            draw = True if ranklist[1].rank == 1 else False
                            scores = f"{ranklist[0].points}-{ranklist[1].points}" if res[
                                1]['player1'] == res[1][
                                    ranklist[0].
                                    id] else f"{ranklist[1].points}-{ranklist[0].points}"
                            match_resp = await api.post_match_results(
                                res[1]['tournament_id'], res[1]['match_id'],
                                scores,
                                res[1][ranklist[0].id] if not draw else "tie")
                            if not match_resp or 'errors' in match_resp:
                                await discord_.send_message(
                                    channel,
                                    "Some error occurred while validating tournament match. \n\nType `.tournament forcewin <handle>` to grant victory to a user manually"
                                )
                                if match_resp and 'errors' in match_resp:
                                    logging_channel = await client.fetch_channel(
                                        os.environ.get("LOGGING_CHANNEL"))
                                    await logging_channel.send(
                                        f"Error while validating tournament rounds: {match_resp['errors']}"
                                    )
                                continue
                            winner_handle = db.get_handle(
                                round_info.guild, ranklist[0].id)
                            await discord_.send_message(
                                channel,
                                f"{f'Congrats **{winner_handle}** for qualifying to the next round. :tada:' if not draw else 'The round ended in a draw!'}\n\nTo view the list of future tournament rounds, type `.tournament matches`"
                            )
                            if await tournament_helper.validate_tournament_completion(
                                    round_info.guild, api, db):
                                await api.finish_tournament(
                                    res[1]['tournament_id'])
                                await asyncio.sleep(3)
                                winner_handle = await tournament_helper.get_winner(
                                    res[1]['tournament_id'], api)
                                await channel.send(
                                    embed=tournament_helper.
                                    tournament_over_embed(
                                        round_info.guild, winner_handle, db))
                                db.add_to_finished_tournaments(
                                    db.get_tournament_info(round_info.guild),
                                    winner_handle)
                                db.delete_tournament(round_info.guild)

        except Exception as e:
            logging_channel = await client.fetch_channel(
                os.environ.get("LOGGING_CHANNEL"))
            await logging_channel.send(
                f"Error while updating rounds: {str(traceback.format_exc())}")
예제 #3
0
    async def challenge(self, ctx, *users: discord.Member):
        users = list(set(users))
        if len(users) == 0:
            await discord_.send_message(
                ctx,
                f"The correct usage is `.round challenge @user1 @user2...`")
            return
        if ctx.author not in users:
            users.append(ctx.author)
        if len(users) > MAX_ROUND_USERS:
            await ctx.send(
                f"{ctx.author.mention} atmost {MAX_ROUND_USERS} users can compete at a time"
            )
            return
        for i in users:
            if not self.db.get_handle(ctx.guild.id, i.id):
                await discord_.send_message(
                    ctx,
                    f"Handle for {i.mention} not set! Use `.handle identify` to register"
                )
                return
            if self.db.in_a_round(ctx.guild.id, i.id):
                await discord_.send_message(
                    ctx, f"{i.mention} is already in a round!")
                return

        embed = discord.Embed(
            description=
            f"{' '.join(x.mention for x in users)} react on the message with ✅ within 30 seconds to join the round. {'Since you are the only participant, this will be a practice round and there will be no rating changes' if len(users) == 1 else ''}",
            color=discord.Color.purple())
        message = await ctx.send(embed=embed)
        await message.add_reaction("✅")

        all_reacted = False
        reacted = []

        def check(reaction, user):
            return reaction.message.id == message.id and reaction.emoji == "✅" and user in users

        while True:
            try:
                reaction, user = await self.client.wait_for('reaction_add',
                                                            timeout=30,
                                                            check=check)
                reacted.append(user)
                if all(item in reacted for item in users):
                    all_reacted = True
                    break
            except asyncio.TimeoutError:
                break

        if not all_reacted:
            await discord_.send_message(
                ctx,
                f"Unable to start round, some participant(s) did not react in time!"
            )
            return

        problem_cnt = await discord_.get_time_response(
            self.client, ctx,
            f"{ctx.author.mention} enter the number of problems between [1, {MAX_PROBLEMS}]",
            30, ctx.author, [1, MAX_PROBLEMS])
        if not problem_cnt[0]:
            await discord_.send_message(
                ctx, f"{ctx.author.mention} you took too long to decide")
            return
        problem_cnt = problem_cnt[1]

        duration = await discord_.get_time_response(
            self.client, ctx,
            f"{ctx.author.mention} enter the duration of match in minutes between {MATCH_DURATION}",
            30, ctx.author, MATCH_DURATION)
        if not duration[0]:
            await discord_.send_message(
                ctx, f"{ctx.author.mention} you took too long to decide")
            return
        duration = duration[1]

        rating = await discord_.get_seq_response(
            self.client, ctx,
            f"{ctx.author.mention} enter {problem_cnt} space seperated integers denoting the ratings of problems (between {LOWER_RATING} and {UPPER_RATING})",
            60, problem_cnt, ctx.author, [LOWER_RATING, UPPER_RATING])
        if not rating[0]:
            await discord_.send_message(
                ctx, f"{ctx.author.mention} you took too long to decide")
            return
        rating = rating[1]

        points = await discord_.get_seq_response(
            self.client, ctx,
            f"{ctx.author.mention} enter {problem_cnt} space seperated integer denoting the points of problems (between 100 and 10,000)",
            60, problem_cnt, ctx.author, [100, 10000])
        if not points[0]:
            await discord_.send_message(
                ctx, f"{ctx.author.mention} you took too long to decide")
            return
        points = points[1]

        repeat = await discord_.get_time_response(
            self.client, ctx,
            f"{ctx.author.mention} do you want a new problem to appear when someone solves a problem (type 1 for yes and 0 for no)",
            30, ctx.author, [0, 1])
        if not repeat[0]:
            await discord_.send_message(
                ctx, f"{ctx.author.mention} you took too long to decide")
            return
        repeat = repeat[1]

        for i in users:
            if self.db.in_a_round(ctx.guild.id, i.id):
                await discord_.send_message(
                    ctx, f"{i.name} is already in a round!")
                return

        alts = await discord_.get_alt_response(
            self.client, ctx,
            f"{ctx.author.mention} Do you want to add any alts? Type none if not applicable else type `alts: handle_1 handle_2 ...` You can add upto **{MAX_ALTS}** alt(s)",
            MAX_ALTS, 60, ctx.author)

        if not alts:
            await discord_.send_message(
                ctx, f"{ctx.author.mention} you took too long to decide")
            return

        alts = alts[1]

        tournament = 0
        if len(users) == 2 and (await tournament_helper.is_a_match(
                ctx.guild.id, users[0].id, users[1].id, self.api, self.db)):
            tournament = await discord_.get_time_response(
                self.client, ctx,
                f"{ctx.author.mention} this round is a part of the tournament. Do you want the result of this round to be counted in the tournament. Type `1` for yes and `0` for no",
                30, ctx.author, [0, 1])
            if not tournament[0]:
                await discord_.send_message(
                    ctx, f"{ctx.author.mention} you took too long to decide")
                return
            tournament = tournament[1]

        await ctx.send(embed=discord.Embed(description="Starting the round...",
                                           color=discord.Color.green()))

        problems = await codeforces.find_problems(
            [self.db.get_handle(ctx.guild.id, x.id) for x in users] + alts,
            rating)
        if not problems[0]:
            await discord_.send_message(ctx, problems[1])
            return

        problems = problems[1]

        self.db.add_to_ongoing_round(ctx, users, rating, points, problems,
                                     duration, repeat, alts, tournament)
        round_info = self.db.get_round_info(ctx.guild.id, users[0].id)

        await ctx.send(embed=discord_.round_problems_embed(round_info))