""" 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))