tux.cogs.admin.dev
¶
Classes:
Name | Description |
---|---|
Dev | |
Classes¶
Dev(bot: Tux)
¶
Bases: Cog
Methods:
Name | Description |
---|---|
dev | Dev related commands. |
sync_tree | Syncs the app command tree. |
clear_tree | Clears the app command tree. |
emoji | Emoji management commands. |
sync_emojis | Synchronize emojis from the local assets directory to the application. |
resync_emoji | Resync a specific emoji from the local assets directory. |
delete_all_emojis | Delete all application emojis that match names from the emoji assets directory. |
list_emojis | List all emojis currently in the emoji manager's cache. |
load_cog | Loads a cog into the bot. |
unload_cog | Unloads a cog from the bot. |
reload_cog | Reloads a cog in the bot. |
stop | Stops the bot. If Tux is running with Docker Compose, this will restart the container. |
Source code in tux/cogs/admin/dev.py
def __init__(self, bot: Tux) -> None:
self.bot = bot
self.sync_tree.usage = generate_usage(self.sync_tree)
self.clear_tree.usage = generate_usage(self.clear_tree)
self.load_cog.usage = generate_usage(self.load_cog)
self.unload_cog.usage = generate_usage(self.unload_cog)
self.reload_cog.usage = generate_usage(self.reload_cog)
self.stop.usage = generate_usage(self.stop)
self.sync_emojis.usage = generate_usage(self.sync_emojis)
self.resync_emoji.usage = generate_usage(self.resync_emoji)
self.delete_all_emojis.usage = generate_usage(self.delete_all_emojis)
Functions¶
dev(ctx: commands.Context[Tux]) -> None
async
¶
Dev related commands.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context[Tux] | The context object for the command. | required |
Raises:
Type | Description |
---|---|
MissingPermissions | If the user does not have the required permissions |
CommandInvokeError | If the subcommand is not found. |
Source code in tux/cogs/admin/dev.py
@commands.hybrid_group(
name="dev",
aliases=["d"],
)
@commands.guild_only()
@checks.has_pl(8)
async def dev(self, ctx: commands.Context[Tux]) -> None:
"""
Dev related commands.
Parameters
----------
ctx : commands.Context[Tux]
The context object for the command.
Raises
------
commands.MissingPermissions
If the user does not have the required permissions
commands.CommandInvokeError
If the subcommand is not found.
"""
if ctx.invoked_subcommand is None:
await ctx.send_help("dev")
sync_tree(ctx: commands.Context[Tux], guild: discord.Guild) -> None
async
¶
Syncs the app command tree.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context | The context in which the command is being invoked. | required |
guild | Guild | The guild to sync application commands to. | required |
Raises:
Type | Description |
---|---|
MissingRequiredArgument | If a guild is not specified. |
Source code in tux/cogs/admin/dev.py
@dev.command(
name="sync_tree",
aliases=["st", "sync", "s"],
)
@commands.guild_only()
@checks.has_pl(8)
async def sync_tree(self, ctx: commands.Context[Tux], guild: discord.Guild) -> None:
"""
Syncs the app command tree.
Parameters
----------
ctx : commands.Context
The context in which the command is being invoked.
guild : discord.Guild
The guild to sync application commands to.
Raises
------
commands.MissingRequiredArgument
If a guild is not specified.
"""
assert ctx.guild
# Copy the global tree to the guild
self.bot.tree.copy_global_to(guild=ctx.guild)
# Sync the guild tree
await self.bot.tree.sync(guild=ctx.guild)
await ctx.send("Application command tree synced.")
clear_tree(ctx: commands.Context[Tux]) -> None
async
¶
Clears the app command tree.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context | The context in which the command is being invoked. | required |
Raises:
Type | Description |
---|---|
MissingPermissions | If the user does not have the required permissions. |
Source code in tux/cogs/admin/dev.py
@dev.command(
name="clear_tree",
aliases=["ct", "clear", "c"],
)
@commands.guild_only()
@checks.has_pl(8)
async def clear_tree(self, ctx: commands.Context[Tux]) -> None:
"""
Clears the app command tree.
Parameters
----------
ctx : commands.Context
The context in which the command is being invoked.
Raises
------
commands.MissingPermissions
If the user does not have the required permissions.
"""
assert ctx.guild
# Clear the slash command tree for the guild.
self.bot.tree.clear_commands(guild=ctx.guild)
# Copy the global slash commands to the guild.
self.bot.tree.copy_global_to(guild=ctx.guild)
# Sync the slash command tree for the guild.
await self.bot.tree.sync(guild=ctx.guild)
await ctx.send("Slash command tree cleared.")
emoji(ctx: commands.Context[Tux]) -> None
async
¶
Emoji management commands.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context[Tux] | The context object for the command. | required |
Source code in tux/cogs/admin/dev.py
@dev.group(
name="emoji",
aliases=["em"],
)
@commands.guild_only()
@checks.has_pl(8)
async def emoji(self, ctx: commands.Context[Tux]) -> None:
"""
Emoji management commands.
Parameters
----------
ctx : commands.Context[Tux]
The context object for the command.
"""
if ctx.invoked_subcommand is None:
await ctx.send_help("dev emoji")
sync_emojis(ctx: commands.Context[Tux]) -> None
async
¶
Synchronize emojis from the local assets directory to the application.
This command: 1. Scans the emoji assets directory 2. Uploads any missing emojis to the application 3. Reports which emojis were created and which were skipped
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context[Tux] | The context object for the command. | required |
Source code in tux/cogs/admin/dev.py
@emoji.command(
name="sync",
aliases=["s"],
)
@commands.guild_only()
@checks.has_pl(8)
async def sync_emojis(self, ctx: commands.Context[Tux]) -> None:
"""
Synchronize emojis from the local assets directory to the application.
This command:
1. Scans the emoji assets directory
2. Uploads any missing emojis to the application
3. Reports which emojis were created and which were skipped
Parameters
----------
ctx : commands.Context[Tux]
The context object for the command.
"""
try:
async with ctx.typing():
created, skipped = await self.bot.emoji_manager.sync_emojis()
created_count = len(created)
skipped_count = len(skipped)
embed = discord.Embed(
title="Emoji Synchronization Results",
color=discord.Color.green() if created_count > 0 else discord.Color.blue(),
)
embed.add_field(
name="Status",
value=f"✅ Created: **{created_count}**\n⏭️ Skipped/Failed: **{skipped_count}**",
inline=False,
)
if created_count > 0:
created_names = [e.name for e in created]
created_str = ", ".join(created_names[:10])
if len(created_names) > 10:
created_str += f" and {len(created_names) - 10} more"
embed.add_field(
name="Created Emojis",
value=created_str,
inline=False,
)
await ctx.send(embed=embed)
except Exception as e:
logger.error(f"Error in sync_emojis command: {e}")
await ctx.send(f"Error synchronizing emojis: {e}")
resync_emoji(ctx: commands.Context[Tux], emoji_name: str) -> None
async
¶
Resync a specific emoji from the local assets directory.
This command: 1. Deletes the existing emoji with the given name (if it exists) 2. Creates a new emoji using the local file with the same name 3. Reports the results
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context[Tux] | The context object for the command. | required |
emoji_name | str | The name of the emoji to resync. | required |
Source code in tux/cogs/admin/dev.py
@emoji.command(
name="resync",
aliases=["r"],
)
@commands.guild_only()
@checks.has_pl(8)
async def resync_emoji(self, ctx: commands.Context[Tux], emoji_name: str) -> None:
"""
Resync a specific emoji from the local assets directory.
This command:
1. Deletes the existing emoji with the given name (if it exists)
2. Creates a new emoji using the local file with the same name
3. Reports the results
Parameters
----------
ctx : commands.Context[Tux]
The context object for the command.
emoji_name : str
The name of the emoji to resync.
"""
try:
async with ctx.typing():
new_emoji = await self.bot.emoji_manager.resync_emoji(emoji_name)
if new_emoji:
embed = discord.Embed(
title="Emoji Resync Successful",
description=f"Emoji `{emoji_name}` has been resynced successfully!",
color=discord.Color.green(),
)
embed.add_field(name="Emoji", value=str(new_emoji))
embed.set_thumbnail(url=new_emoji.url)
else:
embed = discord.Embed(
title="Emoji Resync Failed",
description=f"Failed to resync emoji `{emoji_name}`. Check logs for details.",
color=discord.Color.red(),
)
await ctx.send(embed=embed)
except Exception as e:
logger.error(f"Error in resync_emoji command: {e}")
await ctx.send(f"Error resyncing emoji: {e}")
delete_all_emojis(ctx: commands.Context[Tux]) -> None
async
¶
Delete all application emojis that match names from the emoji assets directory.
This command: 1. Scans the emoji assets directory for valid emoji names 2. Deletes all application emojis with matching names 3. Reports which emojis were deleted and which failed
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context[Tux] | The context object for the command. | required |
Source code in tux/cogs/admin/dev.py
@emoji.command(
name="delete_all",
aliases=["da", "clear"],
)
@commands.guild_only()
@checks.has_pl(8)
async def delete_all_emojis(self, ctx: commands.Context[Tux]) -> None:
"""
Delete all application emojis that match names from the emoji assets directory.
This command:
1. Scans the emoji assets directory for valid emoji names
2. Deletes all application emojis with matching names
3. Reports which emojis were deleted and which failed
Parameters
----------
ctx : commands.Context[Tux]
The context object for the command.
"""
# Ask for confirmation before proceeding
await ctx.send(
"⚠️ **WARNING**: This will delete all application emojis matching the emoji assets directory.\n"
"Are you sure you want to continue? (yes/no)",
)
def check(m: discord.Message) -> bool:
return m.author == ctx.author and m.channel == ctx.channel and m.content.lower() in ["yes", "no"]
try:
response = await self.bot.wait_for("message", check=check, timeout=30.0)
if response.content.lower() != "yes":
await ctx.send("Operation cancelled.")
return
async with ctx.typing():
deleted, failed = await self.bot.emoji_manager.delete_all_emojis()
deleted_count = len(deleted)
failed_count = len(failed)
embed = discord.Embed(
title="Emoji Deletion Results",
color=discord.Color.orange(),
)
embed.add_field(
name="Status",
value=f"🗑️ Deleted: **{deleted_count}**\n❌ Failed/Not Found: **{failed_count}**",
inline=False,
)
if deleted_count > 0:
deleted_str = ", ".join(deleted[:10])
if len(deleted) > 10:
deleted_str += f" and {len(deleted) - 10} more"
embed.add_field(
name="Deleted Emojis",
value=deleted_str,
inline=False,
)
if failed_count > 0:
failed_str = ", ".join(failed[:10])
if len(failed) > 10:
failed_str += f" and {len(failed) - 10} more"
embed.add_field(
name="Failed Emoji Deletions",
value=failed_str,
inline=False,
)
await ctx.send(embed=embed)
except TimeoutError:
await ctx.send("Confirmation timed out. Operation cancelled.")
except Exception as e:
logger.error(f"Error in delete_all_emojis command: {e}")
await ctx.send(f"Error deleting emojis: {e}")
list_emojis(ctx: commands.Context[Tux]) -> None
async
¶
List all emojis currently in the emoji manager's cache.
This command: 1. Shows all emojis in the bot's emoji cache 2. Displays emoji count and names
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context[Tux] | The context object for the command. | required |
Source code in tux/cogs/admin/dev.py
@emoji.command(
name="list",
aliases=["ls", "l"],
)
@commands.guild_only()
@checks.has_pl(8)
async def list_emojis(self, ctx: commands.Context[Tux]) -> None:
"""
List all emojis currently in the emoji manager's cache.
This command:
1. Shows all emojis in the bot's emoji cache
2. Displays emoji count and names
Parameters
----------
ctx : commands.Context[Tux]
The context object for the command.
"""
try:
# Check if emoji manager is initialized by examining the cache
if len(self.bot.emoji_manager.cache) == 0:
await ctx.send("Emoji manager cache is empty. It might not be initialized yet.")
return
# Get all emojis and sort them by name
emojis = sorted(self.bot.emoji_manager.cache.values(), key=lambda e: e.name)
emoji_count = len(emojis)
if emoji_count == 0:
await ctx.send("No emojis found in the emoji manager's cache.")
return
# Create a ViewMenu for pagination
menu = ViewMenu(
ctx,
menu_type=ViewMenu.TypeEmbed,
all_can_click=True,
delete_on_timeout=True,
)
# Paginate emojis
emojis_per_page = 10
for i in range(0, emoji_count, emojis_per_page):
page_emojis = emojis[i : i + emojis_per_page]
embed = discord.Embed(
title="Application Emojis",
description=f"Found **{emoji_count}** emojis in the emoji manager's cache.",
color=discord.Color.blue(),
)
# Add server info and footer
if ctx.guild and ctx.guild.icon:
embed.set_author(name=ctx.guild.name, icon_url=ctx.guild.icon.url)
embed.set_footer(
text=f"Page {i // emojis_per_page + 1}/{(emoji_count + emojis_per_page - 1) // emojis_per_page} • Requested by {ctx.author}",
icon_url=ctx.author.display_avatar.url,
)
# Create a table-like format with headers
table_header = "\n**Emoji**\u2003\u2002**Reference**\n"
embed.description = f"Found **{emoji_count}** emojis in the emoji manager's cache.{table_header}"
for emoji in page_emojis:
# Format with consistent spacing (using unicode spaces for alignment)
emoji_display = str(emoji)
emoji_name = emoji.name
emoji_id = emoji.id
# Create copyable reference format
is_animated = getattr(emoji, "animated", False)
emoji_ref = f"<{'a' if is_animated else ''}:{emoji_name}:{emoji_id}>"
embed.description += f"{emoji_display}\u2003\u2003\u2003`{emoji_ref}`\n"
menu.add_page(embed)
# Add navigation buttons
menu_buttons = [
ViewButton(
style=discord.ButtonStyle.secondary,
custom_id=ViewButton.ID_GO_TO_FIRST_PAGE,
emoji="⏮️",
),
ViewButton(
style=discord.ButtonStyle.secondary,
custom_id=ViewButton.ID_PREVIOUS_PAGE,
emoji="⏪",
),
ViewButton(
style=discord.ButtonStyle.secondary,
custom_id=ViewButton.ID_NEXT_PAGE,
emoji="⏩",
),
ViewButton(
style=discord.ButtonStyle.secondary,
custom_id=ViewButton.ID_GO_TO_LAST_PAGE,
emoji="⏭️",
),
]
menu.add_buttons(menu_buttons)
# Start the menu
await menu.start()
except Exception as e:
logger.error(f"Error in list_emojis command: {e}")
await ctx.send(f"Error listing emojis: {e}")
load_cog(ctx: commands.Context[Tux], *, cog: str) -> None
async
¶
Loads a cog into the bot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context | The context in which the command is being invoked. | required |
cog | str | The name of the cog to load. | required |
Source code in tux/cogs/admin/dev.py
@dev.command(
name="load_cog",
aliases=["lc", "load", "l"],
)
@commands.guild_only()
@checks.has_pl(8)
async def load_cog(self, ctx: commands.Context[Tux], *, cog: str) -> None:
"""
Loads a cog into the bot.
Parameters
----------
ctx : commands.Context
The context in which the command is being invoked.
cog : str
The name of the cog to load.
"""
await self.bot.load_extension(cog)
await ctx.send(f"Cog {cog} loaded.")
logger.info(f"Cog {cog} loaded.")
unload_cog(ctx: commands.Context[Tux], *, cog: str) -> None
async
¶
Unloads a cog from the bot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context | The context in which the command is being invoked. | required |
cog | str | The name of the cog to unload. | required |
Source code in tux/cogs/admin/dev.py
@dev.command(
name="unload_cog",
aliases=["uc", "unload", "u"],
)
@commands.guild_only()
@checks.has_pl(8)
async def unload_cog(self, ctx: commands.Context[Tux], *, cog: str) -> None:
"""
Unloads a cog from the bot.
Parameters
----------
ctx : commands.Context
The context in which the command is being invoked.
cog : str
The name of the cog to unload.
"""
await self.bot.unload_extension(cog)
logger.info(f"Cog {cog} unloaded.")
await ctx.send(f"Cog {cog} unloaded.", ephemeral=True, delete_after=30)
reload_cog(ctx: commands.Context[Tux], *, cog: str) -> None
async
¶
Reloads a cog in the bot.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context | The context in which the command is being invoked. | required |
cog | str | The name of the cog to reload. | required |
Source code in tux/cogs/admin/dev.py
@dev.command(
name="reload_cog",
aliases=["rc", "reload", "r"],
)
@commands.guild_only()
@checks.has_pl(8)
async def reload_cog(self, ctx: commands.Context[Tux], *, cog: str) -> None:
"""
Reloads a cog in the bot.
Parameters
----------
ctx : commands.Context
The context in which the command is being invoked.
cog : str
The name of the cog to reload.
"""
await self.bot.unload_extension(cog)
await self.bot.load_extension(cog)
await ctx.send(f"Cog {cog} reloaded.", ephemeral=True, delete_after=30)
logger.info(f"Cog {cog} reloaded.")
stop(ctx: commands.Context[Tux]) -> None
async
¶
Stops the bot. If Tux is running with Docker Compose, this will restart the container.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ctx | Context | The context in which the command is being invoked. | required |
Source code in tux/cogs/admin/dev.py
@dev.command(
name="stop",
)
@commands.guild_only()
@checks.has_pl(8)
async def stop(self, ctx: commands.Context[Tux]) -> None:
"""
Stops the bot. If Tux is running with Docker Compose, this will restart the container.
Parameters
----------
ctx : commands.Context
The context in which the command is being invoked.
"""
await ctx.send(
"Stopping the bot...\n-# Note: if Tux is running with Docker Compose, this will restart the container.",
)
await self.bot.shutdown()