diff options
Diffstat (limited to '')
| -rw-r--r-- | bot/__main__.py | 8 | ||||
| -rw-r--r-- | bot/cogs/bot.py | 5 | ||||
| -rw-r--r-- | bot/cogs/clean.py | 2 | ||||
| -rw-r--r-- | bot/cogs/defcon.py | 14 | ||||
| -rw-r--r-- | bot/cogs/error_handler.py | 265 | ||||
| -rw-r--r-- | bot/cogs/eval.py | 4 | ||||
| -rw-r--r-- | bot/cogs/extensions.py | 2 | ||||
| -rw-r--r-- | bot/cogs/free.py | 2 | ||||
| -rw-r--r-- | bot/cogs/help.py | 2 | ||||
| -rw-r--r-- | bot/cogs/information.py | 6 | ||||
| -rw-r--r-- | bot/cogs/jams.py | 6 | ||||
| -rw-r--r-- | bot/cogs/logging.py | 2 | ||||
| -rw-r--r-- | bot/cogs/moderation/infractions.py | 2 | ||||
| -rw-r--r-- | bot/cogs/moderation/modlog.py | 22 | ||||
| -rw-r--r-- | bot/cogs/moderation/scheduler.py | 2 | ||||
| -rw-r--r-- | bot/cogs/snekbox.py | 4 | ||||
| -rw-r--r-- | bot/cogs/sync/syncers.py | 8 | ||||
| -rw-r--r-- | bot/cogs/tags.py | 5 | ||||
| -rw-r--r-- | bot/cogs/utils.py | 2 | ||||
| -rw-r--r-- | bot/cogs/verification.py | 11 | ||||
| -rw-r--r-- | bot/constants.py | 52 | ||||
| -rw-r--r-- | config-default.yml | 219 | ||||
| -rw-r--r-- | tests/bot/cogs/sync/test_base.py | 4 | ||||
| -rw-r--r-- | tests/bot/cogs/test_information.py | 10 | 
24 files changed, 386 insertions, 273 deletions
| diff --git a/bot/__main__.py b/bot/__main__.py index d21a1bcbc..3df477a6d 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -7,7 +7,7 @@ from sentry_sdk.integrations.logging import LoggingIntegration  from bot import patches  from bot.bot import Bot -from bot.constants import Bot as BotConfig, DEBUG_MODE +from bot.constants import Bot as BotConfig  sentry_logging = LoggingIntegration(      level=logging.DEBUG, @@ -41,10 +41,8 @@ bot.load_extension("bot.cogs.clean")  bot.load_extension("bot.cogs.extensions")  bot.load_extension("bot.cogs.help") -# Only load this in production -if not DEBUG_MODE: -    bot.load_extension("bot.cogs.doc") -    bot.load_extension("bot.cogs.verification") +bot.load_extension("bot.cogs.doc") +bot.load_extension("bot.cogs.verification")  # Feature cogs  bot.load_extension("bot.cogs.alias") diff --git a/bot/cogs/bot.py b/bot/cogs/bot.py index 73b1e8f41..f17135877 100644 --- a/bot/cogs/bot.py +++ b/bot/cogs/bot.py @@ -34,13 +34,12 @@ class BotCog(Cog, name="Bot"):              Channels.help_5: 0,              Channels.help_6: 0,              Channels.help_7: 0, -            Channels.python: 0, +            Channels.python_discussion: 0,          }          # These channels will also work, but will not be subject to cooldown          self.channel_whitelist = ( -            Channels.bot, -            Channels.devtest, +            Channels.bot_commands,          )          # Stores improperly formatted Python codeblock message ids and the corresponding bot message diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py index 2104efe57..5cdf0b048 100644 --- a/bot/cogs/clean.py +++ b/bot/cogs/clean.py @@ -173,7 +173,7 @@ class Clean(Cog):              colour=Colour(Colours.soft_red),              title="Bulk message delete",              text=message, -            channel_id=Channels.modlog, +            channel_id=Channels.mod_log,          )      @group(invoke_without_command=True, name="clean", aliases=["purge"]) diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index 20961e0a2..cc0f79fe8 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -68,8 +68,8 @@ class Defcon(Cog):          except Exception:  # Yikes!              log.exception("Unable to get DEFCON settings!") -            await self.bot.get_channel(Channels.devlog).send( -                f"<@&{Roles.admin}> **WARNING**: Unable to get DEFCON settings!" +            await self.bot.get_channel(Channels.dev_log).send( +                f"<@&{Roles.admins}> **WARNING**: Unable to get DEFCON settings!"              )          else: @@ -118,7 +118,7 @@ class Defcon(Cog):                  )      @group(name='defcon', aliases=('dc',), invoke_without_command=True) -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def defcon_group(self, ctx: Context) -> None:          """Check the DEFCON status or run a subcommand."""          await ctx.invoke(self.bot.get_command("help"), "defcon") @@ -146,7 +146,7 @@ class Defcon(Cog):              await self.send_defcon_log(action, ctx.author, error)      @defcon_group.command(name='enable', aliases=('on', 'e')) -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def enable_command(self, ctx: Context) -> None:          """          Enable DEFCON mode. Useful in a pinch, but be sure you know what you're doing! @@ -159,7 +159,7 @@ class Defcon(Cog):          await self.update_channel_topic()      @defcon_group.command(name='disable', aliases=('off', 'd')) -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def disable_command(self, ctx: Context) -> None:          """Disable DEFCON mode. Useful in a pinch, but be sure you know what you're doing!"""          self.enabled = False @@ -167,7 +167,7 @@ class Defcon(Cog):          await self.update_channel_topic()      @defcon_group.command(name='status', aliases=('s',)) -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def status_command(self, ctx: Context) -> None:          """Check the current status of DEFCON mode."""          embed = Embed( @@ -179,7 +179,7 @@ class Defcon(Cog):          await ctx.send(embed=embed)      @defcon_group.command(name='days') -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def days_command(self, ctx: Context, days: int) -> None:          """Set how old an account must be to join the server, in days, with DEFCON mode enabled."""          self.days = timedelta(days=days) diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 0d4604430..261769efc 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -1,20 +1,8 @@  import contextlib  import logging +import typing as t -from discord.ext.commands import ( -    BadArgument, -    BotMissingPermissions, -    CheckFailure, -    CommandError, -    CommandInvokeError, -    CommandNotFound, -    CommandOnCooldown, -    DisabledCommand, -    MissingPermissions, -    NoPrivateMessage, -    UserInputError, -) -from discord.ext.commands import Cog, Context +from discord.ext.commands import Cog, Command, Context, errors  from sentry_sdk import push_scope  from bot.api import ResponseCodeError @@ -33,126 +21,185 @@ class ErrorHandler(Cog):          self.bot = bot      @Cog.listener() -    async def on_command_error(self, ctx: Context, e: CommandError) -> None: +    async def on_command_error(self, ctx: Context, e: errors.CommandError) -> None:          """          Provide generic command error handling. -        Error handling is deferred to any local error handler, if present. - -        Error handling emits a single error response, prioritized as follows: -            1. If the name fails to match a command but matches a tag, the tag is invoked -            2. Send a BadArgument error message to the invoking context & invoke the command's help -            3. Send a UserInputError error message to the invoking context & invoke the command's help -            4. Send a NoPrivateMessage error message to the invoking context -            5. Send a BotMissingPermissions error message to the invoking context -            6. Log a MissingPermissions error, no message is sent -            7. Send a InChannelCheckFailure error message to the invoking context -            8. Log CheckFailure, CommandOnCooldown, and DisabledCommand errors, no message is sent -            9. For CommandInvokeErrors, response is based on the type of error: -                * 404: Error message is sent to the invoking context -                * 400: Log the resopnse JSON, no message is sent -                * 500 <= status <= 600: Error message is sent to the invoking context -            10. Otherwise, handling is deferred to `handle_unexpected_error` +        Error handling is deferred to any local error handler, if present. This is done by +        checking for the presence of a `handled` attribute on the error. + +        Error handling emits a single error message in the invoking context `ctx` and a log message, +        prioritised as follows: + +        1. If the name fails to match a command but matches a tag, the tag is invoked +            * If CommandNotFound is raised when invoking the tag (determined by the presence of the +              `invoked_from_error_handler` attribute), this error is treated as being unexpected +              and therefore sends an error message +            * Commands in the verification channel are ignored +        2. UserInputError: see `handle_user_input_error` +        3. CheckFailure: see `handle_check_failure` +        4. CommandOnCooldown: send an error message in the invoking context +        5. ResponseCodeError: see `handle_api_error` +        6. Otherwise, if not a DisabledCommand, handling is deferred to `handle_unexpected_error`          """          command = ctx.command -        parent = None +        if hasattr(e, "handled"): +            log.trace(f"Command {command} had its error already handled locally; ignoring.") +            return + +        # Try to look for a tag with the command's name if the command isn't found. +        if isinstance(e, errors.CommandNotFound) and not hasattr(ctx, "invoked_from_error_handler"): +            if ctx.channel.id != Channels.verification: +                await self.try_get_tag(ctx) +                return  # Exit early to avoid logging. +        elif isinstance(e, errors.UserInputError): +            await self.handle_user_input_error(ctx, e) +        elif isinstance(e, errors.CheckFailure): +            await self.handle_check_failure(ctx, e) +        elif isinstance(e, errors.CommandOnCooldown): +            await ctx.send(e) +        elif isinstance(e, errors.CommandInvokeError): +            if isinstance(e.original, ResponseCodeError): +                await self.handle_api_error(ctx, e.original) +            else: +                await self.handle_unexpected_error(ctx, e.original) +            return  # Exit early to avoid logging. +        elif not isinstance(e, errors.DisabledCommand): +            # ConversionError, MaxConcurrencyReached, ExtensionError +            await self.handle_unexpected_error(ctx, e) +            return  # Exit early to avoid logging. + +        log.debug( +            f"Command {command} invoked by {ctx.message.author} with error " +            f"{e.__class__.__name__}: {e}" +        ) + +    async def get_help_command(self, command: t.Optional[Command]) -> t.Tuple: +        """Return the help command invocation args to display help for `command`.""" +        parent = None          if command is not None:              parent = command.parent          # Retrieve the help command for the invoked command.          if parent and command: -            help_command = (self.bot.get_command("help"), parent.name, command.name) +            return self.bot.get_command("help"), parent.name, command.name          elif command: -            help_command = (self.bot.get_command("help"), command.name) +            return self.bot.get_command("help"), command.name          else: -            help_command = (self.bot.get_command("help"),) +            return self.bot.get_command("help") -        if hasattr(e, "handled"): -            log.trace(f"Command {command} had its error already handled locally; ignoring.") -            return +    async def try_get_tag(self, ctx: Context) -> None: +        """ +        Attempt to display a tag by interpreting the command name as a tag name. -        # Try to look for a tag with the command's name if the command isn't found. -        if isinstance(e, CommandNotFound) and not hasattr(ctx, "invoked_from_error_handler"): -            if not ctx.channel.id == Channels.verification: -                tags_get_command = self.bot.get_command("tags get") -                ctx.invoked_from_error_handler = True - -                log_msg = "Cancelling attempt to fall back to a tag due to failed checks." -                try: -                    if not await tags_get_command.can_run(ctx): -                        log.debug(log_msg) -                        return -                except CommandError as tag_error: -                    log.debug(log_msg) -                    await self.on_command_error(ctx, tag_error) -                    return - -                # Return to not raise the exception -                try: -                    tag_name = await TagNameConverter.convert(ctx, ctx.invoked_with) -                except BadArgument: -                    log.debug( -                        f"{ctx.author} tried to use an invalid command " -                        f"and the fallback tag failed validation in TagNameConverter." -                    ) -                else: -                    with contextlib.suppress(ResponseCodeError): -                        await ctx.invoke(tags_get_command, tag_name=tag_name) +        The invocation of tags get respects its checks. Any CommandErrors raised will be handled +        by `on_command_error`, but the `invoked_from_error_handler` attribute will be added to +        the context to prevent infinite recursion in the case of a CommandNotFound exception. +        """ +        tags_get_command = self.bot.get_command("tags get") +        ctx.invoked_from_error_handler = True + +        log_msg = "Cancelling attempt to fall back to a tag due to failed checks." +        try: +            if not await tags_get_command.can_run(ctx): +                log.debug(log_msg)                  return -        elif isinstance(e, BadArgument): +        except errors.CommandError as tag_error: +            log.debug(log_msg) +            await self.on_command_error(ctx, tag_error) +            return + +        try: +            tag_name = await TagNameConverter.convert(ctx, ctx.invoked_with) +        except errors.BadArgument: +            log.debug( +                f"{ctx.author} tried to use an invalid command " +                f"and the fallback tag failed validation in TagNameConverter." +            ) +        else: +            with contextlib.suppress(ResponseCodeError): +                await ctx.invoke(tags_get_command, tag_name=tag_name) +        # Return to not raise the exception +        return + +    async def handle_user_input_error(self, ctx: Context, e: errors.UserInputError) -> None: +        """ +        Send an error message in `ctx` for UserInputError, sometimes invoking the help command too. + +        * MissingRequiredArgument: send an error message with arg name and the help command +        * TooManyArguments: send an error message and the help command +        * BadArgument: send an error message and the help command +        * BadUnionArgument: send an error message including the error produced by the last converter +        * ArgumentParsingError: send an error message +        * Other: send an error message and the help command +        """ +        # TODO: use ctx.send_help() once PR #519 is merged. +        help_command = await self.get_help_command(ctx.command) + +        if isinstance(e, errors.MissingRequiredArgument): +            await ctx.send(f"Missing required argument `{e.param.name}`.") +            await ctx.invoke(*help_command) +        elif isinstance(e, errors.TooManyArguments): +            await ctx.send(f"Too many arguments provided.") +            await ctx.invoke(*help_command) +        elif isinstance(e, errors.BadArgument):              await ctx.send(f"Bad argument: {e}\n")              await ctx.invoke(*help_command) -        elif isinstance(e, UserInputError): +        elif isinstance(e, errors.BadUnionArgument): +            await ctx.send(f"Bad argument: {e}\n```{e.errors[-1]}```") +        elif isinstance(e, errors.ArgumentParsingError): +            await ctx.send(f"Argument parsing error: {e}") +        else:              await ctx.send("Something about your input seems off. Check the arguments:")              await ctx.invoke(*help_command) -            log.debug( -                f"Command {command} invoked by {ctx.message.author} with error " -                f"{e.__class__.__name__}: {e}" -            ) -        elif isinstance(e, NoPrivateMessage): -            await ctx.send("Sorry, this command can't be used in a private message!") -        elif isinstance(e, BotMissingPermissions): -            await ctx.send(f"Sorry, it looks like I don't have the permissions I need to do that.") -            log.warning( -                f"The bot is missing permissions to execute command {command}: {e.missing_perms}" -            ) -        elif isinstance(e, MissingPermissions): -            log.debug( -                f"{ctx.message.author} is missing permissions to invoke command {command}: " -                f"{e.missing_perms}" + +    @staticmethod +    async def handle_check_failure(ctx: Context, e: errors.CheckFailure) -> None: +        """ +        Send an error message in `ctx` for certain types of CheckFailure. + +        The following types are handled: + +        * BotMissingPermissions +        * BotMissingRole +        * BotMissingAnyRole +        * NoPrivateMessage +        * InChannelCheckFailure +        """ +        bot_missing_errors = ( +            errors.BotMissingPermissions, +            errors.BotMissingRole, +            errors.BotMissingAnyRole +        ) + +        if isinstance(e, bot_missing_errors): +            await ctx.send( +                f"Sorry, it looks like I don't have the permissions or roles I need to do that."              ) -        elif isinstance(e, InChannelCheckFailure): +        elif isinstance(e, (InChannelCheckFailure, errors.NoPrivateMessage)):              await ctx.send(e) -        elif isinstance(e, (CheckFailure, CommandOnCooldown, DisabledCommand)): -            log.debug( -                f"Command {command} invoked by {ctx.message.author} with error " -                f"{e.__class__.__name__}: {e}" -            ) -        elif isinstance(e, CommandInvokeError): -            if isinstance(e.original, ResponseCodeError): -                status = e.original.response.status - -                if status == 404: -                    await ctx.send("There does not seem to be anything matching your query.") -                elif status == 400: -                    content = await e.original.response.json() -                    log.debug(f"API responded with 400 for command {command}: %r.", content) -                    await ctx.send("According to the API, your request is malformed.") -                elif 500 <= status < 600: -                    await ctx.send("Sorry, there seems to be an internal issue with the API.") -                    log.warning(f"API responded with {status} for command {command}") -                else: -                    await ctx.send(f"Got an unexpected status code from the API (`{status}`).") -                    log.warning(f"Unexpected API response for command {command}: {status}") -            else: -                await self.handle_unexpected_error(ctx, e.original) + +    @staticmethod +    async def handle_api_error(ctx: Context, e: ResponseCodeError) -> None: +        """Send an error message in `ctx` for ResponseCodeError and log it.""" +        if e.status == 404: +            await ctx.send("There does not seem to be anything matching your query.") +            log.debug(f"API responded with 404 for command {ctx.command}") +        elif e.status == 400: +            content = await e.response.json() +            log.debug(f"API responded with 400 for command {ctx.command}: %r.", content) +            await ctx.send("According to the API, your request is malformed.") +        elif 500 <= e.status < 600: +            await ctx.send("Sorry, there seems to be an internal issue with the API.") +            log.warning(f"API responded with {e.status} for command {ctx.command}")          else: -            await self.handle_unexpected_error(ctx, e) +            await ctx.send(f"Got an unexpected status code from the API (`{e.status}`).") +            log.warning(f"Unexpected API response for command {ctx.command}: {e.status}")      @staticmethod -    async def handle_unexpected_error(ctx: Context, e: CommandError) -> None: -        """Generic handler for errors without an explicit handler.""" +    async def handle_unexpected_error(ctx: Context, e: errors.CommandError) -> None: +        """Send a generic error message in `ctx` and log the exception as an error with exc_info."""          await ctx.send(              f"Sorry, an unexpected error occurred. Please let us know!\n\n"              f"```{e.__class__.__name__}: {e}```" diff --git a/bot/cogs/eval.py b/bot/cogs/eval.py index 9c729f28a..52136fc8d 100644 --- a/bot/cogs/eval.py +++ b/bot/cogs/eval.py @@ -174,14 +174,14 @@ async def func():  # (None,) -> Any          await ctx.send(f"```py\n{out}```", embed=embed)      @group(name='internal', aliases=('int',)) -    @with_role(Roles.owner, Roles.admin) +    @with_role(Roles.owners, Roles.admins)      async def internal_group(self, ctx: Context) -> None:          """Internal commands. Top secret!"""          if not ctx.invoked_subcommand:              await ctx.invoke(self.bot.get_command("help"), "internal")      @internal_group.command(name='eval', aliases=('e',)) -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def eval(self, ctx: Context, *, code: str) -> None:          """Run eval in a REPL-like format."""          code = code.strip("`") diff --git a/bot/cogs/extensions.py b/bot/cogs/extensions.py index f16e79fb7..b312e1a1d 100644 --- a/bot/cogs/extensions.py +++ b/bot/cogs/extensions.py @@ -221,7 +221,7 @@ class Extensions(commands.Cog):      # This cannot be static (must have a __func__ attribute).      def cog_check(self, ctx: Context) -> bool:          """Only allow moderators and core developers to invoke the commands in this cog.""" -        return with_role_check(ctx, *MODERATION_ROLES, Roles.core_developer) +        return with_role_check(ctx, *MODERATION_ROLES, Roles.core_developers)      # This cannot be static (must have a __func__ attribute).      async def cog_command_error(self, ctx: Context, error: Exception) -> None: diff --git a/bot/cogs/free.py b/bot/cogs/free.py index 49cab6172..02c02d067 100644 --- a/bot/cogs/free.py +++ b/bot/cogs/free.py @@ -22,7 +22,7 @@ class Free(Cog):      PYTHON_HELP_ID = Categories.python_help      @command(name="free", aliases=('f',)) -    @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF_ROLES) +    @redirect_output(destination_channel=Channels.bot_commands, bypass_roles=STAFF_ROLES)      async def free(self, ctx: Context, user: Member = None, seek: int = 2) -> None:          """          Lists free help channels by likeliness of availability. diff --git a/bot/cogs/help.py b/bot/cogs/help.py index fd5bbc3ca..744722220 100644 --- a/bot/cogs/help.py +++ b/bot/cogs/help.py @@ -507,7 +507,7 @@ class Help(DiscordCog):      """Custom Embed Pagination Help feature."""      @commands.command('help') -    @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF_ROLES) +    @redirect_output(destination_channel=Channels.bot_commands, bypass_roles=STAFF_ROLES)      async def new_help(self, ctx: Context, *commands) -> None:          """Shows Command Help."""          try: diff --git a/bot/cogs/information.py b/bot/cogs/information.py index 13c8aabaa..49beca15b 100644 --- a/bot/cogs/information.py +++ b/bot/cogs/information.py @@ -152,8 +152,8 @@ class Information(Cog):          # Non-staff may only do this in #bot-commands          if not with_role_check(ctx, *constants.STAFF_ROLES): -            if not ctx.channel.id == constants.Channels.bot: -                raise InChannelCheckFailure(constants.Channels.bot) +            if not ctx.channel.id == constants.Channels.bot_commands: +                raise InChannelCheckFailure(constants.Channels.bot_commands)          embed = await self.create_user_embed(ctx, user) @@ -332,7 +332,7 @@ class Information(Cog):      @cooldown_with_role_bypass(2, 60 * 3, BucketType.member, bypass_roles=constants.STAFF_ROLES)      @group(invoke_without_command=True) -    @in_channel(constants.Channels.bot, bypass_roles=constants.STAFF_ROLES) +    @in_channel(constants.Channels.bot_commands, bypass_roles=constants.STAFF_ROLES)      async def raw(self, ctx: Context, *, message: Message, json: bool = False) -> None:          """Shows information about the raw API response."""          # I *guess* it could be deleted right as the command is invoked but I felt like it wasn't worth handling diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py index 985f28ce5..1d062b0c2 100644 --- a/bot/cogs/jams.py +++ b/bot/cogs/jams.py @@ -18,7 +18,7 @@ class CodeJams(commands.Cog):          self.bot = bot      @commands.command() -    @with_role(Roles.admin) +    @with_role(Roles.admins)      async def createteam(self, ctx: commands.Context, team_name: str, members: commands.Greedy[Member]) -> None:          """          Create team channels (voice and text) in the Code Jams category, assign roles, and add overwrites for the team. @@ -95,10 +95,10 @@ class CodeJams(commands.Cog):          )          # Assign team leader role -        await members[0].add_roles(ctx.guild.get_role(Roles.team_leader)) +        await members[0].add_roles(ctx.guild.get_role(Roles.team_leaders))          # Assign rest of roles -        jammer_role = ctx.guild.get_role(Roles.jammer) +        jammer_role = ctx.guild.get_role(Roles.jammers)          for member in members:              await member.add_roles(jammer_role) diff --git a/bot/cogs/logging.py b/bot/cogs/logging.py index dbd76672f..94fa2b139 100644 --- a/bot/cogs/logging.py +++ b/bot/cogs/logging.py @@ -34,7 +34,7 @@ class Logging(Cog):          )          if not DEBUG_MODE: -            await self.bot.get_channel(Channels.devlog).send(embed=embed) +            await self.bot.get_channel(Channels.dev_log).send(embed=embed)  def setup(bot: Bot) -> None: diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py index f4e296df9..9ea17b2b3 100644 --- a/bot/cogs/moderation/infractions.py +++ b/bot/cogs/moderation/infractions.py @@ -313,6 +313,6 @@ class Infractions(InfractionScheduler, commands.Cog):      async def cog_command_error(self, ctx: Context, error: Exception) -> None:          """Send a notification to the invoking context on a Union failure."""          if isinstance(error, commands.BadUnionArgument): -            if discord.User in error.converters: +            if discord.User in error.converters or discord.Member in error.converters:                  await ctx.send(str(error.errors[0]))                  error.handled = True diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index e8ae0dbe6..59ae6b587 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -87,7 +87,7 @@ class ModLog(Cog, name="ModLog"):          title: t.Optional[str],          text: str,          thumbnail: t.Optional[t.Union[str, discord.Asset]] = None, -        channel_id: int = Channels.modlog, +        channel_id: int = Channels.mod_log,          ping_everyone: bool = False,          files: t.Optional[t.List[discord.File]] = None,          content: t.Optional[str] = None, @@ -377,7 +377,7 @@ class ModLog(Cog, name="ModLog"):              Icons.user_ban, Colours.soft_red,              "User banned", f"{member} (`{member.id}`)",              thumbnail=member.avatar_url_as(static_format="png"), -            channel_id=Channels.userlog +            channel_id=Channels.user_log          )      @Cog.listener() @@ -399,7 +399,7 @@ class ModLog(Cog, name="ModLog"):              Icons.sign_in, Colours.soft_green,              "User joined", message,              thumbnail=member.avatar_url_as(static_format="png"), -            channel_id=Channels.userlog +            channel_id=Channels.user_log          )      @Cog.listener() @@ -416,7 +416,7 @@ class ModLog(Cog, name="ModLog"):              Icons.sign_out, Colours.soft_red,              "User left", f"{member} (`{member.id}`)",              thumbnail=member.avatar_url_as(static_format="png"), -            channel_id=Channels.userlog +            channel_id=Channels.user_log          )      @Cog.listener() @@ -433,7 +433,7 @@ class ModLog(Cog, name="ModLog"):              Icons.user_unban, Colour.blurple(),              "User unbanned", f"{member} (`{member.id}`)",              thumbnail=member.avatar_url_as(static_format="png"), -            channel_id=Channels.modlog +            channel_id=Channels.mod_log          )      @Cog.listener() @@ -529,7 +529,7 @@ class ModLog(Cog, name="ModLog"):              Icons.user_update, Colour.blurple(),              "Member updated", message,              thumbnail=after.avatar_url_as(static_format="png"), -            channel_id=Channels.userlog +            channel_id=Channels.user_log          )      @Cog.listener() @@ -538,7 +538,7 @@ class ModLog(Cog, name="ModLog"):          channel = message.channel          author = message.author -        if message.guild.id != GuildConstant.id or channel.id in GuildConstant.ignored: +        if message.guild.id != GuildConstant.id or channel.id in GuildConstant.modlog_blacklist:              return          self._cached_deletes.append(message.id) @@ -591,7 +591,7 @@ class ModLog(Cog, name="ModLog"):      @Cog.listener()      async def on_raw_message_delete(self, event: discord.RawMessageDeleteEvent) -> None:          """Log raw message delete event to message change log.""" -        if event.guild_id != GuildConstant.id or event.channel_id in GuildConstant.ignored: +        if event.guild_id != GuildConstant.id or event.channel_id in GuildConstant.modlog_blacklist:              return          await asyncio.sleep(1)  # Wait here in case the normal event was fired @@ -635,7 +635,7 @@ class ModLog(Cog, name="ModLog"):          if (              not msg_before.guild              or msg_before.guild.id != GuildConstant.id -            or msg_before.channel.id in GuildConstant.ignored +            or msg_before.channel.id in GuildConstant.modlog_blacklist              or msg_before.author.bot          ):              return @@ -717,7 +717,7 @@ class ModLog(Cog, name="ModLog"):          if (              not message.guild              or message.guild.id != GuildConstant.id -            or message.channel.id in GuildConstant.ignored +            or message.channel.id in GuildConstant.modlog_blacklist              or message.author.bot          ):              return @@ -769,7 +769,7 @@ class ModLog(Cog, name="ModLog"):          """Log member voice state changes to the voice log channel."""          if (              member.guild.id != GuildConstant.id -            or (before.channel and before.channel.id in GuildConstant.ignored) +            or (before.channel and before.channel.id in GuildConstant.modlog_blacklist)          ):              return diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py index 3c5185468..45cf5ec8a 100644 --- a/bot/cogs/moderation/scheduler.py +++ b/bot/cogs/moderation/scheduler.py @@ -307,7 +307,7 @@ class InfractionScheduler(Scheduler):          Infractions of unsupported types will raise a ValueError.          """          guild = self.bot.get_guild(constants.Guild.id) -        mod_role = guild.get_role(constants.Roles.moderator) +        mod_role = guild.get_role(constants.Roles.moderators)          user_id = infraction["user"]          actor = infraction["actor"]          type_ = infraction["type"] diff --git a/bot/cogs/snekbox.py b/bot/cogs/snekbox.py index da33e27b2..aef12546d 100644 --- a/bot/cogs/snekbox.py +++ b/bot/cogs/snekbox.py @@ -34,7 +34,7 @@ RAW_CODE_REGEX = re.compile(  )  MAX_PASTE_LEN = 1000 -EVAL_ROLES = (Roles.helpers, Roles.moderator, Roles.admin, Roles.owner, Roles.rockstars, Roles.partners) +EVAL_ROLES = (Roles.helpers, Roles.moderators, Roles.admins, Roles.owners, Roles.python_community, Roles.partners)  class Snekbox(Cog): @@ -177,7 +177,7 @@ class Snekbox(Cog):      @command(name="eval", aliases=("e",))      @guild_only() -    @in_channel(Channels.bot, hidden_channels=(Channels.esoteric,), bypass_roles=EVAL_ROLES) +    @in_channel(Channels.bot_commands, hidden_channels=(Channels.esoteric,), bypass_roles=EVAL_ROLES)      async def eval_command(self, ctx: Context, *, code: str = None) -> None:          """          Run Python code and get the results. diff --git a/bot/cogs/sync/syncers.py b/bot/cogs/sync/syncers.py index 6715ad6fb..d6891168f 100644 --- a/bot/cogs/sync/syncers.py +++ b/bot/cogs/sync/syncers.py @@ -23,7 +23,7 @@ _Diff = namedtuple('Diff', ('created', 'updated', 'deleted'))  class Syncer(abc.ABC):      """Base class for synchronising the database with objects in the Discord cache.""" -    _CORE_DEV_MENTION = f"<@&{constants.Roles.core_developer}> " +    _CORE_DEV_MENTION = f"<@&{constants.Roles.core_developers}> "      _REACTION_EMOJIS = (constants.Emojis.check_mark, constants.Emojis.cross_mark)      def __init__(self, bot: Bot) -> None: @@ -54,12 +54,12 @@ class Syncer(abc.ABC):          # Send to core developers if it's an automatic sync.          if not message:              log.trace("Message not provided for confirmation; creating a new one in dev-core.") -            channel = self.bot.get_channel(constants.Channels.devcore) +            channel = self.bot.get_channel(constants.Channels.dev_core)              if not channel:                  log.debug("Failed to get the dev-core channel from cache; attempting to fetch it.")                  try: -                    channel = await self.bot.fetch_channel(constants.Channels.devcore) +                    channel = await self.bot.fetch_channel(constants.Channels.dev_core)                  except HTTPException:                      log.exception(                          f"Failed to fetch channel for sending sync confirmation prompt; " @@ -93,7 +93,7 @@ class Syncer(abc.ABC):          `author` of the prompt.          """          # For automatic syncs, check for the core dev role instead of an exact author -        has_role = any(constants.Roles.core_developer == role.id for role in user.roles) +        has_role = any(constants.Roles.core_developers == role.id for role in user.roles)          return (              reaction.message.id == message.id              and not user.bot diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index b6360dfae..5da9a4148 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -15,8 +15,7 @@ from bot.pagination import LinePaginator  log = logging.getLogger(__name__)  TEST_CHANNELS = ( -    Channels.devtest, -    Channels.bot, +    Channels.bot_commands,      Channels.helpers  ) @@ -221,7 +220,7 @@ class Tags(Cog):          ))      @tags_group.command(name='delete', aliases=('remove', 'rm', 'd')) -    @with_role(Roles.admin, Roles.owner) +    @with_role(Roles.admins, Roles.owners)      async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:          """Remove a tag from the database."""          await self.bot.api_client.delete(f'bot/tags/{tag_name}') diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index da278011a..94b9d6b5a 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -89,7 +89,7 @@ class Utils(Cog):          await ctx.message.channel.send(embed=pep_embed)      @command() -    @in_channel(Channels.bot, bypass_roles=STAFF_ROLES) +    @in_channel(Channels.bot_commands, bypass_roles=STAFF_ROLES)      async def charinfo(self, ctx: Context, *, characters: str) -> None:          """Shows you information on up to 25 unicode characters."""          match = re.match(r"<(a?):(\w+):(\d+)>", characters) diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py index e3c396863..57b50c34f 100644 --- a/bot/cogs/verification.py +++ b/bot/cogs/verification.py @@ -30,15 +30,16 @@ your information removed here as well.  Feel free to review them at any point!  Additionally, if you'd like to receive notifications for the announcements we post in <#{Channels.announcements}> \ -from time to time, you can send `!subscribe` to <#{Channels.bot}> at any time to assign yourself the \ +from time to time, you can send `!subscribe` to <#{Channels.bot_commands}> at any time to assign yourself the \  **Announcements** role. We'll mention this role every time we make an announcement. -If you'd like to unsubscribe from the announcement notifications, simply send `!unsubscribe` to <#{Channels.bot}>. +If you'd like to unsubscribe from the announcement notifications, simply send `!unsubscribe` to \ +<#{Channels.bot_commands}>.  """  PERIODIC_PING = (      f"@everyone To verify that you have read our rules, please type `{BotConfig.prefix}accept`." -    f" If you encounter any problems during the verification process, ping the <@&{Roles.admin}> role in this channel." +    f" If you encounter any problems during the verification process, ping the <@&{Roles.admins}> role in this channel."  )  BOT_MESSAGE_DELETE_DELAY = 10 @@ -136,7 +137,7 @@ class Verification(Cog):                  await ctx.message.delete()      @command(name='subscribe') -    @in_channel(Channels.bot) +    @in_channel(Channels.bot_commands)      async def subscribe_command(self, ctx: Context, *_) -> None:  # We don't actually care about the args          """Subscribe to announcement notifications by assigning yourself the role."""          has_role = False @@ -160,7 +161,7 @@ class Verification(Cog):          )      @command(name='unsubscribe') -    @in_channel(Channels.bot) +    @in_channel(Channels.bot_commands)      async def unsubscribe_command(self, ctx: Context, *_) -> None:  # We don't actually care about the args          """Unsubscribe from announcement notifications by removing the role from yourself."""          has_role = False diff --git a/bot/constants.py b/bot/constants.py index ebd3b3d96..14f8dc094 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -363,18 +363,16 @@ class Channels(metaclass=YAMLGetter):      section = "guild"      subsection = "channels" -    admins: int      admin_spam: int +    admins: int      announcements: int      attachment_log: int      big_brother_logs: int -    bot: int -    checkpoint_test: int +    bot_commands: int      defcon: int -    devcontrib: int -    devcore: int -    devlog: int -    devtest: int +    dev_contrib: int +    dev_core: int +    dev_log: int      esoteric: int      help_0: int      help_1: int @@ -387,19 +385,19 @@ class Channels(metaclass=YAMLGetter):      helpers: int      message_log: int      meta: int +    mod_alerts: int +    mod_log: int      mod_spam: int      mods: int -    mod_alerts: int -    modlog: int      off_topic_0: int      off_topic_1: int      off_topic_2: int      organisation: int -    python: int +    python_discussion: int      reddit: int      talent_pool: int -    userlog: int -    user_event_a: int +    user_event_announcements: int +    user_log: int      verification: int      voice_log: int @@ -419,19 +417,18 @@ class Roles(metaclass=YAMLGetter):      section = "guild"      subsection = "roles" -    admin: int +    admins: int      announcements: int -    champion: int -    contributor: int -    core_developer: int +    contributors: int +    core_developers: int      helpers: int -    jammer: int -    moderator: int +    jammers: int +    moderators: int      muted: int -    owner: int +    owners: int      partners: int -    rockstars: int -    team_leader: int +    python_community: int +    team_leaders: int      verified: int  # This is the Developers role on PyDis, here named verified for readability reasons. @@ -439,9 +436,12 @@ class Guild(metaclass=YAMLGetter):      section = "guild"      id: int -    ignored: List[int] -    staff_channels: List[int] +    moderation_channels: List[int] +    moderation_roles: List[int] +    modlog_blacklist: List[int]      reminder_whitelist: List[int] +    staff_channels: List[int] +    staff_roles: List[int]  class Keys(metaclass=YAMLGetter):      section = "keys" @@ -587,14 +587,14 @@ BOT_DIR = os.path.dirname(__file__)  PROJECT_ROOT = os.path.abspath(os.path.join(BOT_DIR, os.pardir))  # Default role combinations -MODERATION_ROLES = Roles.moderator, Roles.admin, Roles.owner -STAFF_ROLES = Roles.helpers, Roles.moderator, Roles.admin, Roles.owner +MODERATION_ROLES = Guild.moderation_roles +STAFF_ROLES = Guild.staff_roles  # Roles combinations  STAFF_CHANNELS = Guild.staff_channels  # Default Channel combinations -MODERATION_CHANNELS = Channels.admins, Channels.admin_spam, Channels.mod_alerts, Channels.mods, Channels.mod_spam +MODERATION_CHANNELS = Guild.moderation_channels  # Bot replies diff --git a/config-default.yml b/config-default.yml index f70fe3c34..ab237423f 100644 --- a/config-default.yml +++ b/config-default.yml @@ -111,78 +111,135 @@ guild:      id: 267624335836053506      categories: -        python_help:                      356013061213126657 +        python_help:    356013061213126657      channels: -        admins:            &ADMINS        365960823622991872 -        admin_spam:        &ADMIN_SPAM    563594791770914816 -        admins_voice:      &ADMINS_VOICE  500734494840717332 -        announcements:                    354619224620138496 -        attachment_log:    &ATTCH_LOG     649243850006855680 -        big_brother_logs:  &BBLOGS        468507907357409333 -        bot:               &BOT_CMD       267659945086812160 -        checkpoint_test:                  422077681434099723 -        defcon:            &DEFCON        464469101889454091 -        devcontrib:        &DEV_CONTRIB   635950537262759947 -        devcore:                          411200599653351425 -        devlog:            &DEVLOG        622895325144940554 -        devtest:           &DEVTEST       414574275865870337 -        esoteric:                         470884583684964352 -        help_0:                           303906576991780866 -        help_1:                           303906556754395136 -        help_2:                           303906514266226689 -        help_3:                           439702951246692352 -        help_4:                           451312046647148554 -        help_5:                           454941769734422538 -        help_6:                           587375753306570782 -        help_7:                           587375768556797982 -        helpers:           &HELPERS       385474242440986624 -        message_log:       &MESSAGE_LOG   467752170159079424 -        meta:                             429409067623251969 -        mod_spam:          &MOD_SPAM      620607373828030464 -        mods:              &MODS          305126844661760000 -        mod_alerts:                       473092532147060736 -        modlog:            &MODLOG        282638479504965634 -        off_topic_0:                      291284109232308226 -        off_topic_1:                      463035241142026251 -        off_topic_2:                      463035268514185226 -        organisation:      &ORGANISATION  551789653284356126 -        python:                           267624335836053506 -        reddit:                           458224812528238616 -        staff_lounge:      &STAFF_LOUNGE  464905259261755392 -        staff_voice:       &STAFF_VOICE   412375055910043655 -        talent_pool:       &TALENT_POOL   534321732593647616 -        userlog:                          528976905546760203 -        user_event_a:      &USER_EVENT_A  592000283102674944 -        verification:                     352442727016693763 -        voice_log:                        640292421988646961 - -    staff_channels: [*ADMINS, *ADMIN_SPAM, *MOD_SPAM, *MODS, *HELPERS, *ORGANISATION, *DEFCON] -    ignored: [*ADMINS, *MESSAGE_LOG, *MODLOG, *ADMINS_VOICE, *STAFF_VOICE, *ATTCH_LOG] -    reminder_whitelist: [*BOT_CMD, *DEV_CONTRIB] +        announcements:                              354619224620138496 +        user_event_announcements:   &USER_EVENT_A   592000283102674944 + +        # Development +        dev_contrib:        &DEV_CONTRIB    635950537262759947 +        dev_core:           &DEV_CORE       411200599653351425 +        dev_log:            &DEV_LOG        622895325144940554 + +        # Discussion +        meta:               429409067623251969 +        python_discussion:  267624335836053506 + +        # Logs +        attachment_log:     &ATTACH_LOG     649243850006855680 +        message_log:        &MESSAGE_LOG    467752170159079424 +        mod_log:            &MOD_LOG        282638479504965634 +        user_log:                           528976905546760203 +        voice_log:                          640292421988646961 + +        # Off-topic +        off_topic_0:    291284109232308226 +        off_topic_1:    463035241142026251 +        off_topic_2:    463035268514185226 + +        # Python Help +        help_0:         303906576991780866 +        help_1:         303906556754395136 +        help_2:         303906514266226689 +        help_3:         439702951246692352 +        help_4:         451312046647148554 +        help_5:         454941769734422538 +        help_6:         587375753306570782 +        help_7:         587375768556797982 + +        # Special +        bot_commands:       &BOT_CMD        267659945086812160 +        esoteric:                           470884583684964352 +        reddit:                             458224812528238616 +        verification:                       352442727016693763 + +        # Staff +        admins:             &ADMINS         365960823622991872 +        admin_spam:         &ADMIN_SPAM     563594791770914816 +        defcon:             &DEFCON         464469101889454091 +        helpers:            &HELPERS        385474242440986624 +        mods:               &MODS           305126844661760000 +        mod_alerts:         &MOD_ALERTS     473092532147060736 +        mod_spam:           &MOD_SPAM       620607373828030464 +        organisation:       &ORGANISATION   551789653284356126 +        staff_lounge:       &STAFF_LOUNGE   464905259261755392 + +        # Voice +        admins_voice:       &ADMINS_VOICE   500734494840717332 +        staff_voice:        &STAFF_VOICE    412375055910043655 + +        # Watch +        big_brother_logs:   &BB_LOGS        468507907357409333 +        talent_pool:        &TALENT_POOL    534321732593647616 + +    staff_channels: +        - *ADMINS +        - *ADMIN_SPAM +        - *DEFCON +        - *HELPERS +        - *MODS +        - *MOD_SPAM +        - *ORGANISATION + +    moderation_channels: +        - *ADMINS +        - *ADMIN_SPAM +        - *MOD_ALERTS +        - *MODS +        - *MOD_SPAM + +    # Modlog cog ignores events which occur in these channels +    modlog_blacklist: +        - *ADMINS +        - *ADMINS_VOICE +        - *ATTACH_LOG +        - *MESSAGE_LOG +        - *MOD_LOG +        - *STAFF_VOICE + +    reminder_whitelist: +        - *BOT_CMD +        - *DEV_CONTRIB      roles: -        admin:             &ADMIN_ROLE      267628507062992896 -        announcements:                      463658397560995840 -        champion:                           430492892331769857 -        contributor:                        295488872404484098 -        core_developer:                     587606783669829632 -        helpers:                            267630620367257601 -        jammer:                             591786436651646989 -        moderator:         &MOD_ROLE        267629731250176001 -        muted:             &MUTED_ROLE      277914926603829249 -        owner:             &OWNER_ROLE      267627879762755584 -        partners:                           323426753857191936 -        rockstars:         &ROCKSTARS_ROLE  458226413825294336 -        team_leader:                        501324292341104650 -        verified:                           352427296948486144 +        announcements:                          463658397560995840 +        contributors:                           295488872404484098 +        muted:              &MUTED_ROLE         277914926603829249 +        partners:                               323426753857191936 +        python_community:   &PY_COMMUNITY_ROLE  458226413825294336 + +        # This is the Developers role on PyDis, here named verified for readability reasons +        verified:                               352427296948486144 + +        # Staff +        admins:             &ADMINS_ROLE    267628507062992896 +        core_developers:                    587606783669829632 +        helpers:            &HELPERS_ROLE   267630620367257601 +        moderators:         &MODS_ROLE      267629731250176001 +        owners:             &OWNERS_ROLE    267627879762755584 + +        # Code Jam +        jammers:        591786436651646989 +        team_leaders:   501324292341104650 + +    moderation_roles: +        - *OWNERS_ROLE +        - *ADMINS_ROLE +        - *MODS_ROLE + +    staff_roles: +        - *OWNERS_ROLE +        - *ADMINS_ROLE +        - *MODS_ROLE +        - *HELPERS_ROLE      webhooks: -        talent_pool:                        569145364800602132 -        big_brother:                        569133704568373283 -        reddit:                             635408384794951680 -        duck_pond:                          637821475327311927 -        dev_log:                            680501655111729222 +        talent_pool:    569145364800602132 +        big_brother:    569133704568373283 +        reddit:         635408384794951680 +        duck_pond:      637821475327311927 +        dev_log:        680501655111729222  filter: @@ -260,20 +317,19 @@ filter:      # Censor doesn't apply to these      channel_whitelist:          - *ADMINS -        - *MODLOG +        - *MOD_LOG          - *MESSAGE_LOG -        - *DEVLOG -        - *BBLOGS +        - *DEV_LOG +        - *BB_LOGS          - *STAFF_LOUNGE -        - *DEVTEST          - *TALENT_POOL          - *USER_EVENT_A      role_whitelist: -        - *ADMIN_ROLE -        - *MOD_ROLE -        - *OWNER_ROLE -        - *ROCKSTARS_ROLE +        - *ADMINS_ROLE +        - *MODS_ROLE +        - *OWNERS_ROLE +        - *PY_COMMUNITY_ROLE  keys: @@ -441,7 +497,20 @@ sync:  duck_pond:      threshold: 5 -    custom_emojis: [*DUCKY_YELLOW, *DUCKY_BLURPLE, *DUCKY_CAMO, *DUCKY_DEVIL, *DUCKY_NINJA, *DUCKY_REGAL, *DUCKY_TUBE, *DUCKY_HUNT, *DUCKY_WIZARD, *DUCKY_PARTY, *DUCKY_ANGEL, *DUCKY_MAUL, *DUCKY_SANTA] +    custom_emojis: +        - *DUCKY_YELLOW +        - *DUCKY_BLURPLE +        - *DUCKY_CAMO +        - *DUCKY_DEVIL +        - *DUCKY_NINJA +        - *DUCKY_REGAL +        - *DUCKY_TUBE +        - *DUCKY_HUNT +        - *DUCKY_WIZARD +        - *DUCKY_PARTY +        - *DUCKY_ANGEL +        - *DUCKY_MAUL +        - *DUCKY_SANTA  config:      required_keys: ['bot.token'] diff --git a/tests/bot/cogs/sync/test_base.py b/tests/bot/cogs/sync/test_base.py index e6a6f9688..c2e143865 100644 --- a/tests/bot/cogs/sync/test_base.py +++ b/tests/bot/cogs/sync/test_base.py @@ -84,7 +84,7 @@ class SyncerSendPromptTests(unittest.TestCase):                  mock_()                  await self.syncer._send_prompt() -                method.assert_called_once_with(constants.Channels.devcore) +                method.assert_called_once_with(constants.Channels.dev_core)      @helpers.async_test      async def test_send_prompt_returns_None_if_channel_fetch_fails(self): @@ -135,7 +135,7 @@ class SyncerConfirmationTests(unittest.TestCase):      def setUp(self):          self.bot = helpers.MockBot()          self.syncer = TestSyncer(self.bot) -        self.core_dev_role = helpers.MockRole(id=constants.Roles.core_developer) +        self.core_dev_role = helpers.MockRole(id=constants.Roles.core_developers)      @staticmethod      def get_message_reaction(emoji): diff --git a/tests/bot/cogs/test_information.py b/tests/bot/cogs/test_information.py index deae7ebad..8443cfe71 100644 --- a/tests/bot/cogs/test_information.py +++ b/tests/bot/cogs/test_information.py @@ -19,7 +19,7 @@ class InformationCogTests(unittest.TestCase):      @classmethod      def setUpClass(cls): -        cls.moderator_role = helpers.MockRole(name="Moderator", id=constants.Roles.moderator) +        cls.moderator_role = helpers.MockRole(name="Moderator", id=constants.Roles.moderators)      def setUp(self):          """Sets up fresh objects for each test.""" @@ -521,7 +521,7 @@ class UserCommandTests(unittest.TestCase):          """A regular user should not be able to use this command outside of bot-commands."""          constants.MODERATION_ROLES = [self.moderator_role.id]          constants.STAFF_ROLES = [self.moderator_role.id] -        constants.Channels.bot = 50 +        constants.Channels.bot_commands = 50          ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(id=100)) @@ -533,7 +533,7 @@ class UserCommandTests(unittest.TestCase):      def test_regular_user_may_use_command_in_bot_commands_channel(self, create_embed, constants):          """A regular user should be allowed to use `!user` targeting themselves in bot-commands."""          constants.STAFF_ROLES = [self.moderator_role.id] -        constants.Channels.bot = 50 +        constants.Channels.bot_commands = 50          ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(id=50)) @@ -546,7 +546,7 @@ class UserCommandTests(unittest.TestCase):      def test_regular_user_can_explicitly_target_themselves(self, create_embed, constants):          """A user should target itself with `!user` when a `user` argument was not provided."""          constants.STAFF_ROLES = [self.moderator_role.id] -        constants.Channels.bot = 50 +        constants.Channels.bot_commands = 50          ctx = helpers.MockContext(author=self.author, channel=helpers.MockTextChannel(id=50)) @@ -559,7 +559,7 @@ class UserCommandTests(unittest.TestCase):      def test_staff_members_can_bypass_channel_restriction(self, create_embed, constants):          """Staff members should be able to bypass the bot-commands channel restriction."""          constants.STAFF_ROLES = [self.moderator_role.id] -        constants.Channels.bot = 50 +        constants.Channels.bot_commands = 50          ctx = helpers.MockContext(author=self.moderator, channel=helpers.MockTextChannel(id=200)) | 
