Repository: python-discord/bot

Issue:
Tag fallback bypasses the bot's DM restrictions
When a user issues a command that does not exist, we fall back to trying to match it as a tag name. This means that a message containing `!f-strings`, which is not a command in itself, will result in the bot sending the contents of the `f-strings` tag. This is a great feature, but it currently does not respect the DM-restriction we've put in place: Members should only be able to issue commands in public channels, not a DM conversation with the bot.

The [current code responsible for the tag fallback](https://github.com/python-discord/bot/blob/master/bot/cogs/error_handler.py#L73) only checks if the user is not issuing the pseudo-command in checkpoint. The global checks that prevent commands from being issued in DMs are [defined in the security cog](https://github.com/python-discord/bot/blob/master/bot/cogs/security.py).

#### Bot response after command in DM

![command_in_dm](https://user-images.githubusercontent.com/33516116/68120021-923e0180-ff04-11e9-90f4-ac6c0043d98d.png)

#### Bot response after tag in DM

![tag_in_dm](https://user-images.githubusercontent.com/33516116/68120036-99fda600-ff04-11e9-8cb4-6583e7cfd5ad.png)

Tag fallback bypasses the bot's DM restrictions
When a user issues a command that does not exist, we fall back to trying to match it as a tag name. This means that a message containing `!f-strings`, which is not a command in itself, will result in the bot sending the contents of the `f-strings` tag. This is a great feature, but it currently does not respect the DM-restriction we've put in place: Members should only be able to issue commands in public channels, not a DM conversation with the bot.

The [current code responsible for the tag fallback](https://github.com/python-discord/bot/blob/master/bot/cogs/error_handler.py#L73) only checks if the user is not issuing the pseudo-command in checkpoint. The global checks that prevent commands from being issued in DMs are [defined in the security cog](https://github.com/python-discord/bot/blob/master/bot/cogs/security.py).

#### Bot response after command in DM

![command_in_dm](https://user-images.githubusercontent.com/33516116/68120021-923e0180-ff04-11e9-90f4-ac6c0043d98d.png)

#### Bot response after tag in DM

![tag_in_dm](https://user-images.githubusercontent.com/33516116/68120036-99fda600-ff04-11e9-8cb4-6583e7cfd5ad.png)



Discussion:

Resolving patch:
diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py
--- a/bot/cogs/error_handler.py
+++ b/bot/cogs/error_handler.py
@@ -75,6 +75,16 @@ async def on_command_error(self, ctx: Context, e: CommandError) -> None:
                 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
                 with contextlib.suppress(ResponseCodeError):
                     await ctx.invoke(tags_get_command, tag_name=ctx.invoked_with)


Test patch: