discordbot/gurt/emojis.py
Slipstream 6868b2e95e
Feat: Integrate Tenor GIF search and custom emoji/sticker support
This commit introduces two main enhancements to Gurt's capabilities:

1.  **Tenor GIF Integration:**
    *   The AI can now request Tenor GIFs via a `request_tenor_gif_query` field in its response schema.
    *   `api.py` handles these requests by searching Tenor (using the new `TENOR_API_KEY` from config) and appending the GIF URL to the AI's message content.
    *   System prompts have been updated to instruct the AI on how to request GIFs.

2.  **Custom Emoji & Sticker Learning and Usage:**
    *   Introduces an `EmojiManager` (in the new `gurt/emojis.py`) to store and manage mappings of custom emoji/sticker names to their URLs. This data is persisted to `EMOJI_STORAGE_FILE`.
    *   Gurt now automatically learns custom emojis and stickers (and their URLs) from user messages via the `on_message` listener in `listeners.py`.
    *   The AI can be prompted to use these learned emojis/stickers by referencing their `:name:`.
    *   `api.py` processes AI responses to identify known custom emojis/stickers. For stickers, their URL is appended to the content if mentioned.
    *   A `learnemoji` command has been added for manually teaching Gurt emojis/stickers, although automatic learning is the primary mechanism.
    *   System prompts are updated to guide the AI on using custom emojis/stickers.
2025-05-28 14:30:26 -06:00

94 lines
3.9 KiB
Python

import json
import os
from typing import Dict, Optional, Tuple
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": {}}
self._load_data()
def _load_data(self):
"""Loads emoji and sticker data from the JSON file."""
try:
if os.path.exists(self.data_file):
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", {})
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.")
self._save_data() # Initialize with empty structure if format is wrong
else:
print(f"{self.data_file} not found. Initializing with empty data.")
self._save_data() # Create the file if it doesn't exist
except json.JSONDecodeError:
print(f"Error decoding JSON from {self.data_file}. Initializing with empty data.")
self._save_data()
except Exception as e:
print(f"Error loading emoji/sticker data: {e}")
# Ensure data is initialized even on other errors
if "emojis" not in self.data: self.data["emojis"] = {}
if "stickers" not in self.data: self.data["stickers"] = {}
def _save_data(self):
"""Saves the current emoji and sticker data to the JSON file."""
try:
os.makedirs(os.path.dirname(self.data_file), exist_ok=True)
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(self.data, f, indent=4)
print(f"Saved emoji and sticker data to {self.data_file}")
return True
except Exception as e:
print(f"Error saving emoji/sticker data: {e}")
return False
async def add_emoji(self, name: str, url: str) -> bool:
"""Adds a custom emoji."""
if name in self.data["emojis"]:
return False # Emoji already exists
self.data["emojis"][name] = url
return self._save_data()
async def remove_emoji(self, name: str) -> bool:
"""Removes a custom emoji."""
if name not in self.data["emojis"]:
return False # Emoji not found
del self.data["emojis"][name]
return self._save_data()
async def list_emojis(self) -> Dict[str, str]:
"""Lists all custom emojis."""
return self.data["emojis"]
async def get_emoji(self, name: str) -> Optional[str]:
"""Gets a specific custom emoji by name."""
return self.data["emojis"].get(name)
async def add_sticker(self, name: str, url: str) -> bool:
"""Adds a custom sticker."""
if name in self.data["stickers"]:
return False # Sticker already exists
self.data["stickers"][name] = url
return self._save_data()
async def remove_sticker(self, name: str) -> bool:
"""Removes a custom sticker."""
if name not in self.data["stickers"]:
return False # Sticker not found
del self.data["stickers"][name]
return self._save_data()
async def list_stickers(self) -> Dict[str, str]:
"""Lists all custom stickers."""
return self.data["stickers"]
async def get_sticker(self, name: str) -> Optional[str]:
"""Gets a specific custom sticker by name."""
return self.data["stickers"].get(name)