feat(economy): add /bank vault (rob-proof storage) + net-worth leaderboard
A strategic counterpart to /rob: coins moved to the bank are safe from /rob and
/heist (which only touch liquid balance) but earn no Bot Farm interest and can't
be spent, gambled or given until withdrawn - the deliberate trade-off against
keeping coins liquid.
- New bank.py (do_deposit/do_withdraw), bank_balance schema field.
- /bank (view), /deposit, /withdraw commands ('all' supported), with db_error
handling; /balance shows the vault when non-zero.
- Coins leaderboard and /status money-supply now count net worth
(balance + bank_balance), so banking never hides you from the board or the
supply metric.
- Season reset wipes bank_balance too (no cross-season wealth hiding).
10 tests: deposit/withdraw math, guards, coin conservation, rob cannot touch the
vault, and net-worth accounting on the leaderboard + stats.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VbAVsrZuYesea99mPMmPT
This commit is contained in:
@@ -363,6 +363,69 @@ def register_economy_support_commands(
|
||||
embed.set_footer(text=foot)
|
||||
await msg.edit(embed=embed)
|
||||
|
||||
# -- /bank, /deposit, /withdraw -----------------------------------------
|
||||
@tree.command(name="bank", description=S.CMD["bank"])
|
||||
async def cmd_bank(interaction: discord.Interaction):
|
||||
data = await economy.get_user(interaction.user.id)
|
||||
embed = discord.Embed(
|
||||
title=S.BANK_UI["title"], description=S.BANK_UI["desc"], color=0xF4C430
|
||||
)
|
||||
embed.add_field(name=S.BANK_UI["f_liquid"], value=coin(data.get("balance", 0)), inline=True)
|
||||
embed.add_field(name=S.BANK_UI["f_bank"], value=coin(data.get("bank_balance", 0)), inline=True)
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
|
||||
@tree.command(name="deposit", description=S.CMD["deposit"])
|
||||
@app_commands.describe(summa=S.OPT["deposit_summa"])
|
||||
async def cmd_deposit(interaction: discord.Interaction, summa: str):
|
||||
data = await economy.get_user(interaction.user.id)
|
||||
amount, err = parse_amount(summa, data.get("balance", 0))
|
||||
if err or amount is None:
|
||||
await interaction.response.send_message(err or S.ERR["invalid_amount"], ephemeral=True)
|
||||
return
|
||||
if amount <= 0:
|
||||
await interaction.response.send_message(S.ERR["positive_amount"], ephemeral=True)
|
||||
return
|
||||
res = await economy.do_deposit(interaction.user.id, amount)
|
||||
if not res["ok"]:
|
||||
if res["reason"] == "db_error":
|
||||
await reply_db_error(interaction)
|
||||
elif res["reason"] == "banned":
|
||||
await interaction.response.send_message(S.MSG_BANNED, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(S.BANK_UI["nothing_liquid"], ephemeral=True)
|
||||
return
|
||||
await interaction.response.send_message(
|
||||
S.BANK_UI["deposited"].format(
|
||||
amount=coin(res["amount"]), balance=coin(res["balance"]), bank=coin(res["bank"])
|
||||
)
|
||||
)
|
||||
|
||||
@tree.command(name="withdraw", description=S.CMD["withdraw"])
|
||||
@app_commands.describe(summa=S.OPT["withdraw_summa"])
|
||||
async def cmd_withdraw(interaction: discord.Interaction, summa: str):
|
||||
data = await economy.get_user(interaction.user.id)
|
||||
amount, err = parse_amount(summa, data.get("bank_balance", 0))
|
||||
if err or amount is None:
|
||||
await interaction.response.send_message(err or S.ERR["invalid_amount"], ephemeral=True)
|
||||
return
|
||||
if amount <= 0:
|
||||
await interaction.response.send_message(S.ERR["positive_amount"], ephemeral=True)
|
||||
return
|
||||
res = await economy.do_withdraw(interaction.user.id, amount)
|
||||
if not res["ok"]:
|
||||
if res["reason"] == "db_error":
|
||||
await reply_db_error(interaction)
|
||||
elif res["reason"] == "banned":
|
||||
await interaction.response.send_message(S.MSG_BANNED, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(S.BANK_UI["nothing_bank"], ephemeral=True)
|
||||
return
|
||||
await interaction.response.send_message(
|
||||
S.BANK_UI["withdrawn"].format(
|
||||
amount=coin(res["amount"]), balance=coin(res["balance"]), bank=coin(res["bank"])
|
||||
)
|
||||
)
|
||||
|
||||
class RemindersSelect(discord.ui.Select):
|
||||
def __init__(self, user_id: int, current: list[str]):
|
||||
self.user_id = user_id
|
||||
|
||||
Reference in New Issue
Block a user