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)
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'))
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}')
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)
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'))
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'))
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.'))
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'))
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.")
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)
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)
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)
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)
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)
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)
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))
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)
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))
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)
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)
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)
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'))
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.'))
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'))
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)