feat(status): show money-supply metric in /status

Adds economy.get_economy_stats() (single-scan total coins, house balance,
player-held coins, player count) and a "Rahavaru" field to /status, so admins
can see whether the sinks are keeping pace with minted income. Also removed a
duplicate bot_admin_check import.

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:
Rene Arumetsa
2026-09-04 02:28:19 +03:00
parent eb7346b851
commit 4cd13503a7
4 changed files with 72 additions and 1 deletions

View File

@@ -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}")

View File

@@ -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,
}

View File

@@ -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": "-",

View File

@@ -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