discordbot/cogs/emoji_cog.py
2025-06-05 21:31:06 -06:00

347 lines
13 KiB
Python

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 <a:name:id>
if emoji.startswith("<") and emoji.endswith(">"):
parts = emoji.strip("<>").split(":")
if len(parts) == 3: # <a:name:id> 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 <a:name:id>
if emoji.startswith("<") and emoji.endswith(">"):
parts = emoji.strip("<>").split(":")
if len(parts) == 3: # <a:name:id> 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")