import discord from discord.ext import commands from discord import app_commands import logging import io import aiohttp from typing import Optional, Union log = logging.getLogger(__name__) class EmojiCog(commands.Cog, name="Emoji"): """Cog for emoji management commands""" def __init__(self, bot: commands.Bot): self.bot = bot # Create the main command group for this cog self.emoji_group = app_commands.Group( name="emoji", description="Manage server emojis" ) # Register commands self.register_commands() # Add command group to the bot's tree self.bot.tree.add_command(self.emoji_group) log.info("EmojiCog initialized with emoji command group.") def register_commands(self): """Register all commands for this cog""" # Create emoji command create_command = app_commands.Command( name="create", description="Create a new emoji from an uploaded image", callback=self.emoji_create_callback, parent=self.emoji_group, ) app_commands.describe( name="The name for the new emoji", image="The image to use for the emoji", reason="Reason for creating this emoji", )(create_command) self.emoji_group.add_command(create_command) # List emojis command list_command = app_commands.Command( name="list", description="List all emojis in the server", callback=self.emoji_list_callback, parent=self.emoji_group, ) self.emoji_group.add_command(list_command) # Delete emoji command delete_command = app_commands.Command( name="delete", description="Delete an emoji from the server", callback=self.emoji_delete_callback, parent=self.emoji_group, ) app_commands.describe( emoji="The emoji to delete", reason="Reason for deleting this emoji" )(delete_command) self.emoji_group.add_command(delete_command) # Emoji info command info_command = app_commands.Command( name="info", description="Get information about an emoji", callback=self.emoji_info_callback, parent=self.emoji_group, ) app_commands.describe(emoji="The emoji to get information about")(info_command) self.emoji_group.add_command(info_command) # --- Command Callbacks --- @app_commands.checks.has_permissions(manage_emojis=True) async def emoji_create_callback( self, interaction: discord.Interaction, name: str, image: discord.Attachment, reason: Optional[str] = None, ): """Create a new emoji from an uploaded image""" await interaction.response.defer(ephemeral=False, thinking=True) try: # Check if the image is valid if not image.content_type.startswith("image/"): await interaction.followup.send( "❌ The uploaded file is not an image.", ephemeral=True ) return # Check file size (Discord limit is 256KB for emoji images) if image.size > 256 * 1024: await interaction.followup.send( "❌ Image is too large. Emoji images must be under 256KB.", ephemeral=True, ) return # Read the image data image_data = await image.read() # Create the emoji emoji = await interaction.guild.create_custom_emoji( name=name, image=image_data, reason=f"{reason or 'No reason provided'} (Created by {interaction.user})", ) # Create a success embed embed = discord.Embed( title="✅ Emoji Created", description=f"Successfully created emoji {emoji}", color=discord.Color.green(), ) embed.add_field(name="Name", value=emoji.name, inline=True) embed.add_field(name="ID", value=emoji.id, inline=True) embed.add_field( name="Created by", value=interaction.user.mention, inline=True ) if reason: embed.add_field(name="Reason", value=reason, inline=False) embed.set_thumbnail(url=emoji.url) await interaction.followup.send(embed=embed) log.info( f"Emoji '{emoji.name}' created by {interaction.user} in {interaction.guild.name}" ) except discord.Forbidden: await interaction.followup.send( "❌ I don't have permission to create emojis in this server.", ephemeral=True, ) except discord.HTTPException as e: await interaction.followup.send( f"❌ Failed to create emoji: {e}", ephemeral=True ) async def emoji_list_callback(self, interaction: discord.Interaction): """List all emojis in the server""" await interaction.response.defer(ephemeral=False) try: # Get all emojis in the guild emojis = interaction.guild.emojis if not emojis: await interaction.followup.send("This server has no custom emojis.") return # Create an embed to display the emojis embed = discord.Embed( title=f"Emojis in {interaction.guild.name}", description=f"Total: {len(emojis)} emojis", color=discord.Color.blue(), ) # Split emojis into animated and static animated_emojis = [e for e in emojis if e.animated] static_emojis = [e for e in emojis if not e.animated] # Add static emojis to the embed if static_emojis: static_emoji_text = " ".join(str(e) for e in static_emojis[:20]) if len(static_emojis) > 20: static_emoji_text += f" ... and {len(static_emojis) - 20} more" embed.add_field( name=f"Static Emojis ({len(static_emojis)})", value=static_emoji_text or "None", inline=False, ) # Add animated emojis to the embed if animated_emojis: animated_emoji_text = " ".join(str(e) for e in animated_emojis[:20]) if len(animated_emojis) > 20: animated_emoji_text += f" ... and {len(animated_emojis) - 20} more" embed.add_field( name=f"Animated Emojis ({len(animated_emojis)})", value=animated_emoji_text or "None", inline=False, ) await interaction.followup.send(embed=embed) except Exception as e: await interaction.followup.send( f"❌ An error occurred: {e}", ephemeral=True ) log.error(f"Error listing emojis: {e}") @app_commands.checks.has_permissions(manage_emojis=True) async def emoji_delete_callback( self, interaction: discord.Interaction, emoji: str, reason: Optional[str] = None ): """Delete an emoji from the server""" await interaction.response.defer(ephemeral=False) try: # Parse the emoji string to get the ID emoji_id = None emoji_name = None # Check if it's a custom emoji format <:name:id> or if emoji.startswith("<") and emoji.endswith(">"): parts = emoji.strip("<>").split(":") if len(parts) == 3: # format emoji_name = parts[1] emoji_id = int(parts[2]) elif len(parts) == 2: # <:name:id> format emoji_name = parts[0] emoji_id = int(parts[1]) # If we couldn't parse the emoji, try to find it by name emoji_obj = None if emoji_id: emoji_obj = discord.utils.get(interaction.guild.emojis, id=emoji_id) else: # Try to find by name emoji_obj = discord.utils.get(interaction.guild.emojis, name=emoji) if not emoji_obj: await interaction.followup.send( "❌ Emoji not found. Please provide a valid emoji from this server.", ephemeral=True, ) return # Store emoji info before deletion for the embed emoji_name = emoji_obj.name emoji_url = str(emoji_obj.url) emoji_id = emoji_obj.id # Delete the emoji await emoji_obj.delete( reason=f"{reason or 'No reason provided'} (Deleted by {interaction.user})" ) # Create a success embed embed = discord.Embed( title="✅ Emoji Deleted", description=f"Successfully deleted emoji `{emoji_name}`", color=discord.Color.red(), ) embed.add_field(name="Name", value=emoji_name, inline=True) embed.add_field(name="ID", value=emoji_id, inline=True) embed.add_field( name="Deleted by", value=interaction.user.mention, inline=True ) if reason: embed.add_field(name="Reason", value=reason, inline=False) embed.set_thumbnail(url=emoji_url) await interaction.followup.send(embed=embed) log.info( f"Emoji '{emoji_name}' deleted by {interaction.user} in {interaction.guild.name}" ) except discord.Forbidden: await interaction.followup.send( "❌ I don't have permission to delete emojis in this server.", ephemeral=True, ) except discord.HTTPException as e: await interaction.followup.send( f"❌ Failed to delete emoji: {e}", ephemeral=True ) except Exception as e: await interaction.followup.send( f"❌ An error occurred: {e}", ephemeral=True ) log.error(f"Error deleting emoji: {e}") async def emoji_info_callback(self, interaction: discord.Interaction, emoji: str): """Get information about an emoji""" await interaction.response.defer(ephemeral=False) try: # Parse the emoji string to get the ID emoji_id = None # Check if it's a custom emoji format <:name:id> or if emoji.startswith("<") and emoji.endswith(">"): parts = emoji.strip("<>").split(":") if len(parts) == 3: # format emoji_id = int(parts[2]) elif len(parts) == 2: # <:name:id> format emoji_id = int(parts[1]) # If we couldn't parse the emoji, try to find it by name emoji_obj = None if emoji_id: emoji_obj = discord.utils.get(interaction.guild.emojis, id=emoji_id) else: # Try to find by name emoji_obj = discord.utils.get(interaction.guild.emojis, name=emoji) if not emoji_obj: await interaction.followup.send( "❌ Emoji not found. Please provide a valid emoji from this server.", ephemeral=True, ) return # Create an embed with emoji information embed = discord.Embed( title=f"Emoji Information: {emoji_obj.name}", color=discord.Color.blue() ) embed.add_field(name="Name", value=emoji_obj.name, inline=True) embed.add_field(name="ID", value=emoji_obj.id, inline=True) embed.add_field( name="Animated", value="Yes" if emoji_obj.animated else "No", inline=True, ) embed.add_field( name="Created At", value=discord.utils.format_dt(emoji_obj.created_at), inline=True, ) embed.add_field(name="URL", value=f"[Link]({emoji_obj.url})", inline=True) embed.add_field(name="Usage", value=f"`{str(emoji_obj)}`", inline=True) embed.set_thumbnail(url=emoji_obj.url) await interaction.followup.send(embed=embed) except Exception as e: await interaction.followup.send( f"❌ An error occurred: {e}", ephemeral=True ) log.error(f"Error getting emoji info: {e}") async def setup(bot: commands.Bot): """Setup function for the emoji cog""" await bot.add_cog(EmojiCog(bot)) log.info("EmojiCog loaded")