async def _limited(self, ctx): ''' Manages settings for limited role requests ''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) channel = doc['requests_opts']['channel'] hidejoins = doc['requests_opts']['hidejoins'] ratelimit = doc['requests_opts']['ratelimit'] embed = discord.Embed( title=f'Limited Role Request Options for: {ctx.guild}') embed.set_footer( text= f'Use the "{ctx.prefix}help limited" command for help on changing these settings.' ) if channel == None: embed.description = 'Requests are currently disabled for this guild.' return await ctx.send(embed=embed) embed.add_field(name='Posting Channel', value=f'<#{channel}>') embed.add_field(name='Join Command Hiding', value='Enabled' if hidejoins else 'Disabled') embed.add_field(name='Join Command Ratelimiting', value='Enabled' if ratelimit else 'Disabled') return await ctx.send(embed=embed)
async def _list_send_embed(self, ctx, title, roles, *, footer=None): doc = utils.getGuildDoc(ctx.bot, ctx.guild) roles = list( filter(lambda r: not r.is_default(), reversed(roles))) # Reversed without @everyone role role_list = [] raw_list = [] for role in roles: typeStr = doc['roles'][str(role.id)]['type'].title() if ( doc and str(role.id) in doc['roles']) else None colorStr = '' if role.color == discord.Colour.default( ) else f' [{role.color}]' role_list.append(f'<@&{role.id}> (`{role.id}`)' + (f' **{typeStr}**' if typeStr else '')) raw_list.append(f'{role.name}{colorStr} ({role.id})' + f' {typeStr}' if typeStr else '') await utils.sendListEmbed(ctx, title, role_list, raw_override='\r\n'.join(raw_list), footer=footer)
async def _limited_disable(self, ctx): '''Disables limited role requests for the guild''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) if doc['requests_opts']['channel'] == None: return await utils.cmdFail( ctx, f'Requests are already disabled for this guild.') utils.guildKeySet(ctx.bot, ctx.guild, 'requests_opts.channel', None) return await utils.cmdSuccess( ctx, f'Requests are now disabled for this guild.')
async def _limited_channel(self, ctx, channel: discord.TextChannel): '''Sets the channel that limited role requests will be posted in''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) if doc['requests_opts']['channel'] == channel.id: return await utils.cmdFail( ctx, f'The requests channel is already {channel}.') utils.guildKeySet(ctx.bot, ctx.guild, 'requests_opts.channel', channel.id) return await utils.cmdSuccess( ctx, f'The requests channel is now {channel}.')
async def on_raw_reaction_add(self, payload): if not payload.member: return if payload.member.bot: return doc = utils.getGuildDoc(self.bot, payload.member.guild) if not doc: return request = doc['requests'][str(payload.message_id)] if not request: return if str(payload.emoji) == config.greenTick: await self.request_update(payload.member.guild, payload.message_id, request, 'approved', payload.member) elif str(payload.emoji) == config.redTick: await self.request_update(payload.member.guild, payload.message_id, request, 'denied', payload.member) return
async def _leave(self, ctx, role: discord.Role): '''Leaves or cancels a request for a requestable role''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) if not (doc and str(role.id) in doc['roles']): return await utils.cmdFail( ctx, f'"{role.name}" is not a requestable role.') if not role in ctx.author.roles: if doc['roles'][str(role.id)]['type'] == 'limited': return await self.bot.get_cog('LimitedRequests' ).request_cancel(ctx, role) return await utils.cmdFail( ctx, f'You do not have the role "{role.name}".') await ctx.author.remove_roles(role, reason='User left role via command') return await utils.cmdSuccess(ctx, f'You left the role "{role.name}".')
async def request_cancel(self, ctx, role): doc = utils.getGuildDoc(ctx.bot, ctx.guild) requests = list( filter( lambda e: e[1]['user'] == ctx.author.id and e[1]['role'] == role.id, doc['requests'].items())) request = requests[-1] if requests else (None, None) if not request[1] or request[1]['status'] != 'pending': return await utils.cmdFail( ctx, f'You do not have a request pending for the role "{role.name}".' ) await self.request_update(ctx.guild, request[0], request[1], 'cancelled') return await utils.cmdSuccess( ctx, f'Your request for "{role.name}" has been cancelled.')
async def _limited_option_toggle(self, ctx, user_setting, setting_key, setting_string): doc = utils.getGuildDoc(ctx.bot, ctx.guild) current = doc['requests_opts'][setting_key] if user_setting is None: user_setting = not current human = 'enabled' if user_setting else 'disabled' if user_setting == current: return await utils.cmdFail( ctx, f'Limited role join command {setting_string} is already **{human}**.' ) utils.guildKeySet(ctx.bot, ctx.guild, f'requests_opts.{setting_key}', user_setting) return await utils.cmdSuccess( ctx, f'Limited role join command {setting_string} is now **{human}**.')
async def _list(self, ctx): '''Lists all requestable roles''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) has_manage_roles = commands.has_permissions(manage_roles=True)(ctx) if not (doc and len(doc['roles'])): return await utils.cmdFail( ctx, f'This server does not have any requestable roles.' + f' (Use the `{ctx.prefix}list all` command to list all server roles.)' if has_manage_roles else '') roles = list( filter(lambda r: str(r.id) in doc['roles'], ctx.guild.roles)) # Roles in requestable roles await self._list_send_embed( ctx, 'Requestable Roles', roles, footer= f'Use the "{ctx.prefix}list all" command to list all server roles.' if has_manage_roles else None)
async def _join(self, ctx, role: discord.Role): ''' Joins or requests a requestable role If the role is a open role, it will be joined. If the role is a limited role, a request is submitted. ''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) if not (doc and str(role.id) in doc['roles']): return await utils.cmdFail( ctx, f'"{role.name}" is not a requestable role.') if role in ctx.author.roles: return await utils.cmdFail( ctx, f'You already have the role "{role.name}".') if doc['roles'][str(role.id)]['type'] == 'limited': return await self.bot.get_cog('LimitedRequests').request_create( ctx, role) await ctx.author.add_roles(role, reason='User joined role via command') return await utils.cmdSuccess( ctx, f'You have joined the role "{role.name}".')
async def _role(self, ctx, role: discord.Role, option: typing.Optional[str]): '''Adds, modifies, or removes a requestable role Adds or removes a role from the server's requestable roles or modifies an existing requestable roles type.''' doc = utils.getGuildDoc(ctx.bot, ctx.guild) options = { 'open': ['open', 'o'], 'limited': ['limited', 'limit', 'l'], 'remove': ['delete', 'del', 'd', 'remove', 'rem', 'r'], 'add': ['add', 'a'] # Option resolves to 'open' but only for new roles. } resolved_option = None for key, val in options.items(): if option in val: resolved_option = key if option and not resolved_option: return await utils.cmdFail(ctx, f'"{option}" is not a valid option.') # @everyone role or managed if role.is_default() or role.managed: return await utils.cmdFail(ctx, f'"{role.name}" is not a valid role.') role_request_type = None if not (doc and str( role.id) in doc['roles']) else doc['roles'][str(role.id)]['type'] # Role info if resolved_option is None: embed = discord.Embed( title='Role Info', description=f'<@&{role.id}> (`{role.id}`)\n' + f'**Color:** {"None" if role.color == discord.Colour.default() else role.color}\n' + f'**Hoisted:** {"Yes" if role.hoist else "No"}\n' + f'**Mentionable:** {"Yes" if role.mentionable else "No"}\n' + '**Requestable:** ' + ('No' if not role_request_type else f'Yes, {role_request_type.title()}'), color=discord.Embed.Empty if role.color == discord.Colour.default() else role.colour) if commands.has_permissions(manage_roles=True)(ctx): embed.set_footer( text=f'See "{ctx.prefix}help role" for valid subcommands.') return await ctx.send(embed=embed) # Remove role if resolved_option == 'remove': if not role_request_type: return await utils.cmdFail( ctx, f'"{role.name}" is not a requestable role.') utils.guildKeyDel(ctx.bot, ctx.guild, f'roles.{role.id}') return await utils.cmdSuccess( ctx, f'"{role.name}" has been removed as a requestable role.') # Modify role type if role_request_type: if resolved_option == 'add': return await utils.cmdFail( ctx, f'"{role.name}" is already a requestable role.') if role_request_type == resolved_option: return await utils.cmdFail( ctx, f'"{role.name}" is already a {resolved_option} requestable role.' ) utils.guildKeySet(ctx.bot, ctx.guild, f'roles.{role.id}.type', resolved_option) return await utils.cmdSuccess( ctx, f'"{role.name}" is now a {resolved_option} requestable role.') # Add role else: if resolved_option == 'add': resolved_option = 'open' utils.guildKeySet(ctx.bot, ctx.guild, f'roles.{role.id}', {'type': resolved_option}) return await utils.cmdSuccess( ctx, f'"{role.name}" has been added as a requestable {resolved_option} role.' )
async def request_create(self, ctx, role): doc = utils.getGuildDoc(ctx.bot, ctx.guild) channel = doc['requests_opts']['channel'] users_requests = list( filter(lambda e: e['user'] == ctx.author.id, doc['requests'].values())) if doc['requests_opts']['hidejoins']: try: await ctx.message.delete(delay=5) except: pass delete = 15 else: delete = None if not channel: return await utils.cmdFail( ctx, f'Limited role requests are currently disabled for this guild.', delete_after=delete) existing_request = list( filter(lambda e: e['role'] == role.id, users_requests)) if existing_request and existing_request[-1]['status'] == 'pending': return await utils.cmdFail( ctx, f'You already have a request pending for the role "{role.name}".', delete_after=delete) # Ratelimit if enabled & ratelimit score above maximum; score calculated from status of requests in last 24h if doc['requests_opts']['ratelimit']: rl_score = 0 for r in users_requests: rl_score += 0 if not r[ "status"] in LIMITED_RATELIMIT_SCORES else LIMITED_RATELIMIT_SCORES[ r["status"]] if rl_score > LIMITED_RATELIMIT_SCORE_MAX: return await utils.cmdFail( ctx, 'You have too many recent requests. Please try again later.', delete_after=delete) embed = discord.Embed( title='Limited Role Request', description= f'<@{ctx.message.author.id}> requested the <@&{role.id}> role.', color=discord.Colour.blurple(), timestamp=datetime.datetime.utcnow() + datetime.timedelta(hours=24)) embed.set_author( name=f'{ctx.message.author} ({ctx.message.author.id})', icon_url=ctx.message.author.avatar_url) embed.add_field(name='Status', value='Pending. React to approve or deny the request.') embed.set_footer(text='Request expires') embed_message = await ctx.guild.get_channel(channel).send(embed=embed) await embed_message.add_reaction(config.greenTick) await embed_message.add_reaction(config.redTick) utils.guildKeySet( ctx.bot, ctx.guild, f'requests.{embed_message.id}', { 'channel': embed_message.channel.id, 'created': datetime.datetime.utcnow().timestamp(), 'role': role.id, 'status': 'pending', 'user': ctx.author.id, }) return await utils.cmdSuccess( ctx, f'Your request for "{role.name}" has been submitted.', delete_after=delete)