diff options
| -rw-r--r-- | bot/cogs/information.py | 35 | ||||
| -rw-r--r-- | bot/cogs/tags.py | 2 | ||||
| -rw-r--r-- | bot/decorators.py | 36 | ||||
| -rw-r--r-- | bot/utils/checks.py | 56 |
4 files changed, 102 insertions, 27 deletions
diff --git a/bot/cogs/information.py b/bot/cogs/information.py index 7a244cdbe..129166d2f 100644 --- a/bot/cogs/information.py +++ b/bot/cogs/information.py @@ -1,11 +1,13 @@ import logging +import random import textwrap from discord import CategoryChannel, Colour, Embed, Member, TextChannel, VoiceChannel -from discord.ext.commands import Bot, Context, command +from discord.ext.commands import BadArgument, Bot, CommandError, Context, MissingPermissions, command -from bot.constants import Emojis, Keys, Roles, URLs +from bot.constants import Channels, Emojis, Keys, NEGATIVE_REPLIES, Roles, URLs from bot.decorators import with_role +from bot.utils.checks import with_role_check from bot.utils.time import time_since log = logging.getLogger(__name__) @@ -121,13 +123,23 @@ class Information: await ctx.send(embed=embed) - @with_role(*MODERATION_ROLES) @command(name="user", aliases=["user_info", "member", "member_info"]) async def user_info(self, ctx: Context, user: Member = None, hidden: bool = False): """ Returns info about a user. """ + # Do a role check if this is being executed on + # someone other than the caller + if user and user != ctx.author: + if not with_role_check(ctx, *MODERATION_ROLES): + raise BadArgument("You do not have permission to use this command on users other than yourself.") + + # Non-moderators may only do this in #bot-commands + if not with_role_check(ctx, *MODERATION_ROLES): + if not ctx.channel.id == Channels.bot: + raise MissingPermissions("You can't do that here!") + # Validates hidden input hidden = str(hidden) @@ -192,6 +204,23 @@ class Information: await ctx.send(embed=embed) + @user_info.error + async def user_info_command_error(self, ctx: Context, error: CommandError): + embed = Embed(colour=Colour.red()) + + if isinstance(error, BadArgument): + embed.title = random.choice(NEGATIVE_REPLIES) + embed.description = str(error) + await ctx.send(embed=embed) + + elif isinstance(error, MissingPermissions): + embed.title = random.choice(NEGATIVE_REPLIES) + embed.description = f"Sorry, but you may only use this command within <#{Channels.bot}>." + await ctx.send(embed=embed) + + else: + log.exception(f"Unhandled error: {error}") + def setup(bot): bot.add_cog(Information(bot)) diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index b128b6de1..8ecd80127 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -149,7 +149,7 @@ class Tags: tags = [] - embed = Embed() + embed: Embed = Embed() embed.colour = Colour.red() tag_data = await self.get_tag_data(tag_name) diff --git a/bot/decorators.py b/bot/decorators.py index 87877ecbf..710045c10 100644 --- a/bot/decorators.py +++ b/bot/decorators.py @@ -10,6 +10,7 @@ from discord.ext import commands from discord.ext.commands import CheckFailure, Context from bot.constants import ERROR_REPLIES +from bot.utils.checks import with_role_check, without_role_check log = logging.getLogger(__name__) @@ -47,35 +48,24 @@ def in_channel(*channels: int, bypass_roles: typing.Container[int] = None): def with_role(*role_ids: int): - async def predicate(ctx: Context): - if not ctx.guild: # Return False in a DM - log.debug(f"{ctx.author} tried to use the '{ctx.command.name}'command from a DM. " - "This command is restricted by the with_role decorator. Rejecting request.") - return False - - for role in ctx.author.roles: - if role.id in role_ids: - log.debug(f"{ctx.author} has the '{role.name}' role, and passes the check.") - return True + """ + Returns True if the user has any one + of the roles in role_ids. + """ - log.debug(f"{ctx.author} does not have the required role to use " - f"the '{ctx.command.name}' command, so the request is rejected.") - return False + async def predicate(ctx: Context): + return with_role_check(ctx, *role_ids) return commands.check(predicate) def without_role(*role_ids: int): - async def predicate(ctx: Context): - if not ctx.guild: # Return False in a DM - log.debug(f"{ctx.author} tried to use the '{ctx.command.name}' command from a DM. " - "This command is restricted by the without_role decorator. Rejecting request.") - return False + """ + Returns True if the user does not have any + of the roles in role_ids. + """ - author_roles = [role.id for role in ctx.author.roles] - check = all(role not in author_roles for role in role_ids) - log.debug(f"{ctx.author} tried to call the '{ctx.command.name}' command. " - f"The result of the without_role check was {check}.") - return check + async def predicate(ctx: Context): + return without_role_check(ctx, *role_ids) return commands.check(predicate) diff --git a/bot/utils/checks.py b/bot/utils/checks.py new file mode 100644 index 000000000..37dc657f7 --- /dev/null +++ b/bot/utils/checks.py @@ -0,0 +1,56 @@ +import logging + +from discord.ext.commands import Context + +log = logging.getLogger(__name__) + + +def with_role_check(ctx: Context, *role_ids: int) -> bool: + """ + Returns True if the user has any one + of the roles in role_ids. + """ + + if not ctx.guild: # Return False in a DM + log.trace(f"{ctx.author} tried to use the '{ctx.command.name}'command from a DM. " + "This command is restricted by the with_role decorator. Rejecting request.") + return False + + for role in ctx.author.roles: + if role.id in role_ids: + log.trace(f"{ctx.author} has the '{role.name}' role, and passes the check.") + return True + + log.trace(f"{ctx.author} does not have the required role to use " + f"the '{ctx.command.name}' command, so the request is rejected.") + return False + + +def without_role_check(ctx: Context, *role_ids: int) -> bool: + """ + Returns True if the user does not have any + of the roles in role_ids. + """ + + if not ctx.guild: # Return False in a DM + log.trace(f"{ctx.author} tried to use the '{ctx.command.name}' command from a DM. " + "This command is restricted by the without_role decorator. Rejecting request.") + return False + + author_roles = (role.id for role in ctx.author.roles) + check = all(role not in author_roles for role in role_ids) + log.trace(f"{ctx.author} tried to call the '{ctx.command.name}' command. " + f"The result of the without_role check was {check}.") + return check + + +def in_channel_check(ctx: Context, channel_id: int) -> bool: + """ + Checks if the command was executed + inside of the specified channel. + """ + + check = ctx.channel.id == channel_id + log.trace(f"{ctx.author} tried to call the '{ctx.command.name}' command. " + f"The result of the in_channel check was {check}.") + return check |