Exemple #1
0
 async def publish(self, ctx, arg):
     """This is a feature to publish a summary of rank changes and top rating
     increases in a particular contest for members of this server. 'here' will
     automatically publish the summary to this channel whenever rating changes on
     Codeforces are released. 'off' will disable auto publishing. Specifying a
     contest id will publish the summary immediately.
     """
     if arg == 'here':
         cf_common.user_db.set_rankup_channel(ctx.guild.id, ctx.channel.id)
         await ctx.send(embed=discord_common.embed_success(
             'Auto rank update publishing enabled.'))
     elif arg == 'off':
         rc = cf_common.user_db.clear_rankup_channel(ctx.guild.id)
         if not rc:
             raise HandleCogError(
                 'Rank update publishing is already disabled.')
         await ctx.send(embed=discord_common.embed_success(
             'Rank update publishing disabled.'))
     else:
         try:
             contest_id = int(arg)
         except ValueError:
             raise ValueError(
                 f"arg must be 'here', 'off' or a contest ID, got '{arg}' instead."
             )
         await self._publish_now(ctx, contest_id)
Exemple #2
0
    async def me(self, ctx, arg: str = None):
        settings = cf_common.user_db.get_reminder_settings(ctx.guild.id)
        if settings is None:
            raise ContestCogError(
                'To use this command, reminder settings must be set by an admin'
            )
        _, role_id, _ = settings
        role = ctx.guild.get_role(int(role_id))
        if role is None:
            raise ContestCogError(
                'The role set for reminders is no longer available')

        if arg is None:
            if role in ctx.author.roles:
                await ctx.send(embed=discord_common.embed_neutral(
                    'You are already subscribed to contest reminders'))
                return
            await ctx.author.add_roles(
                role, reason='User subscribed to contest reminders')
            await ctx.send(embed=discord_common.embed_success(
                'Successfully subscribed to contest reminders'))
        elif arg == 'not':
            if role not in ctx.author.roles:
                await ctx.send(embed=discord_common.embed_neutral(
                    'You are not subscribed to contest reminders'))
                return
            await ctx.author.remove_roles(
                role, reason='User unsubscribed from contest reminders')
            await ctx.send(embed=discord_common.embed_success(
                'Successfully unsubscribed from contest reminders'))
Exemple #3
0
 async def _generic_remind(self, ctx, action, role_name, what):
     roles = [role for role in ctx.guild.roles if role.name == role_name]
     if not roles:
         raise HandleCogError(
             f'Role `{role_name}` not present in the server')
     role = roles[0]
     if action == 'give':
         if role in ctx.author.roles:
             await ctx.send(embed=discord_common.embed_neutral(
                 f'You are already subscribed to {what} reminders'))
             return
         await ctx.author.add_roles(
             role, reason=f'User subscribed to {what} reminders')
         await ctx.send(embed=discord_common.embed_success(
             f'Successfully subscribed to {what} reminders'))
     elif action == 'remove':
         if role not in ctx.author.roles:
             await ctx.send(embed=discord_common.embed_neutral(
                 f'You are not subscribed to {what} reminders'))
             return
         await ctx.author.remove_roles(
             role, reason=f'User unsubscribed from {what} reminders')
         await ctx.send(embed=discord_common.embed_success(
             f'Successfully unsubscribed from {what} reminders'))
     else:
         raise HandleCogError(f'Invalid action {action}')
Exemple #4
0
 async def settings(self, ctx):
     """Shows the role, channel and before time settings."""
     settings = cf_common.user_db.get_reminder_settings(ctx.guild.id)
     if settings is None:
         await ctx.send(
             embed=discord_common.embed_neutral('Reminder not set'))
         return
     channel_id, role_id, before = settings
     channel_id, role_id, before = int(channel_id), int(
         role_id), json.loads(before)
     channel, role = ctx.guild.get_channel(channel_id), ctx.guild.get_role(
         role_id)
     if channel is None:
         raise ContestCogError(
             'The channel set for reminders is no longer available')
     if role is None:
         raise ContestCogError(
             'The role set for reminders is no longer available')
     before_str = ', '.join(str(before_mins) for before_mins in before)
     embed = discord_common.embed_success('Current reminder settings')
     embed.add_field(name='Channel', value=channel.mention)
     embed.add_field(name='Role', value=role.mention)
     embed.add_field(name='Before',
                     value=f'At {before_str} mins before contest')
     await ctx.send(embed=embed)
Exemple #5
0
 async def remove(self, ctx, original_message_id: int):
     """Remove a particular message from the starboard database."""
     rc = cf_common.user_db.remove_starboard_message(original_msg_id=original_message_id)
     if rc:
         await ctx.send(embed=discord_common.embed_success('Successfully removed'))
     else:
         await ctx.send(embed=discord_common.embed_alert('Not found in database'))
Exemple #6
0
 async def clear(self, ctx):
     """Stop tracking starboard messages and remove the currently set starboard channel
     from settings."""
     cf_common.user_db.clear_starboard(ctx.guild.id)
     cf_common.user_db.clear_starboard_messages_for_guild(ctx.guild.id)
     await ctx.send(
         embed=discord_common.embed_success('Starboard channel cleared'))
Exemple #7
0
 async def _unregistervc(self, ctx, user: discord.Member):
     """ Unregister this user from an ongoing ratedvc.
     """
     ongoing_vc_member_ids = _get_ongoing_vc_participants()
     if str(user.id) not in ongoing_vc_member_ids:
         raise ContestCogError(f'{user.mention} has no ongoing ratedvc!')
     cf_common.user_db.remove_last_ratedvc_participation(user.id)
     await ctx.send(embed=discord_common.embed_success(f'Successfully unregistered {user.mention} from the ongoing vc.'))
Exemple #8
0
 async def here(self, ctx):
     """Set the current channel as starboard."""
     res = cf_common.user_db.get_starboard(ctx.guild.id)
     if res is not None:
         raise StarboardCogError('The starboard channel is already set. Use `clear` before '
                                 'attempting to set a different channel as starboard.')
     cf_common.user_db.set_starboard(ctx.guild.id, ctx.channel.id)
     await ctx.send(embed=discord_common.embed_success('Starboard channel set'))
Exemple #9
0
 async def auto(self, ctx, arg):
     """Auto role update refers to automatic updating of rank roles when rating
     changes are released on Codeforces. 'on'/'off' disables or enables auto role
     updates.
     """
     if arg == 'on':
         rc = cf_common.user_db.enable_auto_role_update(ctx.guild.id)
         if not rc:
             raise HandleCogError('Auto role update is already enabled.')
         await ctx.send(embed=discord_common.embed_success('Auto role updates enabled.'))
     elif arg == 'off':
         rc = cf_common.user_db.disable_auto_role_update(ctx.guild.id)
         if not rc:
             raise HandleCogError('Auto role update is already disabled.')
         await ctx.send(embed=discord_common.embed_success('Auto role updates disabled.'))
     else:
         raise ValueError(f"arg must be 'on' or 'off', got '{arg}' instead.")
Exemple #10
0
 async def remove(self, ctx, member: discord.Member):
     """Remove Codeforces handle of a user."""
     rc = cf_common.user_db.removehandle(member.id)
     if not rc:
         raise HandleCogError(
             f'Handle for {member.mention} not found in database')
     embed = discord_common.embed_success(
         f'Removed handle for {member.mention}')
     await ctx.send(embed=embed)
Exemple #11
0
 async def off(self, ctx):
     """Unsubscribes you from contest reminders."""
     role = self._get_remind_role(ctx.guild)
     if role not in ctx.author.roles:
         embed = discord_common.embed_neutral('You are not subscribed to contest reminders')
     else:
         await ctx.author.remove_roles(role, reason='User unsubscribed from contest reminders')
         embed = discord_common.embed_success('Successfully unsubscribed from contest reminders')
     await ctx.send(embed=embed)
Exemple #12
0
 async def remove(self, ctx, member: discord.Member):
     """Remove Codeforces handle of a user."""
     rc = cf_common.user_db.remove_handle(member.id, ctx.guild.id)
     if not rc:
         raise HandleCogError(f'Handle for {member.mention} not found in database')
     await self.update_member_rank_role(member, role_to_assign=None,
                                        reason='Handle removed for user')
     embed = discord_common.embed_success(f'Removed handle for {member.mention}')
     await ctx.send(embed=embed)
Exemple #13
0
 async def get_ratedvc_channel(self, ctx):
     """ Gets the rated vc channel.
     """
     channel_id = cf_common.user_db.get_rated_vc_channel(ctx.guild.id)
     channel = ctx.guild.get_channel(channel_id)
     if channel is None:
         raise ContestCogError('There is no rated vc channel')
     embed = discord_common.embed_success('Current rated vc channel')
     embed.add_field(name='Channel', value=channel.mention)
     await ctx.send(embed=embed)
Exemple #14
0
 async def here(self, ctx, role: discord.Role, *before: int):
     """Sets reminder channel to current channel, role to the given role, and reminder
     times to the given values in minutes."""
     if not role.mentionable:
         raise ContestCogError('The role for reminders must be mentionable')
     if not before or any(before_mins <= 0 for before_mins in before):
         raise ContestCogError('Please provide valid `before` values')
     before = sorted(before, reverse=True)
     cf_common.user_db.set_reminder_settings(ctx.guild.id, ctx.channel.id, role.id, json.dumps(before))
     await ctx.send(embed=discord_common.embed_success('Reminder settings saved successfully'))
     self._reschedule_tasks(ctx.guild.id)
Exemple #15
0
 async def on(self, ctx):
     """Subscribes you to contest reminders. Use ';remind settings' to see the current
     settings.
     """
     role = self._get_remind_role(ctx.guild)
     if role in ctx.author.roles:
         embed = discord_common.embed_neutral('You are already subscribed to contest reminders')
     else:
         await ctx.author.add_roles(role, reason='User subscribed to contest reminders')
         embed = discord_common.embed_success('Successfully subscribed to contest reminders')
     await ctx.send(embed=embed)
Exemple #16
0
    async def _fix_and_report(self, ctx, redirections):
        fixed = []
        failed = []
        for (member, handle), cf_user in redirections.items():
            if not cf_user:
                failed.append(handle)
            else:
                await self._set(ctx, member, cf_user)
                fixed.append((handle, cf_user.handle))

        # Return summary embed
        lines = []
        if not fixed and not failed:
            return discord_common.embed_success('No handles updated')
        if fixed:
            lines.append('**Fixed**')
            lines += (f'{old} -> {new}' for old, new in fixed)
        if failed:
            lines.append('**Failed**')
            lines += failed
        return discord_common.embed_success('\n'.join(lines))
Exemple #17
0
 async def here(self, ctx, role: discord.Role, *before: int):
     """Sets reminder channel to current channel, role to the given role, and reminder
     times to the given values in minutes."""
     if not role.mentionable:
         await ctx.send(embed=discord_common.embed_alert(
             'The role for reminders must be mentionable'))
         return
     if not before or any(before_mins <= 0 for before_mins in before):
         return
     cf_common.user_db.set_reminder_settings(ctx.guild.id, ctx.channel.id,
                                             role.id, json.dumps(before))
     await ctx.send(embed=discord_common.embed_success(
         'Reminder settings saved successfully'))
     self._reschedule_tasks(ctx.guild.id)
Exemple #18
0
    async def _fix(self, ctx, to_fix):
        fixed = []
        failed = []
        for member, handle in to_fix:
            new_handle = await cf.resolve_redirect(handle)
            if not new_handle:
                failed.append(handle)
            else:
                user, = await cf.user.info(handles=[new_handle])
                await self._set(ctx, member, user)
                fixed.append((handle, user.handle))

        # Return summary embed
        lines = []
        if not fixed and not failed:
            return discord_common.embed_success('No handles updated')
        if fixed:
            lines.append('**Fixed**')
            lines += (f'{old} -> {new}' for old, new in fixed)
        if failed:
            lines.append('**Failed**')
            lines += failed
        return discord_common.embed_success('\n'.join(lines))
Exemple #19
0
    async def remove(self, ctx, handle: str):
        """Remove Codeforces handle of a user."""
        handle, = await cf_common.resolve_handles(ctx, self.converter,
                                                  [handle])
        user_id = cf_common.user_db.get_user_id(handle, ctx.guild.id)
        if user_id is None:
            raise HandleCogError(f'{handle} not found in database')

        cf_common.user_db.remove_handle(handle, ctx.guild.id)
        member = ctx.guild.get_member(user_id)
        await self.update_member_rank_role(member,
                                           role_to_assign=None,
                                           reason='Handle unlinked')
        embed = discord_common.embed_success(f'Removed {handle} from database')
        await ctx.send(embed=embed)
Exemple #20
0
    async def unmagic(self, ctx):
        """Updates handle of the calling user if they have changed handles
        (typically new year's magic)"""
        member = ctx.author
        handle = cf_common.user_db.get_handle(member.id, ctx.guild.id)
        redirected_handle = await cf.resolve_redirect(handle)

        if not redirected_handle:
            raise HandleCogError(f'Redirection detection failed for {handle}')
        if redirected_handle == handle:
            raise HandleCogError(f'No redirection for handle {handle}')

        user, = await cf.user.info(handles=[redirected_handle])
        await self._set(ctx, member, user)
        embed = discord_common.embed_success(
            f'Corrected {handle} -> {redirected_handle}')
        await ctx.send(embed=embed)
Exemple #21
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)
Exemple #22
0
    async def updateroles(self, ctx):
        """Update Codeforces rank roles for everyone."""
        res = cf_common.user_db.getallhandles()
        member_handles = [(ctx.guild.get_member(int(user_id)), handle)
                          for user_id, handle in res]
        member_handles = [(member, handle) for member, handle in member_handles
                          if member is not None]
        if not member_handles:
            raise HandleCogError('Handles not set for any user')
        members, handles = zip(*member_handles)
        users = await cf.user.info(handles=handles)
        for user in users:
            cf_common.user_db.cache_cfuser(user)

        required_roles = {
            user.rank.title
            for user in users if user.rank != cf.UNRATED_RANK
        }
        rank2role = {
            role.name: role
            for role in ctx.guild.roles if role.name in required_roles
        }
        missing_roles = required_roles - rank2role.keys()
        if missing_roles:
            roles_str = ', '.join(f'`{role}`' for role in missing_roles)
            plural = 's' if len(missing_roles) > 1 else ''
            raise HandleCogError(
                f'Role{plural} for rank{plural} {roles_str} not present in the server'
            )

        for member, user in zip(members, users):
            role_to_assign = None if user.rank == cf.UNRATED_RANK else rank2role[
                user.rank.title]
            await self.update_member_rank_role(member,
                                               role_to_assign,
                                               reason='Codeforces rank update')

        await ctx.send(
            embed=discord_common.embed_success('Roles updated successfully'))
Exemple #23
0
 async def now(self, ctx):
     """Updates Codeforces rank roles for every member in this server."""
     await self._update_ranks(ctx.guild)
     await ctx.send(
         embed=discord_common.embed_success('Roles updated successfully.'))
Exemple #24
0
 async def set_ratedvc_channel(self, ctx):
     """ Sets the rated vc channel to the current channel.
     """
     cf_common.user_db.set_rated_vc_channel(ctx.guild.id, ctx.channel.id)
     await ctx.send(embed=discord_common.embed_success(
         'Rated VC channel saved successfully'))
Exemple #25
0
 async def clear(self, ctx):
     cf_common.user_db.clear_reminder_settings(ctx.guild.id)
     await ctx.send(embed=discord_common.embed_success('Reminder settings cleared'))
     self._reschedule_tasks(ctx.guild.id)