forked from sass/tipibot
Merge branch 'master' of https://git.lapikud.ee/renkar/tipibot into fienta
This commit is contained in:
106
core/economy.py
106
core/economy.py
@@ -12,6 +12,8 @@ import random
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
from typing import TypedDict
|
||||
|
||||
import aiohttp
|
||||
|
||||
from . import pb_client
|
||||
|
||||
import strings
|
||||
@@ -19,6 +21,11 @@ import strings
|
||||
_txn_log = logging.getLogger("tipiCOIN.txn")
|
||||
|
||||
|
||||
class DatabaseError(Exception):
|
||||
"""Raised when PocketBase is unreachable or returns an error."""
|
||||
pass
|
||||
|
||||
|
||||
def _txn(event: str, **fields) -> None:
|
||||
"""Log a single economy transaction to the transactions logger."""
|
||||
body = " ".join(f"{k}={v}" for k, v in fields.items())
|
||||
@@ -583,11 +590,15 @@ def format_td(td: timedelta) -> str:
|
||||
async def get_user(user_id: int) -> UserData:
|
||||
"""Fetch user data from PocketBase, creating a default record if first seen."""
|
||||
uid = str(user_id)
|
||||
record = await pb_client.get_record(uid)
|
||||
if record is None:
|
||||
default = _default_user()
|
||||
default["user_id"] = uid # type: ignore[typeddict-unknown-key]
|
||||
record = await pb_client.create_record(default)
|
||||
try:
|
||||
record = await pb_client.get_record(uid)
|
||||
if record is None:
|
||||
default = _default_user()
|
||||
default["user_id"] = uid # type: ignore[typeddict-unknown-key]
|
||||
record = await pb_client.create_record(default)
|
||||
except (aiohttp.ClientError, asyncio.TimeoutError, RuntimeError) as exc:
|
||||
_log.error("PocketBase unreachable for user %s: %s", user_id, exc)
|
||||
raise DatabaseError(f"Database unavailable: {exc}") from exc
|
||||
user = _default_user()
|
||||
for key in list(user.keys()):
|
||||
if key in record:
|
||||
@@ -696,7 +707,10 @@ async def _commit(user_id: int, user: UserData) -> None:
|
||||
# /daily
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_daily(user_id: int) -> dict:
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -772,7 +786,10 @@ _WORK_JOBS = strings.WORK_JOBS
|
||||
|
||||
|
||||
async def do_work(user_id: int) -> dict:
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -822,7 +839,10 @@ _BEG_JAIL_LINES = strings.BEG_JAIL_LINES
|
||||
|
||||
|
||||
async def do_beg(user_id: int) -> dict:
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -857,7 +877,10 @@ async def do_beg(user_id: int) -> dict:
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_fish_start(user_id: int) -> dict:
|
||||
"""Check cooldown + jail, set cooldown. Call before starting the fishing minigame."""
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
if jail := _is_jailed(user):
|
||||
@@ -975,7 +998,10 @@ async def do_fishbook(user_id: int) -> dict:
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_prestige(user_id: int) -> dict:
|
||||
"""Prestige: requires level 30, earns PP, resets balance/exp/items/cooldowns."""
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -1023,7 +1049,10 @@ async def do_prestige_buy(user_id: int, upgrade_id: str) -> dict:
|
||||
if upgrade_id not in PRESTIGE_SHOP:
|
||||
return {"ok": False, "reason": "not_found"}
|
||||
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -1117,7 +1146,10 @@ _CRIME_LOSE = strings.CRIME_LOSE
|
||||
|
||||
|
||||
async def do_crime(user_id: int) -> dict:
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -1210,10 +1242,16 @@ async def do_bail(user_id: int) -> dict:
|
||||
# /rob
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_rob(robber_id: int, target_id: int) -> dict:
|
||||
robber = await get_user(robber_id)
|
||||
try:
|
||||
robber = await get_user(robber_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if robber.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
target = await get_user(target_id)
|
||||
try:
|
||||
target = await get_user(target_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
|
||||
if cd := _cooldown_remaining(robber, "rob"):
|
||||
return {"ok": False, "reason": "cooldown", "remaining": cd}
|
||||
@@ -1285,7 +1323,10 @@ async def do_rob(robber_id: int, target_id: int) -> dict:
|
||||
# /roulette
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_roulette(user_id: int, bet: int, colour: str) -> dict:
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
if jail := _is_jailed(user):
|
||||
@@ -1325,7 +1366,10 @@ async def do_roulette(user_id: int, bet: int, colour: str) -> dict:
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_game_bet(user_id: int, bet: int, outcome: str) -> dict:
|
||||
"""Settle a simple win/tie/lose bet. outcome: 'win' | 'tie' | 'lose'."""
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
if jail := _is_jailed(user):
|
||||
@@ -1378,7 +1422,10 @@ def _spin() -> str:
|
||||
|
||||
|
||||
async def do_slots(user_id: int, bet: int) -> dict:
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
if jail := _is_jailed(user):
|
||||
@@ -1431,7 +1478,10 @@ async def do_slots(user_id: int, bet: int) -> dict:
|
||||
# /give
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_give(giver_id: int, receiver_id: int, amount: int) -> dict:
|
||||
giver = await get_user(giver_id)
|
||||
try:
|
||||
giver = await get_user(giver_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if giver.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -1441,7 +1491,10 @@ async def do_give(giver_id: int, receiver_id: int, amount: int) -> dict:
|
||||
if giver["balance"] < amount:
|
||||
return {"ok": False, "reason": "insufficient"}
|
||||
|
||||
receiver = await get_user(receiver_id)
|
||||
try:
|
||||
receiver = await get_user(receiver_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
giver["balance"] -= amount
|
||||
receiver["balance"] += amount
|
||||
giver["total_given"] = giver.get("total_given", 0) + amount
|
||||
@@ -1463,7 +1516,10 @@ async def do_give(giver_id: int, receiver_id: int, amount: int) -> dict:
|
||||
async def do_buy(user_id: int, item_id: str) -> dict:
|
||||
if item_id not in SHOP:
|
||||
return {"ok": False, "reason": "not_found"}
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
|
||||
@@ -1628,7 +1684,10 @@ async def do_set_reminders(user_id: int, commands: list[str]) -> None:
|
||||
# ---------------------------------------------------------------------------
|
||||
async def do_blackjack_bet(user_id: int, bet: int) -> dict:
|
||||
"""Deduct the initial blackjack bet. Returns ok/fail."""
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
if jail := _is_jailed(user):
|
||||
@@ -1683,7 +1742,10 @@ async def do_get_jailed() -> list[tuple[int, timedelta]]:
|
||||
|
||||
async def do_heist_check(user_id: int) -> dict:
|
||||
"""Check whether a user is eligible to join a heist."""
|
||||
user = await get_user(user_id)
|
||||
try:
|
||||
user = await get_user(user_id)
|
||||
except DatabaseError:
|
||||
return {"ok": False, "reason": "db_error"}
|
||||
if user.get("eco_banned"):
|
||||
return {"ok": False, "reason": "banned"}
|
||||
if jail := _is_jailed(user):
|
||||
|
||||
@@ -28,7 +28,7 @@ PB_ADMIN_EMAIL = config.PB_ADMIN_EMAIL
|
||||
PB_ADMIN_PASSWORD = config.PB_ADMIN_PASSWORD
|
||||
ECONOMY_COLLECTION = config.PB_ECONOMY_COLLECTION
|
||||
|
||||
_TIMEOUT = aiohttp.ClientTimeout(total=10)
|
||||
_TIMEOUT = aiohttp.ClientTimeout(total=4)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Persistent session (created once, reused for the lifetime of the process)
|
||||
|
||||
Reference in New Issue
Block a user