diff --git a/commands/ops_admin_commands.py b/commands/ops_admin_commands.py index 00c1547..1099170 100644 --- a/commands/ops_admin_commands.py +++ b/commands/ops_admin_commands.py @@ -13,9 +13,9 @@ from pathlib import Path import discord from discord import app_commands +from core import economy from core.admin import bot_admin_check import strings as S -from core.admin import bot_admin_check def register_ops_admin_commands( @@ -45,6 +45,7 @@ def register_ops_admin_commands( latency_ms = round(bot.latency * 1000, 1) cache_size = get_member_cache_size() user_count = await count_economy_users() + eco_stats = await economy.get_economy_stats() embed = discord.Embed(title=S.STATUS_UI["title"], color=0x57F287) embed.add_field( @@ -82,6 +83,18 @@ def register_ops_admin_commands( value=str(cache_size), inline=True, ) + total = eco_stats["total_coins"] + house_pct = round(eco_stats["house_balance"] / total * 100) if total else 0 + embed.add_field( + name=S.STATUS_UI["supply_field"], + value=S.STATUS_UI["supply_val"].format( + total=f"{total:,}".replace(",", " "), + players=f"{eco_stats['player_coins']:,}".replace(",", " "), + house=f"{eco_stats['house_balance']:,}".replace(",", " "), + house_pct=house_pct, + ), + inline=False, + ) log_lines = [ S.STATUS_UI["log_line"].format(name=p.name, size_kb=f"{p.stat().st_size / 1024:.1f}") diff --git a/core/economy/leaderboards.py b/core/economy/leaderboards.py index 55be52f..7e31d34 100644 --- a/core/economy/leaderboards.py +++ b/core/economy/leaderboards.py @@ -3,6 +3,7 @@ from __future__ import annotations from .. import pb_client +from . import house from .levels import get_level @@ -109,3 +110,30 @@ async def get_all_leaderboards() -> dict[str, list[tuple]]: "fish": [(r["user_id"], r.get("total_fish_caught", 0)) for r in desc(lambda r: r.get("total_fish_caught", 0))], } + + +async def get_economy_stats() -> dict[str, int]: + """Money-supply snapshot from a single scan: total coins in circulation, how + much players hold vs. the house. Lets /status show whether the sinks (house, + vanity burn, bail, fines) are keeping pace with minted income.""" + records = await pb_client.list_all_records() + house_id = str(house.HOUSE_ID) if house.HOUSE_ID is not None else None + total = 0 + house_balance = 0 + player_count = 0 + for r in records: + uid = r.get("user_id") + if not uid: + continue + bal = r.get("balance", 0) or 0 + total += bal + if uid == house_id: + house_balance = bal + else: + player_count += 1 + return { + "total_coins": total, + "house_balance": house_balance, + "player_coins": total - house_balance, + "player_count": player_count, + } diff --git a/strings/common.py b/strings/common.py index 4308af8..0a70b73 100644 --- a/strings/common.py +++ b/strings/common.py @@ -243,6 +243,8 @@ STATUS_UI: dict[str, str] = { "tasks_field": "🔄 Async tasks", "eco_players_field": "👤 Eco players", "members_cache_field": "📋 Liikmed (cache)", + "supply_field": "💰 Rahavaru", + "supply_val": "Kokku {total}\nMängijatel {players}\nKassas {house} ({house_pct}%)", "log_files_field": "📂 Log files", "log_line": "`{name}` - {size_kb} KB", "none": "-", diff --git a/tests/test_leaderboards.py b/tests/test_leaderboards.py index f150442..c3f79bc 100644 --- a/tests/test_leaderboards.py +++ b/tests/test_leaderboards.py @@ -47,3 +47,31 @@ class TestGetAllLeaderboards: fish = run(economy.get_all_leaderboards())["fish"] counts = [c for _, c in fish] assert counts == sorted(counts, reverse=True) + + +class TestEconomyStats: + def test_totals_split_house_from_players(self, fake_pb, monkeypatch): + monkeypatch.setattr(economy.house, "HOUSE_ID", 999) + monkeypatch.setattr(economy.house, "_house_pb_id", None) + # three players + the house + for uid, bal in [(1, 100), (2, 250), (3, 0)]: + run(economy.get_user(uid)) + fake_pb.record_for(uid)["balance"] = bal + run(economy.get_user(999)) + fake_pb.record_for(999)["balance"] = 5000 + + stats = run(economy.get_economy_stats()) + assert stats["total_coins"] == 100 + 250 + 0 + 5000 + assert stats["house_balance"] == 5000 + assert stats["player_coins"] == 350 + assert stats["player_count"] == 3 # house excluded + + def test_no_house_configured(self, fake_pb, monkeypatch): + monkeypatch.setattr(economy.house, "HOUSE_ID", None) + run(economy.get_user(1)) + fake_pb.record_for(1)["balance"] = 42 + stats = run(economy.get_economy_stats()) + assert stats["total_coins"] == 42 + assert stats["house_balance"] == 0 + assert stats["player_coins"] == 42 + assert stats["player_count"] == 1