Source code for discordSuperUtils.CommandHinter
from abc import ABC, abstractmethod
from difflib import SequenceMatcher
from typing import (
List,
Union
)
import discord
from discord.ext import commands
from .Base import get_generator_response
__all__ = ("CommandResponseGenerator", "DefaultResponseGenerator", "CommandHinter")
[docs]class CommandResponseGenerator(ABC):
__slots__ = ()
[docs] @abstractmethod
def generate(self, invalid_command: str, suggestions: List[str]) -> Union[str, discord.Embed]:
pass
[docs]class DefaultResponseGenerator(CommandResponseGenerator):
__slots__ = ()
[docs] def generate(self, invalid_command: str, suggestions: List[str]) -> discord.Embed:
embed = discord.Embed(
title="Invalid command!",
description=f"**`{invalid_command}`** is invalid. Did you mean:",
color=0x00ff00
)
for index, suggestion in enumerate(suggestions[:3]):
embed.add_field(name=f"**{index + 1}.**", value=f"**`{suggestion}`**", inline=False)
return embed
[docs]class CommandHinter:
__slots__ = ("bot", "generator")
def __init__(self,
bot: commands.Bot,
generator=None):
self.bot = bot
self.generator = DefaultResponseGenerator if generator is None else generator
self.bot.add_listener(self.__handle_hinter, "on_command_error")
@property
def command_names(self) -> List[str]:
names = []
for command in self.bot.commands:
if isinstance(command, commands.Group):
names += [command.name] + command.aliases
for inner_command in command.commands:
names += [inner_command.name] + inner_command.aliases
else:
names += [command.name] + command.aliases
return names
async def __handle_hinter(self, ctx: commands.Context, error) -> None:
if isinstance(error, commands.CommandNotFound):
command_similarity = {}
command_used = ctx.message.content.lstrip(ctx.prefix)[:max([len(c) for c in self.command_names])]
for command in self.command_names:
command_similarity[SequenceMatcher(None, command, command_used).ratio()] = command
generated_message = get_generator_response(
self.generator,
CommandResponseGenerator,
command_used,
[x[1] for x in sorted(command_similarity.items(), reverse=True)]
)
if isinstance(generated_message, discord.Embed):
await ctx.send(embed=generated_message)
elif isinstance(generated_message, str):
await ctx.send(generated_message)
else:
raise TypeError("The generated message must be of type 'discord.Embed' or 'str'.")
else:
raise error