""" Engagement cog: Leveling, economy, tournaments, and minigames. Enhanced with rich emoji decorations, beautiful formatting, and multi-language support. """ import datetime as dt import random import discord from discord.ext import commands from bot.theme import ( fancy_header, NEON_CYAN, NEON_LIME, NEON_ORANGE, NEON_PURPLE, NEON_YELLOW, NEON_RED, progress_bar, panel_divider, xp_progress_bar, coin_display, rank_display, level_display, economy_embed, success_embed, error_embed, info_embed, leaderboard_embed, profile_embed, gaming_embed, tournament_embed, format_leaderboard, money_display, timestamp_display, double_line, triple_line, pick_neon_color, shimmer, idle_embed_for_guild ) from bot.i18n import get_cmd_desc from bot.emojis import ( ui, E_DIAMOND, E_STAR, E_CATJAM, E_ARROW_BLUE, E_ARROW_GREEN, E_ARROW_PINK, E_ARROW_PURPLE, E_CROWN, E_TROPHY, E_FIRE, E_SPARKLE, E_MONEY, E_GEM, E_ROCKET, E_HEART, E_STAR2, E_DIZZY, get_custom_emoji ) class EconomyPanelView(discord.ui.View): def __init__(self, cog: "Engagement", guild_id: int) -> None: super().__init__(timeout=None) self.cog = cog self.guild_id = guild_id economy_emoji = get_custom_emoji("economy") admin_emoji = get_custom_emoji("admin") for item in self.children: if isinstance(item, discord.ui.Button): if item.custom_id == "economy:daily": item.emoji = economy_emoji item.row = 0 elif item.custom_id == "economy:work": item.emoji = "๐Ÿ’ผ" item.row = 0 elif item.custom_id == "economy:bank": item.emoji = admin_emoji item.row = 0 elif item.custom_id == "economy:refresh": item.row = 1 async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item) -> None: try: if not interaction.response.is_done(): await interaction.response.send_message("โš ๏ธ ุญุฏุซ ุฎุทุฃ ููŠ ุงู„ุชูุงุนู„ุŒ ุฃุนุฏ ุงู„ู…ุญุงูˆู„ุฉ.", ephemeral=True) else: await interaction.followup.send("โš ๏ธ ุญุฏุซ ุฎุทุฃ ููŠ ุงู„ุชูุงุนู„ุŒ ุฃุนุฏ ุงู„ู…ุญุงูˆู„ุฉ.", ephemeral=True) except Exception: pass @discord.ui.button(label="Daily", emoji=ui("calendar"), style=discord.ButtonStyle.success, custom_id="economy:daily", row=0) async def daily_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message(await self.cog.bot.get_text(self.guild_id, "common.server_only"), ephemeral=True) return guild_id = interaction.guild.id _lang = await self.cog.bot.get_guild_language(guild_id) await interaction.response.defer(ephemeral=True) today = dt.datetime.utcnow().date().isoformat() row = await self.cog.bot.db.fetchone( "SELECT claimed_date FROM user_daily_claim WHERE guild_id = ? AND user_id = ?", guild_id, interaction.user.id, ) if row and row[0] == today: msg = await self.cog.bot.get_text(guild_id, "economy.daily.already_claimed") await interaction.followup.send(msg, ephemeral=True) else: salary_row = await self.cog.bot.db.fetchone( "SELECT daily_min, daily_max FROM economy_salaries WHERE guild_id = ?", guild_id, ) daily_min = salary_row[0] if salary_row else 100 daily_max = salary_row[1] if salary_row else 250 reward = random.randint(daily_min, daily_max) await self.cog._add_coins(guild_id, interaction.user.id, reward) await self.cog.bot.db.execute( "INSERT INTO user_daily_claim(guild_id, user_id, claimed_date) VALUES (?, ?, ?) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET claimed_date = excluded.claimed_date", guild_id, interaction.user.id, today, ) msg = await self.cog.bot.get_text(guild_id, "economy.daily.reward_received", reward=reward) await interaction.followup.send(msg, ephemeral=True) if interaction.message: embed = await self._build_embed(interaction) await interaction.message.edit(embed=embed, view=self) @discord.ui.button(label="Work", emoji=ui("briefcase"), style=discord.ButtonStyle.primary, custom_id="economy:work", row=0) async def work_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message(await self.cog.bot.get_text(self.guild_id, "common.server_only"), ephemeral=True) return guild_id = interaction.guild.id _lang = await self.cog.bot.get_guild_language(guild_id) await interaction.response.defer(ephemeral=True) row = await self.cog.bot.db.fetchone( "SELECT last_work FROM user_work_cooldown WHERE guild_id = ? AND user_id = ?", guild_id, interaction.user.id, ) last = row[0] if row and row[0] else "" now = dt.datetime.utcnow() cooldown_minutes = 45 if last: try: last_dt = dt.datetime.fromisoformat(last) remaining = (cooldown_minutes * 60) - (now - last_dt).total_seconds() if remaining > 0: mins = int(remaining / 60) msg = await self.cog.bot.get_text(guild_id, "economy.work.cooldown", mins=mins) await interaction.followup.send(msg, ephemeral=True) return except ValueError: pass salary_row = await self.cog.bot.db.fetchone( "SELECT min_salary, max_salary FROM economy_salaries WHERE guild_id = ?", guild_id, ) min_sal = salary_row[0] if salary_row else 50 max_sal = salary_row[1] if salary_row else 150 reward = random.randint(min_sal, max_sal) await self.cog._add_coins(guild_id, interaction.user.id, reward) await self.cog.bot.db.execute( "INSERT INTO user_work_cooldown(guild_id, user_id, last_work) VALUES (?, ?, ?) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET last_work = excluded.last_work", guild_id, interaction.user.id, now.isoformat(), ) msg = await self.cog.bot.get_text(guild_id, "economy.work.earned", reward=reward) await interaction.followup.send(msg, ephemeral=True) if interaction.message: embed = await self._build_embed(interaction) await interaction.message.edit(embed=embed, view=self) @discord.ui.button(label="Bank", emoji=ui("bank"), style=discord.ButtonStyle.secondary, custom_id="economy:bank", row=0) async def bank_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild_id = interaction.guild.id row = await self.cog.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, interaction.user.id, ) wallet, bank = row if row else (0, 0) msg = await self.cog.bot.get_text(guild_id, "economy.bank.snapshot", wallet=wallet, bank=bank) await interaction.response.send_message(msg, ephemeral=True) @discord.ui.button(label="Deposit", emoji=ui("moneybag"), style=discord.ButtonStyle.blurple, custom_id="economy:deposit", row=1) async def deposit_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild_id = interaction.guild.id await interaction.response.send_modal(DepositModal(self.cog, guild_id, interaction.user.id)) @discord.ui.button(label="Withdraw", emoji=ui("moneybag"), style=discord.ButtonStyle.blurple, custom_id="economy:withdraw", row=1) async def withdraw_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild_id = interaction.guild.id await interaction.response.send_modal(WithdrawModal(self.cog, guild_id, interaction.user.id)) @discord.ui.button(label="Gamble", emoji=ui("game"), style=discord.ButtonStyle.danger, custom_id="economy:gamble", row=1) async def gamble_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return gambling_cog = interaction.client.get_cog("Gambling") if gambling_cog is None: guild_id = interaction.guild.id await interaction.response.send_modal(GambleModal(self.cog, guild_id, interaction.user.id)) return from bot.cogs.gambling import GamblingPanelView embed = discord.Embed( title="Casino & Gambling Panel", description=( "Choose a game:\n\n" "Blackjack\n" "Roulette\n" "RPG Adventure" ), color=NEON_ORANGE, ) await interaction.response.send_message( embed=embed, view=GamblingPanelView(gambling_cog, interaction.guild.id, interaction.user.id), ephemeral=True, ) @discord.ui.button(label="Rob", emoji=ui("gun"), style=discord.ButtonStyle.danger, custom_id="economy:rob", row=1) async def rob_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild_id = interaction.guild.id await interaction.response.send_modal(RobModal(self.cog, guild_id, interaction.user.id)) @discord.ui.button(label="RPS", emoji="โœ‚๏ธ", style=discord.ButtonStyle.primary, custom_id="economy:rps", row=1) async def rps_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild_id = interaction.guild.id await interaction.response.send_message( "Choose your move:", view=RPSView(self.cog, guild_id, interaction.user.id), ephemeral=True, ) @discord.ui.button(label="Leaderboard", emoji=ui("trophy"), style=discord.ButtonStyle.primary, custom_id="economy:leaderboard", row=2) async def leaderboard_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return guild_id = interaction.guild.id await interaction.response.defer(ephemeral=True) rows = await self.cog.bot.db.fetchall( "SELECT user_id, wallet + bank AS total FROM user_balance WHERE guild_id = ? ORDER BY total DESC LIMIT 10", guild_id, ) if not rows: await interaction.followup.send( embed=await idle_embed_for_guild( "Economy Leaderboard Idle", "No economy data is available yet.", guild=interaction.guild, bot=self.cog.bot, ), ephemeral=True, ) return lines = [] for rank, (uid, total) in enumerate(rows, 1): medal = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}.get(rank, f"{rank}.") member = interaction.guild.get_member(uid) name = member.display_name if member else f"<@{uid}>" lines.append(f"{medal} **{name}** โ€” `{total:,}` coins") embed = discord.Embed( title="๐Ÿ† Top 10 Richest", description="\n".join(lines), color=NEON_YELLOW, ) await interaction.followup.send(embed=embed, ephemeral=True) @discord.ui.button(label="Refresh", emoji=ui("refresh"), style=discord.ButtonStyle.secondary, custom_id="economy:refresh", row=2) async def refresh_btn(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild or not interaction.message: return await interaction.response.defer(ephemeral=True) embed = await self._build_embed(interaction) await interaction.message.edit(embed=embed, view=self) guild_id = interaction.guild.id msg = await self.cog.bot.get_text(guild_id, "success.panel_refreshed") await interaction.followup.send(msg, ephemeral=True) async def _build_embed(self, interaction: discord.Interaction) -> discord.Embed: guild_id = interaction.guild.id _lang = await self.cog.bot.get_guild_language(guild_id) row = await self.cog.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, interaction.user.id, ) wallet, bank = row if row else (0, 0) xp_row = await self.cog.bot.db.fetchone( "SELECT xp, level FROM user_xp WHERE guild_id = ? AND user_id = ?", guild_id, interaction.user.id, ) xp, level = xp_row if xp_row else (0, 1) target = self.cog.xp_target(level) divider = await self.cog.bot.get_text(guild_id, "panels.global.divider") bullet = await self.cog.bot.get_text(guild_id, "panels.global.bullet") title = await self.cog.bot.get_text(guild_id, "panels.economy.header") desc = ( f"{divider}\n" f"{bullet} {await self.cog.bot.get_text(guild_id, 'economy.panel.line_one')}\n" f"{bullet} {await self.cog.bot.get_text(guild_id, 'economy.panel.line_two')}\n" f"{divider}" ) wallet_label = await self.cog.bot.get_text(guild_id, "economy.labels.wallet") bank_label = await self.cog.bot.get_text(guild_id, "economy.labels.bank") level_label = await self.cog.bot.get_text(guild_id, "economy.labels.level") progress_label = await self.cog.bot.get_text(guild_id, "economy.labels.progress") embed = discord.Embed( title=f"{ui('economy')} {title}", description=desc, color=NEON_CYAN, ) embed.add_field(name=f"๐Ÿ’ฐ {wallet_label}", value=f"`{wallet:,}`", inline=True) embed.add_field(name=f"๐Ÿฆ {bank_label}", value=f"`{bank:,}`", inline=True) embed.add_field(name=f"โญ {level_label}", value=f"**{level}**", inline=True) embed.add_field(name=f"๐Ÿ“Š XP", value=f"`{xp:,}/{target:,}`", inline=True) embed.add_field(name=f"๐Ÿ“ˆ {progress_label}", value=xp_progress_bar(xp, target, level), inline=False) if interaction.guild: await add_banner_to_embed(embed, interaction.guild) embed.set_thumbnail(url=interaction.user.display_avatar.url) embed.set_footer(text=await self.cog.bot.get_text(guild_id, "economy.panel.footer")) return embed class DepositModal(discord.ui.Modal, title="๐Ÿ’ฐ Deposit to Bank"): amount = discord.ui.TextInput( label="Amount (or 'all')", placeholder="500 or all", required=True, max_length=16, ) def __init__(self, cog: "Engagement", guild_id: int, user_id: int) -> None: super().__init__(timeout=None) self.cog = cog self.guild_id = guild_id self.user_id = user_id async def on_submit(self, interaction: discord.Interaction) -> None: await interaction.response.defer(ephemeral=True) val = self.amount.value.strip().lower() row = await self.cog.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", self.guild_id, self.user_id, ) wallet, bank = row if row else (0, 0) if val == "all": amt = wallet else: try: amt = int(val) except ValueError: await interaction.followup.send("Invalid amount.", ephemeral=True) return if amt <= 0: await interaction.followup.send("Amount must be positive.", ephemeral=True) return if amt > wallet: await interaction.followup.send("Not enough coins in wallet.", ephemeral=True) return await self.cog.bot.db.execute( "INSERT INTO user_balance(guild_id, user_id, wallet, bank) VALUES (?, ?, ?, ?) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET wallet = wallet - ?, bank = bank + ?", self.guild_id, self.user_id, wallet - amt, bank + amt, amt, amt, ) await interaction.followup.send(f"โœ… Deposited `{amt:,}` coins to bank.", ephemeral=True) class WithdrawModal(discord.ui.Modal, title="๐Ÿ’ธ Withdraw from Bank"): amount = discord.ui.TextInput( label="Amount (or 'all')", placeholder="500 or all", required=True, max_length=16, ) def __init__(self, cog: "Engagement", guild_id: int, user_id: int) -> None: super().__init__(timeout=None) self.cog = cog self.guild_id = guild_id self.user_id = user_id async def on_submit(self, interaction: discord.Interaction) -> None: await interaction.response.defer(ephemeral=True) val = self.amount.value.strip().lower() row = await self.cog.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", self.guild_id, self.user_id, ) wallet, bank = row if row else (0, 0) if val == "all": amt = bank else: try: amt = int(val) except ValueError: await interaction.followup.send("Invalid amount.", ephemeral=True) return if amt <= 0: await interaction.followup.send("Amount must be positive.", ephemeral=True) return if amt > bank: await interaction.followup.send("Not enough coins in bank.", ephemeral=True) return await self.cog.bot.db.execute( "INSERT INTO user_balance(guild_id, user_id, wallet, bank) VALUES (?, ?, ?, ?) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET wallet = wallet + ?, bank = bank - ?", self.guild_id, self.user_id, wallet + amt, bank - amt, amt, amt, ) await interaction.followup.send(f"โœ… Withdrew `{amt:,}` coins from bank.", ephemeral=True) class GambleModal(discord.ui.Modal, title="๐ŸŽฒ Gamble Coins"): amount = discord.ui.TextInput( label="Bet Amount", placeholder="100", required=True, max_length=16, ) def __init__(self, cog: "Engagement", guild_id: int, user_id: int) -> None: super().__init__(timeout=None) self.cog = cog self.guild_id = guild_id self.user_id = user_id async def on_submit(self, interaction: discord.Interaction) -> None: await interaction.response.defer(ephemeral=True) try: amt = int(self.amount.value.strip()) except ValueError: await interaction.followup.send("Invalid amount.", ephemeral=True) return if amt <= 0: await interaction.followup.send("Bet must be positive.", ephemeral=True) return row = await self.cog.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", self.guild_id, self.user_id, ) wallet, bank = row if row else (0, 0) total = wallet + bank if amt > total: await interaction.followup.send("Not enough coins.", ephemeral=True) return win = random.random() < 0.45 if win: gained = amt if wallet >= amt: await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = wallet + ? WHERE guild_id = ? AND user_id = ?", gained, self.guild_id, self.user_id, ) else: await self.cog.bot.db.execute( "UPDATE user_balance SET bank = bank + ? WHERE guild_id = ? AND user_id = ?", gained, self.guild_id, self.user_id, ) await interaction.followup.send(f"๐ŸŽ‰ You won `{gained:,}` coins!", ephemeral=True) else: lost = amt if wallet >= amt: await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = wallet - ? WHERE guild_id = ? AND user_id = ?", lost, self.guild_id, self.user_id, ) elif wallet > 0: from_wallet = wallet from_bank = amt - wallet await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = 0, bank = bank - ? WHERE guild_id = ? AND user_id = ?", from_bank, self.guild_id, self.user_id, ) else: await self.cog.bot.db.execute( "UPDATE user_balance SET bank = bank - ? WHERE guild_id = ? AND user_id = ?", lost, self.guild_id, self.user_id, ) await interaction.followup.send(f"๐Ÿ’€ You lost `{lost:,}` coins!", ephemeral=True) class RobModal(discord.ui.Modal, title="๐Ÿ”ซ Rob a User"): target = discord.ui.TextInput( label="User ID or Mention (optional)", placeholder="Leave empty for random target", required=False, max_length=64, ) def __init__(self, cog: "Engagement", guild_id: int, user_id: int) -> None: super().__init__(timeout=None) self.cog = cog self.guild_id = guild_id self.user_id = user_id async def on_submit(self, interaction: discord.Interaction) -> None: await interaction.response.defer(ephemeral=True) tid: int | None = None val = self.target.value.strip().replace("<", "").replace(">", "").replace("@", "").replace("!", "") if val: try: tid = int(val) except ValueError: await interaction.followup.send("Invalid user ID.", ephemeral=True) return elif interaction.guild: eligible = [ m for m in interaction.guild.members if (not m.bot) and m.id != self.user_id and m.guild_permissions.send_messages ] if eligible: tid = random.choice(eligible).id if tid is None: await interaction.followup.send("No valid robbery target found.", ephemeral=True) return if tid == self.user_id: await interaction.followup.send("You can't rob yourself!", ephemeral=True) return row = await self.cog.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", self.guild_id, tid, ) victim_wallet, victim_bank = row if row else (0, 0) if victim_wallet <= 0: await interaction.followup.send("That user has no coins in wallet to rob!", ephemeral=True) return steal = random.randint(1, victim_wallet) success = random.random() < 0.5 if success: await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = wallet - ? WHERE guild_id = ? AND user_id = ?", steal, self.guild_id, tid, ) await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = wallet + ? WHERE guild_id = ? AND user_id = ?", steal, self.guild_id, self.user_id, ) await interaction.followup.send(f"๐ŸŽ‰ You robbed `{steal:,}` coins!", ephemeral=True) else: fine = min(victim_wallet, random.randint(10, 100)) await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = wallet - ? WHERE guild_id = ? AND user_id = ?", fine, self.guild_id, self.user_id, ) await interaction.followup.send(f"๐Ÿš” You got caught and paid `{fine:,}` coins fine!", ephemeral=True) class RPSView(discord.ui.View): def __init__(self, cog: "Engagement", guild_id: int, user_id: int) -> None: super().__init__(timeout=60) self.cog = cog self.guild_id = guild_id self.user_id = user_id @discord.ui.button(label="Rock ๐Ÿชจ", style=discord.ButtonStyle.primary) async def rock(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: await self._resolve(interaction, "rock") @discord.ui.button(label="Paper ๐Ÿ“„", style=discord.ButtonStyle.primary) async def paper(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: await self._resolve(interaction, "paper") @discord.ui.button(label="Scissors โœ‚๏ธ", style=discord.ButtonStyle.primary) async def scissors(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: await self._resolve(interaction, "scissors") async def _resolve(self, interaction: discord.Interaction, choice: str) -> None: if interaction.user.id != self.user_id: await interaction.response.send_message("This isn't your game!", ephemeral=True) return bot_choice = random.choice(["rock", "paper", "scissors"]) emoji_map = {"rock": "๐Ÿชจ", "paper": "๐Ÿ“„", "scissors": "โœ‚๏ธ"} result_map = { ("rock", "scissors"): "win", ("paper", "rock"): "win", ("scissors", "paper"): "win", } key = (choice, bot_choice) if choice == bot_choice: result = "tie" else: result = result_map.get(key, "lose") reward = 0 if result == "win": reward = random.randint(10, 50) await self.cog.bot.db.execute( "UPDATE user_balance SET wallet = wallet + ? WHERE guild_id = ? AND user_id = ?", reward, self.guild_id, self.user_id, ) msg = f"You: {emoji_map[choice]} | Bot: {emoji_map[bot_choice]}\n" if result == "win": msg += f"๐ŸŽ‰ You win `{reward:,}` coins!" elif result == "lose": msg += "๐Ÿ’€ You lose!" else: msg += "๐Ÿค It's a tie!" await interaction.response.send_message(msg, ephemeral=True) self.stop() class TournamentJoinView(discord.ui.View): def __init__(self, cog: "Engagement", guild_id: int, name: str) -> None: super().__init__(timeout=None) self.cog = cog self.guild_id = guild_id self.name = name async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item) -> None: try: if not interaction.response.is_done(): await interaction.response.send_message("โš ๏ธ Interaction failed, please retry.", ephemeral=True) else: await interaction.followup.send("โš ๏ธ Interaction failed, please retry.", ephemeral=True) except Exception: pass @discord.ui.button(label="Join Tournament", emoji=ui("flag"), style=discord.ButtonStyle.success, custom_id="tournament:join") async def join(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: if not interaction.guild: await interaction.response.send_message("Server only.", ephemeral=True) return lang = await self.cog.bot.get_guild_language(self.guild_id) await self.cog.bot.db.execute( "INSERT OR IGNORE INTO tournament_participants(guild_id, tournament_name, user_id) VALUES (?, ?, ?)", self.guild_id, self.name, interaction.user.id, ) if lang == "ar": msg = f"โœ… ุงู†ุถู…ู…ุช ุฅู„ู‰ **{self.name}**! {E_TROPHY}" else: msg = f"โœ… You joined **{self.name}**! {E_TROPHY}" await interaction.response.send_message(msg, ephemeral=True) @discord.ui.button(label="Tournament Info", emoji=ui("game"), style=discord.ButtonStyle.secondary, custom_id="tournament:info") async def info(self, interaction: discord.Interaction, _: discord.ui.Button) -> None: lang = await self.cog.bot.get_guild_language(self.guild_id) games_rows = await self.cog.bot.db.fetchall( "SELECT game_name FROM tournament_games WHERE guild_id = ? AND tournament_name = ? ORDER BY game_name ASC", self.guild_id, self.name, ) players_rows = await self.cog.bot.db.fetchall( "SELECT user_id FROM tournament_participants WHERE guild_id = ? AND tournament_name = ?", self.guild_id, self.name, ) games = ", ".join(game for (game,) in games_rows) if games_rows else ("ู„ู… ุชุญุฏุฏ" if lang == "ar" else "Not set") status_row = await self.cog.bot.db.fetchone( "SELECT status FROM tournaments WHERE guild_id = ? AND name = ?", self.guild_id, self.name, ) status = status_row[0] if status_row and status_row[0] else "open" if lang == "ar": title = f"๐Ÿ† **{self.name}**" desc = ( f"{panel_divider('lime')}\n" f"๐Ÿ“Š ุงู„ุญุงู„ุฉ: **{status}**\n" f"๐Ÿ‘ฅ ุงู„ู„ุงุนุจูŠู†: **{len(players_rows)}**\n" f"๐ŸŽฎ ุงู„ุฃู„ุนุงุจ: {games}" ) else: title = f"๐Ÿ† **{self.name}**" desc = ( f"{panel_divider('lime')}\n" f"๐Ÿ“Š Status: **{status}**\n" f"๐Ÿ‘ฅ Players: **{len(players_rows)}**\n" f"๐ŸŽฎ Games: {games}" ) await interaction.response.send_message(f"{title}\n{desc}", ephemeral=True) class Engagement(commands.Cog): """Leveling and economy 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(EconomyPanelView(self, 0)) self.bot.add_view(TournamentJoinView(self, 0, "")) def xp_target(self, level: int) -> int: return max(150, level * 150) async def _add_coins(self, guild_id: int, user_id: int, amount: int) -> None: 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 + excluded.wallet", guild_id, user_id, amount, ) @commands.Cog.listener() async def on_message(self, message: discord.Message) -> None: if message.author.bot or not message.guild: return gained = random.randint(5, 15) row = await self.bot.db.fetchone( "SELECT xp, level FROM user_xp WHERE guild_id = ? AND user_id = ?", message.guild.id, message.author.id, ) if not row: xp, level = 0, 1 await self.bot.db.execute( "INSERT INTO user_xp(guild_id, user_id, xp, level) VALUES (?, ?, ?, ?)", message.guild.id, message.author.id, xp, level, ) else: xp, level = row xp += gained target = self.xp_target(level) if xp >= target: level += 1 lang = await self.bot.get_guild_language(message.guild.id) if lang == "ar": await message.channel.send(f"๐ŸŽ‰ {message.author.mention} ูˆุตู„ ุฅู„ู‰ **ุงู„ู…ุณุชูˆู‰ {level}**! {E_STAR2}") else: await message.channel.send(f"๐ŸŽ‰ {message.author.mention} reached **Level {level}**! {E_STAR2}") await self.bot.db.execute( "UPDATE user_xp SET xp = ?, level = ? WHERE guild_id = ? AND user_id = ?", xp, level, message.guild.id, message.author.id, ) @commands.hybrid_command(name="rank", description=get_cmd_desc("commands.economy.rank_desc")) async def rank(self, ctx: commands.Context, member: discord.Member | None = None) -> None: member = member or ctx.author guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) row = await self.bot.db.fetchone( "SELECT xp, level FROM user_xp WHERE guild_id = ? AND user_id = ?", guild_id, member.id, ) if not row: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ุญุงู„ุฉ ุงู„ุฑุชุจุฉ ุงู„ุฎุงู…ู„ุฉ", "ู„ุง ุชูˆุฌุฏ ุจูŠุงู†ุงุช ู…ุณุชูˆู‰ ุจุนุฏ. ุงุจุฏุฃ ุงู„ุฏุฑุฏุดุฉ ู„ูƒุณุจ XP!", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Rank Idle", "No rank data yet. Start chatting to earn XP!", guild=ctx.guild, bot=self.bot, )) return xp, level = row target = self.xp_target(level) if lang == "ar": title = f"{E_STAR} ู…ุณุชูˆู‰ {member.display_name}" desc = ( f"{panel_divider('purple')}\n" f"{level_display(level)}\n" f"๐Ÿ“ˆ ุงู„ุฎุจุฑุฉ: `{xp:,}/{target:,}`\n" f"๐Ÿ“Š ุงู„ุชู‚ุฏู…: {xp_progress_bar(xp, target, level)}\n" f"{panel_divider('purple')}" ) else: title = f"{E_STAR} {member.display_name}'s Rank" desc = ( f"{panel_divider('purple')}\n" f"{level_display(level)}\n" f"๐Ÿ“ˆ XP: `{xp:,}/{target:,}`\n" f"๐Ÿ“Š Progress: {xp_progress_bar(xp, target, level)}\n" f"{panel_divider('purple')}" ) embed = discord.Embed( title=title, description=desc, color=NEON_PURPLE, ) embed.set_thumbnail(url=member.display_avatar.url) await ctx.reply(embed=embed) @commands.hybrid_command(name="leaderboard", description=get_cmd_desc("commands.economy.leaderboard_desc")) async def leaderboard(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) rows = await self.bot.db.fetchall( "SELECT user_id, SUM(wallet + bank) AS total FROM user_balance GROUP BY user_id ORDER BY total DESC LIMIT 10" ) if not rows: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ุงู„ู„ูˆุญุฉ ุงู„ุฎุงู…ู„ุฉ", "ู„ุง ุชูˆุฌุฏ ุจูŠุงู†ุงุช ุจุนุฏ. ุงุจุฏุฃ ุงู„ุฏุฑุฏุดุฉ ู„ูƒุณุจ XP!", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Leaderboard Idle", "No data yet. Start chatting to earn XP!", guild=ctx.guild, bot=self.bot, )) return lines = [] for index, (uid, total) in enumerate(rows, start=1): user = ctx.guild.get_member(uid) if ctx.guild else None name = user.display_name if user else f"User {uid}" badge = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}.get(index, f"#{index}") if lang == "ar": lines.append(f"{badge} **{name}** โ€” {money_display(int(total or 0))}") else: lines.append(f"{badge} **{name}** โ€” {money_display(int(total or 0))}") if lang == "ar": title = "๐Ÿ† ู„ูˆุญุฉ ุงู„ุงู‚ุชุตุงุฏ ุงู„ุนุงู„ู…ูŠุฉ" else: title = "๐Ÿ† Global Economy Leaderboard" embed = discord.Embed( title=title, description=f"{panel_divider('yellow')}\n" + "\n".join(lines) + f"\n{panel_divider('yellow')}", color=NEON_YELLOW, ) await ctx.reply(embed=embed) @commands.hybrid_command(name="daily", description=get_cmd_desc("commands.economy.daily_desc")) async def daily(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) today = dt.datetime.utcnow().date().isoformat() row = await self.bot.db.fetchone( "SELECT claimed_date FROM user_daily_claim WHERE guild_id = ? AND user_id = ?", guild_id, ctx.author.id, ) if row and row[0] == today: if lang == "ar": await ctx.reply("๐ŸŒ… ู„ู‚ุฏ ุงุณุชู„ู…ุช ู…ูƒุงูุฃุชูƒ ุงู„ูŠูˆู…ูŠุฉ ุจุงู„ูุนู„. ุงุฑุฌุน ุบุฏู‹ุง!") else: await ctx.reply("๐ŸŒ… You already claimed your daily reward! Come back tomorrow.") return # Get salary settings salary_row = await self.bot.db.fetchone( "SELECT daily_min, daily_max FROM economy_salaries WHERE guild_id = ?", guild_id, ) daily_min = salary_row[0] if salary_row else 100 daily_max = salary_row[1] if salary_row else 250 reward = random.randint(daily_min, daily_max) await self._add_coins(guild_id, ctx.author.id, reward) await self.bot.db.execute( "INSERT INTO user_daily_claim(guild_id, user_id, claimed_date) VALUES (?, ?, ?) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET claimed_date = excluded.claimed_date", guild_id, ctx.author.id, today, ) if lang == "ar": embed = success_embed("ู…ูƒุงูุฃุฉ ูŠูˆู…ูŠุฉ!", f"{E_MONEY} ุญุตู„ุช ุนู„ู‰ **{reward}** ุนู…ู„ุฉ!") else: embed = success_embed("Daily Reward Claimed!", f"{E_MONEY} You received **{reward}** coins!") await ctx.reply(embed=embed) @commands.hybrid_command(name="balance", description=get_cmd_desc("commands.economy.balance_desc")) async def balance(self, ctx: commands.Context, member: discord.Member | None = None) -> None: member = member or ctx.author guild_id = ctx.guild.id row = await self.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, member.id, ) wallet, bank = row if row else (0, 0) embed = discord.Embed( title=f"{E_GEM} {member.display_name}'s Balance", description=panel_divider('cyan'), color=NEON_CYAN, ) embed.add_field(name="๐Ÿ’ฐ Wallet", value=money_display(wallet), inline=True) embed.add_field(name="๐Ÿ’ณ Bank", value=money_display(bank), inline=True) embed.add_field(name="๐Ÿงพ Net Worth", value=money_display(wallet + bank), inline=True) embed.set_thumbnail(url=member.display_avatar.url) embed.set_footer(text="Use /deposit and /withdraw to manage funds.") await ctx.reply(embed=embed) @commands.hybrid_command(name="economy_admin", hidden=True, with_app_command=False, description=get_cmd_desc("commands.economy.economy_admin_desc")) @commands.has_permissions(administrator=True) async def economy_admin( self, ctx: commands.Context, action: str, member: discord.Member, amount: int, ) -> None: """Hidden admin command to add/remove funds from a user wallet.""" if action.lower() not in {"add", "remove"}: await ctx.reply("Use `add` or `remove`.") return delta = amount if action.lower() == "add" else -amount await self._add_coins(ctx.guild.id, member.id, delta) await ctx.reply(f"โœ… {action.title()} {money_display(amount)} for {member.mention}.") @commands.hybrid_command(name="work", description=get_cmd_desc("commands.economy.work_desc")) async def work(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) row = await self.bot.db.fetchone( "SELECT last_work FROM user_work_cooldown WHERE guild_id = ? AND user_id = ?", guild_id, ctx.author.id, ) last = row[0] if row and row[0] else "" now = dt.datetime.utcnow() cooldown_minutes = 45 if last: try: last_dt = dt.datetime.fromisoformat(last) remaining = (cooldown_minutes * 60) - (now - last_dt).total_seconds() if remaining > 0: mins = int(remaining / 60) if lang == "ar": await ctx.reply(f"โณ ุงุฑุฌุน ุจุนุฏ **{mins}** ุฏู‚ูŠู‚ุฉ.") else: await ctx.reply(f"โณ Come back in **{mins}** minutes.") return except ValueError: pass # Get salary settings salary_row = await self.bot.db.fetchone( "SELECT min_salary, max_salary FROM economy_salaries WHERE guild_id = ?", guild_id, ) min_sal = salary_row[0] if salary_row else 50 max_sal = salary_row[1] if salary_row else 150 reward = random.randint(min_sal, max_sal) await self._add_coins(guild_id, ctx.author.id, reward) await self.bot.db.execute( "INSERT INTO user_work_cooldown(guild_id, user_id, last_work) VALUES (?, ?, ?) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET last_work = excluded.last_work", guild_id, ctx.author.id, now.isoformat(), ) if lang == "ar": embed = success_embed("ุชู… ุงู„ุนู…ู„!", f"๐Ÿ’ผ ุนู…ู„ุช ุจุฌุฏ ูˆุญุตู„ุช ุนู„ู‰ **{reward}** ุนู…ู„ุฉ! {E_MONEY}") else: embed = success_embed("Work Complete!", f"๐Ÿ’ผ You worked hard and earned **{reward}** coins! {E_MONEY}") await ctx.reply(embed=embed) async def gamble(self, ctx: commands.Context, bet: int) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) bet = max(1, bet) 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 bet < 50: if lang == "ar": await ctx.reply("โŒ ุงู„ุญุฏ ุงู„ุฃุฏู†ู‰ ู„ู„ุฑู‡ุงู† **50** ุนู…ู„ุฉ.") else: await ctx.reply("โŒ Minimum bet is **50** coins.") return if wallet < bet: if lang == "ar": await ctx.reply(f"โŒ ู„ุฏูŠูƒ ูู‚ุท **{wallet}** ุนู…ู„ุฉ.") else: await ctx.reply(f"โŒ You only have **{wallet}** coins.") return win = random.random() < 0.45 change = bet if win else -bet await self._add_coins(guild_id, ctx.author.id, change) if win: if lang == "ar": embed = success_embed("๐ŸŽฐ ููˆุฒ!", f"{E_FIRE} ุฑุงู‡ู†ุช **{bet}** ูˆ **ูุฒุช!**\n\n๐Ÿ’ฐ **+{bet}** ุนู…ู„ุฉ!") else: embed = success_embed("๐ŸŽฐ JACKPOT!", f"{E_FIRE} You bet **{bet}** and **WON!**\n\n๐Ÿ’ฐ **+{bet}** coins!") else: if lang == "ar": embed = error_embed("๐ŸŽฐ ุญุธ ุฃูˆูุฑ!", f"๐Ÿ’€ ุฑุงู‡ู†ุช **{bet}** ูˆุฎุณุฑุช.\n\n๐Ÿ’ฐ **-{bet}** ุนู…ู„ุฉ") else: embed = error_embed("๐ŸŽฐ Better Luck Next Time!", f"๐Ÿ’€ You bet **{bet}** and lost.\n\n๐Ÿ’ฐ **-{bet}** coins") await ctx.reply(embed=embed) @commands.hybrid_command(name="rob", description=get_cmd_desc("commands.economy.rob_desc"), hidden=True, with_app_command=False) async def rob(self, ctx: commands.Context, target: discord.Member | None = None) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) if target is None: eligible = [ member for member in ctx.guild.members if (not member.bot) and member.id != ctx.author.id and member.guild_permissions.send_messages ] target = random.choice(eligible) if eligible else None if target is None: await ctx.reply("โŒ No valid robbery targets found.") return if target.id == ctx.author.id or target.bot: if lang == "ar": await ctx.reply("โŒ ู‡ุฏู ุบูŠุฑ ุตุงู„ุญ.") else: await ctx.reply("โŒ Invalid target.") return row = await self.bot.db.fetchone( "SELECT wallet FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, target.id, ) target_wallet = row[0] if row else 0 if target_wallet < 100: if lang == "ar": await ctx.reply("โŒ ุงู„ู‡ุฏู ู„ุฏูŠู‡ ุฃู‚ู„ ู…ู† **100** ุนู…ู„ุฉุŒ ู„ุง ูŠุณุชุญู‚.") else: await ctx.reply("โŒ Target has less than **100** coins, not worth it.") return success = random.random() < 0.40 if success: stolen = random.randint(50, min(target_wallet // 2, 500)) await self.bot.db.execute( "INSERT INTO user_balance(guild_id, user_id, wallet, bank) VALUES (?, ?, 0, 0) " "ON CONFLICT(guild_id, user_id) DO UPDATE SET wallet = wallet - ?", guild_id, target.id, stolen, ) await self._add_coins(guild_id, ctx.author.id, stolen) if lang == "ar": embed = success_embed("๐Ÿฆ ุณุฑู‚ุฉ ู†ุงุฌุญุฉ!", f"ุณุฑู‚ุช **{stolen}** ุนู…ู„ุฉ ู…ู† {target.mention}! {E_FIRE}") else: embed = success_embed("๐Ÿฆ Heist Successful!", f"You stole **{stolen}** coins from {target.mention}! {E_FIRE}") else: fine = random.randint(50, 200) await self._add_coins(guild_id, ctx.author.id, -fine) if lang == "ar": embed = error_embed("๐Ÿšจ ุชู… ุงู„ู‚ุจุถ ุนู„ูŠูƒ!", f"ุชู… ุงู„ู‚ุจุถ ุนู„ูŠูƒ ูˆุฃู†ุช ุชุญุงูˆู„ ุณุฑู‚ุฉ {target.mention}!\n\n๐Ÿ’ธ ุฏูุนุช **{fine}** ุนู…ู„ุฉ ูƒุบุฑุงู…ุฉ.") else: embed = error_embed("๐Ÿšจ BUSTED!", f"You got caught trying to rob {target.mention}!\n\n๐Ÿ’ธ Paid **{fine}** coins as a fine.") await ctx.reply(embed=embed) @commands.hybrid_group(name="economy", fallback="panel", description="Economy panel with quick actions") async def economy(self, ctx: commands.Context) -> None: if ctx.interaction and not ctx.interaction.response.is_done(): await ctx.interaction.response.defer() guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) row = await self.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, ctx.author.id, ) wallet, bank = row if row else (0, 0) xp_row = await self.bot.db.fetchone( "SELECT xp, level FROM user_xp WHERE guild_id = ? AND user_id = ?", guild_id, ctx.author.id, ) xp, level = xp_row if xp_row else (0, 1) target = self.xp_target(level) divider = await self.bot.get_text(guild_id, "panels.global.divider") bullet = await self.bot.get_text(guild_id, "panels.global.bullet") title = await self.bot.get_text(guild_id, "panels.economy.header") desc = ( f"{divider}\n" f"{bullet} {await self.bot.get_text(guild_id, 'economy.panel.line_one')}\n" f"{bullet} {await self.bot.get_text(guild_id, 'economy.panel.line_two')}\n" f"{divider}" ) wallet_label = await self.bot.get_text(guild_id, "economy.labels.wallet") bank_label = await self.bot.get_text(guild_id, "economy.labels.bank") level_label = await self.bot.get_text(guild_id, "economy.labels.level") progress_label = await self.bot.get_text(guild_id, "economy.labels.progress") footer = await self.bot.get_text(guild_id, "economy.panel.footer") embed = discord.Embed( title=f"{get_custom_emoji('economy')} {await self.bot.get_text(guild_id, 'panels.global.prefix')} {title}", description=desc, color=NEON_CYAN, ) embed.add_field(name=wallet_label, value=f"`{wallet:,}`", inline=True) embed.add_field(name=bank_label, value=f"`{bank:,}`", inline=True) embed.add_field(name=level_label, value=f"**{level}** (`{xp:,}/{target:,}`)", inline=True) embed.add_field(name=progress_label, value=xp_progress_bar(xp, target, level), inline=False) embed.set_thumbnail(url=ctx.author.display_avatar.url) embed.set_footer(text=footer) if ctx.interaction: await ctx.interaction.followup.send(embed=embed, view=EconomyPanelView(self, guild_id), ephemeral=True) else: await ctx.reply(embed=embed, view=EconomyPanelView(self, guild_id)) @economy.command(name="deposit", description="Move coins from wallet to bank") async def economy_deposit(self, ctx: commands.Context, amount: int) -> None: await self.deposit(ctx, amount) @economy.command(name="withdraw", description="Move coins from bank to wallet") async def economy_withdraw(self, ctx: commands.Context, amount: int) -> None: await self.withdraw(ctx, amount) @economy.command(name="gamble", description="Gamble your coins") async def economy_gamble(self, ctx: commands.Context, bet: int) -> None: await self.gamble(ctx, bet) @economy.command(name="rob", description="Attempt to rob another user") async def economy_rob(self, ctx: commands.Context, target: discord.Member | None = None) -> None: await self.rob(ctx, target) @commands.hybrid_command(name="profile", description=get_cmd_desc("commands.economy.profile_desc")) async def profile(self, ctx: commands.Context, member: discord.Member | None = None) -> None: member = member or ctx.author guild_id = ctx.guild.id xp_row = await self.bot.db.fetchone( "SELECT xp, level FROM user_xp WHERE guild_id = ? AND user_id = ?", guild_id, member.id, ) bal_row = await self.bot.db.fetchone( "SELECT wallet, bank FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, member.id, ) xp, level = xp_row if xp_row else (0, 1) wallet, bank = bal_row if bal_row else (0, 0) target = self.xp_target(level) embed = discord.Embed( title=f"๐Ÿ‘ค {member.display_name}'s Profile", description=panel_divider('purple'), color=NEON_PURPLE, ) embed.add_field(name=f"{E_STAR} Level", value=level_display(level), inline=True) embed.add_field(name="๐Ÿ“ˆ XP", value=f"`{xp:,}/{target:,}`", inline=True) embed.add_field(name="๐Ÿ“Š Progress", value=xp_progress_bar(xp, target, level), inline=False) embed.add_field(name="๐Ÿ’ฐ Wallet", value=f"`{wallet:,}`", inline=True) embed.add_field(name="๐Ÿ’ณ Bank", value=f"`{bank:,}`", inline=True) embed.set_thumbnail(url=member.display_avatar.url) embed.set_footer(text="Unified profile card โ€ข Economy + Leveling") await ctx.reply(embed=embed) # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # ECONOMY MANAGEMENT COMMANDS # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• @commands.hybrid_group(name="econadmin", description="Economy management commands") @commands.has_permissions(manage_guild=True) async def econadmin(self, ctx: commands.Context) -> None: """Economy management commands for admins.""" if ctx.invoked_subcommand is None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) if lang == "ar": desc = ( f"{panel_divider('cyan')}\n" f"๐Ÿ’ฐ `/econadmin setbalance @user ` - ุชุนูŠูŠู† ุฑุตูŠุฏ ุงู„ู…ุณุชุฎุฏู…\n" f"๐Ÿ’Ž `/econadmin addcoins @user ` - ุฅุถุงูุฉ ุนู…ู„ุงุช\n" f"๐Ÿ’ธ `/econadmin removecoins @user ` - ุฅุฒุงู„ุฉ ุนู…ู„ุงุช\n" f"๐Ÿ’ผ `/econadmin salary ` - ุชุนูŠูŠู† ุฑูˆุงุชุจ ุงู„ุนู…ู„\n" f"๐ŸŒ… `/econadmin daily ` - ุชุนูŠูŠู† ู…ูƒุงูุขุช ูŠูˆู…ูŠุฉ\n" f"{panel_divider('cyan')}" ) else: desc = ( f"{panel_divider('cyan')}\n" f"๐Ÿ’ฐ `/econadmin setbalance @user ` - Set user balance\n" f"๐Ÿ’Ž `/econadmin addcoins @user ` - Add coins to user\n" f"๐Ÿ’ธ `/econadmin removecoins @user ` - Remove coins from user\n" f"๐Ÿ’ผ `/econadmin salary ` - Set work salary range\n" f"๐ŸŒ… `/econadmin daily ` - Set daily reward range\n" f"{panel_divider('cyan')}" ) embed = discord.Embed( title=f"โš™๏ธ {'ุฅุฏุงุฑุฉ ุงู„ุงู‚ุชุตุงุฏ' if lang == 'ar' else 'Economy Management'}", description=desc, color=NEON_CYAN, ) await ctx.reply(embed=embed) @econadmin.command(name="setbalance") @commands.has_permissions(manage_guild=True) async def setbalance(self, ctx: commands.Context, member: discord.Member, amount: int) -> None: """Set a user's wallet balance.""" guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) amount = max(0, amount) 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 = excluded.wallet", guild_id, member.id, amount, ) if lang == "ar": embed = success_embed("๐Ÿ’ฐ ุชู… ุชุนูŠูŠู† ุงู„ุฑุตูŠุฏ", f"ุชู… ุชุนูŠูŠู† ุฑุตูŠุฏ {member.mention} ุฅู„ู‰ **{amount:,}** ุนู…ู„ุฉ.") else: embed = success_embed("๐Ÿ’ฐ Balance Set", f"Set {member.mention}'s wallet to **{amount:,}** coins.") await ctx.reply(embed=embed) @econadmin.command(name="addcoins") @commands.has_permissions(manage_guild=True) async def addcoins(self, ctx: commands.Context, member: discord.Member, amount: int) -> None: """Add coins to a user's wallet.""" guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) amount = max(1, amount) await self._add_coins(guild_id, member.id, amount) if lang == "ar": embed = success_embed("๐Ÿ’Ž ุนู…ู„ุงุช ู…ุถุงูุฉ", f"ุชู…ุช ุฅุถุงูุฉ **{amount:,}** ุนู…ู„ุฉ ุฅู„ู‰ {member.mention}.") else: embed = success_embed("๐Ÿ’Ž Coins Added", f"Added **{amount:,}** coins to {member.mention}.") await ctx.reply(embed=embed) @econadmin.command(name="removecoins") @commands.has_permissions(manage_guild=True) async def removecoins(self, ctx: commands.Context, member: discord.Member, amount: int) -> None: """Remove coins from a user's wallet.""" guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) amount = max(1, amount) row = await self.bot.db.fetchone( "SELECT wallet FROM user_balance WHERE guild_id = ? AND user_id = ?", guild_id, member.id, ) current = row[0] if row else 0 new_amount = max(0, current - amount) 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 = excluded.wallet", guild_id, member.id, new_amount, ) if lang == "ar": embed = success_embed("๐Ÿ’ธ ุนู…ู„ุงุช ู…ุญุฐูˆูุฉ", f"ุชู…ุช ุฅุฒุงู„ุฉ **{amount:,}** ุนู…ู„ุฉ ู…ู† {member.mention}.\nุงู„ุฑุตูŠุฏ ุงู„ุฌุฏูŠุฏ: **{new_amount:,}**") else: embed = success_embed("๐Ÿ’ธ Coins Removed", f"Removed **{amount:,}** coins from {member.mention}.\nNew balance: **{new_amount:,}**") await ctx.reply(embed=embed) @econadmin.command(name="salary") @commands.has_permissions(manage_guild=True) async def salary(self, ctx: commands.Context, min_salary: int, max_salary: int) -> None: """Set the work salary range.""" guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) min_salary = max(1, min_salary) max_salary = max(min_salary, max_salary) await self.bot.db.execute( "INSERT INTO economy_salaries(guild_id, min_salary, max_salary) VALUES (?, ?, ?) " "ON CONFLICT(guild_id) DO UPDATE SET min_salary = excluded.min_salary, max_salary = excluded.max_salary", guild_id, min_salary, max_salary, ) if lang == "ar": embed = success_embed("๐Ÿ’ผ ุฑูˆุงุชุจ ุงู„ุนู…ู„", f"ุชู… ุชุนูŠูŠู† ู†ุทุงู‚ ุงู„ุฑุงุชุจ: **{min_salary}** - **{max_salary}** ุนู…ู„ุฉ.") else: embed = success_embed("๐Ÿ’ผ Work Salary Set", f"Work salary range set to: **{min_salary}** - **{max_salary}** coins.") await ctx.reply(embed=embed) @econadmin.command(name="daily") @commands.has_permissions(manage_guild=True) async def daily_reward(self, ctx: commands.Context, min_reward: int, max_reward: int) -> None: """Set the daily reward range.""" guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) min_reward = max(1, min_reward) max_reward = max(min_reward, max_reward) await self.bot.db.execute( "INSERT INTO economy_salaries(guild_id, daily_min, daily_max) VALUES (?, ?, ?) " "ON CONFLICT(guild_id) DO UPDATE SET daily_min = excluded.daily_min, daily_max = excluded.daily_max", guild_id, min_reward, max_reward, ) if lang == "ar": embed = success_embed("๐ŸŒ… ุงู„ู…ูƒุงูุฃุฉ ุงู„ูŠูˆู…ูŠุฉ", f"ุชู… ุชุนูŠูŠู† ู†ุทุงู‚ ุงู„ู…ูƒุงูุฃุฉ: **{min_reward}** - **{max_reward}** ุนู…ู„ุฉ.") else: embed = success_embed("๐ŸŒ… Daily Reward Set", f"Daily reward range set to: **{min_reward}** - **{max_reward}** coins.") await ctx.reply(embed=embed) # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # MINI GAMES # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• @commands.hybrid_command(name="rps", description=get_cmd_desc("commands.economy.rps_desc")) async def rps(self, ctx: commands.Context, choice: str) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) picks = {"rock": "๐Ÿชจ", "paper": "๐Ÿ“„", "scissors": "โœ‚๏ธ"} arabic_picks = {"ุญุฌุฑ": "rock", "ูˆุฑู‚ุฉ": "paper", "ู…ู‚ุต": "scissors"} choice = choice.strip().lower() if choice in arabic_picks: choice = arabic_picks[choice] if choice not in picks: if lang == "ar": await ctx.reply("๐ŸŽฎ ุงุณุชุฎุฏู…: `ุญุฌุฑ` / `ูˆุฑู‚ุฉ` / `ู…ู‚ุต` ุฃูˆ `rock` / `paper` / `scissors`") else: await ctx.reply("๐ŸŽฎ Use: `rock` / `paper` / `scissors`") return bot_pick = random.choice(list(picks)) win = (choice, bot_pick) in {("rock", "scissors"), ("paper", "rock"), ("scissors", "paper")} draw = choice == bot_pick if win: reward = random.randint(15, 35) await self._add_coins(guild_id, ctx.author.id, reward) if lang == "ar": result = f"โœ… **ูุฒุช!** {E_FIRE} ุญุตู„ุช ุนู„ู‰ **{reward}** ุนู…ู„ุฉ!" else: result = f"โœ… **You win!** {E_FIRE} Earned **{reward}** coins!" elif draw: result = "๐Ÿค **ุชุนุงุฏู„!** ู„ุง ุฎุณุงุฑุฉ ูˆู„ุง ุฑุจุญ." if lang == "ar" else "๐Ÿค **Draw!** No coins lost or gained." else: result = "โŒ **ุฎุณุฑุช!** ุญุธ ุฃูˆูุฑ." if lang == "ar" else "โŒ **You lost!** Better luck next time." embed = gaming_embed("Rock Paper Scissors" if lang != "ar" else "ุญุฌุฑ ูˆุฑู‚ุฉ ู…ู‚ุต", f"๐ŸŽฎ {'ุฃู†ุช' if lang == 'ar' else 'You'}: {picks[choice]} vs Bot: {picks[bot_pick]}\n\n{result}") await ctx.reply(embed=embed) @commands.hybrid_command(name="guess", description=get_cmd_desc("commands.economy.guess_desc")) async def guess(self, ctx: commands.Context, number: int) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) number = max(1, min(10, number)) secret = random.randint(1, 10) if number == secret: reward = random.randint(20, 50) await self._add_coins(guild_id, ctx.author.id, reward) if lang == "ar": embed = success_embed("๐ŸŽฏ ุตุญูŠุญ!", f"ุงู„ุฑู‚ู… ูƒุงู† **{secret}**!\n\n{E_MONEY} ุญุตู„ุช ุนู„ู‰ **{reward}** ุนู…ู„ุฉ!") else: embed = success_embed("๐ŸŽฏ Correct!", f"The number was **{secret}**!\n\n{E_MONEY} You earned **{reward}** coins!") else: if lang == "ar": embed = error_embed("๐Ÿ˜… ุฎุทุฃ!", f"ุงู„ุฑู‚ู… ูƒุงู† **{secret}**. ุญุงูˆู„ ู…ุฑุฉ ุฃุฎุฑู‰!") else: embed = error_embed("๐Ÿ˜… Wrong Guess!", f"The number was **{secret}**. Try again!") await ctx.reply(embed=embed) @commands.hybrid_command(name="coinflip", description=get_cmd_desc("commands.economy.coinflip_desc")) async def coinflip_cmd(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id if ctx.guild else None lang = await self.bot.get_guild_language(guild_id) result = random.choice(["Heads", "Tails"]) emoji = "๐Ÿ‘‘" if result == "Heads" else "๐Ÿฆ…" if lang == "ar": result_ar = "ูˆุฌู‡" if result == "Heads" else "ุธู‡ุฑ" embed = gaming_embed("ุฑู…ูŠ ุงู„ุนู…ู„ุฉ", f"{emoji} ุงู„ุนู…ู„ุฉ ุณู‚ุทุช ุนู„ู‰ **{result_ar}**!") else: embed = gaming_embed("Coin Flip", f"{emoji} The coin landed on **{result}**!") await ctx.reply(embed=embed) # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # TOURNAMENTS # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• @commands.hybrid_group(name="tournament", fallback="create", description="Tournament system") async def tournament(self, ctx: commands.Context, name: str, *, games: str = "chess, checkers, connect4, othello") -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) selected_games = [g.strip() for g in games.split(",") if g.strip()][:6] if not selected_games: selected_games = ["Valorant"] await self.bot.db.execute( "INSERT OR REPLACE INTO tournaments(guild_id, name, status, created_by, winner_id) VALUES (?, ?, 'open', ?, NULL)", guild_id, name, ctx.author.id, ) await self.bot.db.execute( "DELETE FROM tournament_games WHERE guild_id = ? AND tournament_name = ?", guild_id, name, ) for game in selected_games: await self.bot.db.execute( "INSERT OR IGNORE INTO tournament_games(guild_id, tournament_name, game_name) VALUES (?, ?, ?)", guild_id, name, game, ) if lang == "ar": desc = ( f"{panel_divider('lime')}\n" f"๐ŸŽฎ **ุงู„ุฃู„ุนุงุจ:** {', '.join(selected_games)}\n" f"๐Ÿ‘ฅ ุงุณุชุฎุฏู… ุงู„ุฒุฑ ุฃุฏู†ุงู‡ ู„ู„ุงู†ุถู…ุงู…!\n" f"โš™๏ธ ุงู„ู…ุดุฑููˆู† ูŠู…ูƒู†ู‡ู… ุงุณุชุฎุฏุงู… `/tournament match` ู„ู„ุฃู„ุนุงุจ ุงู„ุญู‚ูŠู‚ูŠุฉ.\n" f"{panel_divider('lime')}" ) title = f"๐Ÿ ุจุทูˆู„ุฉ ุฌุฏูŠุฏุฉ: {name}" players_text = "0 ุงู†ุถู…ูˆุง ุญุชู‰ ุงู„ุขู† โ€” ูƒู† ุงู„ุฃูˆู„!" status_text = "ู…ูุชูˆุญ" else: desc = ( f"{panel_divider('lime')}\n" f"๐ŸŽฎ **Games:** {', '.join(selected_games)}\n" f"๐Ÿ‘ฅ Use the button below to join!\n" f"โš™๏ธ Admins can use `/tournament match` for real games.\n" f"{panel_divider('lime')}" ) title = f"๐Ÿ New Tournament: {name}" players_text = "0 joined yet โ€” be the first!" status_text = "Open" embed = discord.Embed( title=title, description=desc, color=NEON_LIME, ) embed.add_field(name="๐Ÿ‘ฅ " + ("ุงู„ู„ุงุนุจูŠู†" if lang == "ar" else "Players"), value=players_text, inline=True) embed.add_field(name="โš™๏ธ " + ("ุงู„ุญุงู„ุฉ" if lang == "ar" else "Status"), value=status_text, inline=True) embed.set_footer(text=f"Tip: use /tournament panel {name} anytime") await ctx.reply(embed=embed, view=TournamentJoinView(self, guild_id, name)) @tournament.command(name="close") @commands.has_permissions(manage_guild=True) async def tournament_close(self, ctx: commands.Context, *, name: str) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) await self.bot.db.execute( "UPDATE tournaments SET status = 'closed' WHERE guild_id = ? AND name = ?", guild_id, name, ) if lang == "ar": await ctx.reply(f"๐Ÿ”’ ุงู„ุจุทูˆู„ุฉ **{name}** ู…ุบู„ู‚ุฉ ู„ู„ู…ุดุงุฑูƒูŠู† ุงู„ุฌุฏุฏ.") else: await ctx.reply(f"๐Ÿ”’ Tournament **{name}** closed for new participants.") @tournament.command(name="play") @commands.has_permissions(manage_guild=True) async def tournament_play(self, ctx: commands.Context, min_players: int = 2, *, name: str) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) rows = await self.bot.db.fetchall( "SELECT user_id FROM tournament_participants WHERE guild_id = ? AND tournament_name = ?", guild_id, name, ) min_players = max(1, min(64, min_players)) if len(rows) < min_players: if lang == "ar": await ctx.reply(f"โŒ ูŠุญุชุงุฌ ุนู„ู‰ ุงู„ุฃู‚ู„ **{min_players}** ู„ุงุนุจูŠู†.") else: await ctx.reply(f"โŒ Need at least **{min_players}** players.") return winner_id = random.choice(rows)[0] games_rows = await self.bot.db.fetchall( "SELECT game_name FROM tournament_games WHERE guild_id = ? AND tournament_name = ?", guild_id, name, ) chosen_game = random.choice(games_rows)[0] if games_rows else "Random Game" reward = random.randint(120, 300) await self._add_coins(guild_id, winner_id, reward) await self.bot.db.execute( "UPDATE tournaments SET winner_id = ?, status = 'finished' WHERE guild_id = ? AND name = ?", winner_id, guild_id, name, ) if lang == "ar": embed = success_embed(f"๐Ÿ† ูุงุฆุฒ ุงู„ุจุทูˆู„ุฉ!", f"๐ŸŽ‰ <@{winner_id}> ูุงุฒ ุจู€ **{chosen_game}**!\n\n{E_MONEY} ุงู„ุฌุงุฆุฒุฉ: **{reward}** ุนู…ู„ุฉ!") else: embed = success_embed(f"๐Ÿ† Tournament Winner!", f"๐ŸŽ‰ <@{winner_id}> won **{chosen_game}**!\n\n{E_MONEY} Prize: **{reward}** coins!") await ctx.reply(embed=embed) @tournament.command(name="panel") async def tournament_panel(self, ctx: commands.Context, *, name: str) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) row = await self.bot.db.fetchone( "SELECT name, status, winner_id FROM tournaments WHERE guild_id = ? AND name = ?", guild_id, name, ) if not row: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ุงู„ุจุทูˆู„ุฉ ุบูŠุฑ ู…ุชุงุญุฉ", "ู„ุง ุชูˆุฌุฏ ุจุทูˆู„ุฉ ุจู‡ุฐุง ุงู„ุงุณู… ุญุงู„ูŠุงู‹.", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Tournament Not Found", "No tournament exists with that name right now.", guild=ctx.guild, bot=self.bot, )) return _, status, winner_id = row games_rows = await self.bot.db.fetchall( "SELECT game_name FROM tournament_games WHERE guild_id = ? AND tournament_name = ? ORDER BY game_name ASC", guild_id, name, ) games = ", ".join(game for (game,) in games_rows) if games_rows else ("ู„ู… ุชุญุฏุฏ" if lang == "ar" else "Not set") participants = await self.bot.db.fetchall( "SELECT user_id FROM tournament_participants WHERE guild_id = ? AND tournament_name = ?", guild_id, name, ) tournament_lb = await self.bot.db.fetchall( "SELECT winner_id, COUNT(*) as wins FROM tournaments WHERE guild_id = ? AND winner_id IS NOT NULL GROUP BY winner_id ORDER BY wins DESC LIMIT 5", guild_id, ) if lang == "ar": desc = f"{panel_divider('orange')}\n๐ŸŽฎ ุงู†ุถู… ู…ู† ุงู„ุฒุฑ ุฃุฏู†ุงู‡ ูˆุงุจุฏุฃ ุงู„ู…ู†ุงูุณุฉ!" title = f"๐ŸŽฎ ู„ูˆุญุฉ ุงู„ุจุทูˆู„ุฉ: {name}" else: desc = f"{panel_divider('orange')}\n๐ŸŽฎ Join from the button below and start battling!" title = f"๐ŸŽฎ Tournament Panel: {name}" embed = discord.Embed( title=title, description=desc, color=NEON_LIME, ) embed.add_field(name="๐ŸŽฏ " + ("ุงู„ุฃู„ุนุงุจ" if lang == "ar" else "Games Pool"), value=games, inline=False) embed.add_field(name="๐Ÿ‘ฅ " + ("ุงู„ู…ุดุงุฑูƒูŠู†" if lang == "ar" else "Joined"), value=str(len(participants)), inline=True) embed.add_field(name="๐Ÿ“Œ " + ("ุงู„ุญุงู„ุฉ" if lang == "ar" else "Status"), value=str(status or "open"), inline=True) embed.add_field(name="๐Ÿฅ‡ " + ("ุงู„ูุงุฆุฒ ุงู„ุญุงู„ูŠ" if lang == "ar" else "Current Winner"), value=(f"<@{winner_id}>" if winner_id else ("ู„ุง ูŠูˆุฌุฏ" if lang == "ar" else "None yet")), inline=True) if tournament_lb: lb_lines = [f"{idx}. <@{uid}> โ€” {wins}" for idx, (uid, wins) in enumerate(tournament_lb, start=1)] embed.add_field(name="๐Ÿ† " + ("ู„ูŠุฏุฑุจูˆุฑุฏ ุงู„ุจุทูˆู„ุงุช" if lang == "ar" else "Tournament Leaderboard"), value="\n".join(lb_lines), inline=False) embed.add_field(name="๐Ÿงญ " + ("ุงู„ุฃูˆุงู…ุฑ" if lang == "ar" else "Commands"), value="`/tournament games` โ€ข `/tournament match` โ€ข `/tournament play`", inline=True) await ctx.reply(embed=embed, view=TournamentJoinView(self, guild_id, name)) @tournament.command(name="games") async def tournament_games(self, ctx: commands.Context, *, name: str) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) rows = await self.bot.db.fetchall( "SELECT game_name FROM tournament_games WHERE guild_id = ? AND tournament_name = ? ORDER BY game_name ASC", guild_id, name, ) if not rows: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ู‚ุงุฆู…ุฉ ุงู„ุฃู„ุนุงุจ ูุงุฑุบุฉ", "ู„ู… ูŠุชู… ุฅุนุฏุงุฏ ุฃู„ุนุงุจ ู„ู‡ุฐู‡ ุงู„ุจุทูˆู„ุฉ ุจุนุฏ.", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Tournament Games Idle", "No games are configured for this tournament yet.", guild=ctx.guild, bot=self.bot, )) return games = "๐ŸŽฎ " + " โ€ข ".join(game for (game,) in rows) await ctx.reply(games) @tournament.command(name="match") @commands.has_permissions(manage_guild=True) async def tournament_match( self, ctx: commands.Context, name: str, game: str, player1: discord.Member, player2: discord.Member | None = None, ) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) game = game.strip().lower() board_cog = self.bot.get_cog("BoardGames") if board_cog is None: if lang == "ar": await ctx.reply("โŒ ู…ูƒูˆู† ุงู„ุฃู„ุนุงุจ ุบูŠุฑ ู…ุญู…ู„.") else: await ctx.reply("โŒ BoardGames cog is not loaded.") return row = await self.bot.db.fetchone( "SELECT status FROM tournaments WHERE guild_id = ? AND name = ?", guild_id, name, ) if not row: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ุงู„ุจุทูˆู„ุฉ ุบูŠุฑ ู…ุชุงุญุฉ", "ู„ุง ุชูˆุฌุฏ ุจุทูˆู„ุฉ ุจู‡ุฐุง ุงู„ุงุณู… ุญุงู„ูŠุงู‹.", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Tournament Not Found", "No tournament exists with that name right now.", guild=ctx.guild, bot=self.bot, )) return game_rows = await self.bot.db.fetchall( "SELECT game_name FROM tournament_games WHERE guild_id = ? AND tournament_name = ?", guild_id, name, ) allowed = {g.lower() for (g,) in game_rows} if game_rows and game not in allowed: if lang == "ar": await ctx.reply(f"โŒ ุงู„ู„ุนุจุฉ `{game}` ู„ูŠุณุช ููŠ ู‚ุงุฆู…ุฉ ู‡ุฐู‡ ุงู„ุจุทูˆู„ุฉ.") else: await ctx.reply(f"โŒ Game `{game}` is not in this tournament's game list.") return joined_rows = await self.bot.db.fetchall( "SELECT user_id FROM tournament_participants WHERE guild_id = ? AND tournament_name = ?", guild_id, name, ) joined = {uid for (uid,) in joined_rows} if player1.id not in joined or (player2 and player2.id not in joined): if lang == "ar": await ctx.reply("โŒ ูƒู„ุง ุงู„ู„ุงุนุจูŠู† ูŠุฌุจ ุฃู† ูŠู†ุถู…ูˆุง ู„ู„ุจุทูˆู„ุฉ ุฃูˆู„ุงู‹.") else: await ctx.reply("โŒ Both players must join the tournament first.") return if lang == "ar": desc = ( f"๐ŸŽฎ ุจุฏุก ู…ุจุงุฑุงุฉ ุงู„ุจุทูˆู„ุฉ **{name}**\n" f"๐ŸŽฏ ุงู„ู„ุนุจุฉ: **{game}**\n" f"โš”๏ธ {player1.mention} ุถุฏ {(player2.mention if player2 else '๐Ÿค– ุจูˆุช')}" ) else: desc = ( f"๐ŸŽฎ Starting tournament match **{name}**\n" f"๐ŸŽฏ Game: **{game}**\n" f"โš”๏ธ {player1.mention} vs {(player2.mention if player2 else '๐Ÿค– Bot')}" ) await ctx.reply(desc) await board_cog.start_tournament_duel(ctx, game, player1, player2, tournament_name=name) @tournament.command(name="gamehub", description="Show tournament + gamehub quick actions") async def tournament_gamehub(self, ctx: commands.Context, *, name: str) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) row = await self.bot.db.fetchone( "SELECT status FROM tournaments WHERE guild_id = ? AND name = ?", guild_id, name, ) if not row: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ุงู„ุจุทูˆู„ุฉ ุบูŠุฑ ู…ุชุงุญุฉ", "ู„ุง ุชูˆุฌุฏ ุจุทูˆู„ุฉ ุจู‡ุฐุง ุงู„ุงุณู… ุญุงู„ูŠุงู‹.", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Tournament Not Found", "No tournament exists with that name right now.", guild=ctx.guild, bot=self.bot, )) return if lang == "ar": msg = ( f"๐Ÿ”— ุชู… ุฑุจุท ู…ุณุงุฑ ุงู„ุจุทูˆู„ุฉ **{name}** ู…ุน ุฃูˆุงู…ุฑ ุงู„ุฃู„ุนุงุจ.\n" f"ุงุณุชุฎุฏู…: `/tournament panel {name}` ุซู… `/tournament match {name} [player2]`.\n" f"ูŠู…ูƒู†ูƒ ุฃูŠุถุงู‹ ูุชุญ `/gamehub` ู„ุจุฏุก ุฃู„ุนุงุจ ุณุฑูŠุนุฉ ุฎุงุฑุฌ ุงู„ุจุทูˆู„ุฉ." ) else: msg = ( f"๐Ÿ”— Tournament **{name}** is linked with game commands.\n" f"Use: `/tournament panel {name}` then `/tournament match {name} [player2]`.\n" f"You can also open `/gamehub` for quick non-tournament games." ) await ctx.reply(msg) @commands.hybrid_command(name="tournament_lb", description=get_cmd_desc("commands.economy.tournament_lb_desc")) async def tournament_lb(self, ctx: commands.Context) -> None: guild_id = ctx.guild.id lang = await self.bot.get_guild_language(guild_id) rows = await self.bot.db.fetchall( "SELECT winner_id, COUNT(*) FROM tournaments WHERE guild_id = ? AND winner_id IS NOT NULL GROUP BY winner_id ORDER BY COUNT(*) DESC LIMIT 10", guild_id, ) if not rows: if lang == "ar": await ctx.reply(embed=await idle_embed_for_guild( "ู„ูˆุญุฉ ุงู„ุจุทูˆู„ุงุช ุงู„ุฎุงู…ู„ุฉ", "ู„ุง ูŠูˆุฌุฏ ูุงุฆุฒูˆู† ู…ุณุฌู„ูˆู† ุญุชู‰ ุงู„ุขู†.", guild=ctx.guild, bot=self.bot, )) else: await ctx.reply(embed=await idle_embed_for_guild( "Tournament Leaderboard Idle", "No tournament winners are recorded yet.", guild=ctx.guild, bot=self.bot, )) return lines = [] for idx, (uid, wins) in enumerate(rows, start=1): member = ctx.guild.get_member(uid) name = member.display_name if member else str(uid) badge = {1: "๐Ÿฅ‡", 2: "๐Ÿฅˆ", 3: "๐Ÿฅ‰"}.get(idx, f"#{idx}") if lang == "ar": lines.append(f"{badge} **{name}** โ€” {wins} ููˆุฒ") else: lines.append(f"{badge} **{name}** โ€” {wins} wins") if lang == "ar": title = "๐Ÿ† ุฃุจุทุงู„ ุงู„ุจุทูˆู„ุงุช" else: title = "๐Ÿ† Tournament Champions" embed = discord.Embed( title=title, description=f"{panel_divider('lime')}\n" + "\n".join(lines) + f"\n{panel_divider('lime')}", color=NEON_LIME, ) await ctx.reply(embed=embed) async def setup(bot: commands.Bot) -> None: await bot.add_cog(Engagement(bot))