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

View file

@ -12,7 +12,7 @@ class Dev(commands.Cog):
@commands.hybrid_group( @commands.hybrid_group(
name="dev", name="dev",
aliases=["d"], aliases=["d"],
usage="$dev <subcommand>", usage="dev <subcommand>",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(8) @checks.has_pl(8)
@ -38,7 +38,7 @@ class Dev(commands.Cog):
@dev.command( @dev.command(
name="sync_tree", name="sync_tree",
aliases=["st", "sync", "s"], aliases=["st", "sync", "s"],
usage="$dev sync_tree [guild]", usage="dev sync_tree [guild]",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(8) @checks.has_pl(8)
@ -69,21 +69,10 @@ class Dev(commands.Cog):
await self.bot.tree.sync(guild=ctx.guild) await self.bot.tree.sync(guild=ctx.guild)
await ctx.send("Application command tree synced.") 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( @dev.command(
name="clear_tree", name="clear_tree",
aliases=["ct", "clear", "c"], aliases=["ct", "clear", "c"],
usage="$dev clear_tree", usage="dev clear_tree",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(8) @checks.has_pl(8)
@ -118,7 +107,7 @@ class Dev(commands.Cog):
@dev.command( @dev.command(
name="load_cog", name="load_cog",
aliases=["lc", "load", "l"], aliases=["lc", "load", "l"],
usage="$dev load_cog [cog]", usage="dev load_cog [cog]",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(8) @checks.has_pl(8)
@ -156,30 +145,10 @@ class Dev(commands.Cog):
await ctx.send(f"Cog {cog} loaded.") await ctx.send(f"Cog {cog} loaded.")
logger.info(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( @dev.command(
name="unload_cog", name="unload_cog",
aliases=["uc", "unload", "u"], aliases=["uc", "unload", "u"],
usage="$dev unload_cog [cog]", usage="dev unload_cog [cog]",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(8) @checks.has_pl(8)
@ -211,19 +180,10 @@ class Dev(commands.Cog):
logger.info(f"Cog {cog} unloaded.") logger.info(f"Cog {cog} unloaded.")
await ctx.send(f"Cog {cog} unloaded.", ephemeral=True, delete_after=30) 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( @dev.command(
name="reload_cog", name="reload_cog",
aliases=["rc", "reload", "r"], aliases=["rc", "reload", "r"],
usage="$dev reload_cog [cog]", usage="dev reload_cog [cog]",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(8) @checks.has_pl(8)
@ -256,16 +216,6 @@ class Dev(commands.Cog):
await ctx.send(f"Cog {cog} reloaded.", ephemeral=True, delete_after=30) await ctx.send(f"Cog {cog} reloaded.", ephemeral=True, delete_after=30)
logger.info(f"Cog {cog} reloaded.") 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: async def setup(bot: commands.Bot) -> None:
await bot.add_cog(Dev(bot)) await bot.add_cog(Dev(bot))

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@ class Query(commands.Cog):
@commands.hybrid_command( @commands.hybrid_command(
name="query", name="query",
aliases=["q"], aliases=["q"],
usage="$query [search_term]", usage="query [search_term]",
) )
async def query(self, ctx: commands.Context[commands.Bot], *, search_term: str) -> None: 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( @commands.command(
name="snippets", name="snippets",
aliases=["ls"], aliases=["ls"],
usage="$snippets", usage="snippets",
) )
@commands.guild_only() @commands.guild_only()
async def list_snippets(self, ctx: commands.Context[commands.Bot]) -> None: async def list_snippets(self, ctx: commands.Context[commands.Bot]) -> None:
@ -104,7 +104,7 @@ class Snippets(commands.Cog):
@commands.command( @commands.command(
name="deletesnippet", name="deletesnippet",
aliases=["ds"], aliases=["ds"],
usage="$deletesnippet [name]", usage="deletesnippet [name]",
) )
@commands.guild_only() @commands.guild_only()
async def delete_snippet(self, ctx: commands.Context[commands.Bot], name: str) -> None: async def delete_snippet(self, ctx: commands.Context[commands.Bot], name: str) -> None:
@ -154,7 +154,7 @@ class Snippets(commands.Cog):
@commands.command( @commands.command(
name="forcedeletesnippet", name="forcedeletesnippet",
aliases=["fds"], aliases=["fds"],
usage="$forcedeletesnippet [name]", usage="forcedeletesnippet [name]",
) )
@commands.guild_only() @commands.guild_only()
@checks.has_pl(2) @checks.has_pl(2)
@ -193,7 +193,7 @@ class Snippets(commands.Cog):
@commands.command( @commands.command(
name="snippet", name="snippet",
aliases=["s"], aliases=["s"],
usage="$snippet [name]", usage="snippet [name]",
) )
@commands.guild_only() @commands.guild_only()
async def get_snippet(self, ctx: commands.Context[commands.Bot], name: str) -> None: async def get_snippet(self, ctx: commands.Context[commands.Bot], name: str) -> None:
@ -230,7 +230,7 @@ class Snippets(commands.Cog):
@commands.command( @commands.command(
name="snippetinfo", name="snippetinfo",
aliases=["si"], aliases=["si"],
usage="$snippetinfo [name]", usage="snippetinfo [name]",
) )
@commands.guild_only() @commands.guild_only()
async def get_snippet_info(self, ctx: commands.Context[commands.Bot], name: str) -> None: async def get_snippet_info(self, ctx: commands.Context[commands.Bot], name: str) -> None:
@ -287,7 +287,7 @@ class Snippets(commands.Cog):
@commands.command( @commands.command(
name="createsnippet", name="createsnippet",
aliases=["cs"], aliases=["cs"],
usage="$createsnippet [name] [content]", usage="createsnippet [name] [content]",
) )
@commands.guild_only() @commands.guild_only()
async def create_snippet(self, ctx: commands.Context[commands.Bot], *, arg: str) -> None: 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( @commands.command(
name="tldr", name="tldr",
aliases=["man"], aliases=["man"],
usage="$tldr [command]", usage="tldr [command]",
) )
@commands.guild_only() @commands.guild_only()
async def prefix_tldr(self, ctx: commands.Context[commands.Bot], command: str) -> None: 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( @commands.hybrid_group(
name="wiki", name="wiki",
usage="$wiki [arch|atl]", usage="wiki [arch|atl]",
aliases=["wk"], aliases=["wk"],
) )
async def wiki(self, ctx: commands.Context[commands.Bot]) -> None: async def wiki(self, ctx: commands.Context[commands.Bot]) -> None:
@ -101,7 +101,7 @@ class Wiki(commands.Cog):
@wiki.command( @wiki.command(
name="arch", name="arch",
usage="$wiki arch [query]", usage="wiki arch [query]",
) )
async def arch_wiki(self, ctx: commands.Context[commands.Bot], query: str) -> None: async def arch_wiki(self, ctx: commands.Context[commands.Bot], query: str) -> None:
""" """
@ -131,7 +131,7 @@ class Wiki(commands.Cog):
@wiki.command( @wiki.command(
name="atl", name="atl",
usage="$wiki atl [query]", usage="wiki atl [query]",
) )
async def atl_wiki(self, ctx: commands.Context[commands.Bot], query: str) -> None: 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 from loguru import logger
import tux.handlers.error as error import tux.handlers.error as error
from tux.utils.embeds import create_error_embed
class PermissionLevelError(commands.CheckFailure): 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.CheckFailure: "User not in sudoers file. This incident will be reported. (Permission Check Failed)",
commands.CommandNotFound: "This command was not found.", commands.CommandNotFound: "This command was not found.",
commands.CommandOnCooldown: "This command is on cooldown. Try again in {error.retry_after:.2f} seconds.", 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.BadArgument: "Invalid argument passed. Correct usage: `{ctx.prefix}{ctx.command.usage}`",
commands.MissingRequiredArgument: "Missing required argument. Correct usage: `{ctx.command.usage}`", commands.MissingRequiredArgument: "Missing required arg. Correct usage: `{ctx.prefix}{ctx.command.usage}`",
commands.MissingRequiredAttachment: "Missing required attachment.", commands.MissingRequiredAttachment: "Missing required attachment.",
commands.NotOwner: "User not in sudoers file. This incident will be reported. (Not Owner)", 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)", commands.BotMissingPermissions: "User not in sudoers file. This incident will be reported. (Bot Missing Permissions)",
# Custom errors # Custom errors
error.PermissionLevelError: "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. (You do not have the 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) error_message = error_map.get(type(error), self.error_message).format(error=error)
embed = create_error_embed(error_message)
if interaction.response.is_done(): if interaction.response.is_done():
await interaction.followup.send(error_message, ephemeral=True) await interaction.followup.send(embed=embed, ephemeral=True)
else: 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: if type(error) in error_map:
sentry_sdk.capture_exception(error) sentry_sdk.capture_exception(error)
@ -112,18 +115,20 @@ class ErrorHandler(commands.Cog):
""" """
# # If the command has its own error handler, return # # If the command has its own error handler, return
if hasattr(ctx.command, "on_error"): # if hasattr(ctx.command, "on_error"):
logger.debug(f"Command {ctx.command} has its own error handler.") # logger.debug(f"Command {ctx.command} has its own error handler.")
return # return
# # If the cog 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: # 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.") # logger.debug(f"Cog {ctx.cog} has its own error handler.")
return # return
if isinstance(error, commands.CheckFailure): if isinstance(error, commands.CheckFailure):
message = error_map.get(type(error), self.error_message).format(error=error, ctx=ctx) 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) sentry_sdk.capture_exception(error)
return return
@ -140,7 +145,10 @@ class ErrorHandler(commands.Cog):
# Get the error message and send it to the user # Get the error message and send it to the user
message: str = self.get_error_message(error, ctx) 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 # Log the error traceback if it's not in the error map
if type(error) not in 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): class TuxHelp(commands.HelpCommand):
def __init__(self): def __init__(self):
"""Initializes the TuxHelp command with necessary attributes.""" """Initializes the TuxHelp command with necessary attributes."""
self.prefix = CONST.PREFIX
super().__init__( super().__init__(
command_attrs={ command_attrs={
"help": "Lists all commands and sub-commands.", "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: def _embed_base(self, title: str, description: str | None = None) -> discord.Embed:
""" """
Creates a base embed with uniform styling. Creates a base embed with uniform styling.
@ -62,8 +72,10 @@ class TuxHelp(commands.HelpCommand):
prefix : str prefix : str
The prefix used to invoke the command. The prefix used to invoke the command.
""" """
command_aliases = ", ".join(command.aliases) if command.aliases else "No aliases."
embed.add_field( 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.'}", value=f"> {command.short_doc or 'No documentation summary.'}",
inline=False, 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!", "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) menu.add_page(embed)
self._add_cog_pages(menu, mapping) await self._add_cog_pages(menu, mapping)
await menu.start() 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. Adds additional help information about the bot.
@ -188,19 +200,21 @@ class TuxHelp(commands.HelpCommand):
embed : discord.Embed embed : discord.Embed
The embed to which the help information will be added. The embed to which the help information will be added.
""" """
prefix = await self._get_prefix()
embed.add_field( embed.add_field(
name="How to Use", 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, inline=False,
) )
embed.add_field( embed.add_field(
name="Command Help", 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, inline=False,
) )
embed.add_field( embed.add_field(
name="Flag Help", 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, inline=False,
) )
embed.add_field( embed.add_field(
@ -214,7 +228,7 @@ class TuxHelp(commands.HelpCommand):
inline=True, inline=True,
) )
def _add_cog_pages( async def _add_cog_pages(
self, self,
menu: ViewMenu, menu: ViewMenu,
mapping: Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]], 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]]] mapping : Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]]
The mapping of cogs to commands. 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() 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) self._add_navigation_and_selection(menu, select_options)
def _get_command_categories( async def _get_command_categories(
self, self,
mapping: Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]], mapping: Mapping[commands.Cog | None, list[commands.Command[Any, Any, Any]]],
) -> dict[str, dict[str, str]]: ) -> dict[str, dict[str, str]]:
@ -257,8 +271,11 @@ class TuxHelp(commands.HelpCommand):
if cog and len(mapping_commands) > 0: if cog and len(mapping_commands) > 0:
cog_group = self._extract_cog_group(cog) or "extra" cog_group = self._extract_cog_group(cog) or "extra"
command_categories.setdefault(cog_group, {}) command_categories.setdefault(cog_group, {})
cmd = cog.qualified_name for command in mapping_commands:
command_categories[cog_group][cmd] = " ".join(f"`{command.name}`" 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 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__"] 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, self,
command_categories: dict[str, dict[str, str]], command_categories: dict[str, dict[str, str]],
cog_groups: list[str], cog_groups: list[str],
@ -301,7 +318,9 @@ class TuxHelp(commands.HelpCommand):
for index, cog_group in enumerate(cog_groups, start=1): for index, cog_group in enumerate(cog_groups, start=1):
if cog_group in command_categories and any(command_categories[cog_group].values()): if cog_group in command_categories and any(command_categories[cog_group].values()):
embed = self._embed_base(f"{cog_group.capitalize()} Commands", "\n") 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(): for cmd, command_list in command_categories[cog_group].items():
embed.add_field(name=cmd, value=command_list, inline=False) embed.add_field(name=cmd, value=command_list, inline=False)
@ -358,14 +377,15 @@ class TuxHelp(commands.HelpCommand):
cog : commands.Cog cog : commands.Cog
The cog for which the help message is to be sent. 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") embed = self._embed_base(f"{cog.qualified_name} Commands")
for command in cog.get_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): if isinstance(command, commands.Group):
for subcommand in command.commands: 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) await self.get_destination().send(embed=embed)
@ -378,19 +398,21 @@ class TuxHelp(commands.HelpCommand):
command : commands.Command[Any, Any, Any] command : commands.Command[Any, Any, Any]
The command for which the help message is to be sent. The command for which the help message is to be sent.
""" """
prefix = await self._get_prefix()
embed = self._embed_base( 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.'}", 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): if flag_details := self._format_flag_details(command):
embed.add_field(name="Flags", value=f"```\n{flag_details}\n```", inline=False) embed.add_field(name="Flags", value=f"```\n{flag_details}\n```", inline=False)
await self.get_destination().send(embed=embed) 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. 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] command : commands.Command[Any, Any, Any]
The command whose details are to be added. The command whose details are to be added.
""" """
prefix = await self._get_prefix()
embed.add_field( embed.add_field(
name="Usage", name="Usage",
value=f"`{command.signature or 'No usage.'}`", value=f"`{prefix}{command.usage or 'No usage.'}`",
inline=False, inline=False,
) )
embed.add_field( embed.add_field(
@ -421,12 +445,14 @@ class TuxHelp(commands.HelpCommand):
group : commands.Group[Any, Any, Any] group : commands.Group[Any, Any, Any]
The group for which the help message is to be sent. 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.'}") 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: 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) await self.get_destination().send(embed=embed)

View file

@ -30,6 +30,14 @@ def create_embed_footer(
return (fallback_text, fallback_icon_url) 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: class EmbedCreator:
@staticmethod @staticmethod
def get_timestamp( def get_timestamp(