예제 #1
0
    def _make_standings_pages(self, contest, problem_indices, handle_standings, deltas=None):
        pages = []
        handle_standings_chunks = paginator.chunkify(handle_standings, _STANDINGS_PER_PAGE)
        num_chunks = len(handle_standings_chunks)
        delta_chunks = paginator.chunkify(deltas, _STANDINGS_PER_PAGE) if deltas else [None] * num_chunks

        if contest.type == 'CF':
            get_table = functools.partial(self._get_cf_or_ioi_standings_table, mode='cf')
        elif contest.type == 'ICPC':
            get_table = self._get_icpc_standings_table
        elif contest.type == 'IOI':
            get_table = functools.partial(self._get_cf_or_ioi_standings_table, mode='ioi')
        else:
            assert False, f'Unexpected contest type {contest.type}'

        num_pages = 1
        for handle_standings_chunk, delta_chunk in zip(handle_standings_chunks, delta_chunks):
            header_style, body_style, header, body = get_table(problem_indices,
                                                               handle_standings_chunk,
                                                               delta_chunk)
            t = table.Table(table.Style(header=header_style, body=body_style))
            t += table.Header(*header)
            t += table.Line('\N{EM DASH}')
            for row in body:
                t += table.Data(*row)
            t += table.Line('\N{EM DASH}')
            page_num_footer = f' # Page: {num_pages} / {num_chunks}' if num_chunks > 1 else ''

            # We use yaml to get nice colors in the ranklist.
            content = f'```yaml\n{t}\n{page_num_footer}```'
            pages.append((content, None))
            num_pages += 1

        return pages
예제 #2
0
    async def _needs_fixing(self, ctx, user_id_and_handles):
        handles = []
        rev_lookup = {}
        for user_id, handle in user_id_and_handles:
            member = ctx.guild.get_member(user_id)
            handles.append(handle)
            rev_lookup[handle] = member

        to_fix = []
        chunks = paginator.chunkify(handles, cf.MAX_HANDLES_PER_QUERY)
        for handle_chunk in chunks:
            while handle_chunk:
                try:
                    users = await cf.user.info(handles=handle_chunk)

                    # Users could still have changed capitalization
                    for handle, user in zip(handle_chunk, users):
                        assert handle.lower() == user.handle.lower()
                        if handle != user.handle:
                            to_fix.append((rev_lookup[handle], handle))
                    break
                except cf.HandleNotFoundError as e:
                    to_fix.append((rev_lookup[e.handle], e.handle))
                    handle_chunk.remove(e.handle)
        return to_fix
예제 #3
0
    async def ranklist(self, ctx):
        """Show the list of duelists with their duel rating."""
        users = [(ctx.guild.get_member(user_id), rating) for user_id, rating in cf_common.user_db.get_duelists()]
        users = [(member, cf_common.user_db.get_handle(member.id, ctx.guild.id), rating)
                 for member, rating in users
                 if member is not None and cf_common.user_db.get_num_duel_completed(member.id) > 0]

        _PER_PAGE = 10
        def make_page(chunk, page_num):
            style = table.Style('{:>}  {:<}  {:<}  {:<}')
            t = table.Table(style)
            t += table.Header('#', 'Name', 'Handle', 'Rating')
            t += table.Line()
            for index, (member, handle, rating) in enumerate(chunk):
                rating_str = f'{rating} ({rating2rank(rating).title_abbr})'
                t += table.Data(_PER_PAGE * page_num + index, f'{member.display_name}', handle, rating_str)

            table_str = f'```\n{t}\n```'
            embed = discord_common.cf_color_embed(description=table_str)
            return 'List of duelists', embed

        if not users:
            raise DuelCogError('There are no active duelists.')

        pages = [make_page(chunk, k) for k, chunk in enumerate(paginator.chunkify(users, _PER_PAGE))]
        paginator.paginate(self.bot, ctx.channel, pages, wait_time=5 * 60, set_pagenum_footers=True)
예제 #4
0
    def _paginate_duels(self, data, message, guild_id, show_id):
        def make_line(entry):
            duelid, start_time, finish_time, name, challenger, challengee, winner = entry
            duel_time = cf_common.pretty_time_format(finish_time - start_time,
                                                     shorten=True,
                                                     always_seconds=True)
            problem = cf_common.cache2.problem_cache.problem_by_name[name]
            when = cf_common.days_ago(start_time)
            idstr = f'{duelid}: '
            if winner != Winner.DRAW:
                loser = get_cf_user(
                    challenger if winner == Winner.CHALLENGEE else challengee,
                    guild_id)
                winner = get_cf_user(
                    challenger if winner == Winner.CHALLENGER else challengee,
                    guild_id)
                return f'{idstr if show_id else str()}[{name}]({problem.url}) [{problem.rating}] won by [{winner.handle}]({winner.url}) vs [{loser.handle}]({loser.url}) {when} in {duel_time}'
            else:
                challenger = get_cf_user(challenger, guild_id)
                challengee = get_cf_user(challengee, guild_id)
                return f'{idstr if show_id else str()}[{name}]({problem.url}) [{problem.rating}] drawn by [{challenger.handle}]({challenger.url}) and [{challengee.handle}]({challengee.url}) {when} after {duel_time}'

        def make_page(chunk):
            log_str = '\n'.join(make_line(entry) for entry in chunk)
            embed = discord_common.cf_color_embed(description=log_str)
            return message, embed

        if not data:
            raise DuelCogError('There are no duels to show.')

        return [make_page(chunk) for chunk in paginator.chunkify(data, 7)]
예제 #5
0
    async def ongoing(self, ctx, member: discord.Member = None):
        def make_line(entry):
            start_time, name, challenger, challengee = entry
            problem = cf_common.cache2.problem_cache.problem_by_name[name]
            now = datetime.datetime.now().timestamp()
            when = cf_common.pretty_time_format(now - start_time,
                                                shorten=True,
                                                always_seconds=True)
            challenger = get_cf_user(challenger, ctx.guild.id)
            challengee = get_cf_user(challengee, ctx.guild.id)
            return f'[{challenger.handle}]({challenger.url}) vs [{challengee.handle}]({challengee.url}): [{name}]({problem.url}) [{problem.rating}] {when}'

        def make_page(chunk):
            message = f'List of ongoing duels:'
            log_str = '\n'.join(make_line(entry) for entry in chunk)
            embed = discord_common.cf_color_embed(description=log_str)
            return message, embed

        member = member or ctx.author
        data = cf_common.user_db.get_ongoing_duels()
        if not data:
            raise DuelCogError('There are no ongoing duels.')

        pages = [make_page(chunk) for chunk in paginator.chunkify(data, 7)]
        paginator.paginate(self.bot,
                           ctx.channel,
                           pages,
                           wait_time=5 * 60,
                           set_pagenum_footers=True)
예제 #6
0
    async def vcratings(self, ctx):
        users = [(await self.member_converter.convert(ctx, str(member_id)), handle, cf_common.user_db.get_vc_rating(member_id, default_if_not_exist=False))
                 for member_id, handle in cf_common.user_db.get_handles_for_guild(ctx.guild.id)]
        # Filter only rated users. (Those who entered at least one rated vc.)
        users = [(member, handle, rating)
                 for member, handle, rating in users
                 if rating is not None]
        users.sort(key=lambda user: -user[2])

        _PER_PAGE = 10

        def make_page(chunk, page_num):
            style = table.Style('{:>}  {:<}  {:<}  {:<}')
            t = table.Table(style)
            t += table.Header('#', 'Name', 'Handle', 'Rating')
            t += table.Line()
            for index, (member, handle, rating) in enumerate(chunk):
                rating_str = f'{rating} ({cf.rating2rank(rating).title_abbr})'
                t += table.Data(_PER_PAGE * page_num + index, f'{member.display_name}', handle, rating_str)

            table_str = f'```\n{t}\n```'
            embed = discord_common.cf_color_embed(description=table_str)
            return 'VC Ratings', embed

        if not users:
            raise ContestCogError('There are no active VCers.')

        pages = [make_page(chunk, k) for k, chunk in enumerate(paginator.chunkify(users, _PER_PAGE))]
        paginator.paginate(self.bot, ctx.channel, pages, wait_time=5 * 60, set_pagenum_footers=True)
예제 #7
0
    async def gitlog(self, ctx, member: discord.Member = None):
        """Displays the list of gitgud problems issued to the specified member, excluding those noguded by admins.
        If the challenge was completed, time of completion and amount of points gained will also be displayed.
        """
        def make_line(entry):
            issue, finish, name, contest, index, delta, status = entry
            problem = cf_common.cache2.problem_cache.problem_by_name[name]
            line = f"[{name}]({problem.url})\N{EN SPACE}[{problem.rating}]"
            if finish:
                time_str = cf_common.days_ago(finish)
                points = f"{_GITGUD_SCORE_DISTRIB[delta // 100 + 3]:+}"
                line += f"\N{EN SPACE}{time_str}\N{EN SPACE}[{points}]"
            return line

        def make_page(chunk):
            message = f"gitgud log for {member.display_name}"
            log_str = "\n".join(make_line(entry) for entry in chunk)
            embed = discord_common.cf_color_embed(description=log_str)
            return message, embed

        member = member or ctx.author
        data = cf_common.user_db.gitlog(member.id)
        pages = [make_page(chunk) for chunk in paginator.chunkify(data, 7)]
        paginator.paginate(
            self.bot,
            ctx.channel,
            pages,
            wait_time=5 * 60,
            set_pagenum_footers=True,
        )
예제 #8
0
    async def upsolve(self, ctx, choice: int = -1):
        """Request an unsolved problem from a contest you participated in
        delta  | -300 | -200 | -100 |  0  | +100 | +200 | +300 | +400 | +500
        points |   2  |   3  |   5  |  8  |  12  |  17  |  23  |  23  |  23
        """
        await self._validate_gitgud_status(ctx, delta=None)
        handle, = await cf_common.resolve_handles(ctx, self.converter,
                                                  ('!' + str(ctx.author), ))
        user = cf_common.user_db.fetch_cf_user(handle)
        rating = round(user.effective_rating, -2)
        resp = await cf.user.rating(handle=handle)
        contests = {change.contestId for change in resp}
        submissions = await cf.user.status(handle=handle)
        solved = {
            sub.problem.name
            for sub in submissions if sub.verdict == 'OK'
        }
        problems = [
            prob for prob in cf_common.cache2.problem_cache.problems
            if prob.name not in solved and prob.contestId in contests and (
                (prob.rating - rating) >= _GITGUD_MAX_NEG_DELTA_VALUE and
                (prob.rating - rating) <= _GITGUD_MAX_POS_DELTA_VALUE)
        ]

        if not problems:
            raise CodeforcesCogError(
                'Problems not found within the search parameters')

        problems.sort(key=lambda problem: cf_common.cache2.contest_cache.
                      get_contest(problem.contestId).startTimeSeconds,
                      reverse=True)

        if choice > 0 and choice <= len(problems):
            problem = problems[choice - 1]
            await self._gitgud(ctx, handle, problem, problem.rating - rating)
        else:
            problems = problems[:100]

            def make_line(i, prob):
                data = (f'{i + 1}: [{prob.name}]({prob.url}) [{prob.rating}]')
                return data

            def make_page(chunk, pi, num):
                title = f'Select a problem to upsolve (1-{num}):'
                msg = '\n'.join(
                    make_line(10 * pi + i, prob)
                    for i, prob in enumerate(chunk))
                embed = discord_common.cf_color_embed(description=msg)
                return title, embed

            pages = [
                make_page(chunk, pi, len(problems))
                for pi, chunk in enumerate(paginator.chunkify(problems, 10))
            ]
            paginator.paginate(self.bot,
                               ctx.channel,
                               pages,
                               wait_time=5 * 60,
                               set_pagenum_footers=True)
예제 #9
0
 async def _updatestatus(self, ctx):
     gid = ctx.guild.id
     active_ids = [m.id for m in ctx.guild.members]
     cf_common.user_db.reset_status(gid)
     rc = sum(
         cf_common.user_db.update_status(gid, chunk)
         for chunk in paginator.chunkify(active_ids, 100))
     await ctx.send(f'{rc} members active with handle')
예제 #10
0
    async def vc(self, ctx, *args: str):
        """Recommends a contest based on Codeforces rating of the handle provided.
        e.g ;vc mblazev c1729 +global +hello +goodbye +avito"""
        markers = [x for x in args if x[0] == '+']
        handles = [x for x in args if x[0] != '+'] or ('!' + str(ctx.author), )
        handles = await cf_common.resolve_handles(ctx,
                                                  self.converter,
                                                  handles,
                                                  maxcnt=25)
        info = await cf.user.info(handles=handles)
        contests = cf_common.cache2.contest_cache.get_contests_in_phase(
            'FINISHED')

        if not markers:
            divr = sum(user.effective_rating for user in info) / len(handles)
            div1_indicators = ['div1', 'global', 'avito', 'goodbye', 'hello']
            markers = [
                'div3'
            ] if divr < 1600 else ['div2'] if divr < 2100 else div1_indicators

        recommendations = {
            contest.id
            for contest in contests if contest.matches(markers)
            and not cf_common.is_nonstandard_contest(contest) and not any(
                cf_common.is_contest_writer(contest.id, handle)
                for handle in handles)
        }

        # Discard contests in which user has non-CE submissions.
        visited_contests = await cf_common.get_visited_contests(handles)
        recommendations -= visited_contests

        if not recommendations:
            raise CodeforcesCogError('Unable to recommend a contest')

        recommendations = list(recommendations)
        random.shuffle(recommendations)
        contests = [
            cf_common.cache2.contest_cache.get_contest(contest_id)
            for contest_id in recommendations[:25]
        ]

        def make_line(c):
            return f'[{c.name}]({c.url}) {cf_common.pretty_time_format(c.durationSeconds)}'

        def make_page(chunk):
            str_handles = '`, `'.join(handles)
            message = f'Recommended contest(s) for `{str_handles}`'
            vc_str = '\n'.join(make_line(contest) for contest in chunk)
            embed = discord_common.cf_color_embed(description=vc_str)
            return message, embed

        pages = [make_page(chunk) for chunk in paginator.chunkify(contests, 5)]
        paginator.paginate(self.bot,
                           ctx.channel,
                           pages,
                           wait_time=5 * 60,
                           set_pagenum_footers=True)
예제 #11
0
 def _make_contest_pages(contests, title):
     pages = []
     chunks = paginator.chunkify(contests, _CONTESTS_PER_PAGE)
     for chunk in chunks:
         embed = discord_common.cf_color_embed()
         for name, value in _get_embed_fields_from_contests(chunk):
             embed.add_field(name=name, value=value, inline=False)
         pages.append((title, embed))
     return pages
예제 #12
0
 async def fetch_missing_contests(self):
     """Fetch rating changes for contests which are not saved in database. Intended for
     manual trigger."""
     contests = self.cache_master.contest_cache.contests_by_phase['FINISHED']
     contests = [
         contest for contest in contests if not self.has_rating_changes_saved(contest.id)]
     total_changes = 0
     for contests_chunk in paginator.chunkify(contests, _CONTESTS_PER_BATCH_IN_CACHE_UPDATES):
         contests_chunk = await self._fetch(contests_chunk)
         self._save_changes(contests_chunk)
         total_changes += len(contests_chunk)
     return total_changes
예제 #13
0
    async def fullsolve(self, ctx, *args: str):
        """Displays a list of contests, sorted by number of unsolved problems.
        Contest names matching any of the provided tags will be considered. e.g ;fullsolve +edu"""
        handle, = await cf_common.resolve_handles(ctx, self.converter, ('!' + str(ctx.author),))
        tags = [x for x in args if x[0] == '+']

        problem_to_contests = cf_common.cache2.problemset_cache.problem_to_contests
        contests = [contest for contest in cf_common.cache2.contest_cache.get_contests_in_phase('FINISHED')
                    if (not tags or contest.matches(tags)) and not cf_common.is_nonstandard_contest(contest)]

        # subs_by_contest_id contains contest_id mapped to [list of problem.name]
        subs_by_contest_id = defaultdict(set)
        for sub in await cf.user.status(handle=handle):
            if sub.verdict == 'OK':
                try:
                    contest = cf_common.cache2.contest_cache.get_contest(sub.problem.contestId)
                    problem_id = (sub.problem.name, contest.startTimeSeconds)
                    for contestId in problem_to_contests[problem_id]:
                        subs_by_contest_id[contestId].add(sub.problem.name)
                except cache_system2.ContestNotFound:
                    pass

        contest_unsolved_pairs = []
        for contest in contests:
            num_solved = len(subs_by_contest_id[contest.id])
            try:
                num_problems = len(cf_common.cache2.problemset_cache.get_problemset(contest.id))
                if 0 < num_solved < num_problems:
                    contest_unsolved_pairs.append((contest, num_solved, num_problems))
            except cache_system2.ProblemsetNotCached:
                # In case of recent contents or cetain bugged contests
                pass

        contest_unsolved_pairs.sort(key=lambda p: (p[2] - p[1], -p[0].startTimeSeconds))

        if not contest_unsolved_pairs:
            await ctx.send(f'`{handle}` has no contests to fullsolve :confetti_ball:')
            return

        def make_line(entry):
            contest, solved, total = entry
            return f'[{contest.name}]({contest.url})\N{EN SPACE}[{solved}/{total}]'

        def make_page(chunk):
            message = f'Fullsolve list for `{handle}`'
            full_solve_list = '\n'.join(make_line(entry) for entry in chunk)
            embed = discord_common.cf_color_embed(description=full_solve_list)
            return message, embed

        pages = [make_page(chunk) for chunk in paginator.chunkify(contest_unsolved_pairs, 10)]
        paginator.paginate(self.bot, ctx.channel, pages, wait_time=5 * 60, set_pagenum_footers=True)
예제 #14
0
    async def stalk(self, ctx, *args):
        """Print problems solved by user sorted by time (default) or rating.
        All submission types are included by default (practice, contest, etc.)
        """
        (hardest, ), args = cf_common.filter_flags(args, ['+hardest'])
        filt = cf_common.SubFilter(False)
        args = filt.parse(args)
        handles = args or ('!' + str(ctx.author), )
        handles = await cf_common.resolve_handles(ctx, self.converter, handles)
        submissions = [
            await cf.user.status(handle=handle) for handle in handles
        ]
        submissions = [sub for subs in submissions for sub in subs]
        submissions = filt.filter_subs(submissions)

        if not submissions:
            raise CodeforcesCogError(
                'Submissions not found within the search parameters')

        if hardest:
            submissions.sort(
                key=lambda sub:
                (sub.problem.rating or 0, sub.creationTimeSeconds),
                reverse=True)
        else:
            submissions.sort(key=lambda sub: sub.creationTimeSeconds,
                             reverse=True)

        def make_line(sub):
            data = (f'[{sub.problem.name}]({sub.problem.url})',
                    f'[{sub.problem.rating if sub.problem.rating else "?"}]',
                    f'({cf_common.days_ago(sub.creationTimeSeconds)})')
            return '\N{EN SPACE}'.join(data)

        def make_page(chunk):
            title = '{} solved problems by `{}`'.format(
                'Hardest' if hardest else 'Recently', '`, `'.join(handles))
            hist_str = '\n'.join(make_line(sub) for sub in chunk)
            embed = discord_common.cf_color_embed(description=hist_str)
            return title, embed

        pages = [
            make_page(chunk)
            for chunk in paginator.chunkify(submissions[:100], 10)
        ]
        paginator.paginate(self.bot,
                           ctx.channel,
                           pages,
                           wait_time=5 * 60,
                           set_pagenum_footers=True)
예제 #15
0
    async def unmagic_all(self, ctx):
        """Updates handles of all users that have changed handles
        (typically new year's magic)"""
        handles = []
        rev_lookup = {}
        for user_id, handle in cf_common.user_db.get_handles_for_guild(
                ctx.guild.id):
            member = ctx.guild.get_member(user_id)
            handles.append(handle)
            rev_lookup[handle] = member

        n_fixed = 0
        n_failed = 0
        n_same = 0

        chunks = paginator.chunkify(handles, cf.MAX_HANDLES_PER_QUERY)
        for handles in chunks:
            while handles:
                try:
                    await cf.user.info(handles=handles)
                    n_same += len(handles)
                    break  # no failed handles
                except cf.HandleNotFoundError as e:
                    handle = e.handle
                    member = rev_lookup[handle]
                    new_handle = await cf.resolve_redirect(handle)

                    if not new_handle:
                        self.logger.info(
                            f'Redirection detection failed for {handle}')
                        n_failed += 1
                    elif new_handle == handle:
                        self.logger.info(f'No redirection for handle {handle}')
                        n_same += 1
                    else:
                        user, = await cf.user.info(handles=[new_handle])
                        await self._set(ctx, member, user)
                        n_fixed += 1
                    handles.remove(handle)

        embed = discord_common.embed_success(
            f'{n_fixed} corrected, {n_failed} failed and {n_same} unchanged handles'
        )
        await ctx.send(embed=embed)
예제 #16
0
    async def info(*, handles):
        chunks = chunkify(handles, MAX_HANDLES_PER_QUERY)
        if len(chunks) > 1:
            logger.warning(f'cf.info request with {len(handles)} handles,'
                           f'will be chunkified into {len(chunks)} requests.')

        result = []
        for chunk in chunks:
            params = {'handles': ';'.join(chunk)}
            try:
                resp = await _query_api('user.info', params)
            except TrueApiError as e:
                if 'not found' in e.comment:
                    # Comment format is "handles: User with handle ***** not found"
                    handle = e.comment.partition('not found')[0].split()[-1]
                    raise HandleNotFoundError(e.comment, handle)
                raise
            result += [make_from_dict(User, user_dict) for user_dict in resp]
        return result
예제 #17
0
    async def registered(self, ctx):
        """Show the list of register contestants."""
        users = [(ctx.guild.get_member(user_id))
                 for user_id, aux in cf_common.user_db.get_contestants()]
        users = [(member, cf_common.user_db.get_handle(member.id,
                                                       ctx.guild.id))
                 for member in users if member is not None]
        users = [(member, handle) for member, handle in users
                 if handle is not None]

        _PER_PAGE = 10

        def make_page(chunk, page_num):
            style = table.Style("{:>}  {:<}  {:<}")
            t = table.Table(style)
            t += table.Header("#", "Name", "Handle")
            t += table.Line()
            for index, (member, handle) in enumerate(chunk):
                t += table.Data(
                    _PER_PAGE * page_num + index,
                    f"{member.display_name}",
                    handle,
                )

            table_str = f"```\n{t}\n```"
            embed = discord_common.cf_color_embed(description=table_str)
            return "List of contestants", embed

        if not users:
            raise DuelCogError("There are no registered contestants.")

        pages = [
            make_page(chunk, k)
            for k, chunk in enumerate(paginator.chunkify(users, _PER_PAGE))
        ]
        paginator.paginate(
            self.bot,
            ctx.channel,
            pages,
            wait_time=5 * 60,
            set_pagenum_footers=True,
        )
예제 #18
0
def _make_pages(users):
    chunks = paginator.chunkify(users, _HANDLES_PER_PAGE)
    pages = []
    done = 0

    style = table.Style('{:>}  {:<}  {:<}  {:<}')
    for chunk in chunks:
        t = table.Table(style)
        t += table.Header('#', 'Name', 'Handle', 'Rating')
        t += table.Line()
        for i, (member, handle, rating) in enumerate(chunk):
            rank = cf.rating2rank(rating)
            rating_str = 'N/A' if rating is None else str(rating)
            t += table.Data(i + done, member.display_name, handle,
                            f'{rating_str} ({rank.title_abbr})')
        table_str = '```\n' + str(t) + '\n```'
        embed = discord_common.cf_color_embed(description=table_str)
        pages.append(('Handles of server members', embed))
        done += len(chunk)
    return pages
예제 #19
0
def _make_pages_gudgitters(users, title):
    chunks = paginator.chunkify(users, _HANDLES_PER_PAGE)
    pages = []
    done = 0

    style = table.Style('{:>}  {:<}  {:<}  {:<}')
    for chunk in chunks:
        t = table.Table(style)
        t += table.Header('#', 'Name', 'Handle', 'Rating')
        t += table.Line()
        for i, (member, handle, rating) in enumerate(chunk):
            name = member
            if len(name) > _NAME_MAX_LEN:
                name = name[:_NAME_MAX_LEN - 1] + '…'
            rank = cf.rating2rank(rating)
            rating_str = 'N/A' if rating is None else str(rating)
            t += table.Data(i + done, name, handle, f'{rating_str}')
        table_str = '```\n' + str(t) + '\n```'
        embed = discord_common.cf_color_embed(description=table_str)
        pages.append((title, embed))
        done += len(chunk)
    return pages
예제 #20
0
    async def pending(self, ctx, member: discord.Member = None):
        """Shows current pending duels"""
        def make_line(entry):
            challenger, challengee = entry
            challenger = get_cf_user(challenger, ctx.guild.id)
            challengee = get_cf_user(challengee, ctx.guild.id)
            return f"[{challenger.handle}]({challenger.url}) vs [{challengee.handle}]({challengee.url})"

        def make_page(chunk):
            message = f"List of pending matches:"
            log_str = "\n".join(make_line(entry) for entry in chunk)
            embed = discord_common.cf_color_embed(description=log_str)
            return message, embed

        status = cf_common.user_db.get_tour_status()
        if status == 0:
            raise DuelCogError(f"Tournament is not going on :/")
        global curr_tour
        index = cf_common.user_db.get_tour_index()
        await get_tour(index)
        matches = await curr_tour.get_matches(force_update=True)
        data = []
        for match in matches:
            if match.state == "open":
                player1 = await curr_tour.get_participant(match.player1_id)
                player2 = await curr_tour.get_participant(match.player2_id)
                data.append((player1.misc, player2.misc))

        if not data:
            raise DuelCogError("There are no pending matches.")

        pages = [make_page(chunk) for chunk in paginator.chunkify(data, 7)]
        paginator.paginate(
            self.bot,
            ctx.channel,
            pages,
            wait_time=5 * 60,
            set_pagenum_footers=True,
        )
예제 #21
0
파일: handles.py 프로젝트: Vidit-Jain/TLE
def _make_pages(users, title):
    chunks = paginator.chunkify(users, _HANDLES_PER_PAGE)
    pages = []
    done = 0

    style = table.Style("{:>}  {:<}  {:<}  {:<}")
    for chunk in chunks:
        t = table.Table(style)
        t += table.Header("#", "Name", "Handle", "Rating")
        t += table.Line()
        for i, (member, handle, rating) in enumerate(chunk):
            name = member.display_name
            if len(name) > _NAME_MAX_LEN:
                name = name[: _NAME_MAX_LEN - 1] + "…"
            rank = cf.rating2rank(rating)
            rating_str = "N/A" if rating is None else str(rating)
            t += table.Data(i + done, name, handle, f"{rating_str} ({rank.title_abbr})")
        table_str = "```\n" + str(t) + "\n```"
        embed = discord_common.cf_color_embed(description=table_str)
        pages.append((title, embed))
        done += len(chunk)
    return pages
예제 #22
0
    def _make_rankup_embeds(guild, contest, change_by_handle):
        """Make an embed containing a list of rank changes and top rating increases for the members
        of this guild.
        """
        user_id_handle_pairs = cf_common.user_db.get_handles_for_guild(
            guild.id)
        member_handle_pairs = [(guild.get_member(user_id), handle)
                               for user_id, handle in user_id_handle_pairs]

        def ispurg(member):
            # TODO: temporary code, todo properly later
            return any(role.name == 'Purgatory' for role in member.roles)

        member_change_pairs = [
            (member, change_by_handle[handle])
            for member, handle in member_handle_pairs if member is not None
            and handle in change_by_handle and not ispurg(member)
        ]
        if not member_change_pairs:
            raise HandleCogError(
                f'Contest `{contest.id} | {contest.name}` was not rated for any '
                'member of this server.')

        member_change_pairs.sort(key=lambda pair: pair[1].newRating,
                                 reverse=True)
        rank_to_role = {role.name: role for role in guild.roles}

        def rating_to_displayable_rank(rating):
            rank = cf.rating2rank(rating).title
            role = rank_to_role.get(rank)
            return role.mention if role else rank

        rank_changes_str = []
        for member, change in member_change_pairs:
            cache = cf_common.cache2.rating_changes_cache
            if (change.oldRating == 1500 and len(
                    cache.get_rating_changes_for_handle(change.handle)) == 1):
                # If this is the user's first rated contest.
                old_role = 'Unrated'
            else:
                old_role = rating_to_displayable_rank(change.oldRating)
            new_role = rating_to_displayable_rank(change.newRating)
            if new_role != old_role:
                rank_change_str = (
                    f'{member.mention} [{change.handle}]({cf.PROFILE_BASE_URL}{change.handle}): {old_role} '
                    f'\N{LONG RIGHTWARDS ARROW} {new_role}')
                rank_changes_str.append(rank_change_str)

        member_change_pairs.sort(
            key=lambda pair: pair[1].newRating - pair[1].oldRating,
            reverse=True)
        top_increases_str = []
        for member, change in member_change_pairs[:_TOP_DELTAS_COUNT]:
            delta = change.newRating - change.oldRating
            if delta <= 0:
                break
            increase_str = (
                f'{member.mention} [{change.handle}]({cf.PROFILE_BASE_URL}{change.handle}): {change.oldRating} '
                f'\N{HORIZONTAL BAR} **{delta:+}** \N{LONG RIGHTWARDS ARROW} '
                f'{change.newRating}')
            top_increases_str.append(increase_str)

        rank_changes_str = rank_changes_str or ['No rank changes']

        embed_heading = discord.Embed(title=contest.name,
                                      url=contest.url,
                                      description="")
        embed_heading.set_author(name="Rank updates")
        embeds = [embed_heading]

        for rank_changes_chunk in paginator.chunkify(
                rank_changes_str, _MAX_RATING_CHANGES_PER_EMBED):
            desc = '\n'.join(rank_changes_chunk)
            embed = discord.Embed(description=desc)
            embeds.append(embed)

        top_rating_increases_embed = discord.Embed(
            description='\n'.join(top_increases_str)
            or 'Nobody got a positive delta :(')
        top_rating_increases_embed.set_author(name='Top rating increases')

        embeds.append(top_rating_increases_embed)
        discord_common.set_same_cf_color(embeds)

        return embeds