- Все 4 SQLite базы объединены в одну PostgreSQL - Новый db.py с ThreadedConnectionPool (psycopg2) - docker-compose: сервис postgres с healthcheck, данные в ./db/postgres - scripts/migrate_sqlite_to_pg.py — миграция существующих данных - Убраны мёртвый код и дублирующий import в main.py и talk_handler.py - .env удалён из git-индекса Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
3.9 KiB
Python
114 lines
3.9 KiB
Python
import logging
|
||
import random
|
||
|
||
import psycopg2.extras
|
||
|
||
import config
|
||
from db import get_conn
|
||
from .economy import economy
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
SLOT_SYMBOLS = ["🍒", "🍋", "🔔", "💎", "7️⃣", "🍀"]
|
||
SLOT_WEIGHTS = [30, 25, 20, 12, 8, 5]
|
||
|
||
MULTIPLIERS = {
|
||
2: (1.2, 1.8),
|
||
3: (2.0, 3.0),
|
||
}
|
||
|
||
CASINO_WIN_CHANCE = 0.35
|
||
|
||
|
||
def spin_slots() -> tuple[list[str], int]:
|
||
reels = random.choices(SLOT_SYMBOLS, weights=SLOT_WEIGHTS, k=3)
|
||
|
||
if random.random() > CASINO_WIN_CHANCE:
|
||
while len(set(reels)) < 3:
|
||
reels = random.choices(SLOT_SYMBOLS, weights=SLOT_WEIGHTS, k=3)
|
||
return reels, 0
|
||
|
||
counts = {}
|
||
for s in reels:
|
||
counts[s] = counts.get(s, 0) + 1
|
||
return reels, max(counts.values())
|
||
|
||
|
||
def get_user_length(user_id: int) -> float | None:
|
||
try:
|
||
with get_conn() as conn:
|
||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||
cur.execute('SELECT balance FROM user_balances WHERE user_id = %s', (user_id,))
|
||
row = cur.fetchone()
|
||
return float(row['balance']) if row else None
|
||
except Exception:
|
||
logger.exception("casino: failed to get balance")
|
||
return None
|
||
|
||
|
||
def update_user_length(user_id: int, delta: float) -> float | None:
|
||
try:
|
||
with get_conn() as conn:
|
||
cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
||
cur.execute('SELECT balance FROM user_balances WHERE user_id = %s', (user_id,))
|
||
row = cur.fetchone()
|
||
if row is None:
|
||
return None
|
||
new_balance = round(float(row['balance']) + delta, 1)
|
||
if delta < 0 and new_balance < 0:
|
||
new_balance = 0.0
|
||
wcur = conn.cursor()
|
||
wcur.execute(
|
||
'UPDATE user_balances SET balance = %s WHERE user_id = %s',
|
||
(new_balance, user_id),
|
||
)
|
||
return new_balance
|
||
except Exception:
|
||
logger.exception("casino: failed to update balance")
|
||
return None
|
||
|
||
|
||
def play_casino(user_id: int, bet: float) -> str:
|
||
current = economy.get_user_balance(user_id)
|
||
|
||
if bet > config.CASINO_MAX_BET:
|
||
return f"Максимальная ставка — {config.CASINO_MAX_BET} см, не жадничай."
|
||
if bet <= 0:
|
||
return "Ставка должна быть больше нуля, фраер."
|
||
if bet > current:
|
||
return f"У тебя {current:.1f} см, а ставишь {bet:.1f}. Столько нет, фраер."
|
||
|
||
reels, matches = spin_slots()
|
||
slots_display = " | ".join(reels)
|
||
|
||
if matches >= 2:
|
||
min_mult, max_mult = MULTIPLIERS[matches]
|
||
multiplier = round(random.uniform(min_mult, max_mult), 1)
|
||
winnings = round(bet * multiplier, 1)
|
||
economy.update_balance(user_id, winnings, 'casino_win', f'Выигрыш в казино x{multiplier}')
|
||
new_balance = economy.get_user_balance(user_id)
|
||
|
||
if matches == 3:
|
||
return (
|
||
f"🎰 {slots_display}\n\n"
|
||
f"🎉 ДЖЕКПОТ!!! Три одинаковых!\n"
|
||
f"Множитель: x{multiplier}\n"
|
||
f"Выигрыш: +{winnings:.1f} см\n"
|
||
f"💰 Баланс: {new_balance:.1f} см"
|
||
)
|
||
return (
|
||
f"🎰 {slots_display}\n\n"
|
||
f"✅ Выигрыш! Две совпали.\n"
|
||
f"Множитель: x{multiplier}\n"
|
||
f"Выигрыш: +{winnings:.1f} см\n"
|
||
f"💰 Баланс: {new_balance:.1f} см"
|
||
)
|
||
else:
|
||
economy.update_balance(user_id, -bet, 'casino_loss', f'Проигрыш в казино -{bet} см')
|
||
new_balance = economy.get_user_balance(user_id)
|
||
return (
|
||
f"🎰 {slots_display}\n\n"
|
||
f"❌ Мимо, братуха.\n"
|
||
f"Проигрыш: -{bet:.1f} см\n"
|
||
f"💰 Баланс: {new_balance:.1f} см"
|
||
)
|