""" Fun cog: Games, trivia, memes, and entertainment commands. Enhanced with rich emoji decorations, beautiful formatting, and multi-language support. """ import html import random import aiohttp import discord from discord.ext import commands from bot.cogs.ai_suite import ImperialMotaz from bot.theme import ( fancy_header, NEON_CYAN, NEON_PINK, NEON_PURPLE, NEON_LIME, NEON_ORANGE, NEON_BLUE, panel_divider, gaming_embed, success_embed, error_embed, info_embed, double_line, triple_line, shimmer, pick_neon_color ) from bot.i18n import get_cmd_desc from bot.emojis import ( ui, E_GAMEHUB, E_ARROW_PINK, E_ARROW_GREEN, E_ARROW_PURPLE, E_STAR, E_TROPHY, E_FIRE, E_SPARKLE, E_DIZZY, E_BOOM, E_ZAP, E_DIAMOND, E_CROWN, E_GEM ) from bot.utils.shared import fetch_gaming_news, fetch_free_games, store_icon, FreeGameClaimView TRIVIA_BANK: dict[str, dict[str, list[dict[str, object]]]] = { "en": { "gaming": [ {"q": "Which studio created The Witcher 3?", "c": ["CD Projekt Red", "Rockstar", "Ubisoft", "Bethesda"], "a": 0}, {"q": "What is the default block in Minecraft?", "c": ["Stone", "Dirt", "Grass Block", "Sand"], "a": 2}, {"q": "In which game would you find Master Chief?", "c": ["Halo", "Destiny", "Call of Duty", "Gears of War"], "a": 0}, {"q": "What year was Fortnite released?", "c": ["2015", "2017", "2018", "2019"], "a": 1}, {"q": "Which company makes the PlayStation?", "c": ["Microsoft", "Nintendo", "Sony", "Sega"], "a": 2}, ], "movies": [ {"q": "Who directed Inception?", "c": ["Christopher Nolan", "James Cameron", "Denis Villeneuve", "Ridley Scott"], "a": 0}, {"q": "In the MCU, who is Peter Parker?", "c": ["Iron Man", "Spider-Man", "Doctor Strange", "Hawkeye"], "a": 1}, {"q": "What is the highest-grossing film of all time?", "c": ["Avengers: Endgame", "Avatar", "Titanic", "Star Wars"], "a": 1}, {"q": "Who played the Joker in The Dark Knight?", "c": ["Joaquin Phoenix", "Jack Nicholson", "Heath Ledger", "Jared Leto"], "a": 2}, ], "series": [ {"q": "Which series features Walter White?", "c": ["Narcos", "Breaking Bad", "Dark", "Sherlock"], "a": 1}, {"q": "In Game of Thrones, what is Jon Snow's house at first?", "c": ["Lannister", "Baratheon", "Stark", "Targaryen"], "a": 2}, {"q": "What is the name of the coffee shop in Friends?", "c": ["Central Park", "Central Perk", "Coffee House", "The Brew"], "a": 1}, {"q": "How many seasons does Stranger Things have?", "c": ["3", "4", "5", "6"], "a": 2}, ], }, "ar": { "gaming": [ {"q": "أي شركة طورت لعبة The Witcher 3؟", "c": ["CD Projekt Red", "Rockstar", "Ubisoft", "Bethesda"], "a": 0}, {"q": "ما هو البلوك الافتراضي في Minecraft؟", "c": ["Stone", "Dirt", "Grass Block", "Sand"], "a": 2}, {"q": "في أي لعبة تجد Master Chief؟", "c": ["Halo", "Destiny", "Call of Duty", "Gears of War"], "a": 0}, {"q": "متى صدرت Fortnite؟", "c": ["2015", "2017", "2018", "2019"], "a": 1}, ], "movies": [ {"q": "من مخرج فيلم Inception؟", "c": ["كريستوفر نولان", "جيمس كاميرون", "دينيس فيلنوف", "ريدلي سكوت"], "a": 0}, {"q": "في عالم مارفل، بيتر باركر هو؟", "c": ["Iron Man", "Spider-Man", "Doctor Strange", "Hawkeye"], "a": 1}, {"q": "ما هو أعلى فيلم إيرادات في التاريخ؟", "c": ["Avengers: Endgame", "Avatar", "Titanic", "Star Wars"], "a": 1}, ], "series": [ {"q": "أي مسلسل يظهر فيه Walter White؟", "c": ["Narcos", "Breaking Bad", "Dark", "Sherlock"], "a": 1}, {"q": "في Game of Thrones، جون سنو كان ضمن أي بيت بالبداية؟", "c": ["Lannister", "Baratheon", "Stark", "Targaryen"], "a": 2}, ], }, } class TriviaAnswerButton(discord.ui.Button["TriviaView"]): def __init__(self, label: str, index: int) -> None: super().__init__(label=label[:80], style=discord.ButtonStyle.secondary) self.index = index async def callback(self, interaction: discord.Interaction) -> None: await self.view.on_answer(interaction, self.index) class FreeGameClaimView(discord.ui.View): def __init__(self, url: str) -> None: super().__init__(timeout=None) self.add_item( discord.ui.Button( label=f"{ui('gift')} Claim Your Prize {ui('gift')}", style=discord.ButtonStyle.link, url=url, ) ) class TriviaView(discord.ui.View): def __init__(self, owner_id: int, correct_index: int, lang: str, cog: "Fun", guild_id: int | None) -> None: super().__init__(timeout=None) self.owner_id = owner_id self.correct_index = correct_index self.lang = lang self.answered = False self.cog = cog self.guild_id = guild_id async def on_answer(self, interaction: discord.Interaction, picked_index: int) -> None: if interaction.user.id != self.owner_id: msg = "❌ هذا السؤال لصاحب الأمر فقط." if self.lang == "ar" else "❌ This question is for the command author only." await interaction.response.send_message(msg, ephemeral=True) return if self.answered: msg = "⚠️ تمت الإجابة بالفعل." if self.lang == "ar" else "⚠️ Already answered." await interaction.response.send_message(msg, ephemeral=True) return self.answered = True is_correct = picked_index == self.correct_index for idx, child in enumerate(self.children): if isinstance(child, discord.ui.Button): child.disabled = True if idx == self.correct_index: child.style = discord.ButtonStyle.success elif idx == picked_index and not is_correct: child.style = discord.ButtonStyle.danger if is_correct: msg = f"✅ إجابة صحيحة! {E_STAR}" if self.lang == "ar" else f"✅ Correct answer! {E_STAR}" else: msg = "❌ إجابة خاطئة." if self.lang == "ar" else "❌ Wrong answer." await interaction.response.edit_message(view=self) if is_correct and interaction.guild and self.guild_id: reward = random.randint(20, 45) await self.cog.bot.db.execute( "INSERT INTO user_balance(guild_id, user_id, wallet, bank) VALUES (?, ?, ?, 0) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET wallet = wallet + excluded.wallet", self.guild_id, interaction.user.id, reward, ) if self.lang == "ar": msg += f" 💰 **+{reward}** عملة!" else: msg += f" 💰 **+{reward}** coins!" await interaction.followup.send(msg, ephemeral=True) GAME_HUB_GAMES = [ {"id": "chess", "name": "Chess", "emoji": "♟️", "description": "Strategic board game"}, {"id": "checkers", "name": "Checkers", "emoji": "🔴", "description": "Classic draughts game"}, {"id": "connect4", "name": "Connect 4", "emoji": "🔴", "description": "Drop and connect"}, {"id": "othello", "name": "Othello", "emoji": "⚪", "description": "Reversi strategy"}, {"id": "tictactoe", "name": "TicTacToe", "emoji": "❎", "description": "X and O battle"}, {"id": "rps", "name": "Rock Paper Scissors", "emoji": "✂️", "description": "Quick decision game"}, {"id": "trivia", "name": "Trivia Challenge", "emoji": "🧠", "description": "Knowledge contest"}, {"id": "guess", "name": "Number Guess", "emoji": "🔢", "description": "Guess 1-10"}, ] class GameHubSelect(discord.ui.Select): def __init__(self, cog: "Fun", invitee_id: int | None, lang: str = "en") -> None: self.cog = cog self.invitee_id = invitee_id self.lang = lang options = [] for game in GAME_HUB_GAMES: if lang == "ar": desc = game["description"] else: desc = game["description"] options.append( discord.SelectOption( label=game["name"], value=game["id"], description=desc, emoji=game["emoji"] ) ) super().__init__( placeholder="🎮 Choose a game", min_values=1, max_values=1, options=options, custom_id="gamehub_select", ) async def callback(self, interaction: discord.Interaction) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild = interaction.guild game_id = self.values[0] invitee = guild.get_member(self.invitee_id) if self.invitee_id else None guild_id = guild.id lang = await self.cog.bot.get_guild_language(guild_id) category = discord.utils.get(guild.categories, name="🕹️ Game Rooms") if category is None: category = await guild.create_category("🕹️ Game Rooms", reason="GameHub temporary rooms") overwrites = { guild.default_role: discord.PermissionOverwrite(view_channel=False), interaction.user: discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True), } if invitee: overwrites[invitee] = discord.PermissionOverwrite(view_channel=True, send_messages=True, read_message_history=True) if guild.me: overwrites[guild.me] = discord.PermissionOverwrite(view_channel=True, send_messages=True, manage_channels=True) room = await guild.create_text_channel( name=f"game-{game_id}-{interaction.user.name[:8].lower()}", category=category, overwrites=overwrites, reason=f"GameHub room for {interaction.user}", ) board_cog = self.cog.bot.get_cog("BoardGames") try: if game_id == "tictactoe": view = TicTacToeView(interaction.user, invitee) first = interaction.user.mention second = invitee.mention if invitee else "🤖 Bot" if lang == "ar": msg = await room.send(f"❎ تيك تاك تو: {first} (X) ضد {second} (O)\n🎮 الدور: {interaction.user.mention}", view=view) else: msg = await room.send(f"❎ TicTacToe: {first} (X) vs {second} (O)\n🎮 Turn: {interaction.user.mention}", view=view) if invitee is None: await view._maybe_bot(msg) elif game_id == "trivia": # Start trivia game item = random.choice(TRIVIA_BANK.get(lang, TRIVIA_BANK["en"])["gaming"]) view = TriviaView(owner_id=interaction.user.id, correct_index=int(item["a"]), lang=lang, cog=self.cog, guild_id=guild_id) for idx, choice in enumerate(item["c"]): view.add_item(TriviaAnswerButton(str(choice), idx)) if lang == "ar": q_title = "🧠 سؤال تراڤيا!" else: q_title = "🧠 Trivia Time!" embed = discord.Embed( title=q_title, description=f"{panel_divider('blue')}\n❓ {str(item['q'])}\n{panel_divider('blue')}", color=NEON_CYAN, ) await room.send(embed=embed, view=view) elif game_id == "rps": # RPS game in channel if lang == "ar": await room.send(f"✂️ حجر ورقة مقص!\nاستخدم `rps rock/paper/scissors` للعب!") else: await room.send(f"✂️ Rock Paper Scissors!\nUse `rps rock/paper/scissors` to play!") elif game_id == "guess": # Number guessing game secret = random.randint(1, 10) if lang == "ar": await room.send(f"🔢 خمن الرقم من 1 إلى 10!\nاستخدم `/guess <رقم>` للعب!") else: await room.send(f"🔢 Guess a number from 1 to 10!\nUse `/guess ` to play!") elif board_cog and game_id in {"chess", "checkers", "connect4", "othello"}: fake_ctx = type("_Ctx", (), {"guild": guild, "channel": room, "author": interaction.user, "reply": room.send}) opponent = invitee ok = await board_cog.start_game_session(fake_ctx, game_id, opponent) if not ok: if lang == "ar": await room.send("⚠️ تعذر تشغيل اللعبة. اختر لعبة أخرى.") else: await room.send("⚠️ Could not initialize selected board game. Choose another one.") else: game_name = next((g["name"] for g in GAME_HUB_GAMES if g["id"] == game_id), game_id.title()) if lang == "ar": await room.send(f"🎮 **{game_name}** الغرفة جاهزة. العب الآن! {E_FIRE}") else: await room.send(f"🎮 **{game_name}** room is ready. Play now! {E_FIRE}") except Exception: if lang == "ar": await room.send("⚠️ تعذر تشغيل هذه اللعبة. جرب إنشاء غرفة أخرى.") else: await room.send("⚠️ Could not start this game. Try creating another room.") if lang == "ar": invite_text = ( f"🎮 **{game_id.title()}** الغرفة جاهزة!\n" f"{panel_divider('purple')}\n" f"👤 **المضيف:** {interaction.user.mention}" ) close_text = "🔒 أغلق الغرفة عند الانتهاء 👇" else: invite_text = ( f"🎮 **{game_id.title()}** Room Ready!\n" f"{panel_divider('purple')}\n" f"👤 **Host:** {interaction.user.mention}" ) close_text = "🔒 Close the room when done 👇" if invitee: if lang == "ar": invite_text += f"\n🎯 **مدعو:** {invitee.mention}" else: invite_text += f"\n🎯 **Invited:** {invitee.mention}" invite_text += f"\n\n{close_text}" await room.send(invite_text, view=RoomControlView(interaction.user.id)) if lang == "ar": response_msg = f"✅ تم إنشاء الغرفة: {room.mention} 🎮" if invitee: response_msg += f" | تمت دعوة {invitee.mention}" else: response_msg = f"✅ Room created: {room.mention} 🎮" if invitee: response_msg += f" | Invited {invitee.mention}" await interaction.response.send_message(response_msg, ephemeral=True) class GameHubView(discord.ui.View): def __init__(self, cog: "Fun", invitee_id: int | None = None, lang: str = "en") -> None: super().__init__(timeout=None) self.add_item(GameHubSelect(cog, invitee_id, lang)) class RoomControlView(discord.ui.View): def __init__(self, owner_id: int) -> None: super().__init__(timeout=None) self.owner_id = owner_id @discord.ui.button(label="End Game & Close Room", style=discord.ButtonStyle.danger, emoji=ui("lock"), custom_id="room_close") async def close_room(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not isinstance(interaction.channel, discord.TextChannel): await interaction.response.send_message("Text channel only.", ephemeral=True) return if interaction.user.id != self.owner_id and not interaction.user.guild_permissions.manage_channels: await interaction.response.send_message("❌ Only room owner or admins can close this room.", ephemeral=True) return await interaction.response.send_message("🔒 Closing room...", ephemeral=True) try: await interaction.channel.delete(reason=f"Game room closed by {interaction.user}") except Exception: pass @discord.ui.button(label="Invite Friend", style=discord.ButtonStyle.success, emoji=ui("plus"), custom_id="room_invite") async def invite_friend(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if interaction.user.id != self.owner_id: await interaction.response.send_message("❌ Only room owner can invite.", ephemeral=True) return await interaction.response.send_message("➕ Use `/gamehub @user` to invite someone to a new game room!", ephemeral=True) class TicTacToeButton(discord.ui.Button["TicTacToeView"]): def __init__(self, index: int) -> None: super().__init__(label=" ", style=discord.ButtonStyle.secondary, row=index // 3) self.index = index async def callback(self, interaction: discord.Interaction) -> None: if self.view: await self.view.play(interaction, self.index) class TicTacToeView(discord.ui.View): def __init__(self, player_x: discord.Member, player_o: discord.Member | None) -> None: super().__init__(timeout=None) self.player_x = player_x self.player_o = player_o self.turn_x = True self.board = ["" for _ in range(9)] for i in range(9): self.add_item(TicTacToeButton(i)) def _check(self, mark: str) -> bool: lines = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)] return any(self.board[a]==self.board[b]==self.board[c]==mark for a,b,c in lines) def _free(self) -> list[int]: return [i for i,v in enumerate(self.board) if not v] async def _maybe_bot(self, message: discord.Message) -> None: if self.player_o is not None or not self._free() or not self.turn_x: return move = random.choice(self._free()) self.board[move] = "O" btn = self.children[move] if isinstance(btn, discord.ui.Button): btn.label = "O" btn.style = discord.ButtonStyle.danger btn.disabled = True self.turn_x = True if self._check("O"): for child in self.children: if isinstance(child, discord.ui.Button): child.disabled = True await message.edit(content=f"🤖 Bot won TicTacToe! {E_BOOM}", view=self) return if not self._free(): await message.edit(content="🤝 TicTacToe draw!", view=self) return await message.edit(content=f"❎ Turn: {self.player_x.mention}", view=self) async def play(self, interaction: discord.Interaction, index: int) -> None: if self.board[index]: await interaction.response.send_message("This cell is already used.", ephemeral=True) return current = self.player_x if self.turn_x else self.player_o if self.turn_x and interaction.user.id != self.player_x.id: await interaction.response.send_message("It is X player's turn.", ephemeral=True) return if not self.turn_x and self.player_o and interaction.user.id != self.player_o.id: await interaction.response.send_message("It is O player's turn.", ephemeral=True) return mark = "X" if self.turn_x else "O" self.board[index] = mark btn = self.children[index] if isinstance(btn, discord.ui.Button): btn.label = mark btn.style = discord.ButtonStyle.success if mark == "X" else discord.ButtonStyle.danger btn.disabled = True if self._check(mark): for child in self.children: if isinstance(child, discord.ui.Button): child.disabled = True winner = interaction.user.mention if self.player_o else (self.player_x.mention if mark == "X" else "🤖 Bot") await interaction.response.edit_message(content=f"🏆 TicTacToe Winner: {winner}! {E_TROPHY}", view=self) return if not self._free(): await interaction.response.edit_message(content="🤝 TicTacToe draw!", view=self) return self.turn_x = not self.turn_x nxt = self.player_x.mention if self.turn_x else (self.player_o.mention if self.player_o else "🤖 Bot") await interaction.response.edit_message(content=f"🎮 Turn: {nxt}", view=self) if not self.turn_x and self.player_o is None and interaction.message: await self._maybe_bot(interaction.message) class Fun(commands.Cog): """Games and entertainment 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(GameHubView(self)) async def _fetch_trivia_api(self) -> dict[str, object] | None: url = "https://opentdb.com/api.php?amount=50" async with aiohttp.ClientSession() as session: async with session.get(url, timeout=15) as response: data = await response.json(content_type=None) results = data.get("results") if isinstance(data, dict) else None if not isinstance(results, list) or not results: return None item = random.choice(results) question = html.unescape(str(item.get("question", "")).strip()) correct = html.unescape(str(item.get("correct_answer", "")).strip()) wrong = [html.unescape(str(c).strip()) for c in item.get("incorrect_answers", []) if str(c).strip()] if not question or not correct or not wrong: return None choices = wrong[:] choices.append(correct) random.shuffle(choices) answer_index = choices.index(correct) return {"q": question, "c": choices[:4], "a": answer_index} @commands.hybrid_command(name="8ball", description=get_cmd_desc("commands.tools.8ball_desc")) async def eightball(self, ctx: commands.Context, *, question: str) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) if lang == "ar": answers = [ "نعم! ✅", "لا ❌", "ربما 🤔", "بالتأكيد! 💯", "ليس الآن ⏰", "اسأل لاحقاً 🔮", "100% نعم! 🔥", "غير واضح 🌫️" ] embed = discord.Embed( title="🎱 الكرة السحرية", description=( f"{panel_divider('pink')}\n" f"❓ **السؤال:** {question}\n" f"🎱 **الجواب:** {random.choice(answers)}\n" f"{panel_divider('pink')}" ), color=NEON_PINK, ) else: answers = [ "Yes! ✅", "No ❌", "Maybe 🤔", "Definitely! 💯", "Not now ⏰", "Ask later 🔮", "100% Yes! 🔥", "Unclear 🌫️" ] embed = discord.Embed( title="🎱 Magic 8 Ball", description=( f"{panel_divider('pink')}\n" f"❓ **Question:** {question}\n" f"🎱 **Answer:** {random.choice(answers)}\n" f"{panel_divider('pink')}" ), color=NEON_PINK, ) await ctx.reply(embed=embed) @commands.hybrid_command(name="meme", description=get_cmd_desc("commands.tools.meme_desc")) async def meme(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) url = "https://meme-api.com/gimme" try: async with aiohttp.ClientSession() as session: async with session.get(url, timeout=10) as response: data = await response.json() except Exception: if lang == "ar": await ctx.reply("⚠️ تعذر تحميل الميم الآن. جرب مرة أخرى! 🔄") else: await ctx.reply("⚠️ Could not load meme right now. Try again in a minute! 🔄") return embed = discord.Embed( title=f"😂 {data.get('title', 'Random Meme')}", color=NEON_CYAN, url=data.get("postLink") ) if data.get("url"): embed.set_image(url=data["url"]) embed.set_footer(text=f"👍 {data.get('ups', 0)} | r/{data.get('subreddit', 'meme')}") await ctx.reply(embed=embed) @commands.hybrid_command(name="trivia", description=get_cmd_desc("commands.tools.trivia_desc"), hidden=True, with_app_command=False) async def trivia(self, ctx: commands.Context, category: str = "", source: str = "local") -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) normalized_source = source.strip().lower() or "local" if normalized_source not in {"local", "api"}: if lang == "ar": await ctx.reply("❌ المصدر يجب أن يكون `local` أو `api`") else: await ctx.reply("❌ Source must be `local` or `api`") return normalized = category.strip().lower() item: dict[str, object] | None = None if lang == "ar": topic_text = "الألعاب • الأفلام • المسلسلات" else: topic_text = "Gaming • Movies • Series" if normalized_source == "api": try: item = await self._fetch_trivia_api() topic_text = "OpenTDB (50 questions pool)" except Exception: item = None if item is None: aliases = { "gaming": "gaming", "games": "gaming", "game": "gaming", "قيمز": "gaming", "العاب": "gaming", "movies": "movies", "movie": "movies", "افلام": "movies", "أفلام": "movies", "series": "series", "show": "series", "shows": "series", "مسلسلات": "series", } normalized = aliases.get(normalized, normalized) if normalized else random.choice(["gaming", "movies", "series"]) if normalized not in {"gaming", "movies", "series"}: if lang == "ar": await ctx.reply("❌ قسم غير مدعوم. استخدم: gaming أو movies أو series") else: await ctx.reply("❌ Unsupported category. Use: gaming, movies, or series") return # Get trivia from appropriate language bank trivia_bank = TRIVIA_BANK.get(lang, TRIVIA_BANK["en"]) if normalized not in trivia_bank: trivia_bank = TRIVIA_BANK["en"] item = random.choice(trivia_bank[normalized]) view = TriviaView(owner_id=ctx.author.id, correct_index=int(item["a"]), lang=lang, cog=self, guild_id=guild_id) for idx, choice in enumerate(item["c"]): view.add_item(TriviaAnswerButton(str(choice), idx)) if lang == "ar": title = "🧠 سؤال تراڤيا!" cat_label = "📁 القسم" topics_label = "📚 المواضيع" else: title = "🧠 Trivia Time!" cat_label = "📁 Category" topics_label = "📚 Topics" embed = discord.Embed( title=title, description=f"{panel_divider('blue')}\n❓ {str(item['q'])}\n{panel_divider('blue')}", color=NEON_CYAN, ) cat_value = normalized.title() if normalized else ("General" if normalized_source == "api" else "Random") embed.add_field(name=cat_label, value=cat_value, inline=True) embed.add_field(name=topics_label, value=topic_text, inline=True) await ctx.reply(embed=embed, view=view) async def _fetch_gaming_news(self) -> list[dict[str, str]]: return await fetch_gaming_news() async def _fetch_free_games(self) -> list[dict[str, str]]: return await fetch_free_games() @staticmethod def _store_icon(platform: str) -> str: return store_icon(platform) def _free_game_embed(self, item: dict[str, str]) -> tuple[discord.Embed, discord.ui.View]: title = item["title"][:120] link = item["link"] embed = ImperialMotaz.craft_embed( title=f"[FREE GAME] | {title}", description=( f"**{item.get('description', 'Limited-time free game offer!')}**\n\n" "Claim it now from the official store page before it expires." ), color=NEON_PINK, footer="Free Games Tracker", ) embed.url = link embed.add_field(name="Platform", value=f"「 {item.get('platform', 'Unknown')} 」", inline=True) embed.add_field(name="Type", value=f"「 {item.get('game_type', 'Game')} 」", inline=True) embed.add_field(name="Original Price", value=f"「 {item.get('original_price', 'N/A')} 」", inline=True) embed.add_field(name="Ends On", value=f"「 {item.get('end_date', 'N/A')} 」", inline=True) embed.add_field(name="Link", value=f"[Open Giveaway]({link})", inline=False) if item.get("image"): embed.set_image(url=item["image"]) embed.set_thumbnail(url=self._store_icon(item.get("platform", ""))) return embed, FreeGameClaimView(link) @commands.hybrid_command(name="gaming_news", description=get_cmd_desc("commands.tools.gaming_news_desc")) async def gaming_news(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) try: items = await self._fetch_gaming_news() except Exception: if lang == "ar": await ctx.reply("❌ تعذر جلب أخبار الألعاب الآن.") else: await ctx.reply("❌ Could not fetch gaming news right now.") return if not items: if lang == "ar": await ctx.reply("لا توجد أخبار متاحة الآن.") else: await ctx.reply("No gaming news available right now.") return if lang == "ar": title = "🕹️ آخر أخبار الألعاب عالميًا" else: title = "🕹️ Latest Gaming News Worldwide" embed = discord.Embed( title=title, description=f"{panel_divider('green')}", color=NEON_CYAN, ) for item in items: date_hint = f"\n🗓️ {item['pub_date'][:16]}" if item.get("pub_date") else "" if lang == "ar": embed.add_field(name=item["title"][:256], value=f"📰 [اقرأ المقال]({item['link']}){date_hint}", inline=False) else: embed.add_field(name=item["title"][:256], value=f"📰 [Read Article]({item['link']}){date_hint}", inline=False) await ctx.reply(embed=embed) @commands.hybrid_command(name="freegames", description=get_cmd_desc("commands.tools.freegames_desc"), with_app_command=False) async def freegames(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) try: items = await self._fetch_free_games() except Exception: if lang == "ar": await ctx.reply("❌ تعذر جلب العروض الآن.") else: await ctx.reply("❌ Could not fetch free game offers right now.") return if not items: if lang == "ar": await ctx.reply("لا توجد عروض مجانية نشطة الآن.") else: await ctx.reply("No active free game offers right now.") return for item in items: embed, view = self._free_game_embed(item) await ctx.reply(embed=embed, view=view) @commands.hybrid_command(name="free_games", description=get_cmd_desc("commands.tools.free_games_desc")) async def free_games(self, ctx: commands.Context) -> None: await self.freegames(ctx) @commands.hybrid_group(name="gamehub", fallback="panel", description="Interactive game hub with temporary private rooms") async def gamehub(self, ctx: commands.Context, invite: discord.Member | None = None) -> None: if not ctx.guild: await ctx.reply("Server only.") return guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) if invite and invite.bot: if lang == "ar": await ctx.reply("❌ ادعُ عضواً حقيقياً، ليس بوت.") else: await ctx.reply("❌ Invite a real member, not a bot.") return if invite and invite.id == ctx.author.id: if lang == "ar": await ctx.reply("❌ ادعُ عضواً آخر.") else: await ctx.reply("❌ Invite another member.") return games_list = " • ".join([f"{g['emoji']} {g['name']}" for g in GAME_HUB_GAMES]) if lang == "ar": title = "🎮 مركز الألعاب" desc = ( f"{panel_divider('pink')}\n" f"🕹️ اختر لعبة أدناه لإنشاء **غرفة خاصة**.\n" f"👥 ادعُ صديقاً أو العب ضد البوت.\n" f"🗑️ الغرفة تُحذف تلقائياً عند إغلاقها.\n" f"{panel_divider('pink')}\n\n" f"**الألعاب:** {games_list}" ) invited_label = "🎯 العضو المدعو" else: title = "🎮 Game Hub" desc = ( f"{panel_divider('pink')}\n" f"🕹️ Select a game below to create a **private room**.\n" f"👥 Invite a friend or play solo vs the bot.\n" f"🗑️ The room auto-deletes when you close it.\n" f"{panel_divider('pink')}\n\n" f"**Games:** {games_list}" ) invited_label = "🎯 Invited Member" embed = discord.Embed( title=title, description=desc, color=NEON_CYAN, ) if invite: embed.add_field(name=invited_label, value=invite.mention, inline=False) await ctx.reply(embed=embed, view=GameHubView(self, invite.id if invite else None, lang)) @gamehub.command(name="xo", description="TicTacToe vs member or bot") async def gamehub_xo(self, ctx: commands.Context, opponent: discord.Member | None = None) -> None: await self.xo(ctx, opponent=opponent) @gamehub.command(name="mario", description="Mario/Pac-Man arcade challenge") async def gamehub_mario(self, ctx: commands.Context) -> None: await self.mario(ctx) @gamehub.command(name="dice", description="Roll dice") async def gamehub_dice(self, ctx: commands.Context, sides: int = 6) -> None: await self.dice(ctx, sides=sides) @gamehub.command(name="slots", description="Slot machine game") async def gamehub_slots(self, ctx: commands.Context, bet: int = 50) -> None: await self.slots(ctx, bet=bet) @gamehub.command(name="trivia", description="Gaming/Movies/Series trivia") async def gamehub_trivia(self, ctx: commands.Context, category: str = "gaming", difficulty: str = "medium") -> None: await self.trivia(ctx, category=category) @commands.hybrid_command(name="xo", description=get_cmd_desc("commands.tools.xo_desc"), hidden=True, with_app_command=False) async def xo(self, ctx: commands.Context, opponent: discord.Member | None = None) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) if opponent and opponent.bot: opponent = None view = TicTacToeView(ctx.author, opponent) second = opponent.mention if opponent else "🤖 Bot" if lang == "ar": msg = await ctx.reply( f"❎ تيك تاك تو: {ctx.author.mention} (X) ضد {second} (O)\n" f"🎮 الدور: {ctx.author.mention}", view=view ) else: msg = await ctx.reply( f"❎ TicTacToe: {ctx.author.mention} (X) vs {second} (O)\n" f"🎮 Turn: {ctx.author.mention}", view=view ) if opponent is None: await view._maybe_bot(msg) @commands.hybrid_command(name="choose", hidden=True, with_app_command=False, description=get_cmd_desc("commands.tools.choose_desc")) async def choose(self, ctx: commands.Context, *, options: str) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) parts = [p.strip() for p in options.split(",") if p.strip()] if len(parts) < 2: if lang == "ar": await ctx.reply("❌ أدخل خيارين على الأقل مفصولين بفواصل.") else: await ctx.reply("❌ Provide at least 2 options separated by commas.") return choice = random.choice(parts) if lang == "ar": embed = discord.Embed( title="🎲 اختيار عشوائي", description=( f"{panel_divider('purple')}\n" f"📝 الخيارات: {', '.join(parts)}\n" f"🎯 **المختار:** {choice}\n" f"{panel_divider('purple')}" ), color=NEON_PINK, ) else: embed = discord.Embed( title="🎲 Random Choice", description=( f"{panel_divider('purple')}\n" f"📝 Options: {', '.join(parts)}\n" f"🎯 **Selected:** {choice}\n" f"{panel_divider('purple')}" ), color=NEON_PINK, ) await ctx.reply(embed=embed) @commands.hybrid_command(name="mario", description=get_cmd_desc("commands.tools.mario_desc"), hidden=True, with_app_command=False) async def mario(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) coins = random.randint(5, 120) level = random.randint(1, 8) if lang == "ar": embed = discord.Embed( title="🍄 تحدي الآركيد", description=( f"{panel_divider('orange')}\n" f"🍄 {ctx.author.mention} جمع **{coins}** عملة!\n" f"✅ تجاوز المرحلة **{level}**!\n" f"{panel_divider('orange')}" ), color=NEON_ORANGE, ) embed.add_field(name="🎁 مكافأة", value="تم فتح combo باك مان! <:animatedarrowyellow:1477261257592668271>", inline=False) else: embed = discord.Embed( title="🍄 Arcade Challenge", description=( f"{panel_divider('orange')}\n" f"🍄 {ctx.author.mention} collected **{coins}** coins!\n" f"✅ Cleared level **{level}**!\n" f"{panel_divider('orange')}" ), color=NEON_ORANGE, ) embed.add_field(name="🎁 Bonus", value="Pac-Man combo unlocked! <:animatedarrowyellow:1477261257592668271>", inline=False) embed.set_thumbnail(url="https://upload.wikimedia.org/wikipedia/en/a/a9/MarioNSMBUDeluxe.png") await ctx.reply(embed=embed) @commands.hybrid_command(name="dice", description=get_cmd_desc("commands.tools.dice_desc"), hidden=True, with_app_command=False) async def dice(self, ctx: commands.Context, sides: int = 6) -> None: """Roll a dice with specified sides.""" guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) sides = max(2, min(sides, 100)) result = random.randint(1, sides) if lang == "ar": embed = gaming_embed( "🎲 رمي النرد", f"🎲 رميت نرد بـ **{sides}** أوجه\n🎯 النتيجة: **{result}**" ) else: embed = gaming_embed( "🎲 Dice Roll", f"🎲 Rolled a **{sides}**-sided dice\n🎯 Result: **{result}**" ) await ctx.reply(embed=embed) @commands.hybrid_command(name="slots", description=get_cmd_desc("commands.tools.slots_desc"), hidden=True, with_app_command=False) async def slots(self, ctx: commands.Context, bet: int = 10) -> None: """Play a slot machine game.""" guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) bet = max(10, bet) # Check balance row = await self.bot.db.fetchone( "SELECT wallet FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, ctx.author.id, ) wallet = row[0] if row else 0 if wallet < bet: if lang == "ar": await ctx.reply(f"❌ ليس لديك رصيد كافي. محفظتك: **{wallet}** عملة") else: await ctx.reply(f"❌ Insufficient balance. Your wallet: **{wallet}** coins") return # Slot symbols symbols = ["🍒", "🍋", "🍊", "🍇", "✅", "💎", "7️⃣"] weights = [25, 20, 18, 15, 12, 7, 3] # Weighted probabilities # Spin results = random.choices(symbols, weights=weights, k=3) # Calculate winnings if results[0] == results[1] == results[2]: # Jackpot! if results[0] == "7️⃣": multiplier = 10 elif results[0] == "💎": multiplier = 7 elif results[0] == "✅": multiplier = 5 else: multiplier = 3 winnings = bet * multiplier elif results[0] == results[1] or results[1] == results[2] or results[0] == results[2]: winnings = bet * 2 else: winnings = 0 # Update balance if winnings > 0: await self._add_coins(guild_id, ctx.author.id, winnings - bet) else: await self._add_coins(guild_id, ctx.author.id, -bet) # Build display slot_display = " | ".join(results) if winnings >= bet * 5: result_text = f"🎰 **{'جائزة كبرى!' if lang == 'ar' else 'JACKPOT!'}** {E_FIRE}" elif winnings > 0: result_text = f"✅ **{'فزت!' if lang == 'ar' else 'You won!'}**" else: result_text = f"❌ **{'حظ أوفر' if lang == 'ar' else 'Better luck next time'}**" if lang == "ar": embed = gaming_embed( "🎰 ماكينة القمار", f"┌───────┐\n│ {slot_display} │\n└───────┘\n\n{result_text}\n💰 **الرابح:** {winnings} عملة" ) else: embed = gaming_embed( "🎰 Slot Machine", f"┌───────┐\n│ {slot_display} │\n└───────┘\n\n{result_text}\n💰 **Won:** {winnings} coins" ) await ctx.reply(embed=embed) async def _add_coins(self, guild_id: int, user_id: int, amount: int) -> None: """Helper to add/remove coins from user balance.""" if amount == 0: return await self.bot.db.execute( "INSERT INTO user_balance(guild_id, user_id, wallet, bank) VALUES (?, ?, ?, 0) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET wallet = wallet + ?", guild_id, user_id, amount if amount > 0 else 0, amount, ) async def setup(bot: commands.Bot) -> None: await bot.add_cog(Fun(bot))