test / bot /emojis.py
mtaaz's picture
Upload 93 files
e699b46 verified
"""
Enhanced Emoji system for the bot.
Loads custom emojis from emojies.txt and provides UI emoji aliases with rich decorations.
"""
import os
from pathlib import Path
import re
import discord
# ═══════════════════════════════════════════════════════════════════════════════
# FALLBACK UNICODE EMOJIS (used when custom emojis are not available)
# ═══════════════════════════════════════════════════════════════════════════════
FALLBACK_EMOJIS: dict[str, str] = {
# Core actions
"ok": "✅", "no": "❌", "warn": "⚠️", "globe": "🌐", "music": "🎛️",
"join": "✅", "leave": "👋", "play": "▶️", "pause": "⏸️", "resume": "▶️",
"skip": "⏭️", "stop": "⏹️", "queue": "🎵", "video": "🎬",
# Games & fun
"mario": "🍄", "choose": "🤖", "coin": "🪙", "ping": "🏓", "roll": "🎲",
"menu": "📘", "stats": "📊", "members": "👥", "channels": "🧵", "roles": "🛡️",
"boost": "🚀", "user": "👤", "refresh": "🔄", "trivia": "🧠", "bot": "🤖",
# Settings & controls
"settings": "⚙️", "volume": "🔊", "shuffle": "🔀", "loop": "🔁",
"previous": "⏮️", "search": "🔎", "preview": "🎬", "stay_247": "♾️", "joystick": "🕹️",
# Economy
"suggest": "💡", "economy": "💰", "gamehub": "🎮", "diamond": "💎", "star": "✅",
"catjam": "😺", "djpeepo": "🎧", "spotify": "🎵", "partytime": "🎉", "letsgo": "🚀",
# Arrows & decorations
"arrow_green": "<:animatedarrowgreen:1477261279428087979>", "arrow_pink": "✅", "arrow_blue": "🔵", "arrow_purple": "<:animatedarrowgreen:1477261279428087979>",
"arrow_orange": "🟠", "arrow_yellow": "<:animatedarrowyellow:1477261257592668271>", "shield": "🛡️", "gift": "🎁", "hype": "🔥",
# UI elements
"notebook": "📓", "ticket": "🎫", "verified": "✅", "error": "❌", "loading": "⏳",
# Number emojis
"num_1": "1️⃣", "num_2": "2️⃣", "num_3": "3️⃣", "num_4": "4️⃣", "num_5": "5️⃣",
"num_6": "6️⃣", "num_7": "7️⃣", "num_8": "8️⃣", "num_9": "9️⃣", "num_10": "🔟",
# Enhanced decorations
"sparkle": "✨", "fire": "🔥", "heart": "✅", "purple_heart": "<:animatedarrowgreen:1477261279428087979>", "blue_heart": "🔵",
"green_heart": "<:animatedarrowgreen:1477261279428087979>", "yellow_heart": "<:animatedarrowyellow:1477261257592668271>", "orange_heart": "🟠", "pink_heart": "✅",
"crown": "👑", "trophy": "🏆", "medal": "🎖️", "ribbon": "🎀", "gem": "💎",
"crystal_ball": "🔮", "comet": "☄️", "star2": "✅", "star3": "✅", "dizzy": "💫",
"boom": "💥", "zap": "⚡", "warning": "⚠️", "information": "ℹ️", "question": "❓",
"exclamation": "❗", "white_check_mark": "✅", "x": "❌", "lock": "🔒", "unlock": "🔓",
"key": "🔑", "door": "🚪", "bell": "🔔", "megaphone": "📢", "speech": "💬",
"envelope": "✉️", "pencil": "📝", "book": "📖", "scroll": "📜", "page": "📄",
"folder": "📁", "file": "🗃️", "card_index": "📇", "clipboard": "📋",
# Gaming
"controller": "🎮", "dice": "🎲", "slots": "🎰", "8ball": "🎱", "bowling": "🎳",
"dart": "🎯", "video_game": "🕹️", "chess": "♟️", "jigsaw": "🧩",
# Music enhanced
"notes": "🎶", "musical_score": "🎼", "studio_microphone": "🎙️", "microphone": "🎤",
"headphones": "🎧", "radio": "📻", "saxophone": "🎷", "guitar": "🎸", "drum": "🥁",
# Money
"money_bag": "💰", "dollar": "💵", "yen": "💴", "euro": "💶", "pound": "💷",
"credit_card": "💳", "chart": "📈", "bank": "🏦", "atm": "🏧", "receipt": "🧾",
# Decorative borders
"line": "▬", "dash": "—", "dot": "•", "bullet": "•", "circle": "<:animatedarrowgreen:1477261279428087979>",
"square": "■", "diamond_shape": "◆", "triangle": "▲", "star_border": "✅",
}
# ═══════════════════════════════════════════════════════════════════════════════
# LOAD CUSTOM EMOJIS FROM FILE
# ═══════════════════════════════════════════════════════════════════════════════
_EMOJI_FILES = (
Path("emojies.txt"),
Path("bot/emojies.txt"),
Path(os.getenv("EMOJI_FILE_PATH", "/opt/bot/emojies.txt")) if os.getenv("EMOJI_FILE_PATH") else None,
)
_EMOJI_FILES = tuple(p for p in _EMOJI_FILES if p is not None)
_EMOJI_ID_RE = re.compile(r"\d{6,}")
_EMOJI_TAG_RE = re.compile(r"^<(a?):([\w~]+):(\d{6,})>$")
_EMOJI_BOT: discord.Client | None = None
_DYNAMIC_EMOJI_MAP: dict[str, tuple[str, ...]] = {
"economy": ("coin", "money", "bank", "work", "daily", "rob", "gamble"),
"admin": ("shield", "hammer", "warn", "trash", "mute", "lock"),
"ai": ("robot", "brain", "sparkles", "setup", "speak"),
"music": ("musicbeat", "play", "pause", "skip", "stop", "queue", "loop"),
}
def _normalize_key(value: str) -> str:
return (value or "").strip().lower().replace(" ", "_")
def _sanitize_emoji_name(name: str) -> str:
cleaned = re.sub(r"[^A-Za-z0-9_]", "_", (name or "").strip())
cleaned = re.sub(r"_+", "_", cleaned).strip("_")
if not cleaned:
return "emoji"
if len(cleaned) < 2:
return f"{cleaned}_x"
return cleaned[:32]
def set_emoji_bot(bot: discord.Client) -> None:
"""Register bot/client instance for dynamic custom emoji lookup."""
global _EMOJI_BOT
_EMOJI_BOT = bot
def _extract_emoji_id(value: str) -> int | None:
cleaned = (value or "").strip()
if not cleaned:
return None
if cleaned.isdigit():
return int(cleaned)
match = _EMOJI_ID_RE.search(cleaned)
return int(match.group(0)) if match else None
def _format_custom_emoji(emoji_obj: discord.Emoji | discord.PartialEmoji) -> str:
prefix = "a" if emoji_obj.animated else ""
return f"<{prefix}:{emoji_obj.name}:{emoji_obj.id}>"
def _build_custom_emoji_code(name: str, emoji_id: int, animated: bool) -> str:
"""Build a Discord custom emoji tag without relying on cache objects."""
prefix = "a" if animated else ""
return f"<{prefix}:{name}:{emoji_id}>"
def _ensure_unescaped_emoji(value: str) -> str:
"""Keep emoji tags raw so Discord can render them."""
return value.replace("\\<", "<")
def resolve_emoji_value(value: str, fallback: str = "✨", *, bot: discord.Client | None = None) -> str:
"""Resolve emoji config for display in embeds/messages.
Returns the full custom emoji tag <:name:id> so Discord can render it.
Only falls back to unicode if the value is invalid or empty.
Custom emoji tags work in embed descriptions if the bot has access to the emoji.
"""
parsed = _parse_custom_emoji_config(value)
if parsed is not None:
config_name, emoji_id, animated = parsed
# Return the full custom emoji tag — Discord renders it if valid
return _ensure_unescaped_emoji(_build_custom_emoji_code(config_name, emoji_id, animated))
emoji_id = _extract_emoji_id(value)
active_bot = bot or _EMOJI_BOT
if emoji_id is not None and active_bot is not None:
resolved = active_bot.get_emoji(emoji_id)
if resolved is not None:
return _ensure_unescaped_emoji(_format_custom_emoji(resolved))
# Return as-is (might be unicode emoji or already-resolved tag)
return _ensure_unescaped_emoji(value or fallback)
def _parse_custom_emoji_config(value: str) -> tuple[str, int, bool] | None:
"""Parse a custom emoji value and return (name, id, animated)."""
cleaned = (value or "").strip()
if not cleaned:
return None
match = _EMOJI_TAG_RE.fullmatch(cleaned)
if match:
return match.group(2), int(match.group(3)), bool(match.group(1))
if cleaned.isdigit():
return "emoji", int(cleaned), False
return None
def _parse_emoji_file() -> dict[str, str]:
"""Parse emoji file and return name -> emoji mapping.
Supports two formats:
- name=<:name:id> (full format)
- name=id (just the ID)
"""
parsed: dict[str, str] = {}
for file in _EMOJI_FILES:
if not file.is_file():
continue
try:
raw = file.read_text(encoding="utf-8", errors="ignore")
except Exception:
continue
for line in raw.splitlines():
line = line.strip()
# Skip comments and empty lines
if not line or line.startswith("#"):
continue
# Parse key=value format
if "=" in line:
name, value = line.split("=", 1)
key = _normalize_key(name)
value = value.strip()
if key and value:
# Check if value is already in full format
if (value.startswith("<:") or value.startswith("<a:")) and value.endswith(">"):
cfg = _parse_custom_emoji_config(value)
if cfg is not None:
cfg_name, emoji_id, animated = cfg
parsed[key] = _build_custom_emoji_code(
_sanitize_emoji_name(cfg_name), emoji_id, animated
)
else:
parsed[key] = value
elif value.isdigit():
# Just the ID, create full format
parsed[key] = f"<:{_sanitize_emoji_name(name)}:{value}>"
else:
# Some other format, try to use as-is
parsed[key] = value
return parsed
# Load custom emojis
CUSTOM_EMOJIS: dict[str, str] = _parse_emoji_file()
# ═══════════════════════════════════════════════════════════════════════════════
# UI EMOJI ALIASES - Maps UI names to custom emoji names from your server
# ═══════════════════════════════════════════════════════════════════════════════
# Mapping from UI names to possible custom emoji names
_UI_ALIASES: dict[str, tuple[str, ...]] = {
# Status & Connection
"ok": ("accountisconnected", "white_check_mark"),
"no": ("accountisnotconnected", "x"),
"warn": ("warning",),
"connected": ("accountisconnected", "accountisconnectedwhite"),
"disconnected": ("accountisnotconnected",),
# Music Controls
"play": ("djpeepo", "letsgo", "partytime"),
"pause": ("spotifypause", "pause"),
"resume": ("djpeepo", "play"),
"skip": ("letsgo", "skip"),
"stop": ("stop", "soundmute"),
"queue": ("spotifyqueueadd", "queue"),
"shuffle": ("shuffle",),
"loop": ("loop",),
"previous": ("previous",),
"volume": ("soundwhite", "sounddark", "volume"),
"volume_mute": ("soundmutewhite", "soundmutedark"),
# Music & Spotify
"music": ("spotify", "spotifymusicdisc", "listeningtomusic"),
"spotify": ("spotify", "spotify2", "spotify3"),
"spotify_download": ("spotifydownload",),
"spotify_added": ("spotifyadded",),
"spotify_remove": ("spotifyremove",),
"spotify_favorite": ("spotifyfavourite",),
"spotify_listening": ("spotifylistening",),
"music_note": ("excitedmusicnote", "wingedmusicnote"),
"music_beat": ("musicbeat",),
"boombox": ("_Boombox",),
# Microphone
"microphone": ("microphonewhite", "microphonedark"),
"microphone_mute": ("microphonemutewhite", "microphonemutedark"),
# Settings & Configuration
"settings": ("settingswhite", "settingsdark", "settings"),
"edit_profile": ("editprofilewhite", "editprofiledark"),
# Animated Arrows (for dividers and decorations)
"arrow_green": ("animatedarrowgreen",),
"arrow_purple": ("animatedarrowpurple",),
"arrow_red": ("animatedarrowred",),
"arrow_white": ("animatedarrowwhite",),
"arrow_pink": ("animatedarrowpink", "animatedarrowpink2"),
"arrow_orange": ("animatedarroworange",),
"arrow_yellow": ("animatedarrowyellow",),
"arrow_blue": ("animatedarrowblue", "animatedarrowbluelite"),
# Gaming & Entertainment
"gamehub": ("xbox", "controller"),
"youtube": ("youtube",),
"cinema": ("cinema1", "cinema2", "cinema3"),
"chess": ("whiteking", "blackking"),
# Fun & Reactions
"catjam": ("catjam",),
"djpeepo": ("djpeepo",),
"partytime": ("partytime",),
"letsgo": ("letsgo", "letsgo2"),
"hype": ("hype",),
"cooked": ("cooked", "cooked2", "cookedsoldier"),
"gigachad": ("gigachad",),
"pepejam": ("pepejam",),
"pepe_guitar": ("pepeguitar",),
"therock": ("therock",),
"skull": ("skull",),
"lol": ("lol",),
"sus": ("sus",),
"uwu": ("uwu",),
"rip": ("rip",),
"crying": ("crying",),
"sad": ("sad",),
"bye": ("bye",),
"hi": ("hi",),
"slay": ("slay",),
"ahhhhh": ("ahhhhh",),
# Cats & Animals
"bongo_cat": ("bongocatbmo", "whalebongocat", "rubberhosebongocat"),
"cat_happy": ("cathappy",),
"cat_sad": ("catsobbing", "catsob1", "catsob2"),
"cat_burning": ("catburning",),
"cat_pleading": ("catpleading",),
# Badges & Roles
"diamond": ("diamond",),
"star": ("yellowstar",),
"crown": ("clovercrown",),
"shield": ("shield",),
"staff": ("staff",),
"owner": ("owner",),
"partner": ("partner",),
"boost": ("discordboost", "naboosticon"),
"developer": ("discorddeveloper",),
"bug_hunter": ("purplebughunter",),
"premium": ("whitepremium",),
"mod": ("modswordsids",),
"security": ("securityfilter",),
# Items & Objects
"notebook": ("notebook",),
"gift": ("redgift",),
"trash": ("trashcan",),
"info": ("info",),
"gg": ("gg",),
# Economy
"economy": ("diamond", "nacashicon"),
"coin": ("naemeraldicon",),
# Misc
"globe": ("globe",),
"join": ("accountisconnected",),
"leave": ("bye",),
"refresh": ("refresh",),
"search": ("search",),
"menu": ("notebook",),
"suggest": ("suggest",),
}
def _resolve_emoji(*candidates: str) -> str | None:
"""Resolve emoji from custom emojis or return None."""
for candidate in candidates:
key = _normalize_key(candidate)
if key in CUSTOM_EMOJIS:
return CUSTOM_EMOJIS[key]
return None
# ═══════════════════════════════════════════════════════════════════════════════
# MAIN EMOJI RESOLUTION
# ═══════════════════════════════════════════════════════════════════════════════
# Set to False to prefer custom emojis
USE_UNICODE_EMOJIS = False
def _build_ui_emojis() -> dict[str, str]:
"""Build the UI emojis dictionary."""
emojis = {}
for name, aliases in _UI_ALIASES.items():
if USE_UNICODE_EMOJIS:
emojis[name] = FALLBACK_EMOJIS.get(name, "✨")
else:
# Try custom emoji first, then fallback
custom = _resolve_emoji(*aliases)
emojis[name] = custom or FALLBACK_EMOJIS.get(name, "✨")
return emojis
UI_EMOJIS: dict[str, str] = _build_ui_emojis()
def ui(name: str) -> str:
"""Get a UI emoji by name.
Resolution order:
1) Hardcoded custom key from `emojies.txt`.
2) Alias definitions (custom match via _UI_ALIASES / UI_EMOJIS).
3) Windows/Unicode fallback from FALLBACK_EMOJIS.
"""
key = _normalize_key(name)
fallback = FALLBACK_EMOJIS.get(key, "✨")
# 1) Hardcoded custom key from emojies.txt
if not USE_UNICODE_EMOJIS:
if key in CUSTOM_EMOJIS:
return resolve_emoji_value(CUSTOM_EMOJIS[key], fallback)
# 2) Alias definitions -> try custom candidates first
aliases = _UI_ALIASES.get(key, ())
custom_alias = _resolve_emoji(*aliases) if aliases else None
if custom_alias:
return resolve_emoji_value(custom_alias, fallback)
# 2b) Alias definitions pre-built map
if key in UI_EMOJIS:
return resolve_emoji_value(UI_EMOJIS[key], fallback)
# 3) Windows/Unicode fallback
return fallback
def custom_emoji(name: str) -> str | None:
"""Get a custom emoji by name, returns None if not found."""
key = _normalize_key(name)
return CUSTOM_EMOJIS.get(key)
def get_emoji(name: str, fallback: str = "✨") -> str:
"""Get emoji by name with custom fallback."""
result = ui(name)
return result if result != "✨" else fallback
def get_custom_emoji(category: str, fallback: str = "✨") -> str:
"""Return a best-match custom emoji for a semantic category.
Uses the loaded custom emoji map first, then live bot emoji cache, and finally
falls back to unicode defaults.
"""
category_key = _normalize_key(category)
category_keywords: dict[str, tuple[str, ...]] = {
"economy": ("coin", "money", "bank", "gem", "cash", "wallet"),
"admin": ("shield", "hammer", "ban", "mute", "mod", "security"),
"games": ("dice", "board", "crown", "chess", "controller", "game"),
}
keywords = category_keywords.get(category_key, (category_key,))
for key, value in CUSTOM_EMOJIS.items():
if any(word in key for word in keywords):
return resolve_emoji_value(value, fallback)
active_bot = _EMOJI_BOT
if active_bot is not None:
for emoji_obj in getattr(active_bot, "emojis", []):
name = _normalize_key(getattr(emoji_obj, "name", ""))
if any(word in name for word in keywords):
return resolve_emoji_value(_format_custom_emoji(emoji_obj), fallback, bot=active_bot)
fallback_map = {
"economy": "💰",
"admin": "🛡️",
"games": "🎲",
}
return fallback_map.get(category_key, fallback)
# ═══════════════════════════════════════════════════════════════════════════════
# COMMAND EMOJIS - Emojis for specific commands
# ═══════════════════════════════════════════════════════════════════════════════
COMMAND_EMOJIS: dict[str, str] = {
# Music Commands
"play": ui("play"),
"pause": ui("pause"),
"resume": ui("resume"),
"skip": ui("skip"),
"stop": ui("stop"),
"queue": ui("queue"),
"nowplaying": ui("music"),
"volume": ui("volume"),
"shuffle": ui("shuffle"),
"loop": ui("loop"),
"previous": ui("previous"),
"seek": "⏩",
"rewind": "⏪",
"forward": "⏩",
"filter": "🎛️",
"clear": ui("trash"),
"remove": ui("spotify_remove"),
"move": "↔️",
"autoplay": "🔄",
"disconnect": ui("disconnected"),
"join": ui("connected"),
"247": "♾️",
# Admin Commands
"ban": "🔨",
"kick": "👢",
"mute": "🔇",
"unmute": "🔊",
"warn": "⚠️",
"unwarn": "✅",
"clearwarns": "🧹",
"slowmode": "🐌",
"lock": "🔒",
"unlock": "🔓",
"purge": "🧹",
# Config Commands
"setup": ui("settings"),
"config": ui("settings"),
"prefix": "📝",
"language": "🌐",
"dj": ui("djpeepo"),
"log": "📋",
# Economy Commands
"balance": ui("economy"),
"daily": "📅",
"work": "💼",
"rob": "🦹",
"give": "🎁",
"leaderboard": "🏆",
"shop": "🛒",
"buy": "🛒",
# Fun Commands
"8ball": "🎱",
"roll": "🎲",
"flip": "🪙",
"meme": "😂",
"joke": "😂",
"quote": "💬",
"fact": "📚",
# Utility Commands
"avatar": "👤",
"userinfo": "👤",
"serverinfo": "📊",
"ping": "🏓",
"help": "❓",
"invite": "📨",
"stats": "📈",
"uptime": "⏱️",
# Ticket Commands
"ticket": "🎫",
"close": "❌",
"open": "✅",
# Giveaway Commands
"giveaway": "🎁",
"reroll": "🔄",
"end": "🛑",
}
def get_command_emoji(command_name: str) -> str:
"""Get emoji for a specific command."""
key = _normalize_key(command_name)
return COMMAND_EMOJIS.get(key, "✨")
# ═══════════════════════════════════════════════════════════════════════════════
# DECORATION FUNCTIONS
# ═══════════════════════════════════════════════════════════════════════════════
def decorative_emoji(index: int) -> str:
"""Get a decorative emoji by index (cycles through available emojis)."""
values = list(CUSTOM_EMOJIS.values()) if CUSTOM_EMOJIS else list(FALLBACK_EMOJIS.values())
if not values:
return "✨"
return values[index % len(values)]
def random_decorative_emoji() -> str:
"""Get a random decorative emoji."""
import random
values = list(CUSTOM_EMOJIS.values()) if CUSTOM_EMOJIS else list(FALLBACK_EMOJIS.values())
return random.choice(values) if values else "✨"
def decorated_title(emoji: str, text: str) -> str:
"""Create a beautifully decorated title with emoji and sparkle effects."""
return f"✨ {emoji} **{text}** ✨"
def panel_header(emoji: str, title: str, subtitle: str = "") -> str:
"""Create a rich panel header with decorations."""
header = f"╔══════════════════════════════╗\n║ {emoji} **{title}**"
if subtitle:
header += f"\n║ {subtitle}"
header += "\n╚══════════════════════════════╝"
return header
def status_emoji(success: bool) -> str:
"""Get appropriate status emoji."""
return ui("ok") if success else ui("no")
def rank_badge(rank: int) -> str:
"""Get rank badge emoji for leaderboards."""
badges = {
1: "🥇",
2: "🥈",
3: "🥉",
}
return badges.get(rank, f"#{rank}")
# ═══════════════════════════════════════════════════════════════════════════════
# NAMED EMOJI CONSTANTS FOR EASY IMPORT
# ═══════════════════════════════════════════════════════════════════════════════
# Core emojis
E_CATJAM = ui("catjam")
E_DIAMOND = ui("diamond")
E_STAR = ui("star")
E_SPOTIFY = ui("spotify")
E_PARTYTIME = ui("partytime")
E_LETSGO = ui("letsgo")
E_DJPEEPO = ui("djpeepo")
E_SHIELD = ui("shield")
E_GIFT = ui("gift")
E_HYPE = ui("hype")
E_NOTEBOOK = ui("notebook")
E_OK = ui("ok")
E_NO = ui("no")
E_WARN = ui("warn")
E_MUSIC = ui("music")
E_PLAY = ui("play")
E_PAUSE = ui("pause")
E_SKIP = ui("skip")
E_STOP = ui("stop")
E_QUEUE = ui("queue")
E_JOIN = ui("join")
E_LEAVE = ui("leave")
E_VOLUME = ui("volume")
E_SHUFFLE = ui("shuffle")
E_LOOP = ui("loop")
E_PREVIOUS = ui("previous")
E_SEARCH = ui("search")
E_PREVIEW = ui("preview")
E_STAY_247 = ui("stay_247")
E_ECONOMY = ui("economy")
E_GAMEHUB = ui("gamehub")
# Arrow emojis for dividers
E_ARROW_GREEN = ui("arrow_green")
E_ARROW_PINK = ui("arrow_pink")
E_ARROW_BLUE = ui("arrow_blue")
E_ARROW_PURPLE = ui("arrow_purple")
E_ARROW_ORANGE = ui("arrow_orange")
E_ARROW_YELLOW = ui("arrow_yellow")
# Additional decoration constants
E_CROWN = "👑"
E_TROPHY = "🏆"
E_FIRE = "🔥"
E_SPARKLE = "✨"
E_GEM = "💎"
E_ROCKET = "🚀"
E_MONEY = "💰"
E_CHART = "📈"
E_TARGET = "🎯"
E_DICE = "🎲"
E_GIFT_BOX = "🎁"
E_LOCK = "🔒"
E_KEY = "🔑"
E_BELL = "🔔"
E_STAR2 = "✅"
E_DIZZY = "💫"
E_BOOM = "💥"
E_ZAP = "⚡"
E_HEART = "✅"
E_PURPLE_HEART = "<:animatedarrowgreen:1477261279428087979>"
E_BLUE_HEART = "💙"
E_GREEN_HEART = "<:animatedarrowgreen:1477261279428087979>"
E_YELLOW_HEART = "💛"