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:
@@ -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}")
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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": "-",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user