aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar shtlrs <[email protected]>2024-02-18 20:06:42 +0100
committerGravatar shtlrs <[email protected]>2024-03-21 14:29:02 +0100
commit73ab9d950854b63aaa8c980fcd80eb3f878a4bbb (patch)
treec5602f9863cc8817c88b1ccf5947e07e98143dbd
parentMerge pull request #1481 from python-discord/dependabot/pip/pydis-core-11.0.1 (diff)
implement CommandNotFoundErrorHandler
-rw-r--r--bot/command_error_handlers/__init__.py0
-rw-r--r--bot/command_error_handlers/command_not_found.py64
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)