1
0
forked from sass/tipibot

Compare commits

...

2 Commits

Author SHA1 Message Date
Rene Arumetsa
93f4d471dc Add db error in functions 2026-04-29 00:04:24 +03:00
Rene Arumetsa
de7cfce833 Lower pb token timeout 2026-04-29 00:00:18 +03:00
2 changed files with 85 additions and 23 deletions

View File

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

View File

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