diff options
Diffstat (limited to 'bot/exts/core')
| -rw-r--r-- | bot/exts/core/error_handler.py | 43 | ||||
| -rw-r--r-- | bot/exts/core/help.py | 40 | 
2 files changed, 50 insertions, 33 deletions
| diff --git a/bot/exts/core/error_handler.py b/bot/exts/core/error_handler.py index 983632ba..4578f734 100644 --- a/bot/exts/core/error_handler.py +++ b/bot/exts/core/error_handler.py @@ -1,4 +1,3 @@ -import difflib  import logging  import math  import random @@ -11,6 +10,7 @@ from sentry_sdk import push_scope  from bot.bot import Bot  from bot.constants import Channels, Colours, ERROR_REPLIES, NEGATIVE_REPLIES, RedirectOutput +from bot.utils.commands import get_command_suggestions  from bot.utils.decorators import InChannelCheckFailure, InMonthCheckFailure  from bot.utils.exceptions import APIError, MovedCommandError, UserNotPlayingError @@ -158,31 +158,32 @@ class CommandErrorHandler(commands.Cog):      async def send_command_suggestion(self, ctx: commands.Context, command_name: str) -> None:          """Sends user similar commands if any can be found.""" -        raw_commands = [] -        for cmd in self.bot.walk_commands(): -            if not cmd.hidden: -                raw_commands += (cmd.name, *cmd.aliases) -        if similar_command_data := difflib.get_close_matches(command_name, raw_commands, 1): -            similar_command_name = similar_command_data[0] -            similar_command = self.bot.get_command(similar_command_name) - -            if not similar_command: -                return - -            log_msg = "Cancelling attempt to suggest a command due to failed checks." -            try: -                if not await similar_command.can_run(ctx): +        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 as cmd_error:                      log.debug(log_msg) -                    return -            except commands.errors.CommandError as cmd_error: -                log.debug(log_msg) -                await self.on_command_error(ctx, cmd_error) -                return +                    await self.on_command_error(ctx, cmd_error) +                    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 = misspelled_content.replace(command_name, similar_command_name, 1) +            e.description = "\n".join( +                misspelled_content.replace(command_name, cmd, 1) for cmd in command_suggestions +            )              await ctx.send(embed=e, delete_after=RedirectOutput.delete_delay) diff --git a/bot/exts/core/help.py b/bot/exts/core/help.py index f9b3513f..b5df70ca 100644 --- a/bot/exts/core/help.py +++ b/bot/exts/core/help.py @@ -3,16 +3,16 @@ import asyncio  import itertools  import logging  from contextlib import suppress -from typing import NamedTuple, Union +from typing import NamedTuple, Optional, Union  from discord import Colour, Embed, HTTPException, Message, Reaction, User  from discord.ext import commands  from discord.ext.commands import CheckFailure, Cog as DiscordCog, Command, Context -from rapidfuzz import process  from bot import constants  from bot.bot import Bot  from bot.constants import Emojis +from bot.utils.commands import get_command_suggestions  from bot.utils.pagination import FIRST_EMOJI, LAST_EMOJI, LEFT_EMOJI, LinePaginator, RIGHT_EMOJI  DELETE_EMOJI = Emojis.trashcan @@ -41,14 +41,18 @@ class HelpQueryNotFound(ValueError):      """      Raised when a HelpSession Query doesn't match a command or cog. -    Contains the custom attribute of ``possible_matches``. -    Instances of this object contain a dictionary of any command(s) that were close to matching the -    query, where keys are the possible matched command names and values are the likeness match scores. +    Params: +    possible_matches: list of similar command names. +    parent_command: parent command of an invalid subcommand. Only available when an invalid subcommand +                    has been passed.      """ -    def __init__(self, arg: str, possible_matches: dict = None): +    def __init__( +            self, arg: str, possible_matches: Optional[list[str]] = None, *, parent_command: Optional[Command] = None +    ) -> None:          super().__init__(arg)          self.possible_matches = possible_matches +        self.parent_command = parent_command  class HelpSession: @@ -153,12 +157,17 @@ class HelpSession:          Will pass on possible close matches along with the `HelpQueryNotFound` exception.          """ -        # Combine command and cog names -        choices = list(self._bot.all_commands) + list(self._bot.cogs) +        # Check if parent command is valid in case subcommand is invalid. +        if " " in query: +            parent, *_ = query.split() +            parent_command = self._bot.get_command(parent) + +            if parent_command: +                raise HelpQueryNotFound('Invalid Subcommand.', parent_command=parent_command) -        result = process.extract(query, choices, score_cutoff=90) +        similar_commands = get_command_suggestions(list(self._bot.all_commands.keys()), query) -        raise HelpQueryNotFound(f'Query "{query}" not found.', dict(result)) +        raise HelpQueryNotFound(f'Query "{query}" not found.', similar_commands)      async def timeout(self, seconds: int = 30) -> None:          """Waits for a set number of seconds, then stops the help session.""" @@ -507,13 +516,20 @@ class Help(DiscordCog):          try:              await HelpSession.start(ctx, *commands)          except HelpQueryNotFound as error: + +            # Send help message of parent command if subcommand is invalid. +            if cmd := error.parent_command: +                await ctx.send(str(error)) +                await self.new_help(ctx, cmd.qualified_name) +                return +              embed = Embed()              embed.colour = Colour.red()              embed.title = str(error)              if error.possible_matches: -                matches = "\n".join(error.possible_matches.keys()) -                embed.description = f"**Did you mean:**\n`{matches}`" +                matches = "\n".join(error.possible_matches) +                embed.description = f"**Did you mean:**\n{matches}"              await ctx.send(embed=embed) | 
