1
Fork 0
mirror of https://github.com/allthingslinux/tux.git synced 2024-10-02 16:43:12 +00:00

refactor: remove dollar sign from usage instructions in various commands for consistency

chore: remove error handling blocks from dev.py to simplify code and rely on global error handling

refactor: remove dollar sign from usage instructions in command decorators for clarity and consistency across all command usage instructions

feat(error.py): add prefix to command usage in error messages for better clarity
refactor(error.py): change error messages to be more descriptive and accurate
feat(error.py): use embeds for error messages for better readability
refactor(error.py): comment out specific error handlers to allow global handler to catch all errors for consistency

refactor(help.py): remove static prefix and add dynamic prefix fetching to improve flexibility
feat(help.py): add command aliases to help command categories for better command visibility
refactor(help.py): change several methods to async to support dynamic prefix fetching
feat(embeds.py): add create_error_embed method to standardize error embed creation
This commit is contained in:
kzndotsh 2024-08-15 06:33:00 +00:00
parent 7384af8d1c
commit 9e221709ac
27 changed files with 133 additions and 141 deletions

View file

@ -14,7 +14,7 @@ class Notes(ModerationCogBase):
@commands.hybrid_group(
name="notes",
aliases=["n"],
usage="$notes <subcommand>",
usage="notes <subcommand>",
)
@commands.guild_only()
async def notes(self, ctx: commands.Context[commands.Bot]) -> None:
@ -32,7 +32,7 @@ class Notes(ModerationCogBase):
@notes.command(
name="create",
aliases=["c", "add", "a"],
usage="$notes create [target] [content]",
usage="notes create [target] [content]",
)
@commands.guild_only()
async def create_note(self, ctx: commands.Context[commands.Bot], target: discord.Member, content: str) -> None:
@ -67,7 +67,7 @@ class Notes(ModerationCogBase):
@notes.command(
name="delete",
aliases=["d"],
usage="$notes delete [note_id]",
usage="notes delete [note_id]",
)
@commands.guild_only()
async def delete_note(self, ctx: commands.Context[commands.Bot], note_id: int) -> None:
@ -108,7 +108,7 @@ class Notes(ModerationCogBase):
@notes.command(
name="update",
aliases=["u", "edit", "e", "modify", "m"],
usage="$notes update [note_id] [content]",
usage="notes update [note_id] [content]",
)
@commands.guild_only()
async def update_note(self, ctx: commands.Context[commands.Bot], note_id: int, content: str) -> None:
@ -152,7 +152,7 @@ class Notes(ModerationCogBase):
@notes.command(
name="view",
aliases=["v", "get", "g"],
usage="$notes view [note_id]",
usage="notes view [note_id]",
)
@commands.guild_only()
async def view_note(

View file

@ -12,7 +12,7 @@ class Dev(commands.Cog):
@commands.hybrid_group(
name="dev",
aliases=["d"],
usage="$dev <subcommand>",
usage="dev <subcommand>",
)
@commands.guild_only()
@checks.has_pl(8)
@ -38,7 +38,7 @@ class Dev(commands.Cog):
@dev.command(
name="sync_tree",
aliases=["st", "sync", "s"],
usage="$dev sync_tree [guild]",
usage="dev sync_tree [guild]",
)
@commands.guild_only()
@checks.has_pl(8)
@ -69,21 +69,10 @@ class Dev(commands.Cog):
await self.bot.tree.sync(guild=ctx.guild)
await ctx.send("Application command tree synced.")
@sync_tree.error
async def sync_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send(
f"Please specify a guild to sync application commands to. {error}",
ephemeral=True,
delete_after=30,
)
else:
logger.error(f"Error syncing application commands: {error}")
@dev.command(
name="clear_tree",
aliases=["ct", "clear", "c"],
usage="$dev clear_tree",
usage="dev clear_tree",
)
@commands.guild_only()
@checks.has_pl(8)
@ -118,7 +107,7 @@ class Dev(commands.Cog):
@dev.command(
name="load_cog",
aliases=["lc", "load", "l"],
usage="$dev load_cog [cog]",
usage="dev load_cog [cog]",
)
@commands.guild_only()
@checks.has_pl(8)
@ -156,30 +145,10 @@ class Dev(commands.Cog):
await ctx.send(f"Cog {cog} loaded.")
logger.info(f"Cog {cog} loaded.")
@load_cog.error
async def load_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f"Please specify an cog to load. {error}", ephemeral=True, delete_after=30)
elif isinstance(error, commands.ExtensionAlreadyLoaded):
await ctx.send(f"The specified cog is already loaded. {error}", ephemeral=True, delete_after=30)
elif isinstance(error, commands.ExtensionNotFound):
await ctx.send(f"The specified cog is not found. {error}", ephemeral=True, delete_after=30)
elif isinstance(error, commands.ExtensionFailed):
await ctx.send(f"Failed to load cog: {error}", ephemeral=True, delete_after=30)
elif isinstance(error, commands.NoEntryPointError):
await ctx.send(
f"The specified cog does not have a setup function. {error}",
ephemeral=True,
delete_after=30,
)
else:
await ctx.send(f"Failed to load cog: {error}", ephemeral=True, delete_after=30)
logger.error(f"Failed to load cog: {error}")
@dev.command(
name="unload_cog",
aliases=["uc", "unload", "u"],
usage="$dev unload_cog [cog]",
usage="dev unload_cog [cog]",
)
@commands.guild_only()
@checks.has_pl(8)
@ -211,19 +180,10 @@ class Dev(commands.Cog):
logger.info(f"Cog {cog} unloaded.")
await ctx.send(f"Cog {cog} unloaded.", ephemeral=True, delete_after=30)
@unload_cog.error
async def unload_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f"Please specify an extension to unload. {error}", ephemeral=True, delete_after=30)
elif isinstance(error, commands.ExtensionNotLoaded):
await ctx.send(f"That cog is not loaded. {error}", ephemeral=True, delete_after=30)
else:
logger.error(f"Error unloading cog: {error}")
@dev.command(
name="reload_cog",
aliases=["rc", "reload", "r"],
usage="$dev reload_cog [cog]",
usage="dev reload_cog [cog]",
)
@commands.guild_only()
@checks.has_pl(8)
@ -256,16 +216,6 @@ class Dev(commands.Cog):
await ctx.send(f"Cog {cog} reloaded.", ephemeral=True, delete_after=30)
logger.info(f"Cog {cog} reloaded.")
@reload_cog.error
async def reload_error(self, ctx: commands.Context[commands.Bot], error: Exception) -> None:
if isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f"Please specify a cog to reload. {error}", ephemeral=True, delete_after=30)
elif isinstance(error, commands.ExtensionNotLoaded):
await ctx.send(f"That cog is not loaded. {error}", ephemeral=True, delete_after=30)
else:
await ctx.send(f"Error reloading cog: {error}", ephemeral=True, delete_after=30)
logger.error(f"Error reloading cog: {error}")
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(Dev(bot))

View file

@ -45,7 +45,7 @@ class Eval(commands.Cog):
@commands.command(
name="eval",
aliases=["e"],
usage="$eval [expression]",
usage="eval [expression]",
)
@commands.guild_only()
@checks.has_pl(9)

View file

@ -403,7 +403,7 @@ class Git(commands.Cog):
@commands.hybrid_group(
name="git",
aliases=["g"],
usage="$git <subcommand>",
usage="git <subcommand>",
)
@commands.guild_only()
@checks.has_pl(8)
@ -423,7 +423,7 @@ class Git(commands.Cog):
@git.command(
name="get_repo",
aliases=["r"],
usage="$git get_repo",
usage="git get_repo",
)
@commands.guild_only()
@checks.has_pl(8)
@ -461,7 +461,7 @@ class Git(commands.Cog):
@git.command(
name="create_issue",
aliases=["ci"],
usage="$git create_issue [title] [body]",
usage="git create_issue [title] [body]",
)
@commands.guild_only()
# @checks.has_pl(8)
@ -503,7 +503,7 @@ class Git(commands.Cog):
@git.command(
name="get_issue",
aliases=["gi", "issue", "i"],
usage="$git get_issue [issue_number]",
usage="git get_issue [issue_number]",
)
@commands.guild_only()
@checks.has_pl(8)

View file

@ -12,7 +12,7 @@ class Random(commands.Cog):
@commands.hybrid_group(
name="random",
aliases=["rand"],
usage="$random <subcommand>",
usage="random <subcommand>",
)
@commands.guild_only()
async def random(self, ctx: commands.Context[commands.Bot]) -> None:
@ -30,7 +30,7 @@ class Random(commands.Cog):
@random.command(
name="coinflip",
aliases=["cf"],
usage="$random coinflip",
usage="random coinflip",
)
@commands.guild_only()
async def coinflip(self, ctx: commands.Context[commands.Bot]) -> None:
@ -50,7 +50,7 @@ class Random(commands.Cog):
@random.command(
name="8ball",
aliases=["eightball", "8b"],
usage="$random 8ball [question]",
usage="random 8ball [question]",
)
@commands.guild_only()
async def eight_ball(self, ctx: commands.Context[commands.Bot], *, question: str, cow: bool = False) -> None:
@ -140,7 +140,7 @@ class Random(commands.Cog):
@random.command(
name="dice",
aliases=["d"],
usage="$random dice <sides>",
usage="random dice <sides>",
)
@commands.guild_only()
async def dice(self, ctx: commands.Context[commands.Bot], sides: int = 6) -> None:
@ -170,7 +170,7 @@ class Random(commands.Cog):
@random.command(
name="number",
aliases=["n"],
usage="$random number <min> <max>",
usage="random number <min> <max>",
)
@commands.guild_only()
async def random_number(

View file

@ -15,7 +15,7 @@ class Xkcd(commands.Cog):
@commands.hybrid_group(
name="xkcd",
aliases=["xk"],
usage="$xkcd <subcommand>",
usage="xkcd <subcommand>",
)
@commands.guild_only()
async def xkcd(self, ctx: commands.Context[commands.Bot], comic_id: int | None = None) -> None:
@ -38,7 +38,7 @@ class Xkcd(commands.Cog):
@xkcd.command(
name="latest",
aliases=["l", "new", "n"],
usage="$xkcd latest",
usage="xkcd latest",
)
@commands.guild_only()
async def latest(self, ctx: commands.Context[commands.Bot]) -> None:
@ -61,7 +61,7 @@ class Xkcd(commands.Cog):
@xkcd.command(
name="random",
aliases=["rand", "r"],
usage="$xkcd random",
usage="xkcd random",
)
@commands.guild_only()
async def random(self, ctx: commands.Context[commands.Bot]) -> None:
@ -84,7 +84,7 @@ class Xkcd(commands.Cog):
@xkcd.command(
name="specific",
aliases=["s", "id", "num"],
usage="$xkcd specific [comic_id]",
usage="xkcd specific [comic_id]",
)
@commands.guild_only()
async def specific(self, ctx: commands.Context[commands.Bot], comic_id: int) -> None:

View file

@ -36,7 +36,7 @@ class Avatar(commands.Cog):
@commands.command(
name="avatar",
aliases=["av"],
usage="$avatar <member>",
usage="avatar <member>",
)
@commands.guild_only()
async def prefix_avatar(

View file

@ -232,7 +232,7 @@ class Run(commands.Cog):
@commands.command(
name="run",
aliases=["compile", "exec"],
usage="$run [code]",
usage="run [code]",
)
async def run(
self,
@ -302,7 +302,7 @@ class Run(commands.Cog):
@commands.command(
name="languages",
aliases=["langs"],
usage="$languages",
usage="languages",
)
async def languages(self, ctx: commands.Context[commands.Bot]) -> None:
"""

View file

@ -18,7 +18,7 @@ class Ban(ModerationCogBase):
@commands.hybrid_command(
name="ban",
aliases=["b"],
usage="$ban [target] <flags>",
usage="ban [target] <flags>",
)
@commands.guild_only()
@checks.has_pl(3)

View file

@ -33,7 +33,7 @@ class Cases(ModerationCogBase):
@commands.hybrid_group(
name="cases",
aliases=["c"],
usage="$cases <subcommand>",
usage="cases <subcommand>",
)
@commands.guild_only()
@checks.has_pl(2)
@ -47,7 +47,7 @@ class Cases(ModerationCogBase):
@cases.command(
name="view",
aliases=["v", "ls", "list"],
usage="$cases view <case_number> <flags>",
usage="cases view <case_number> <flags>",
)
@commands.guild_only()
@checks.has_pl(2)
@ -83,7 +83,7 @@ class Cases(ModerationCogBase):
@cases.command(
name="modify",
aliases=["m", "edit"],
usage="$cases modify [case_number] <flags>",
usage="cases modify [case_number] <flags>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -87,7 +87,7 @@ class Jail(ModerationCogBase):
@commands.hybrid_command(
name="jail",
aliases=["j"],
usage="$jail [target] [reason] <silent>",
usage="jail [target] [reason] <silent>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -18,7 +18,7 @@ class Kick(ModerationCogBase):
@commands.hybrid_command(
name="kick",
aliases=["k"],
usage="$kick [target] [reason] <silent>",
usage="kick [target] [reason] <silent>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -73,7 +73,7 @@ class Purge(commands.Cog):
@commands.command(
name="purge",
aliases=["p"],
usage="$purge [limit] <channel>",
usage="purge [limit] <channel>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -12,7 +12,7 @@ class Slowmode(commands.Cog):
@commands.hybrid_command(
name="slowmode",
aliases=["sm"],
usage="$slowmode [delay] <channel>",
usage="slowmode [delay] <channel>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -61,7 +61,7 @@ class Timeout(ModerationCogBase):
@commands.hybrid_command(
name="timeout",
aliases=["t", "to", "mute"],
usage="$timeout [target] [duration] [reason]",
usage="timeout [target] [duration] [reason]",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -18,7 +18,7 @@ class Unban(ModerationCogBase):
@commands.hybrid_command(
name="unban",
aliases=["ub"],
usage="$unban [target] [reason]",
usage="unban [target] [reason]",
)
@commands.guild_only()
@checks.has_pl(3)

View file

@ -18,7 +18,7 @@ class Unjail(ModerationCogBase):
@commands.hybrid_command(
name="unjail",
aliases=["uj"],
usage="$unjail [target] [reason] <silent>",
usage="unjail [target] [reason] <silent>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -18,7 +18,7 @@ class Untimeout(ModerationCogBase):
@commands.hybrid_command(
name="untimeout",
aliases=["ut", "uto", "unmute"],
usage="$untimeout [target] [reason]",
usage="untimeout [target] [reason]",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -18,7 +18,7 @@ class Warn(ModerationCogBase):
@commands.hybrid_command(
name="warn",
aliases=["w"],
usage="$warn [target] <flags>",
usage="warn [target] <flags>",
)
@commands.guild_only()
@checks.has_pl(2)

View file

@ -10,7 +10,7 @@ class Ping(commands.Cog):
@commands.hybrid_command(
name="ping",
usage="$ping",
usage="ping",
)
async def ping(self, ctx: commands.Context[commands.Bot]) -> None:
"""

View file

@ -14,7 +14,7 @@ class Query(commands.Cog):
@commands.hybrid_command(
name="query",
aliases=["q"],
usage="$query [search_term]",
usage="query [search_term]",
)
async def query(self, ctx: commands.Context[commands.Bot], *, search_term: str) -> None:
"""

View file

@ -23,7 +23,7 @@ class Snippets(commands.Cog):
@commands.command(
name="snippets",
aliases=["ls"],
usage="$snippets",
usage="snippets",
)
@commands.guild_only()
async def list_snippets(self, ctx: commands.Context[commands.Bot]) -> None:
@ -104,7 +104,7 @@ class Snippets(commands.Cog):
@commands.command(
name="deletesnippet",
aliases=["ds"],
usage="$deletesnippet [name]",
usage="deletesnippet [name]",
)
@commands.guild_only()
async def delete_snippet(self, ctx: commands.Context[commands.Bot], name: str) -> None:
@ -154,7 +154,7 @@ class Snippets(commands.Cog):
@commands.command(
name="forcedeletesnippet",
aliases=["fds"],
usage="$forcedeletesnippet [name]",
usage="forcedeletesnippet [name]",
)
@commands.guild_only()
@checks.has_pl(2)
@ -193,7 +193,7 @@ class Snippets(commands.Cog):
@commands.command(
name="snippet",
aliases=["s"],
usage="$snippet [name]",
usage="snippet [name]",
)
@commands.guild_only()
async def get_snippet(self, ctx: commands.Context[commands.Bot], name: str) -> None:
@ -230,7 +230,7 @@ class Snippets(commands.Cog):
@commands.command(
name="snippetinfo",
aliases=["si"],
usage="$snippetinfo [name]",
usage="snippetinfo [name]",
)
@commands.guild_only()
async def get_snippet_info(self, ctx: commands.Context[commands.Bot], name: str) -> None:
@ -287,7 +287,7 @@ class Snippets(commands.Cog):
@commands.command(
name="createsnippet",
aliases=["cs"],
usage="$createsnippet [name] [content]",
usage="createsnippet [name] [content]",
)
@commands.guild_only()
async def create_snippet(self, ctx: commands.Context[commands.Bot], *, arg: str) -> None:

View file

@ -70,7 +70,7 @@ class Tldr(commands.Cog):
@commands.command(
name="tldr",
aliases=["man"],
usage="$tldr [command]",
usage="tldr [command]",
)
@commands.guild_only()
async def prefix_tldr(self, ctx: commands.Context[commands.Bot], command: str) -> None:

View file

@ -83,7 +83,7 @@ class Wiki(commands.Cog):
@commands.hybrid_group(
name="wiki",
usage="$wiki [arch|atl]",
usage="wiki [arch|atl]",
aliases=["wk"],
)
async def wiki(self, ctx: commands.Context[commands.Bot]) -> None:
@ -101,7 +101,7 @@ class Wiki(commands.Cog):
@wiki.command(
name="arch",
usage="$wiki arch [query]",
usage="wiki arch [query]",
)
async def arch_wiki(self, ctx: commands.Context[commands.Bot], query: str) -> None:
"""
@ -131,7 +131,7 @@ class Wiki(commands.Cog):
@wiki.command(
name="atl",
usage="$wiki atl [query]",
usage="wiki atl [query]",
)
async def atl_wiki(self, ctx: commands.Context[commands.Bot], query: str) -> None:
"""

View file

@ -7,6 +7,7 @@ from discord.ext import commands
from loguru import logger
import tux.handlers.error as error
from tux.utils.embeds import create_error_embed
class PermissionLevelError(commands.CheckFailure):
@ -44,14 +45,14 @@ error_map: dict[type[Exception], str] = {
commands.CheckFailure: "User not in sudoers file. This incident will be reported. (Permission Check Failed)",
commands.CommandNotFound: "This command was not found.",
commands.CommandOnCooldown: "This command is on cooldown. Try again in {error.retry_after:.2f} seconds.",
commands.BadArgument: "Invalid argument passed. Correct usage: `{ctx.command.usage}`",
commands.MissingRequiredArgument: "Missing required argument. Correct usage: `{ctx.command.usage}`",
commands.BadArgument: "Invalid argument passed. Correct usage: `{ctx.prefix}{ctx.command.usage}`",
commands.MissingRequiredArgument: "Missing required arg. Correct usage: `{ctx.prefix}{ctx.command.usage}`",
commands.MissingRequiredAttachment: "Missing required attachment.",
commands.NotOwner: "User not in sudoers file. This incident will be reported. (Not Owner)",
commands.BotMissingPermissions: "User not in sudoers file. This incident will be reported. (Bot Missing Permissions)",
# Custom errors
error.PermissionLevelError: "User not in sudoers file. This incident will be reported. (You do not have the required permission: {error.permission})",
error.AppCommandPermissionLevelError: "User not in sudoers file. This incident will be reported. (You do not have the required permission: {error.permission})",
error.PermissionLevelError: "User not in sudoers file. This incident will be reported. (Missing required permission: {error.permission})",
error.AppCommandPermissionLevelError: "User not in sudoers file. This incident will be reported. (Missing required permission: {error.permission})",
}
@ -84,10 +85,12 @@ class ErrorHandler(commands.Cog):
error_message = error_map.get(type(error), self.error_message).format(error=error)
embed = create_error_embed(error_message)
if interaction.response.is_done():
await interaction.followup.send(error_message, ephemeral=True)
await interaction.followup.send(embed=embed, ephemeral=True)
else:
await interaction.response.send_message(error_message, ephemeral=True, delete_after=30)
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=30)
if type(error) in error_map:
sentry_sdk.capture_exception(error)
@ -112,18 +115,20 @@ class ErrorHandler(commands.Cog):
"""
# # If the command has its own error handler, return
if hasattr(ctx.command, "on_error"):
logger.debug(f"Command {ctx.command} has its own error handler.")
return
# if hasattr(ctx.command, "on_error"):
# logger.debug(f"Command {ctx.command} has its own error handler.")
# return
# # If the cog has its own error handler, return
if ctx.cog and ctx.cog._get_overridden_method(ctx.cog.cog_command_error) is not None:
logger.debug(f"Cog {ctx.cog} has its own error handler.")
return
# if ctx.cog and ctx.cog._get_overridden_method(ctx.cog.cog_command_error) is not None:
# logger.debug(f"Cog {ctx.cog} has its own error handler.")
# return
if isinstance(error, commands.CheckFailure):
message = error_map.get(type(error), self.error_message).format(error=error, ctx=ctx)
await ctx.send(content=message, ephemeral=True, delete_after=30)
# await ctx.send(content=message, ephemeral=True, delete_after=30)
embed = create_error_embed(message)
await ctx.send(embed=embed, ephemeral=True, delete_after=30)
sentry_sdk.capture_exception(error)
return
@ -140,7 +145,10 @@ class ErrorHandler(commands.Cog):
# Get the error message and send it to the user
message: str = self.get_error_message(error, ctx)
await ctx.send(content=message, ephemeral=True, delete_after=30)
# await ctx.send(content=message, ephemeral=True, delete_after=30)
embed = create_error_embed(message)
await ctx.send(embed=embed, ephemeral=True, delete_after=30)
# Log the error traceback if it's not in the error map
if type(error) not in error_map:

View file

@ -18,7 +18,6 @@ from tux.utils.embeds import EmbedCreator
class TuxHelp(commands.HelpCommand):
def __init__(self):
"""Initializes the TuxHelp command with necessary attributes."""
self.prefix = CONST.PREFIX
super().__init__(
command_attrs={
"help": "Lists all commands and sub-commands.",
@ -27,6 +26,17 @@ class TuxHelp(commands.HelpCommand):
},
)
async def _get_prefix(self):
"""
Dynamically fetches the prefix from the context.
Returns
-------
str
The prefix used to invoke the bot.
"""
return (await self.context.bot.get_prefix(self.context.message))[0] or CONST.PREFIX
def _embed_base(self, title: str, description: str | None = None) -> discord.Embed:
"""
Creates a base embed with uniform styling.
@ -62,8 +72,10 @@ class TuxHelp(commands.HelpCommand):
prefix : str
The prefix used to invoke the command.
"""
command_aliases = ", ".join(command.aliases) if command.aliases else "No aliases."
embed.add_field(
name=f"{prefix}{command.qualified_name} ({', '.join(command.aliases) if command.aliases else 'No aliases.'})",
name=f"{prefix}{command.qualified_name} ({command_aliases})",
value=f"> {command.short_doc or 'No documentation summary.'}",
inline=False,
)
@ -172,14 +184,14 @@ class TuxHelp(commands.HelpCommand):
"Tux is an all-in-one bot for the All Things Linux Discord server. The bot is written in Python 3.12 using discord.py, and we are actively seeking contributors!",
)
self._add_bot_help_fields(embed)
await self._add_bot_help_fields(embed)
menu.add_page(embed)
self._add_cog_pages(menu, mapping)
await self._add_cog_pages(menu, mapping)
await menu.start()
def _add_bot_help_fields(self, embed: discord.Embed) -> None:
async def _add_bot_help_fields(self, embed: discord.Embed) -> None:
"""
Adds additional help information about the bot.
@ -188,19 +200,21 @@ class TuxHelp(commands.HelpCommand):
embed : discord.Embed
The embed to which the help information will be added.
"""
prefix = await self._get_prefix()
embed.add_field(
name="How to Use",
value=f"Most commands are hybrid meaning they can be used via prefix `{self.prefix}` OR slash `/`. Commands strictly available via `/` are not listed in the help menu.",
value=f"Most commands are hybrid meaning they can be used via prefix `{prefix}` OR slash `/`. Commands strictly available via `/` are not listed in the help menu.",
inline=False,
)
embed.add_field(
name="Command Help",
value=f"Use `{self.prefix}help <command>` or `{self.prefix}help <subcommand>` to learn about a specific command.\n> e.g. `{self.prefix}help ban` or `{self.prefix}h dev load_cog`",
value=f"Use `{prefix}help <command>` or `{prefix}help <subcommand>` to learn about a specific command.\n> e.g. `{prefix}help ban` or `{prefix}h dev load_cog`",
inline=False,
)
embed.add_field(
name="Flag Help",
value=f"Flags in `[]` are required and `<>` are optional. Most flags have aliases that can be used.\n> e.g. `{self.prefix}ban @user --reason spamming` or `{self.prefix}b @user -r spamming`",
value=f"Flags in `[]` are required and `<>` are optional. Most flags have aliases that can be used.\n> e.g. `{prefix}ban @user --reason spamming` or `{prefix}b @user -r spamming`",
inline=False,
)
embed.add_field(
@ -214,7 +228,7 @@ class TuxHelp(commands.HelpCommand):
inline=True,
)
def _add_cog_pages(
async def _add_cog_pages(
self,
menu: ViewMenu,
mapping: Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]],
@ -229,12 +243,12 @@ class TuxHelp(commands.HelpCommand):
mapping : Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]]
The mapping of cogs to commands.
"""
command_categories = self._get_command_categories(mapping)
command_categories = await self._get_command_categories(mapping)
cog_groups = self._get_cog_groups()
select_options = self._create_select_options(command_categories, cog_groups, menu)
select_options = await self._create_select_options(command_categories, cog_groups, menu)
self._add_navigation_and_selection(menu, select_options)
def _get_command_categories(
async def _get_command_categories(
self,
mapping: Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]],
) -> dict[str, dict[str, str]]:
@ -257,8 +271,11 @@ class TuxHelp(commands.HelpCommand):
if cog and len(mapping_commands) > 0:
cog_group = self._extract_cog_group(cog) or "extra"
command_categories.setdefault(cog_group, {})
cmd = cog.qualified_name
command_categories[cog_group][cmd] = " ".join(f"`{command.name}`" for command in mapping_commands)
for command in mapping_commands:
cmd_name_and_aliases = f"`{command.name}`"
if command.aliases:
cmd_name_and_aliases += f" ({', '.join(f'`{alias}`' for alias in command.aliases)})"
command_categories[cog_group][command.name] = cmd_name_and_aliases
return command_categories
@ -273,7 +290,7 @@ class TuxHelp(commands.HelpCommand):
"""
return [d for d in os.listdir("./tux/cogs") if Path(f"./tux/cogs/{d}").is_dir() and d != "__pycache__"]
def _create_select_options(
async def _create_select_options(
self,
command_categories: dict[str, dict[str, str]],
cog_groups: list[str],
@ -301,7 +318,9 @@ class TuxHelp(commands.HelpCommand):
for index, cog_group in enumerate(cog_groups, start=1):
if cog_group in command_categories and any(command_categories[cog_group].values()):
embed = self._embed_base(f"{cog_group.capitalize()} Commands", "\n")
embed.set_footer(text=f"Use {self.prefix}help <command> or <subcommand> to learn about it.")
embed.set_footer(
text=f"Use {await self._get_prefix()}help <command> or <subcommand> to learn about it.",
)
for cmd, command_list in command_categories[cog_group].items():
embed.add_field(name=cmd, value=command_list, inline=False)
@ -358,14 +377,15 @@ class TuxHelp(commands.HelpCommand):
cog : commands.Cog
The cog for which the help message is to be sent.
"""
prefix = await self._get_prefix()
embed = self._embed_base(f"{cog.qualified_name} Commands")
for command in cog.get_commands():
self._add_command_field(embed, command, self.prefix)
self._add_command_field(embed, command, prefix)
if isinstance(command, commands.Group):
for subcommand in command.commands:
self._add_command_field(embed, subcommand, self.prefix)
self._add_command_field(embed, subcommand, prefix)
await self.get_destination().send(embed=embed)
@ -378,19 +398,21 @@ class TuxHelp(commands.HelpCommand):
command : commands.Command[Any, Any, Any]
The command for which the help message is to be sent.
"""
prefix = await self._get_prefix()
embed = self._embed_base(
title=f"{self.prefix}{command.qualified_name}",
title=f"{prefix}{command.qualified_name}",
description=f"> {command.help or 'No documentation available.'}",
)
self._add_command_help_fields(embed, command)
await self._add_command_help_fields(embed, command)
if flag_details := self._format_flag_details(command):
embed.add_field(name="Flags", value=f"```\n{flag_details}\n```", inline=False)
await self.get_destination().send(embed=embed)
def _add_command_help_fields(self, embed: discord.Embed, command: commands.Command[Any, Any, Any]) -> None:
async def _add_command_help_fields(self, embed: discord.Embed, command: commands.Command[Any, Any, Any]) -> None:
"""
Adds fields with usage and alias information for a command to an embed.
@ -401,9 +423,11 @@ class TuxHelp(commands.HelpCommand):
command : commands.Command[Any, Any, Any]
The command whose details are to be added.
"""
prefix = await self._get_prefix()
embed.add_field(
name="Usage",
value=f"`{command.signature or 'No usage.'}`",
value=f"`{prefix}{command.usage or 'No usage.'}`",
inline=False,
)
embed.add_field(
@ -421,12 +445,14 @@ class TuxHelp(commands.HelpCommand):
group : commands.Group[Any, Any, Any]
The group for which the help message is to be sent.
"""
prefix = await self._get_prefix()
embed = self._embed_base(f"{group.name}", f"> {group.help or 'No documentation available.'}")
self._add_command_help_fields(embed, group)
await self._add_command_help_fields(embed, group)
for command in group.commands:
self._add_command_field(embed, command, self.prefix)
self._add_command_field(embed, command, prefix)
await self.get_destination().send(embed=embed)

View file

@ -30,6 +30,14 @@ def create_embed_footer(
return (fallback_text, fallback_icon_url)
@staticmethod
def create_error_embed(error: str) -> discord.Embed:
embed = discord.Embed()
embed.color = CONST.EMBED_COLORS["ERROR"]
embed.description = f"<:tux_error:1273494919897681930> {error}"
return embed
class EmbedCreator:
@staticmethod
def get_timestamp(