diff --git a/modules/administration/config.py b/modules/administration/config.py index 9961410b..50077f48 100644 --- a/modules/administration/config.py +++ b/modules/administration/config.py @@ -52,7 +52,6 @@ class ConfigControl(cogs.BaseCog): @config_extension_commands.command( name="enable", description="Enables an extension for the guild by name", - extras={"usage": "[extension-name]"}, ) async def enable_extension( self: Self, interaction: discord.Interaction, extension_name: str @@ -99,7 +98,6 @@ async def enable_extension( @config_extension_commands.command( name="disable", description="Disables an extension for the guild by name", - extras={"usage": "[extension-name]"}, ) async def disable_extension( self: Self, interaction: discord.Interaction, extension_name: str @@ -236,7 +234,6 @@ async def config_json(self: Self, interaction: discord.Interaction) -> None: @config_commands.command( name="patch", description="Edits guild config by uploading JSON", - extras={"usage": "[uploaded-json]"}, ) async def patch_config( self: Self, interaction: discord.Interaction, config_json: discord.Attachment diff --git a/modules/administration/extension.py b/modules/administration/extension.py index 17c51ca2..9db32f46 100644 --- a/modules/administration/extension.py +++ b/modules/administration/extension.py @@ -50,7 +50,6 @@ class ExtensionControl(cogs.BaseCog): @extension_commands.command( name="status", description="Gets the status of an extension by name", - extras={"usage": "[extension-name]"}, ) async def extension_status( self: Self, interaction: discord.Interaction, extension_name: str @@ -87,7 +86,6 @@ async def extension_status( @extension_commands.command( name="load", description="Loads an extension by name", - extras={"usage": "[extension-name]"}, ) async def load_extension( self: Self, interaction: discord.Interaction, extension_name: str @@ -118,7 +116,6 @@ async def load_extension( @extension_commands.command( name="unload", description="Unloads an extension by name", - extras={"usage": "[extension-name]"}, ) async def unload_extension( self: Self, interaction: discord.Interaction, extension_name: str @@ -149,7 +146,6 @@ async def unload_extension( @extension_commands.command( name="reload", description="Reloads an extension by name", - extras={"usage": "[extension-name]"}, ) async def reload_extension( self: Self, interaction: discord.Interaction, extension_name: str @@ -183,9 +179,6 @@ async def reload_extension( @extension_commands.command( name="register", description="Uploads an extension from Discord to be saved on the bot", - extras={ - "usage": "[extension-name] [python-file-upload]", - }, ) async def register_extension( self: Self, diff --git a/modules/fun/xkcd.py b/modules/fun/xkcd.py index 6a157017..7a32278a 100644 --- a/modules/fun/xkcd.py +++ b/modules/fun/xkcd.py @@ -116,7 +116,6 @@ async def xkcd_random(self: Self, interaction: discord.Interaction) -> None: @xkcd.command( name="specific", description="Gets an XKCD comic by number.", - extras={"usage": "[comic_number]"}, ) async def xkcd_specific( self: Self, interaction: discord.Interaction, comic_number: int diff --git a/modules/moderation/nicknamefix.py b/modules/moderation/nicknamefix.py index f75cdebc..6dde0b0b 100644 --- a/modules/moderation/nicknamefix.py +++ b/modules/moderation/nicknamefix.py @@ -31,9 +31,6 @@ class NicknameFixer(cogs.BaseCog): @app_commands.command( name="nicknamefix", description="Auto adjusts a nickname of the given member", - extras={ - "usage": "member", - }, ) async def fixnickname( self: Self, interaction: discord.Interaction, member: discord.Member diff --git a/modules/moderation/notes.py b/modules/moderation/notes.py index 3dc08d4a..23ac7a98 100644 --- a/modules/moderation/notes.py +++ b/modules/moderation/notes.py @@ -110,10 +110,6 @@ class Notes(cogs.BaseCog): @notes.command( name="set", description="Adds a note to a given user.", - extras={ - "brief": "Sets a note for a user", - "usage": "@user [note]", - }, ) async def set_note( self: Self, interaction: discord.Interaction, user: discord.Member, body: str @@ -182,10 +178,6 @@ async def set_note( @notes.command( name="clear", description="Clears all existing notes for a user", - extras={ - "brief": "Clears all notes for a user", - "usage": "@user", - }, ) async def clear_notes( self: Self, interaction: discord.Interaction, user: discord.Member @@ -248,10 +240,6 @@ async def clear_notes( @notes.command( name="all", description="Gets all notes for a user instead of just new ones", - extras={ - "brief": "Gets all notes for a user", - "usage": "@user", - }, ) async def all_notes( self: Self, interaction: discord.Interaction, member: discord.Member diff --git a/modules/moderation/slowmode.py b/modules/moderation/slowmode.py index 97590f2e..033f60f5 100644 --- a/modules/moderation/slowmode.py +++ b/modules/moderation/slowmode.py @@ -30,10 +30,6 @@ class SlowmodeManager(cogs.BaseCog): @app_commands.command( name="slowmode", description="Modifies slowmode on a given channel", - extras={ - "brief": "Changes time for slowmode", - "usage": "seconds, [optional] channel", - }, ) async def slowmode( self: Self, diff --git a/modules/moderation/whois.py b/modules/moderation/whois.py index b1a9ec65..525a59bd 100644 --- a/modules/moderation/whois.py +++ b/modules/moderation/whois.py @@ -34,7 +34,6 @@ class Whois(cogs.BaseCog): name="whois", description="Gets Discord user information", extras={ - "usage": "@user", "ephemeral_error": True, }, ) diff --git a/modules/operation/factoids.py b/modules/operation/factoids.py index 692afafa..a469bdc1 100644 --- a/modules/operation/factoids.py +++ b/modules/operation/factoids.py @@ -941,9 +941,6 @@ async def send_to_logger( @factoid_app_group.command( name="call", description="Calls a factoid from the database and sends it publicy in the channel.", - extras={ - "usage": "[factoid_name]", - }, ) async def factoid_call_command( self: Self, @@ -1732,10 +1729,6 @@ async def info( @factoid_app_group.command( name="all", description="Sends a configurable list of all factoids.", - extras={ - "brief": "Sets a note for a user", - "usage": "[file] [property] [true_all] [ignore_hidden]", - }, ) async def app_command_all( self: Self, diff --git a/modules/operation/xp.py b/modules/operation/xp.py index fb19bd20..6d1a4f66 100644 --- a/modules/operation/xp.py +++ b/modules/operation/xp.py @@ -48,9 +48,6 @@ async def preconfig(self: Self) -> None: @xp.command( name="top", description="Shows the top 10 XP users in the server", - extras={ - "usage": "", - }, ) async def top_xp_command(self: Self, interaction: discord.Interaction) -> None: """This command will display an embed of the top 10 users with XP diff --git a/modules/utility/help.py b/modules/utility/help.py index e48a964a..92d8ed3a 100644 --- a/modules/utility/help.py +++ b/modules/utility/help.py @@ -28,6 +28,7 @@ class PrintableCommand: name (str): The command name usage (str): The usage hints for the command description (str): The description of the command + mention (str): A mention string for the command, only for application commands """ @@ -35,6 +36,7 @@ class PrintableCommand: name: str usage: str description: str + mention: str = None async def setup(bot: bot.TechSupportBot) -> None: @@ -70,6 +72,9 @@ async def help_command( # Build raw lists of commands prefix_command_list = list(self.bot.walk_commands()) app_command_list = list(self.bot.tree.walk_commands()) + fetched_commands = await self.bot.tree.fetch_commands() + + command_mentions = build_command_mentions(fetched_commands) command_prefix = await self.bot.get_prefix(ctx.message) @@ -132,9 +137,14 @@ async def help_command( # We have to manually build a string representation of the usage # We are given it in a list - command_usage = "" - for param in command.parameters: - command_usage += f"[{param.name}] " + command_usage = "".join( + ( + f"[{param.name}: {param.type.name}] " + if param.required + else f"({param.name}: {param.type.name}) " + ) + for param in command.parameters + ) # Append the app commands. # App commands cannot have aliases, so no need to think about that @@ -144,6 +154,7 @@ async def help_command( name=command.qualified_name, usage=command_usage.strip(), description=command.description, + mention=command_mentions.get(command.qualified_name), ) ) @@ -206,10 +217,59 @@ def build_embeds_from_list( for command_list in sublists: embed = discord.Embed(title=title, color=discord.Color.green()) for command in command_list: + display_name = ( + command.mention + if command.mention + else f"{command.prefix}{command.name}" + ) embed.add_field( - name=f"{command.prefix}{command.name} {command.usage}", + name=f"{display_name} {command.usage}", value=command.description, inline=False, ) final_embeds.append(embed) return final_embeds + + +def build_command_mentions( + fetched_commands_list: list[app_commands.AppCommand], +) -> dict[str, str]: + """Build a mapping of command names to mentions. + + Args: + fetched_commands_list (list[app_commands.AppCommand]): + The list of commands fetched from the bot. + + Returns: + dict[str, str]: A dictionary mapping full names to mentions. + """ + mentions: dict[str, str] = {} + + def walk( + command: app_commands.AppCommand | app_commands.AppCommandGroup, + root_id: int, + prefix: str = "", + ) -> None: + """Recursively walk command groups. + + Args: + command (app_commands.AppCommand | app_commands.AppCommandGroup): + The current command/group being processed. + root_id (int): The ID of the top-level command. + prefix (str): The accumulated command path. Defaults to "" + """ + qualified_name = f"{prefix} {command.name}".strip() + + mentions[qualified_name] = f"" + + for option in command.options: + if option.type in ( + discord.AppCommandOptionType.subcommand, + discord.AppCommandOptionType.subcommand_group, + ): + walk(option, root_id, qualified_name) + + for command in fetched_commands_list: + walk(command, command.id) + + return mentions diff --git a/modules/utility/search.py b/modules/utility/search.py index 74fe2366..a1879fde 100644 --- a/modules/utility/search.py +++ b/modules/utility/search.py @@ -80,9 +80,6 @@ async def make_request(self: Self, query: str) -> munch.Munch: @search.command( name="text", description="Returns the top Web search result", - extras={ - "usage": "[query]", - }, ) async def websearch_text( self: Self, interaction: discord.Interaction, query: str @@ -119,9 +116,6 @@ async def websearch_text( @search.command( name="images", description="Returns the top Web search image result", - extras={ - "usage": "[query]", - }, ) async def websearch_image( self: Self, interaction: discord.Interaction, query: str diff --git a/modules/utility/youtube.py b/modules/utility/youtube.py index c4c7b61f..2a949c1c 100644 --- a/modules/utility/youtube.py +++ b/modules/utility/youtube.py @@ -69,9 +69,6 @@ async def get_items( @app_commands.command( name="youtube", description="Returns the top YouTube search result", - extras={ - "usage": "[query]", - }, ) async def youtube(self: Self, interaction: discord.Interaction, query: str) -> None: """The entry point for the youtube search command