"""House account: the bot's own balance, fed by fines and lost bets.""" from __future__ import annotations from .. import pb_client from ..pb_client import DatabaseError from .store import _log, _now, get_user, _commit # --------------------------------------------------------------------------- # House account (bot user) # --------------------------------------------------------------------------- HOUSE_ID: int | None = None _house_pb_id: str | None = None def set_house(user_id: int) -> None: """Register the bot's Discord user ID as the house account.""" global HOUSE_ID, _house_pb_id if HOUSE_ID != user_id: _house_pb_id = None HOUSE_ID = user_id async def _house_record_id() -> str | None: """PocketBase record id of the house account (cached; creates the record on first use).""" global _house_pb_id if HOUSE_ID is None: return None if _house_pb_id is None: house = await get_user(HOUSE_ID) _house_pb_id = house.get("_pb_id") # type: ignore[typeddict-item] return _house_pb_id async def _credit_house(amount: int) -> None: """Add `amount` coins to the house via an atomic PocketBase increment. Deliberately lock-free: callers hold per-user locks, so this must never acquire one itself (see the locking rules above).""" if amount <= 0: return record_id = await _house_record_id() if record_id is None: return await pb_client.update_record(record_id, {"balance+": amount}) async def get_heist_global_cd() -> float: """Return unix timestamp until which no new heist can start. Persisted on house record.""" if HOUSE_ID is None: return 0.0 house = await get_user(HOUSE_ID) return float(house.get("heist_global_cd_until") or 0) async def set_heist_global_cd(until: float) -> None: """Persist heist global cooldown expiry to the house account in PocketBase.""" record_id = await _house_record_id() if record_id is None: return await pb_client.update_record(record_id, {"heist_global_cd_until": until}) async def _refund_house_safe(amount: int, context: str, related_uid: int) -> None: """Best-effort refund of `amount` to the house. Logs critical if it fails.""" if HOUSE_ID is None or amount <= 0: return try: await _credit_house(amount) except DatabaseError as exc: _log.critical( "House compensation failed (%s, related uid %s, amount %s): %s", context, related_uid, amount, exc, ) async def _debit_house_safe(amount: int, context: str, uid: int) -> None: """Best-effort atomic debit of `amount` from the house. Compensates a fine that was credited to the house but whose matching user debit failed to persist (the coins must be pulled back out of the house). Logs critical if it fails.""" if HOUSE_ID is None or amount <= 0: return try: record_id = await _house_record_id() if record_id: await pb_client.update_record(record_id, {"balance-": amount}) except DatabaseError as exc: _log.critical( "House debit compensation failed (%s, related uid %s, amount %s): %s", context, uid, amount, exc, )