92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
"""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 _refund_user_safe(_unused_house_id, amount: int, context: str, uid: int) -> None:
|
|
"""Best-effort atomic debit of `amount` from the house (compensates a failed
|
|
user fine). 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,
|
|
)
|