Refator eco code. Split into modules

This commit is contained in:
Rene Arumetsa
2026-07-27 00:49:34 +03:00
parent d89383ee20
commit 83a60cda55
21 changed files with 2413 additions and 2215 deletions

403
core/economy/income.py Normal file
View File

@@ -0,0 +1,403 @@
"""Income and social commands: daily, work, beg, crime, rob, give."""
from __future__ import annotations
import random
from datetime import date, timedelta
import strings
from ..pb_client import DatabaseError
from . import house
from .store import (
COOLDOWNS, JAIL_DURATION, PRESTIGE_SHOP, _commit, _cooldown_remaining,
_is_jailed, _locked_by, _log, _now, _prestige_mult, _txn, get_user,
)
from .house import _credit_house
# ---------------------------------------------------------------------------
# /daily
# ---------------------------------------------------------------------------
@_locked_by(0)
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"}
daily_cd = timedelta(hours=18) if "korvaklapid" in user["items"] else COOLDOWNS["daily"]
if cd := _cooldown_remaining(user, "daily", override_cd=daily_cd):
return {"ok": False, "reason": "cooldown", "remaining": cd}
today = _now().date()
last_str = user.get("last_streak_date")
last_date = date.fromisoformat(last_str) if last_str else None
if last_date is None:
streak = 1
elif (today - last_date).days == 1:
streak = user["daily_streak"] + 1
elif "karikas" in user["items"]:
streak = user["daily_streak"] # karikas: streak survives missed days
else:
streak = 1 # streak broken
# Streak multiplier tiers
if streak >= 14:
streak_mult = 3.0
elif streak >= 7:
streak_mult = 2.0
elif streak >= 3:
streak_mult = 1.5
else:
streak_mult = 1.0
vip = "lan_pass" in user["items"]
vip_mult = 2.0 if vip else 1.0
daily_plus_level = (user.get("prestige_upgrades") or {}).get("daily_plus", 0)
base = int(150 * (1.0 + daily_plus_level * PRESTIGE_SHOP["daily_plus"]["effect"]))
earned = int(base * streak_mult * vip_mult)
if "korvaklapid" in user["items"]:
earned += 25
coin_mult, _ = _prestige_mult(user)
earned = int(earned * coin_mult)
# Investor interest (capped at 500/day to prevent runaway wealth)
interest = 0
if "gaming_laptop" in user["items"] and user["balance"] > 0:
interest = min(int(user["balance"] * 0.05), 500)
earned += interest
user["balance"] += earned
user["last_daily"] = _now().isoformat()
user["daily_streak"] = streak
user["last_streak_date"] = today.isoformat()
user["lifetime_earned"] = user.get("lifetime_earned", 0) + earned
user["best_daily_streak"] = max(user.get("best_daily_streak", 0), streak)
user["peak_balance"] = max(user.get("peak_balance", 0), user["balance"])
await _commit(user_id, user)
_txn("DAILY", user=user_id, earned=f"+{earned}", streak=streak, bal=user["balance"])
return {
"ok": True,
"earned": earned,
"interest": interest,
"streak": streak,
"streak_mult": streak_mult,
"vip": vip,
"balance": user["balance"],
}
# ---------------------------------------------------------------------------
# /work
# ---------------------------------------------------------------------------
_WORK_JOBS = strings.WORK_JOBS
@_locked_by(0)
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"}
work_cd = timedelta(minutes=40) if "monitor" in user["items"] else COOLDOWNS["work"]
if cd := _cooldown_remaining(user, "work", override_cd=work_cd):
return {"ok": False, "reason": "cooldown", "remaining": cd}
if jail := _is_jailed(user):
return {"ok": False, "reason": "jailed", "remaining": jail}
job, job_mult = random.choice(_WORK_JOBS)
base = random.randint(15, 75)
worker_mult = 1.5 if "gaming_hiir" in user["items"] else 1.0
desk_mult = 1.25 if "reguleeritav_laud" in user["items"] else 1.0
lucky = False
if "energiajook" in user["items"] and random.random() < 0.30:
lucky = True
work_plus_level = (user.get("prestige_upgrades") or {}).get("work_plus", 0)
work_plus_mult = 1.0 + work_plus_level * PRESTIGE_SHOP["work_plus"]["effect"]
coin_mult, _ = _prestige_mult(user)
earned = int(base * job_mult * worker_mult * desk_mult * (3.0 if lucky else 1.0) * work_plus_mult * coin_mult)
user["balance"] += earned
user["last_work"] = _now().isoformat()
user["work_count"] = user.get("work_count", 0) + 1
user["lifetime_earned"] = user.get("lifetime_earned", 0) + earned
user["peak_balance"] = max(user.get("peak_balance", 0), user["balance"])
await _commit(user_id, user)
_txn("WORK", user=user_id, earned=f"+{earned}", lucky=lucky, bal=user["balance"])
return {
"ok": True,
"earned": earned,
"job": job,
"lucky": lucky,
"hiir": worker_mult > 1.0,
"laud": desk_mult > 1.0,
"balance": user["balance"],
}
# ---------------------------------------------------------------------------
# /beg
# ---------------------------------------------------------------------------
_BEG_LINES = strings.BEG_LINES
_BEG_JAIL_LINES = strings.BEG_JAIL_LINES
@_locked_by(0)
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"}
beg_cd = timedelta(minutes=3) if "hiirematt" in user["items"] else COOLDOWNS["beg"]
if cd := _cooldown_remaining(user, "beg", override_cd=beg_cd):
return {"ok": False, "reason": "cooldown", "remaining": cd}
jailed = bool(_is_jailed(user))
beg_mult = 2 if "klaviatuur" in user["items"] else 1
coin_mult, _ = _prestige_mult(user)
earned = int(random.randint(10, 40) * beg_mult * coin_mult)
user["balance"] += earned
user["last_beg"] = _now().isoformat()
user["beg_count"] = user.get("beg_count", 0) + 1
user["lifetime_earned"] = user.get("lifetime_earned", 0) + earned
user["peak_balance"] = max(user.get("peak_balance", 0), user["balance"])
await _commit(user_id, user)
_txn("BEG", user=user_id, earned=f"+{earned}", jailed=jailed, bal=user["balance"])
return {
"ok": True,
"earned": earned,
"text": random.choice(_BEG_JAIL_LINES if jailed else _BEG_LINES),
"klaviatuur": beg_mult > 1,
"jailed": jailed,
"balance": user["balance"],
}
# ---------------------------------------------------------------------------
# /crime
# ---------------------------------------------------------------------------
_CRIME_WIN = strings.CRIME_WIN
_CRIME_LOSE = strings.CRIME_LOSE
@_locked_by(0)
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"}
if cd := _cooldown_remaining(user, "crime"):
return {"ok": False, "reason": "cooldown", "remaining": cd}
if jail := _is_jailed(user):
return {"ok": False, "reason": "jailed", "remaining": jail}
user["last_crime"] = _now().isoformat()
win_chance = 0.75 if "cat6" in user["items"] else 0.60
user["crimes_attempted"] = user.get("crimes_attempted", 0) + 1
if random.random() < win_chance:
earned = random.randint(200, 500)
if "mikrofon" in user["items"]:
earned = int(earned * 1.3)
user["balance"] += earned
user["crimes_succeeded"] = user.get("crimes_succeeded", 0) + 1
user["lifetime_earned"] = user.get("lifetime_earned", 0) + earned
user["peak_balance"] = max(user.get("peak_balance", 0), user["balance"])
await _commit(user_id, user)
_txn("CRIME_WIN", user=user_id, earned=f"+{earned}", bal=user["balance"])
return {
"ok": True, "success": True,
"earned": earned, "text": random.choice(_CRIME_WIN),
"mikrofon": "mikrofon" in user["items"],
"balance": user["balance"],
}
else:
fine = random.randint(50, 150)
user["balance"] = max(0, user["balance"] - fine)
jailed = "gaming_tool" not in user["items"]
if jailed:
user["jailed_until"] = (_now() + JAIL_DURATION).isoformat()
user["jailbreak_used"] = False
user["times_jailed"] = user.get("times_jailed", 0) + 1
user["lifetime_lost"] = user.get("lifetime_lost", 0) + fine
await _commit(user_id, user)
await _credit_house(fine)
_txn("CRIME_FAIL", user=user_id, fine=f"-{fine}", jailed=jailed, bal=user["balance"])
return {
"ok": True, "success": False,
"fine": fine, "text": random.choice(_CRIME_LOSE),
"jailed": jailed,
"balance": user["balance"],
}
# ---------------------------------------------------------------------------
# /rob
# ---------------------------------------------------------------------------
@_locked_by(0, 1)
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}
target_jailed = bool(_is_jailed(target))
if jail := _is_jailed(robber):
if not target_jailed:
return {"ok": False, "reason": "jailed", "remaining": jail}
elif target_jailed:
return {"ok": False, "reason": "target_jailed"}
is_house = (house.HOUSE_ID is not None and target_id == house.HOUSE_ID)
if is_house and target["balance"] < 50:
return {"ok": False, "reason": "broke"}
if not is_house and target["balance"] < 100:
return {"ok": False, "reason": "broke"}
robber["last_rob"] = _now().isoformat()
if "anticheat" in target["items"] and not is_house:
fine = random.randint(100, 200)
robber["balance"] = max(0, robber["balance"] - fine)
# Decrement anticheat uses
uses = target.get("item_uses", {}).get("anticheat", 2) - 1
if "item_uses" not in target:
target["item_uses"] = {}
if uses <= 0:
target["items"] = [i for i in target["items"] if i != "anticheat"]
target["item_uses"].pop("anticheat", None)
else:
target["item_uses"]["anticheat"] = uses
robber["lifetime_lost"] = robber.get("lifetime_lost", 0) + fine
await _commit(robber_id, robber)
await _commit(target_id, target)
await _credit_house(fine)
_txn("ROB_BLOCKED", robber=robber_id, victim=target_id, fine=f"-{fine}", robber_bal=robber["balance"], ac_uses_left=uses)
return {"ok": True, "success": False, "reason": "valvur", "fine": fine}
# Robbing the house has lower success (35%) but jackpot chance
success_chance = 0.35 if is_house else (0.60 if "jellyfin" in robber["items"] else 0.45)
if random.random() < success_chance:
jackpot = is_house and random.random() < 0.10
if jackpot:
pct = 0.40
elif is_house:
pct = random.uniform(0.05, 0.15)
else:
pct = random.uniform(0.10, 0.25)
stolen = max(10, min(int(target["balance"] * pct), target["balance"]))
target["balance"] -= stolen
prev_lifetime_earned = robber.get("lifetime_earned", 0)
prev_biggest_win = robber.get("biggest_win", 0)
prev_peak_balance = robber.get("peak_balance", 0)
robber["balance"] += stolen
robber["lifetime_earned"] = prev_lifetime_earned + stolen
robber["biggest_win"] = max(prev_biggest_win, stolen)
robber["peak_balance"] = max(prev_peak_balance, robber["balance"])
try:
await _commit(robber_id, robber)
except DatabaseError:
return {"ok": False, "reason": "db_error"}
try:
await _commit(target_id, target)
except DatabaseError:
robber["balance"] -= stolen
robber["lifetime_earned"] = prev_lifetime_earned
robber["biggest_win"] = prev_biggest_win
robber["peak_balance"] = prev_peak_balance
try:
await _commit(robber_id, robber)
except DatabaseError as exc2:
_log.critical(
"do_rob rollback failed for robber %s after target commit failed: %s",
robber_id, exc2,
)
return {"ok": False, "reason": "db_error"}
_txn("ROB_WIN", robber=robber_id, victim=target_id, stolen=f"+{stolen}", jackpot=jackpot, robber_bal=robber["balance"], victim_bal=target["balance"])
return {"ok": True, "success": True, "stolen": stolen, "balance": robber["balance"], "jackpot": jackpot}
else:
fine = random.randint(100, 250)
robber["balance"] = max(0, robber["balance"] - fine)
robber["lifetime_lost"] = robber.get("lifetime_lost", 0) + fine
robber["biggest_loss"] = max(robber.get("biggest_loss", 0), fine)
await _commit(robber_id, robber)
await _credit_house(fine)
_txn("ROB_FAIL", robber=robber_id, victim=target_id, fine=f"-{fine}", robber_bal=robber["balance"])
return {"ok": True, "success": False, "reason": "caught", "fine": fine, "balance": robber["balance"]}
# ---------------------------------------------------------------------------
# /give
# ---------------------------------------------------------------------------
@_locked_by(0, 1)
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"}
if rem := _is_jailed(giver):
return {"ok": False, "reason": "jailed", "remaining": rem}
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
receiver["total_received"] = receiver.get("total_received", 0) + amount
try:
await _commit(giver_id, giver)
except DatabaseError:
return {"ok": False, "reason": "db_error"}
try:
await _commit(receiver_id, receiver)
except DatabaseError:
giver["balance"] += amount
giver["total_given"] = max(0, giver.get("total_given", 0) - amount)
try:
await _commit(giver_id, giver)
except DatabaseError as exc2:
_log.critical(
"do_give rollback failed for giver %s after receiver commit failed: %s",
giver_id, exc2,
)
return {"ok": False, "reason": "db_error"}
_txn("GIVE", from_=giver_id, to=receiver_id, amount=amount, from_bal=giver["balance"], to_bal=receiver["balance"])
return {
"ok": True,
"giver_balance": giver["balance"],
"receiver_balance": receiver["balance"],
}