feat: Enhance emoji and sticker management with guild ID support

This commit is contained in:
Slipstream 2025-05-28 15:47:48 -06:00
parent 06269e6eec
commit 9b82460bbf
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
3 changed files with 99 additions and 34 deletions

View File

@ -1494,34 +1494,73 @@ async def get_ai_response(cog: 'GurtCog', message: discord.Message, model_name:
# Check if it's a known custom emoji
emoji_data = await cog.emoji_manager.get_emoji(full_item_name_with_colons)
if isinstance(emoji_data, dict): # Check if emoji_data is a dictionary
can_use_emoji = False
if isinstance(emoji_data, dict):
emoji_id = emoji_data.get("id")
is_animated = emoji_data.get("animated", False)
emoji_guild_id = emoji_data.get("guild_id")
if emoji_id:
if emoji_guild_id is not None:
try:
guild = cog.bot.get_guild(int(emoji_guild_id))
if guild:
can_use_emoji = True
print(f"Emoji '{full_item_name_with_colons}' belongs to guild '{guild.name}' ({emoji_guild_id}), bot is a member.")
else:
print(f"Cannot use emoji '{full_item_name_with_colons}'. Bot is not in guild ID: {emoji_guild_id}.")
except ValueError:
print(f"Invalid guild_id format for emoji '{full_item_name_with_colons}': {emoji_guild_id}")
else: # guild_id is None, considered usable (e.g., DM or old data)
can_use_emoji = True
print(f"Emoji '{full_item_name_with_colons}' has no associated guild_id, allowing usage.")
if can_use_emoji and emoji_id:
discord_emoji_syntax = f"<{'a' if is_animated else ''}:{item_name_key}:{emoji_id}>"
modified_content = modified_content.replace(full_item_name_with_colons, discord_emoji_syntax)
# Ensure replacement happens only once per unique placeholder if it appears multiple times
modified_content = modified_content.replace(full_item_name_with_colons, discord_emoji_syntax, 1)
print(f"Replaced custom emoji '{full_item_name_with_colons}' with Discord syntax: {discord_emoji_syntax}")
else:
elif not emoji_id:
print(f"Found custom emoji '{full_item_name_with_colons}' (dict) but no ID stored.")
elif emoji_data is not None: # Log if it's not a dict but also not None
elif emoji_data is not None:
print(f"Warning: emoji_data for '{full_item_name_with_colons}' is not a dict: {type(emoji_data)}")
# Check if it's a known custom sticker
sticker_data = await cog.emoji_manager.get_sticker(full_item_name_with_colons)
if isinstance(sticker_data, dict): # Check if sticker_data is a dictionary
can_use_sticker = False
if isinstance(sticker_data, dict):
sticker_id = sticker_data.get("id")
sticker_guild_id = sticker_data.get("guild_id")
if sticker_id:
# Remove the sticker text from the content
modified_content = modified_content.replace(full_item_name_with_colons, "").strip()
sticker_ids_to_send.append(sticker_id)
print(f"Found custom sticker '{full_item_name_with_colons}', removed from content, added ID '{sticker_id}' to send list.")
else:
if sticker_guild_id is not None:
try:
guild = cog.bot.get_guild(int(sticker_guild_id))
if guild:
can_use_sticker = True
print(f"Sticker '{full_item_name_with_colons}' belongs to guild '{guild.name}' ({sticker_guild_id}), bot is a member.")
else:
print(f"Cannot use sticker '{full_item_name_with_colons}'. Bot is not in guild ID: {sticker_guild_id}.")
except ValueError:
print(f"Invalid guild_id format for sticker '{full_item_name_with_colons}': {sticker_guild_id}")
else: # guild_id is None, considered usable
can_use_sticker = True
print(f"Sticker '{full_item_name_with_colons}' has no associated guild_id, allowing usage.")
if can_use_sticker and sticker_id:
# Remove the sticker text from the content (only the first instance)
if full_item_name_with_colons in modified_content:
modified_content = modified_content.replace(full_item_name_with_colons, "", 1).strip()
if sticker_id not in sticker_ids_to_send: # Avoid duplicate sticker IDs
sticker_ids_to_send.append(sticker_id)
print(f"Found custom sticker '{full_item_name_with_colons}', removed from content, added ID '{sticker_id}' to send list.")
elif not sticker_id:
print(f"Found custom sticker '{full_item_name_with_colons}' (dict) but no ID stored.")
elif sticker_data is not None: # Log if it's not a dict but also not None
elif sticker_data is not None:
print(f"Warning: sticker_data for '{full_item_name_with_colons}' is not a dict: {type(sticker_data)}")
# Clean up any double spaces or leading/trailing whitespace after replacements
modified_content = re.sub(r'\s+', ' ', modified_content).strip()
modified_content = re.sub(r'\s{2,}', ' ', modified_content).strip()
final_parsed_data["content"] = modified_content
print("Content processed for custom emoji/sticker information.")

View File

@ -1,13 +1,14 @@
import json
import os
from typing import Dict, Optional, Tuple, Union
from typing import Dict, Optional, Tuple, Union, Any
DATA_FILE_PATH = "data/custom_emojis_stickers.json"
class EmojiManager:
def __init__(self, data_file: str = DATA_FILE_PATH):
self.data_file = data_file
self.data: Dict[str, Dict[str, str]] = {"emojis": {}, "stickers": {}}
# Adjusted type hint for self.data to accommodate guild_id
self.data: Dict[str, Dict[str, Dict[str, Any]]] = {"emojis": {}, "stickers": {}}
self._load_data()
def _load_data(self):
@ -17,8 +18,22 @@ class EmojiManager:
with open(self.data_file, 'r', encoding='utf-8') as f:
loaded_json = json.load(f)
if isinstance(loaded_json, dict):
self.data["emojis"] = loaded_json.get("emojis", {})
self.data["stickers"] = loaded_json.get("stickers", {})
# Ensure guild_id is present, defaulting to None if missing for backward compatibility during load
self.data["emojis"] = {
name: {
"id": data.get("id"),
"animated": data.get("animated", False), # Default animated to False
"guild_id": data.get("guild_id") # Will be None if not present
}
for name, data in loaded_json.get("emojis", {}).items() if isinstance(data, dict)
}
self.data["stickers"] = {
name: {
"id": data.get("id"),
"guild_id": data.get("guild_id") # Will be None if not present
}
for name, data in loaded_json.get("stickers", {}).items() if isinstance(data, dict)
}
print(f"Loaded {len(self.data['emojis'])} emojis and {len(self.data['stickers'])} stickers from {self.data_file}")
else:
print(f"Warning: Data in {self.data_file} is not a dictionary. Initializing with empty data.")
@ -48,11 +63,14 @@ class EmojiManager:
print(f"Error saving emoji/sticker data: {e}")
return False
async def add_emoji(self, name: str, emoji_id: str, is_animated: bool) -> bool:
"""Adds a custom emoji."""
async def add_emoji(self, name: str, emoji_id: str, is_animated: bool, guild_id: Optional[int]) -> bool:
"""Adds a custom emoji with its guild ID."""
if name in self.data["emojis"]:
return False # Emoji already exists
self.data["emojis"][name] = {"id": emoji_id, "animated": is_animated}
# Allow update if guild_id was None and is now being set, or if ID changes
existing_data = self.data["emojis"][name]
if existing_data.get("id") == emoji_id and existing_data.get("guild_id") == guild_id and existing_data.get("animated") == is_animated:
return False # No change
self.data["emojis"][name] = {"id": emoji_id, "animated": is_animated, "guild_id": guild_id}
return self._save_data()
async def remove_emoji(self, name: str) -> bool:
@ -62,19 +80,21 @@ class EmojiManager:
del self.data["emojis"][name]
return self._save_data()
async def list_emojis(self) -> Dict[str, Dict[str, Union[str, bool]]]:
async def list_emojis(self) -> Dict[str, Dict[str, Any]]:
"""Lists all custom emojis."""
return self.data["emojis"]
async def get_emoji(self, name: str) -> Optional[Dict[str, Union[str, bool]]]:
async def get_emoji(self, name: str) -> Optional[Dict[str, Any]]:
"""Gets a specific custom emoji by name."""
return self.data["emojis"].get(name)
async def add_sticker(self, name: str, sticker_id: str) -> bool:
"""Adds a custom sticker."""
async def add_sticker(self, name: str, sticker_id: str, guild_id: Optional[int]) -> bool:
"""Adds a custom sticker with its guild ID."""
if name in self.data["stickers"]:
return False # Sticker already exists
self.data["stickers"][name] = {"id": sticker_id}
existing_data = self.data["stickers"][name]
if existing_data.get("id") == sticker_id and existing_data.get("guild_id") == guild_id:
return False # No change
self.data["stickers"][name] = {"id": sticker_id, "guild_id": guild_id}
return self._save_data()
async def remove_sticker(self, name: str) -> bool:
@ -84,10 +104,10 @@ class EmojiManager:
del self.data["stickers"][name]
return self._save_data()
async def list_stickers(self) -> Dict[str, Dict[str, str]]:
async def list_stickers(self) -> Dict[str, Dict[str, Any]]:
"""Lists all custom stickers."""
return self.data["stickers"]
async def get_sticker(self, name: str) -> Optional[Dict[str, str]]:
async def get_sticker(self, name: str) -> Optional[Dict[str, Any]]:
"""Gets a specific custom sticker by name."""
return self.data["stickers"].get(name)

View File

@ -105,11 +105,14 @@ async def on_message_listener(cog: 'GurtCog', message: discord.Message):
for animated, name, emoji_id_str in custom_emojis: # Renamed emoji_id to emoji_id_str
# emoji_url = f"https://cdn.discordapp.com/emojis/{emoji_id_str}.{'gif' if animated else 'png'}"
emoji_name_key = f":{name}:"
current_guild_id = message.guild.id if message.guild else None
# Check if already learned, if not, add it
existing_emoji_data = await cog.emoji_manager.get_emoji(emoji_name_key)
if not existing_emoji_data or existing_emoji_data.get("id") != emoji_id_str:
print(f"Learning custom emoji: {emoji_name_key} (ID: {emoji_id_str}, Animated: {bool(animated)})")
await cog.emoji_manager.add_emoji(emoji_name_key, emoji_id_str, bool(animated))
if not existing_emoji_data or \
existing_emoji_data.get("id") != emoji_id_str or \
existing_emoji_data.get("guild_id") != current_guild_id: # Also check guild_id
print(f"Learning custom emoji: {emoji_name_key} (ID: {emoji_id_str}, Animated: {bool(animated)}, Guild: {current_guild_id})")
await cog.emoji_manager.add_emoji(emoji_name_key, emoji_id_str, bool(animated), current_guild_id)
# Stickers in message
@ -117,11 +120,14 @@ async def on_message_listener(cog: 'GurtCog', message: discord.Message):
for sticker_item in message.stickers:
sticker_name_key = f":{sticker_item.name}:" # Use sticker name as key
sticker_id_str = str(sticker_item.id) # Ensure ID is string
current_guild_id = message.guild.id if message.guild else None
# Check if already learned, if not, add it
existing_sticker_data = await cog.emoji_manager.get_sticker(sticker_name_key)
if not existing_sticker_data or existing_sticker_data.get("id") != sticker_id_str:
print(f"Learning sticker: {sticker_name_key} (ID: {sticker_id_str})")
await cog.emoji_manager.add_sticker(sticker_name_key, sticker_id_str)
if not existing_sticker_data or \
existing_sticker_data.get("id") != sticker_id_str or \
existing_sticker_data.get("guild_id") != current_guild_id: # Also check guild_id
print(f"Learning sticker: {sticker_name_key} (ID: {sticker_id_str}, Guild: {current_guild_id})")
await cog.emoji_manager.add_sticker(sticker_name_key, sticker_id_str, current_guild_id)
# --- End Emoji/Sticker Learning ---
thread_id = message.channel.id if isinstance(message.channel, discord.Thread) else None