diff options
author | 2024-02-18 20:06:42 +0100 | |
---|---|---|
committer | 2024-03-20 19:15:03 +0100 | |
commit | a8859f6e6dac51d7d553b2bd9d5ca0389c69a873 (patch) | |
tree | 63ce06830860002e54cacf6dcb2c2a620d6c8e78 | |
parent | Bump Pydis core to v11 (#1478) (diff) |
implement CommandNotFoundErrorHandler
-rw-r--r-- | bot/command_error_handlers/__init__.py | 0 | ||||
-rw-r--r-- | bot/command_error_handlers/command_not_found.py | 64 |
2 files changed, 64 insertions, 0 deletions
diff --git a/bot/command_error_handlers/__init__.py b/bot/command_error_handlers/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/bot/command_error_handlers/__init__.py diff --git a/bot/command_error_handlers/command_not_found.py b/bot/command_error_handlers/command_not_found.py new file mode 100644 index 00000000..957b4d38 --- /dev/null +++ b/bot/command_error_handlers/command_not_found.py @@ -0,0 +1,64 @@ +from typing import NoReturn + +from discord import Embed, Interaction, errors +from discord.ext import commands +from pydis_core.utils.error_handling.commands import AbstractCommandErrorHandler +from pydis_core.utils.logging import get_logger + +from bot.bot import Bot +from bot.utils.commands import get_command_suggestions + +log = get_logger(__name__) + +DELETE_DELAY = 10 +QUESTION_MARK_ICON = "https://cdn.discordapp.com/emojis/512367613339369475.png" + + +class CommandNotFoundErrorHandler(AbstractCommandErrorHandler): + """A handler for all CommandNotFound exceptions.""" + + def __init__(self, bot: Bot): + self.bot = bot + + async def should_handle_error(self, error: errors.DiscordException) -> bool: + """A predicate that determines whether the error should be handled or not.""" + return isinstance(error, commands.CommandNotFound) + + async def handle_app_command_error(self, interaction: Interaction, error: errors.DiscordException) -> NoReturn: + """Handle error raised in the context of app commands.""" + # CommandNotFound cannot happen with app commands, so there's nothing to do here + return + + async def handle_text_command_error(self, context: commands.Context, error: errors.DiscordException) -> NoReturn: + """Handle error raised in the context of text commands.""" + if not context.invoked_with.startswith("."): + await self.send_command_suggestion(context, context.invoked_with) + + async def send_command_suggestion(self, ctx: commands.Context, command_name: str) -> None: + """Sends user similar commands if any can be found.""" + command_suggestions = [] + if similar_command_names := get_command_suggestions(list(self.bot.all_commands.keys()), command_name): + for similar_command_name in similar_command_names: + similar_command = self.bot.get_command(similar_command_name) + + if not similar_command: + continue + + log_msg = "Cancelling attempt to suggest a command due to failed checks." + try: + if not await similar_command.can_run(ctx): + log.debug(log_msg) + continue + except commands.errors.CommandError: + log.debug(log_msg) + continue + + command_suggestions.append(similar_command_name) + + misspelled_content = ctx.message.content + e = Embed() + e.set_author(name="Did you mean:", icon_url=QUESTION_MARK_ICON) + e.description = "\n".join( + misspelled_content.replace(command_name, cmd, 1) for cmd in command_suggestions + ) + await ctx.send(embed=e, delete_after=DELETE_DELAY) |