from __future__ import annotations import asyncio from collections.abc import Awaitable, Callable import discord from discord import app_commands from core import economy import strings as S def register_prestige_commands( tree: app_commands.CommandTree, check_cmd_rate: Callable[[discord.Interaction], Awaitable[bool]], ensure_level_role: Callable[[discord.Member, int], Awaitable[None]], ) -> None: class PrestigeView(discord.ui.View): def __init__(self, user_id: int, tab: str = "status"): super().__init__(timeout=60) self.user_id = user_id self.tab = tab async def _rebuild(self, data: dict): self.clear_items() pp = data.get("prestige_points", 0) upgrades: dict = data.get("prestige_upgrades") or {} exp = data.get("exp", 0) level = economy.get_level(exp) for tab_id, label in (("status", S.PRESTIGE_UI["btn_tab_status"]), ("shop", S.PRESTIGE_UI["btn_tab_shop"])): btn = discord.ui.Button( label=label, style=discord.ButtonStyle.primary if tab_id == self.tab else discord.ButtonStyle.secondary, disabled=(tab_id == self.tab), row=0, ) btn.callback = self._switch_tab(tab_id) self.add_item(btn) if self.tab == "status" and level >= economy.PRESTIGE_MIN_LEVEL: confirm_btn = discord.ui.Button( label=S.PRESTIGE_UI["btn_confirm"], style=discord.ButtonStyle.danger, row=1, ) confirm_btn.callback = self._do_prestige() cancel_btn = discord.ui.Button( label=S.PRESTIGE_UI["btn_cancel"], style=discord.ButtonStyle.secondary, row=1, ) cancel_btn.callback = self._do_cancel() self.add_item(confirm_btn) self.add_item(cancel_btn) elif self.tab == "shop": for uid, item in economy.PRESTIGE_SHOP.items(): cur_level = upgrades.get(uid, 0) if cur_level >= item["max_level"]: continue cost = item["pp_cost"] btn = discord.ui.Button( label=S.PRESTIGE_UI["btn_buy_upgrade"].format(emoji=item["emoji"], name=S.PRESTIGE_SHOP_NAMES[uid], cost=cost), style=discord.ButtonStyle.success if pp >= cost else discord.ButtonStyle.secondary, disabled=(pp < cost), row=1, ) btn.callback = self._buy_upgrade(uid) self.add_item(btn) def _build_status_embed(self, data: dict) -> discord.Embed: exp = data.get("exp", 0) level = economy.get_level(exp) pp = data.get("prestige_points", 0) p_level = data.get("prestige_level", 0) if level >= economy.PRESTIGE_MIN_LEVEL: pp_preview = max(1, exp // 1000) embed = discord.Embed( title=S.TITLE["prestige_confirm"], description=S.PRESTIGE_UI["confirm_desc"].format(level=level, exp=exp, pp=pp_preview), color=0xF4C430, ) else: embed = discord.Embed( title=S.TITLE["prestige_too_low"], description=S.PRESTIGE_UI["too_low_desc"].format(level=level, required=economy.PRESTIGE_MIN_LEVEL), color=0xED4245, ) if p_level > 0: embed.set_footer(text=S.PRESTIGE_UI["status_footer"].format(level=p_level, pp=pp)) return embed def _build_shop_embed(self, data: dict) -> discord.Embed: pp = data.get("prestige_points", 0) upgrades: dict = data.get("prestige_upgrades") or {} embed = discord.Embed( title=S.TITLE["prestige_shop"], description=S.PRESTIGE_UI["shop_desc"].format(pp=pp), color=0xF4C430, ) for uid, item in economy.PRESTIGE_SHOP.items(): cur_level = upgrades.get(uid, 0) name = f"{item['emoji']} {S.PRESTIGE_SHOP_NAMES[uid]}" cost_str = S.PRESTIGE_UI["shop_maxed"] if cur_level >= item["max_level"] else S.PRESTIGE_UI["shop_cost_fmt"].format(cost=item["pp_cost"]) level_str = S.PRESTIGE_UI["shop_level_fmt"].format(cur=cur_level, max=item["max_level"]) embed.add_field(name=f"{name} · {level_str} · {cost_str}", value=S.PRESTIGE_SHOP_DESCRIPTIONS[uid], inline=False) return embed def _switch_tab(self, tab_id: str): async def _cb(interaction: discord.Interaction): if interaction.user.id != self.user_id: await interaction.response.send_message(S.ERR["not_your_menu"], ephemeral=True) return self.tab = tab_id await interaction.response.defer() data = await economy.get_user(self.user_id) await self._rebuild(data) embed = self._build_status_embed(data) if tab_id == "status" else self._build_shop_embed(data) await interaction.edit_original_response(embed=embed, view=self) return _cb def _do_prestige(self): async def _cb(interaction: discord.Interaction): if interaction.user.id != self.user_id: await interaction.response.send_message(S.ERR["not_your_game"], ephemeral=True) return await interaction.response.defer() res = await economy.do_prestige(self.user_id) self.clear_items() if not res["ok"]: embed = discord.Embed( title=S.TITLE["prestige_too_low"], description=S.PRESTIGE_UI["too_low_desc"].format(level=res.get("level", 0), required=res.get("required", 30)), color=0xED4245, ) else: embed = discord.Embed( title=S.TITLE["prestige_success"].format(level=res["prestige_level"]), description=S.PRESTIGE_UI["success_desc"].format(pp=res["pp_earned"], level=res["prestige_level"], total_pp=res["prestige_points"]), color=0xF4C430, ) await interaction.edit_original_response(embed=embed, view=self) if res.get("ok") and interaction.guild: member = interaction.guild.get_member(self.user_id) if member: asyncio.create_task(ensure_level_role(member, 1)) return _cb def _do_cancel(self): async def _cb(interaction: discord.Interaction): if interaction.user.id != self.user_id: await interaction.response.send_message(S.ERR["not_your_game"], ephemeral=True) return self.clear_items() await interaction.response.edit_message(view=self) return _cb def _buy_upgrade(self, upgrade_id: str): async def _cb(interaction: discord.Interaction): if interaction.user.id != self.user_id: await interaction.response.send_message(S.ERR["not_your_menu"], ephemeral=True) return await interaction.response.defer() res = await economy.do_prestige_buy(self.user_id, upgrade_id) if not res["ok"]: if res["reason"] == "insufficient_pp": err = S.PRESTIGE_UI["buy_no_pp"].format(have=res["have"], need=res["need"]) elif res["reason"] == "maxed": err = S.PRESTIGE_UI["buy_maxed"] else: err = S.ERR["generic_error"].format(error=res["reason"]) await interaction.followup.send(err, ephemeral=True) return data = await economy.get_user(self.user_id) await self._rebuild(data) embed = self._build_shop_embed(data) item = economy.PRESTIGE_SHOP[res["upgrade_id"]] name = f"{item['emoji']} {S.PRESTIGE_SHOP_NAMES[res['upgrade_id']]}" embed.description = S.PRESTIGE_UI["buy_success_desc"].format( name=name, new_level=res["new_level"], max_level=item["max_level"], pp=res["pp_remaining"], ) + "\n\n" + (embed.description or "") await interaction.edit_original_response(embed=embed, view=self) return _cb async def on_timeout(self): self.clear_items() @tree.command(name="prestige", description=S.CMD["prestige"]) @app_commands.guild_only() async def cmd_prestige(interaction: discord.Interaction): if await check_cmd_rate(interaction): return data = await economy.get_user(interaction.user.id) if data.get("eco_banned"): await interaction.response.send_message(S.MSG_BANNED, ephemeral=True) return view = PrestigeView(interaction.user.id) await view._rebuild(data) embed = view._build_status_embed(data) await interaction.response.send_message(embed=embed, view=view, ephemeral=True) @tree.command(name="prestigeshop", description=S.CMD["prestigeshop"]) async def cmd_prestigeshop(interaction: discord.Interaction): data = await economy.get_user(interaction.user.id) view = PrestigeView(interaction.user.id, tab="shop") await view._rebuild(data) embed = view._build_shop_embed(data) await interaction.response.send_message(embed=embed, view=view, ephemeral=True) @tree.command(name="prestigebuy", description=S.CMD["prestigebuy"]) @app_commands.describe(upgrade=S.OPT["prestigebuy_upgrade"]) async def cmd_prestigebuy(interaction: discord.Interaction, upgrade: str): if await check_cmd_rate(interaction): return res = await economy.do_prestige_buy(interaction.user.id, upgrade.strip().lower()) if not res["ok"]: if res["reason"] == "banned": await interaction.response.send_message(S.MSG_BANNED, ephemeral=True) elif res["reason"] == "not_found": await interaction.response.send_message(S.PRESTIGE_UI["buy_not_found"], ephemeral=True) elif res["reason"] == "maxed": await interaction.response.send_message(S.PRESTIGE_UI["buy_maxed"], ephemeral=True) elif res["reason"] == "insufficient_pp": await interaction.response.send_message( S.PRESTIGE_UI["buy_no_pp"].format(have=res["have"], need=res["need"]), ephemeral=True, ) return item = economy.PRESTIGE_SHOP[res["upgrade_id"]] name = f"{item['emoji']} {S.PRESTIGE_SHOP_NAMES[res['upgrade_id']]}" embed = discord.Embed( title=S.TITLE["prestige_buy_ok"], description=S.PRESTIGE_UI["buy_success_desc"].format( name=name, new_level=res["new_level"], max_level=res["max_level"], pp=res["pp_remaining"], ), color=0x57F287, ) await interaction.response.send_message(embed=embed, ephemeral=True)