test / bot /cogs /menu.py
mtaaz's picture
Upload 93 files
e699b46 verified
"""
Menu cog: Interactive command explorer with beautiful panels.
Enhanced with stunning decorations, rich formatting, and multi-language support.
"""
import random
import discord
from discord.ext import commands
from bot.cogs.ai_suite import ImperialMotaz
from bot.i18n import get_cmd_desc
from bot.emojis import ui, E_DIAMOND, E_STAR, E_FIRE, E_SPARKLE, E_GEM, E_CROWN
from bot.theme import (
fancy_header, pick_neon_color, progress_bar, shimmer, panel_divider,
NEON_CYAN, NEON_PINK, NEON_PURPLE, NEON_LIME, NEON_YELLOW, NEON_BLUE,
NEON_GOLD, NEON_MAGENTA,
double_line, triple_line, mega_title, fancy_divider, animated_header,
gradient_header, panel_title, section_header, quick_stats_grid
)
# Beautiful unicode emojis for select menu categories
_CATEGORY_EMOJIS: dict[str, str] = {
"Music": "🎵",
"Admin": "🛡️",
"Fun": "🎮",
"AI": "🤖",
"Utility": "🔧",
"Config": "⚙️",
"Economy": "💰",
"Moderation": "⚔️",
"Tickets": "🎫",
"Welcome": "👋",
"Giveaway": "🎁",
"Verification": "✅",
"Tournament": "🏆",
"Games": "🎲",
"Level": "📊",
"AutoMod": "🤖",
"Logs": "📋",
"DJ": "🎧",
"Developer": "💻",
"default": "📂",
}
# Category descriptions with emojis
_CATEGORY_DESCRIPTIONS: dict[str, str] = {
"Music": "🎵 Music commands for playback control",
"Admin": "🛡️ Server administration tools",
"Fun": "🎮 Fun games and entertainment",
"AI": "🤖 AI-powered features",
"Utility": "🔧 Useful utility commands",
"Config": "⚙️ Bot configuration settings",
"Economy": "💰 Economy and currency system",
"Moderation": "⚔️ Moderation commands",
"Tickets": "🎫 Ticket support system",
"Welcome": "👋 Welcome and goodbye messages",
"Giveaway": "🎁 Giveaway management",
"Verification": "✅ Member verification",
"Tournament": "🏆 Tournament brackets",
"Games": "🎲 Mini games and fun",
"Level": "📊 XP and leveling system",
"AutoMod": "🤖 Auto-moderation features",
"Logs": "📋 Logging configuration",
"DJ": "🎧 DJ music controls",
"Developer": "💻 Developer tools",
}
_CATEGORY_BILINGUAL = {
"__all__": "📚 All Commands | جميع الأوامر",
"__ai__": "🤖 AI | الذكاء الاصطناعي",
"Music": "🎵 Music | الموسيقى",
"Admin": "🛡️ Admin | الإدارة",
"Fun": "🎮 Games | الألعاب",
"AI": "🤖 AI | الذكاء الاصطناعي",
"Utility": "🔧 Utility | الأدوات",
"Config": "⚙️ Config | الإعدادات",
"Economy": "💰 Economy | الاقتصاد",
"Moderation": "⚔️ Moderation | الإشراف",
"Community": "💡 Community | المجتمع",
"AISuite": "🤖 AI Suite | الذكاء",
"Configuration": "⚙️ Configuration | الإعدادات",
"Events": "📋 Events | الأحداث",
"Verification": "✅ Verification | التحقق",
}
def _bilingual_category(name: str) -> str:
return _CATEGORY_BILINGUAL.get(name, f"📂 {name} | {name}")
def _safe_emoji(value: str) -> "discord.Emoji | discord.PartialEmoji | str | None":
"""Safely resolve an emoji value for use in Discord SelectOptions."""
if not value:
return None
value = value.strip()
if not value:
return None
if value.startswith("<") and value.endswith(">"):
try:
return discord.PartialEmoji.from_str(value)
except Exception:
return None
return value
class CommandsMenuSelect(discord.ui.Select):
"""Beautiful dropdown menu for selecting command categories."""
def __init__(
self,
cog_names: list[str],
all_label: str,
ai_label: str,
*,
placeholder: str,
all_desc: str,
ai_desc: str,
cog_desc_map: dict[str, str],
selected_cog: str = "__all__",
) -> None:
options = [
discord.SelectOption(
label=_bilingual_category("__all__"),
description=all_desc,
emoji=ui("book"),
value="__all__",
default=selected_cog == "__all__",
),
discord.SelectOption(
label=_bilingual_category("__ai__"),
description=ai_desc,
emoji=ui("robot"),
value="__ai__",
default=selected_cog == "__ai__",
),
]
# Add cog options with beautiful emojis
for name in sorted(cog_names):
raw_emoji = _CATEGORY_EMOJIS.get(name, _CATEGORY_EMOJIS["default"])
desc = cog_desc_map.get(name, _CATEGORY_DESCRIPTIONS.get(name, f"{name} commands"))
# Truncate description to fit Discord's limits
desc = desc[:100]
# Safely resolve emoji - use PartialEmoji for custom emoji strings
resolved_emoji = _safe_emoji(raw_emoji)
options.append(
discord.SelectOption(
label=_bilingual_category(name)[:100],
description=desc,
emoji=resolved_emoji,
value=name,
default=selected_cog == name,
)
)
super().__init__(
placeholder=placeholder,
min_values=1,
max_values=1,
options=options
)
async def callback(self, interaction: discord.Interaction) -> None:
await interaction.response.defer()
view = self.view
if not isinstance(view, CommandsMenuView):
return
selected = self.values[0]
view.selected_cog = selected
view.page = 0
await view.setup_items()
embed = await view.build_embed(interaction.guild.id if interaction.guild else None, selected)
await interaction.followup.edit_message(interaction.message.id, embed=embed, view=view)
class CommandsMenuView(discord.ui.View):
"""Beautiful command menu view with rich decorations."""
def __init__(self, bot: commands.Bot, guild_id: int | None = None) -> None:
super().__init__(timeout=None)
self.bot = bot
self.guild_id = guild_id
self.selected_cog = "__all__"
self.page = 0
self.page_size = 18
self._accent_seed = random.randrange(0, 1024)
self._color_palette = [NEON_CYAN, NEON_PINK, NEON_PURPLE, NEON_LIME, NEON_GOLD, NEON_MAGENTA]
async def setup_items(self) -> None:
"""Setup the menu items."""
cog_names = list(self.bot.cogs.keys())
all_label = await self.bot.get_text(self.guild_id, "menu.all")
ai_label = await self.bot.get_text(self.guild_id, "menu.ai")
placeholder = await self.bot.get_text(self.guild_id, "menu.select_placeholder")
all_desc = await self.bot.get_text(self.guild_id, "menu.select_all_desc")
ai_desc = await self.bot.get_text(self.guild_id, "menu.select_ai_desc")
cog_desc_map: dict[str, str] = {}
for name in cog_names:
key = f"menu.category_desc.{name.lower()}"
desc = await self.bot.get_text(self.guild_id, key)
if desc == key:
desc = _CATEGORY_DESCRIPTIONS.get(name, f"📂 {name} commands")
cog_desc_map[name] = desc
self.clear_items()
self.add_item(
CommandsMenuSelect(
cog_names,
all_label,
ai_label,
placeholder=placeholder,
all_desc=all_desc,
ai_desc=ai_desc,
cog_desc_map=cog_desc_map,
selected_cog=self.selected_cog,
)
)
refresh_label = await self.bot.get_text(self.guild_id, "menu.refresh")
invite_label = await self.bot.get_text(self.guild_id, "menu.invite_button")
self.add_item(RefreshButton(self, refresh_label))
self.add_item(QuickCategoryButton(self, "Economy | الاقتصاد", "Economy", "💰", row=1))
self.add_item(QuickCategoryButton(self, "Music | الموسيقى", "Music", "🎵", row=1))
self.add_item(QuickCategoryButton(self, "Admin | الإدارة", "Admin", "🛡️", row=1))
self.add_item(QuickCategoryButton(self, "Utility | الأدوات", "Utility", "ℹ️", row=1))
self.add_item(QuickCategoryButton(self, "Community | المجتمع", "Community", "💡", row=2))
self.add_item(QuickCategoryButton(self, "AI | الذكاء", "AISuite", "🤖", row=2))
self.add_item(QuickCategoryButton(self, "Config | الإعداد", "Configuration", "⚙️", row=2))
if self.page > 0:
self.add_item(PageButton(self, "prev"))
if self._has_next_page(self.selected_cog):
self.add_item(PageButton(self, "next"))
if self.bot.user:
self.add_item(InviteButton(invite_label, self.bot.user.id))
def _desc_key(self, qualified_name: str) -> str:
"""Get the translation key for a command description."""
norm = qualified_name.strip().lower().replace(" ", "_")
key_mapping = {
# Music commands
"play": "menu.cmd.play",
"music_play": "menu.cmd.music_play",
"music_panel": "menu.cmd.music_panel",
"music_skip": "menu.cmd.music_skip",
"music_stop": "menu.cmd.music_stop",
"music_pause": "menu.cmd.music_pause",
"music_resume": "menu.cmd.music_resume",
"music_queue": "menu.cmd.music_queue",
"music_playlist": "menu.cmd.music_playlist",
"music_playlist_save": "menu.cmd.music_playlist_save",
"music_playlist_rename": "menu.cmd.music_playlist_rename",
"music_playlist_delete": "menu.cmd.music_playlist_delete",
"music_volume": "menu.cmd.music_volume",
"music_nowplaying": "menu.cmd.music_nowplaying",
"music_filter": "menu.cmd.music_filter",
"music_loop": "menu.cmd.music_loop",
"music_shuffle": "menu.cmd.music_shuffle",
"music_247": "menu.cmd.music_247",
"music_previous": "menu.cmd.music_previous",
"music_seek": "menu.cmd.music_seek",
"music_clear": "menu.cmd.music_clear",
"music_remove": "menu.cmd.music_remove",
"music_move": "menu.cmd.music_move",
"music_jump": "menu.cmd.music_jump",
"music_lyrics": "menu.cmd.music_lyrics",
# Admin commands
"purge": "menu.cmd.purge",
"ban": "menu.cmd.ban",
"unban": "menu.cmd.unban",
"kick": "menu.cmd.kick",
"mute": "menu.cmd.mute",
"unmute": "menu.cmd.unmute",
"warn": "menu.cmd.warn",
"warnings": "menu.cmd.warnings",
"clearwarn": "menu.cmd.clearwarn",
"slowmode": "menu.cmd.slowmode",
"lock": "menu.cmd.lock",
"unlock": "menu.cmd.unlock",
"cloneemoji": "menu.cmd.cloneemoji",
"awesomeroles": "menu.cmd.awesomeroles",
"backupserver": "menu.cmd.backupserver",
# Fun commands
"8ball": "menu.cmd.8ball",
"meme": "menu.cmd.meme",
"trivia": "menu.cmd.trivia",
"gaming_news": "menu.cmd.gaming_news",
"free_games": "menu.cmd.free_games",
"gamehub": "menu.cmd.gamehub",
"xo": "menu.cmd.xo",
"choose": "menu.cmd.choose",
"mario": "menu.cmd.mario",
"dice": "menu.cmd.dice",
"slots": "menu.cmd.slots",
"coinflip": "menu.cmd.coinflip",
"roll": "menu.cmd.roll",
# AI commands
"chat": "menu.cmd.chat",
"ask_image": "menu.cmd.ask_image",
"imagine": "menu.cmd.imagine",
"image_gen": "menu.cmd.image_gen",
"upscale": "menu.cmd.upscale",
"summarize": "menu.cmd.summarize",
"ai": "menu.cmd.ai",
"ai_chat": "menu.cmd.chat",
"ai_ask_image": "menu.cmd.ask_image",
"ai_imagine": "menu.cmd.imagine",
"ai_image_gen": "menu.cmd.image_gen",
"ai_upscale": "menu.cmd.upscale",
"ai_summarize": "menu.cmd.summarize",
"ai_debug": "menu.cmd.debug",
"ai_code_gen": "menu.cmd.code_gen",
"ai_voice": "menu.cmd.speak",
"ai_speak": "menu.cmd.speak",
"ai_tts": "menu.cmd.tts",
"ai_translate_voice": "menu.cmd.translate_voice",
"ai_model": "menu.cmd.ai_model",
"ai_channel": "menu.cmd.ai_channel",
"ai_auto": "menu.cmd.ai_auto",
"ai_setup": "menu.cmd.ai",
"ai_execute": "menu.cmd.ai_execute",
# Utility commands
"serverinfo": "menu.cmd.serverinfo",
"userinfo": "menu.cmd.userinfo",
"botstats": "menu.cmd.botstats",
"poll": "menu.cmd.poll",
"remind": "menu.cmd.remind",
"avatar": "menu.cmd.avatar",
"banner": "menu.cmd.banner",
"translate": "menu.cmd.translate",
# Config commands
"set": "menu.cmd.set",
"set_log": "menu.cmd.set_log",
"set_welcome": "menu.cmd.set_welcome",
"set_suggestions": "menu.cmd.set_suggestions",
"set_automod": "menu.cmd.set_automod",
"set_dailymessage": "menu.cmd.set_dailymessage",
"set_dailychannel": "menu.cmd.set_dailychannel",
"set_dailytime": "menu.cmd.set_dailytime",
"set_dailytitle": "menu.cmd.set_dailytitle",
"set_dailyimage": "menu.cmd.set_dailyimage",
"set_dailybutton": "menu.cmd.set_dailybutton",
"set_dailytoggle": "menu.cmd.set_dailytoggle",
"set_pollchannel": "menu.cmd.set_pollchannel",
"set_freegames": "menu.cmd.set_freegames",
"set_gamenews": "menu.cmd.set_gamenews",
"set_supportai": "menu.cmd.set_supportai",
"set_wisdom": "menu.cmd.set_wisdom",
# Language commands
"language": "menu.cmd.language",
"languages": "menu.cmd.languages",
# Menu commands
"menu": "menu.cmd.menu",
"ping": "menu.cmd.ping",
"load": "menu.cmd.load",
"unload": "menu.cmd.unload",
"reload": "menu.cmd.reload",
"sync": "menu.cmd.sync",
"shutdown": "menu.cmd.shutdown",
"bot_emojis": "menu.cmd.emoji_scan",
"emoji_scan": "menu.cmd.emoji_scan",
# Verification commands
"verify": "menu.cmd.verify",
"verifysetup": "menu.cmd.verifysetup",
"verify_config": "menu.cmd.verify",
"verify_config_panel": "menu.cmd.verify",
"verify_config_setup": "menu.cmd.verifysetup",
# Economy commands
"economy": "menu.cmd.economy",
"economy_deposit": "menu.cmd.economy_deposit",
"economy_withdraw": "menu.cmd.economy_withdraw",
"economy_gamble": "menu.cmd.economy_gamble",
"economy_rob": "menu.cmd.economy_rob",
"balance": "menu.cmd.balance",
"daily": "menu.cmd.daily",
"work": "menu.cmd.work",
"gamble": "menu.cmd.gamble",
"rob": "menu.cmd.rob",
"leaderboard": "menu.cmd.leaderboard",
"shop": "menu.cmd.shop",
"buy": "menu.cmd.buy",
"inventory": "menu.cmd.inventory",
"transfer": "menu.cmd.transfer",
# Engagement commands
"xp": "menu.cmd.xp",
"level": "menu.cmd.level",
"rank": "menu.cmd.rank",
"suggest": "menu.cmd.suggest",
"suggestion": "menu.cmd.suggestion",
"suggestion_show": "menu.cmd.suggestion_show",
"suggestion_panel": "menu.cmd.suggestion_panel",
"suggestion_voters": "menu.cmd.suggestion_voters",
"giveaway_create": "menu.cmd.giveaway_create",
"giveaway_end": "menu.cmd.giveaway_end",
"ticket_panel": "menu.cmd.ticket_panel",
# Tournament commands
"tournament": "menu.cmd.tournament",
"tournament_panel": "menu.cmd.tournament_panel",
"tournament_create": "menu.cmd.tournament_create",
"tournament_join": "menu.cmd.tournament_join",
"tournament_start": "menu.cmd.tournament_start",
"tournament_end": "menu.cmd.tournament_end",
"tournament_gamehub": "menu.cmd.tournament_gamehub",
# Gambling commands
"blackjack": "menu.cmd.blackjack",
"roulette": "menu.cmd.roulette",
"rpg": "menu.cmd.rpg",
# AI Admin commands
"ai_admin": "menu.cmd.ai_admin",
"ai_help": "menu.cmd.ai_help",
# Additional commands/groups to keep menu fully mapped
"admin": "menu.cmd.admin_panel",
"admin_panel": "menu.cmd.admin_panel",
"shield_level": "menu.cmd.shield_level",
"shield_state": "menu.cmd.shield_state",
"econ_admin": "menu.cmd.econ_admin",
"econadmin": "menu.cmd.econ_admin",
"economy_admin": "menu.cmd.econ_admin",
"giveaway": "menu.cmd.giveaway",
"ticket": "menu.cmd.ticket",
"poll_legacy": "menu.cmd.poll",
"setpollchannel": "menu.cmd.set_pollchannel",
"setsuggestionchannel": "menu.cmd.set_suggestions",
"set_freegame": "menu.cmd.set_freegames",
"setupserver": "menu.cmd.setupserver",
"organizechannels": "menu.cmd.organizechannels",
"backup_panel": "menu.cmd.backup_panel",
"system_audit": "menu.cmd.system_audit",
"wisdom_today": "menu.cmd.wisdom_today",
"freegames": "menu.cmd.free_games",
"playlists": "menu.cmd.music_playlist",
"music": "menu.cmd.music_panel",
"profile": "menu.cmd.profile",
"rps": "menu.cmd.rps",
"guess": "menu.cmd.guess",
"make_event": "menu.cmd.make_event",
"gambling_panel": "menu.cmd.gambling_panel",
"add_scam_image": "menu.cmd.add_scam_image",
"set_banner": "menu.cmd.set_banner",
"view_banner": "menu.cmd.view_banner",
"remove_banner": "menu.cmd.remove_banner",
"banner_help": "menu.cmd.banner_help",
"boardgames": "menu.cmd.boardgames",
"board_start": "menu.cmd.board_start",
"board_move": "menu.cmd.board_move",
"board_forfeit": "menu.cmd.board_forfeit",
"games_panel": "menu.cmd.games_panel",
"chess": "menu.cmd.chess",
"checkers": "menu.cmd.checkers",
"connect4": "menu.cmd.connect4",
"othello": "menu.cmd.othello",
"start": "menu.cmd.menu",
"command_fill": "menu.cmd.menu",
"tournament_lb": "menu.cmd.tournament_lb",
"aisetup": "menu.cmd.ai",
}
return key_mapping.get(norm, f"menu.cmd.{norm}")
async def _format_commands(self, guild_id: int | None, cmds: list[commands.Command], max_lines: int = 20, max_chars: int = 980) -> str:
"""Format commands list with beautiful decorations."""
lines: list[str] = []
used = 0
for idx, cmd in enumerate(sorted(cmds, key=lambda c: c.qualified_name), start=1):
if cmd.hidden:
continue
desc_key = self._desc_key(cmd.qualified_name)
translated_desc = await self.bot.get_text(guild_id, desc_key)
json_key = ""
if translated_desc == desc_key:
cog_key = (cmd.cog_name or "").lower()
json_key = f"commands.{cog_key}.{cmd.name}_desc" if cog_key else ""
if json_key:
translated_desc = await self.bot.get_text(guild_id, json_key)
if translated_desc == desc_key or (json_key and translated_desc == json_key):
fallback_desc = (cmd.description or cmd.help or "").strip()
desc = fallback_desc if fallback_desc else "-"
else:
desc = translated_desc
is_hybrid = isinstance(cmd, commands.HybridCommand)
invoke = f"`/{cmd.qualified_name}`" if is_hybrid else f"`!{cmd.qualified_name}`"
line = f"- {invoke} - {desc[:78]}"
projected = used + len(line) + (1 if lines else 0)
if projected > max_chars:
break
lines.append(line)
used = projected
if len(lines) >= max_lines:
break
if len(lines) < len([c for c in cmds if not c.hidden]):
suffix = "\n- ------------"
if used + len(suffix) <= max_chars:
lines.append("- ------------")
return "\n".join(lines)
def _visible_commands_page(self, selected_cog: str) -> tuple[list[commands.Command], int]:
commands_list = sorted(self._selected_commands(selected_cog), key=lambda c: c.qualified_name)
if not commands_list:
return [], 1
total_pages = max(1, (len(commands_list) + self.page_size - 1) // self.page_size)
if self.page >= total_pages:
self.page = total_pages - 1
start = self.page * self.page_size
return commands_list[start:start + self.page_size], total_pages
def _has_next_page(self, selected_cog: str) -> bool:
commands_list = self._selected_commands(selected_cog)
total_pages = max(1, (len(commands_list) + self.page_size - 1) // self.page_size)
return self.page + 1 < total_pages
def _category_stats(self) -> list[tuple[str, int]]:
"""Get category statistics."""
stats: list[tuple[str, int]] = []
for name, cog in self.bot.cogs.items():
cog_commands = [c for c in (cog.get_commands() if cog else []) if not c.hidden]
if cog_commands:
stats.append((name, len(cog_commands)))
return sorted(stats, key=lambda pair: (-pair[1], pair[0]))
async def _category_value(self, stats: list[tuple[str, int]], guild_id: int | None) -> str:
"""Format category statistics with beautiful decorations."""
lang = await self.bot.get_guild_language(guild_id)
if not stats:
return await self.bot.get_text(guild_id, "menu.none")
lines: list[str] = []
for index, (name, count) in enumerate(stats[:8], start=1):
badge = {1: "🥇", 2: "🥈", 3: "🥉"}.get(index, f"#{index}")
emoji = _CATEGORY_EMOJIS.get(name, "📂")
if lang == "ar":
lines.append(f"{badge} {emoji} **{name}** — `{count}` أمر")
else:
lines.append(f"{badge} {emoji} **{name}** — `{count}` cmds")
if len(stats) > 8:
lines.append("✦ ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈")
return "\n".join(lines)
async def _stats_text(
self,
guild_id: int | None,
total_commands: int,
category_count: int,
selection_label: str,
visible_commands: int,
) -> str:
"""Format statistics with beautiful decorations."""
lang = await self.bot.get_guild_language(guild_id)
if lang == "ar":
pieces = [
f"📊 **إجمالي الأوامر:** `{total_commands}`",
f"📂 **الفئات:** `{category_count}`",
f"✨ **المحدد:** {selection_label} (`{visible_commands}` أمر)",
f"📈 {progress_bar(visible_commands, total_commands, size=14)}",
]
else:
pieces = [
f"📊 **Total Commands:** `{total_commands}`",
f"📂 **Categories:** `{category_count}`",
f"✨ **Selected:** {selection_label} (`{visible_commands}` cmds)",
f"📈 {progress_bar(visible_commands, total_commands, size=14)}",
]
return "\n".join(pieces)
async def _selected_label(self, guild_id: int | None, selected_cog: str) -> str:
"""Get the label for the selected category."""
return _bilingual_category(selected_cog)
def _selected_commands(self, selected_cog: str) -> list[commands.Command]:
"""Get commands for the selected category."""
def _flatten(cmds: list[commands.Command]) -> list[commands.Command]:
collected: list[commands.Command] = []
for command in cmds:
if command.hidden:
continue
collected.append(command)
if isinstance(command, commands.Group):
collected.extend([sub for sub in command.walk_commands() if not sub.hidden])
unique: dict[str, commands.Command] = {c.qualified_name: c for c in collected}
return list(unique.values())
if selected_cog == "__all__":
return _flatten(list(self.bot.commands))
if selected_cog == "__ai__":
ai_cog = self.bot.get_cog("AISuite")
return _flatten(list(ai_cog.get_commands() if ai_cog else []))
cog = self.bot.get_cog(selected_cog)
return _flatten(list(cog.get_commands() if cog else []))
def _top_level_slash_count(self) -> int:
"""Count top-level slash-registered hybrid commands/groups (Discord limit: 100)."""
total = 0
for cmd in self.bot.commands:
if not isinstance(cmd, commands.HybridCommand):
continue
if getattr(cmd, "with_app_command", True):
total += 1
return total
async def build_embed(self, guild_id: int | None, selected_cog: str) -> discord.Embed:
"""Build the beautiful embed for the menu."""
color = discord.Color(0x2B2D31)
hub_welcome = await self.bot.get_text(guild_id, "menu.hub_welcome")
hub_explore = await self.bot.get_text(guild_id, "menu.hub_explore")
hub_usage = await self.bot.get_text(guild_id, "menu.hub_usage")
stats_title = await self.bot.get_text(guild_id, "menu.stats_heading")
categories_title = await self.bot.get_text(guild_id, "menu.categories_heading")
tips_title = await self.bot.get_text(guild_id, "menu.tips_heading")
quick_title = await self.bot.get_text(guild_id, "menu.quick_heading")
updates_title = await self.bot.get_text(guild_id, "menu.updates_heading")
title = "BOT- AI System"
desc = f"{hub_welcome}\n{hub_explore}\n{hub_usage}"
tips = (
f"? {await self.bot.get_text(guild_id, 'menu.tip_line_1')}\n"
f"? {await self.bot.get_text(guild_id, 'menu.tip_line_2')}\n"
f"? {await self.bot.get_text(guild_id, 'menu.tip_line_3')}"
)
quick = (
f"- `/music_panel` - {await self.bot.get_text(guild_id, 'menu.quick_music')}\n"
f"- `/gamehub` - {await self.bot.get_text(guild_id, 'menu.quick_gamehub')}\n"
f"- `/tournament panel` - {await self.bot.get_text(guild_id, 'menu.quick_tournament')}\n"
f"- `/economy` - {await self.bot.get_text(guild_id, 'menu.quick_economy')}"
)
quick_ai = await self.bot.get_text(guild_id, 'menu.quick_ai')
updates = (
f"- `/admin emoji clone` - {await self.bot.get_text(guild_id, 'menu.update_cloneemoji')}\n"
f"- `/admin shield add_image` - {await self.bot.get_text(guild_id, 'menu.update_shield_image')}\n"
f"- `/poll create` - {await self.bot.get_text(guild_id, 'menu.update_poll_group')}\n"
f"- `/economy deposit` - {await self.bot.get_text(guild_id, 'menu.update_economy_group')}\n"
f"- `/ai execute` - {quick_ai if quick_ai != 'menu.quick_ai' else 'AI admin request'}"
)
footer = "Powered by BOT- AI Suite"
top_divider = panel_divider("cyan")
mid_divider = panel_divider("purple")
embed = ImperialMotaz.craft_embed(
title=title,
description=f"{top_divider}\n{desc}\n{mid_divider}",
color=color,
footer=footer,
)
if self.bot.user:
embed.set_thumbnail(url=self.bot.user.display_avatar.url)
category_stats = self._category_stats()
selected_label = await self._selected_label(guild_id, selected_cog)
visible_cmds, total_pages = self._visible_commands_page(selected_cog)
content = await self._format_commands(guild_id, visible_cmds, max_lines=18)
total_guilds = len(self.bot.guilds)
total_members = sum((g.member_count or 0) for g in self.bot.guilds)
latency_ms = f"{round(self.bot.latency * 1000)}ms"
slash_used = self._top_level_slash_count()
slash_budget = f"{slash_used}/100"
stats_grid = quick_stats_grid(
[
("Guilds", str(total_guilds), "G"),
("Members", f"{total_members:,}", "M"),
("Latency", latency_ms, "L"),
("Slash", slash_budget, "S"),
],
columns=2,
)
embed.add_field(name=stats_title, value=stats_grid, inline=False)
embed.add_field(
name=categories_title,
value=await self._category_value(category_stats, guild_id),
inline=True,
)
emoji = _CATEGORY_EMOJIS.get(selected_cog, "C")
none_text = await self.bot.get_text(guild_id, "menu.none")
embed.add_field(
name=f"{emoji} {selected_label}",
value=content or none_text,
inline=False,
)
embed.add_field(name=quick_title, value=quick, inline=True)
embed.add_field(name=tips_title, value=tips, inline=True)
embed.add_field(name=updates_title, value=updates, inline=False)
# ═══ What's New section ═══
whats_new = await self.bot.get_text(guild_id, "menu.whats_new_heading")
whats_new_content = await self.bot.get_text(guild_id, "menu.whats_new_content")
if whats_new and whats_new != "menu.whats_new_heading" and whats_new_content and whats_new_content != "menu.whats_new_content":
embed.add_field(name=whats_new, value=whats_new_content[:900], inline=False)
embed.set_footer(text=footer)
embed.description = f"{embed.description}\n\nPage {self.page + 1}/{total_pages}"
return embed
class MainMenuView(CommandsMenuView):
"""Named alias for persistent registration."""
class InviteButton(discord.ui.Button):
"""Beautiful invite button."""
def __init__(self, label: str, client_id: int) -> None:
url = f"https://discord.com/api/oauth2/authorize?client_id={client_id}&permissions=0&scope=applications.commands%20bot"
super().__init__(
label=label,
style=discord.ButtonStyle.link,
url=url,
emoji=ui("link")
)
class RefreshButton(discord.ui.Button):
"""Beautiful refresh button."""
def __init__(self, parent: CommandsMenuView, label: str) -> None:
super().__init__(
label=label,
emoji=ui("refresh"),
style=discord.ButtonStyle.secondary
)
self.parent_view = parent
async def callback(self, interaction: discord.Interaction) -> None:
await interaction.response.defer()
guild_id = interaction.guild.id if interaction.guild else None
self.label = await self.parent_view.bot.get_text(guild_id, "menu.refresh")
await self.parent_view.setup_items()
embed = await self.parent_view.build_embed(guild_id, "__all__")
await interaction.followup.edit_message(interaction.message.id, embed=embed, view=self.parent_view)
class QuickCategoryButton(discord.ui.Button):
def __init__(self, parent: CommandsMenuView, label: str, category: str, emoji: str, *, row: int = 1) -> None:
super().__init__(
label=label,
emoji=emoji,
style=discord.ButtonStyle.secondary,
custom_id=f"menu:quick:{label.lower()}",
row=row,
)
self.parent_view = parent
self.category = category
async def callback(self, interaction: discord.Interaction) -> None:
await interaction.response.defer()
self.parent_view.selected_cog = self.category
self.parent_view.page = 0
await self.parent_view.setup_items()
embed = await self.parent_view.build_embed(interaction.guild.id if interaction.guild else None, self.category)
await interaction.followup.edit_message(interaction.message.id, embed=embed, view=self.parent_view)
class PageButton(discord.ui.Button):
def __init__(self, parent: CommandsMenuView, direction: str) -> None:
self.parent_view = parent
self.direction = direction
label = "Previous" if direction == "prev" else "Next"
emoji = "⬅️" if direction == "prev" else "➡️"
super().__init__(label=label, emoji=emoji, style=discord.ButtonStyle.secondary)
async def callback(self, interaction: discord.Interaction) -> None:
await interaction.response.defer()
if self.direction == "prev":
self.parent_view.page = max(0, self.parent_view.page - 1)
else:
if self.parent_view._has_next_page(self.parent_view.selected_cog):
self.parent_view.page += 1
await self.parent_view.setup_items()
embed = await self.parent_view.build_embed(interaction.guild.id if interaction.guild else None, self.parent_view.selected_cog)
await interaction.followup.edit_message(interaction.message.id, embed=embed, view=self.parent_view)
class Menu(commands.Cog):
"""Interactive command menu with beautiful panels and multi-language support."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
async def cog_load(self) -> None:
self.bot.add_view(CommandsMenuView(self.bot))
@commands.hybrid_command(name="menu", description=get_cmd_desc("commands.tools.menu_desc"))
async def menu(self, ctx: commands.Context) -> None:
"""Display the beautiful command menu."""
if ctx.interaction and not ctx.interaction.response.is_done():
await ctx.interaction.response.defer()
guild_id = ctx.guild.id if ctx.guild else None
view = CommandsMenuView(self.bot, guild_id)
await view.setup_items()
embed = await view.build_embed(guild_id, "__all__")
if ctx.interaction:
await ctx.interaction.followup.send(embed=embed, view=view)
else:
await ctx.reply(embed=embed, view=view)
@commands.hybrid_command(name="start", description=get_cmd_desc("commands.tools.start_desc"), with_app_command=False)
async def start_menu(self, ctx: commands.Context) -> None:
await self.menu(ctx)
async def cog_command_error(self, ctx: commands.Context, error: Exception) -> None:
embed = ImperialMotaz.craft_embed(
title="⚠️ Command Error | خطأ في الأمر",
description=f"「 {str(error)[:1000]} 」",
color=discord.Color(0x2B2D31),
footer="🏮 Powered by BOT- AI Suite 🏮",
)
try:
await ctx.reply(embed=embed)
except Exception:
if ctx.channel:
await ctx.channel.send(embed=embed)
async def setup(bot: commands.Bot) -> None:
await bot.add_cog(Menu(bot))