import logging import random from datetime import datetime, timedelta from typing import Dict import psycopg2.extras from db import get_conn logger = logging.getLogger(__name__) class EconomyManager: def __init__(self): self.init_db() def init_db(self): with get_conn() as conn: cur = conn.cursor() cur.execute(''' CREATE TABLE IF NOT EXISTS user_balances ( user_id BIGINT PRIMARY KEY, balance REAL DEFAULT 0.0, daily_income REAL DEFAULT 0.0, last_daily_reset DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS deposits ( id SERIAL PRIMARY KEY, user_id BIGINT, amount REAL, interest_rate REAL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, matures_at TIMESTAMP, is_active BOOLEAN DEFAULT TRUE ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS loans ( id SERIAL PRIMARY KEY, user_id BIGINT, amount REAL, interest_rate REAL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, due_at TIMESTAMP, is_repaid BOOLEAN DEFAULT FALSE ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS transactions ( id SERIAL PRIMARY KEY, from_user_id BIGINT, to_user_id BIGINT, amount REAL, transaction_type TEXT, description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS taxes ( id SERIAL PRIMARY KEY, user_id BIGINT, amount REAL, tax_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS economy_settings ( key TEXT PRIMARY KEY, value TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS daily_activity ( id SERIAL PRIMARY KEY, user_id BIGINT, activity_date DATE, message_count INTEGER DEFAULT 0, last_activity_time TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (user_id, activity_date) ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS central_bank ( id INTEGER PRIMARY KEY DEFAULT 1, capital REAL DEFAULT 1000.0, total_taxes_collected REAL DEFAULT 0.0, total_inflation_adjustments REAL DEFAULT 0.0, total_emissions REAL DEFAULT 0.0, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE (id) ) ''') cur.execute(''' CREATE TABLE IF NOT EXISTS penis_stats ( user_id BIGINT PRIMARY KEY, display_name TEXT NOT NULL DEFAULT '', last_used_ts BIGINT ) ''') default_settings = { 'central_bank_rate': '7.5', 'deposit_min_amount': '10.0', 'deposit_min_days': '7', 'loan_max_amount': '100.0', 'loan_interest_rate': '15.0', 'tax_rate': '13.0', 'transfer_limit_percent': '20.0', 'inflation_rate': '5.0', 'vanomasa_cooldown': '30', 'message_reward_rate': '0.01', 'last_activity_payout': '', } for key, value in default_settings.items(): cur.execute(''' INSERT INTO economy_settings (key, value) VALUES (%s, %s) ON CONFLICT (key) DO NOTHING ''', (key, value)) cur.execute(''' INSERT INTO central_bank (id, capital) VALUES (1, 1000.0) ON CONFLICT (id) DO NOTHING ''') def get_user_balance(self, user_id: int) -> float: 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,)) result = cur.fetchone() return result['balance'] if result else 0.0 def update_balance(self, user_id: int, amount: float, transaction_type: str, description: str = ""): with get_conn() as conn: cur = conn.cursor() cur.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (%s, %s, 0.0, CURRENT_DATE) ON CONFLICT (user_id) DO UPDATE SET balance = user_balances.balance + %s ''', (user_id, amount, amount)) cur.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (%s, %s, %s, %s, %s) ''', (user_id, user_id, amount, transaction_type, description)) logger.info(f"Balance updated for user {user_id}: +{amount} ({transaction_type})") def transfer_money(self, from_user_id: int, to_user_id: int, amount: float, description: str = "") -> bool: if amount <= 0: return False from_balance = self.get_user_balance(from_user_id) if amount > from_balance * 0.2 or from_balance < amount: return False with get_conn() as conn: cur = conn.cursor() cur.execute( 'UPDATE user_balances SET balance = balance - %s WHERE user_id = %s', (amount, from_user_id), ) cur.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (%s, %s, 0.0, CURRENT_DATE) ON CONFLICT (user_id) DO UPDATE SET balance = user_balances.balance + %s ''', (to_user_id, amount, amount)) cur.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (%s, %s, %s, 'transfer', %s) ''', (from_user_id, to_user_id, amount, description)) logger.info(f"Transfer: {amount} from user {from_user_id} to user {to_user_id}") return True def create_deposit(self, user_id: int, amount: float, days: int) -> bool: if amount <= 0: return False balance = self.get_user_balance(user_id) min_deposit = float(self.get_setting('deposit_min_amount')) if amount < min_deposit or balance < amount: return False central_bank_rate = float(self.get_setting('central_bank_rate')) matures_at = datetime.now() + timedelta(days=days) with get_conn() as conn: cur = conn.cursor() cur.execute( 'UPDATE user_balances SET balance = balance - %s WHERE user_id = %s', (amount, user_id), ) cur.execute(''' UPDATE central_bank SET capital = capital + %s, last_updated = CURRENT_TIMESTAMP WHERE id = 1 ''', (amount,)) cur.execute(''' INSERT INTO deposits (user_id, amount, interest_rate, matures_at) VALUES (%s, %s, %s, %s) ''', (user_id, amount, central_bank_rate, matures_at)) logger.info(f"Deposit created: user {user_id}, amount {amount}, rate {central_bank_rate}%") return True def create_loan(self, user_id: int, amount: float, days: int) -> bool: if amount <= 0: return False max_loan = float(self.get_setting('loan_max_amount')) loan_rate = float(self.get_setting('loan_interest_rate')) if amount > max_loan: return False due_at = datetime.now() + timedelta(days=days) with get_conn() as conn: cur = conn.cursor() cur.execute(''' UPDATE central_bank SET capital = capital - %s, last_updated = CURRENT_TIMESTAMP WHERE id = 1 ''', (amount,)) cur.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (%s, %s, 0.0, CURRENT_DATE) ON CONFLICT (user_id) DO UPDATE SET balance = user_balances.balance + %s ''', (user_id, amount, amount)) cur.execute(''' INSERT INTO loans (user_id, amount, interest_rate, due_at) VALUES (%s, %s, %s, %s) ''', (user_id, amount, loan_rate, due_at)) logger.info(f"Loan created: user {user_id}, amount {amount}, rate {loan_rate}%") return True def repay_loan(self, user_id: int, loan_id: int) -> bool: with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute( 'SELECT amount, interest_rate, is_repaid FROM loans WHERE id = %s AND user_id = %s', (loan_id, user_id), ) loan = cur.fetchone() if not loan or loan['is_repaid']: return False total_repayment = loan['amount'] * (1 + loan['interest_rate'] / 100) balance = self.get_user_balance(user_id) if balance < total_repayment: return False wcur = conn.cursor() wcur.execute( 'UPDATE user_balances SET balance = balance - %s WHERE user_id = %s', (total_repayment, user_id), ) wcur.execute(''' UPDATE central_bank SET capital = capital + %s, last_updated = CURRENT_TIMESTAMP WHERE id = 1 ''', (total_repayment,)) wcur.execute('UPDATE loans SET is_repaid = TRUE WHERE id = %s', (loan_id,)) logger.info(f"Loan repaid: user {user_id}, loan {loan_id}, amount {total_repayment}") return True def collect_daily_taxes(self): tax_rate = float(self.get_setting('tax_rate')) / 100 with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute(''' SELECT user_id, daily_income FROM user_balances WHERE daily_income > 0 AND (last_daily_reset != CURRENT_DATE OR last_daily_reset IS NULL) ''') users = cur.fetchall() total_taxes_collected = 0.0 wcur = conn.cursor() for row in users: user_id = row['user_id'] daily_income = row['daily_income'] tax_amount = daily_income * tax_rate if tax_amount <= 0: continue wcur.execute(''' UPDATE user_balances SET balance = balance - %s, daily_income = 0, last_daily_reset = CURRENT_DATE WHERE user_id = %s ''', (tax_amount, user_id)) wcur.execute( 'INSERT INTO taxes (user_id, amount, tax_date) VALUES (%s, %s, CURRENT_DATE)', (user_id, tax_amount), ) total_taxes_collected += tax_amount if total_taxes_collected > 0: wcur.execute(''' UPDATE central_bank SET capital = capital + %s, total_taxes_collected = total_taxes_collected + %s WHERE id = 1 ''', (total_taxes_collected, total_taxes_collected)) logger.info(f"Daily taxes collected: {total_taxes_collected:.2f} cm from {len(users)} users") def count_message(self, user_id: int): today = datetime.now().strftime('%Y-%m-%d') with get_conn() as conn: cur = conn.cursor() cur.execute(''' INSERT INTO daily_activity (user_id, activity_date, message_count, last_activity_time) VALUES (%s, %s, 1, CURRENT_TIMESTAMP) ON CONFLICT (user_id, activity_date) DO UPDATE SET message_count = daily_activity.message_count + 1, last_activity_time = CURRENT_TIMESTAMP ''', (user_id, today)) def pay_daily_activity_rewards(self) -> int: reward_rate = float(self.get_setting('message_reward_rate')) last_payout = self.get_setting('last_activity_payout') today = datetime.now().strftime('%Y-%m-%d') if last_payout == today: return 0 yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d') with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute(''' SELECT user_id, message_count FROM daily_activity WHERE activity_date = %s AND message_count > 0 ''', (yesterday,)) users = cur.fetchall() total_paid = 0.0 wcur = conn.cursor() for row in users: user_id = row['user_id'] message_count = row['message_count'] reward = message_count * reward_rate if reward <= 0: continue wcur.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (%s, %s, 0.0, CURRENT_DATE) ON CONFLICT (user_id) DO UPDATE SET balance = user_balances.balance + %s, daily_income = user_balances.daily_income + %s ''', (user_id, reward, reward, reward)) wcur.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (%s, %s, %s, 'activity_reward', %s) ''', (user_id, user_id, reward, f'Вознаграждение за {message_count} сообщений')) total_paid += reward wcur.execute( 'UPDATE economy_settings SET value = %s WHERE key = %s', (today, 'last_activity_payout'), ) logger.info(f"Activity rewards paid: {total_paid:.2f} cm to {len(users)} users") return len(users) def get_central_bank_stats(self) -> Dict: with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute(''' SELECT capital, total_taxes_collected, total_inflation_adjustments, total_emissions FROM central_bank WHERE id = 1 ''') result = cur.fetchone() if result: return { 'capital': result['capital'], 'total_taxes_collected': result['total_taxes_collected'], 'total_inflation_adjustments': result['total_inflation_adjustments'], 'total_emissions': result['total_emissions'], } return { 'capital': 1000.0, 'total_taxes_collected': 0.0, 'total_inflation_adjustments': 0.0, 'total_emissions': 0.0, } def update_central_bank(self, capital_change: float, transaction_type: str, description: str = ""): with get_conn() as conn: cur = conn.cursor() cur.execute(''' UPDATE central_bank SET capital = capital + %s, last_updated = CURRENT_TIMESTAMP WHERE id = 1 ''', (capital_change,)) if transaction_type == 'tax': cur.execute(''' UPDATE central_bank SET total_taxes_collected = total_taxes_collected + %s WHERE id = 1 ''', (abs(capital_change),)) elif transaction_type == 'inflation': cur.execute(''' UPDATE central_bank SET total_inflation_adjustments = total_inflation_adjustments + %s WHERE id = 1 ''', (abs(capital_change),)) elif transaction_type == 'emission': cur.execute(''' UPDATE central_bank SET total_emissions = total_emissions + %s WHERE id = 1 ''', (abs(capital_change),)) logger.info(f"Central bank updated: {capital_change} ({transaction_type})") def emit_money(self, amount: float, reason: str = "") -> bool: if self.get_central_bank_stats()['capital'] < amount: return False self.update_central_bank(-amount, 'emission', reason) logger.info(f"Money emitted: {amount} cm ({reason})") return True def vanomasa(self) -> bool: with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute("SELECT value FROM economy_settings WHERE key = %s", ('last_vanomasa',)) last_vanomasa = cur.fetchone() if last_vanomasa and last_vanomasa['value']: try: last_date = datetime.strptime(last_vanomasa['value'], '%Y-%m-%d') cooldown_days = int(self.get_setting('vanomasa_cooldown')) if datetime.now() - last_date < timedelta(days=cooldown_days): return False except ValueError: pass today = datetime.now().strftime('%Y-%m-%d') wcur = conn.cursor() wcur.execute('UPDATE user_balances SET balance = 0, daily_income = 0') wcur.execute(''' INSERT INTO economy_settings (key, value, updated_at) VALUES (%s, %s, CURRENT_TIMESTAMP) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP ''', ('last_vanomasa', today)) logger.warning("VANOMASA: All balances reset to zero!") return True def get_setting(self, key: str) -> str: with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute('SELECT value FROM economy_settings WHERE key = %s', (key,)) result = cur.fetchone() return result['value'] if result else '0' def update_setting(self, key: str, value: str): with get_conn() as conn: cur = conn.cursor() cur.execute(''' INSERT INTO economy_settings (key, value, updated_at) VALUES (%s, %s, CURRENT_TIMESTAMP) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = CURRENT_TIMESTAMP ''', (key, value)) def get_user_stats(self, user_id: int) -> Dict: today = datetime.now().strftime('%Y-%m-%d') yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d') 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() balance = row['balance'] if row else 0.0 cur.execute(''' SELECT COUNT(*) AS cnt, COALESCE(SUM(amount), 0) AS total FROM deposits WHERE user_id = %s AND is_active = TRUE ''', (user_id,)) deposits = cur.fetchone() cur.execute(''' SELECT COUNT(*) AS cnt, COALESCE(SUM(amount), 0) AS total FROM loans WHERE user_id = %s AND is_repaid = FALSE ''', (user_id,)) loans = cur.fetchone() cur.execute( 'SELECT message_count FROM daily_activity WHERE user_id = %s AND activity_date = %s', (user_id, today), ) today_row = cur.fetchone() cur.execute( 'SELECT message_count FROM daily_activity WHERE user_id = %s AND activity_date = %s', (user_id, yesterday), ) yesterday_row = cur.fetchone() return { 'balance': balance, 'active_deposits_count': deposits['cnt'] or 0, 'active_deposits_sum': deposits['total'] or 0.0, 'active_loans_count': loans['cnt'] or 0, 'active_loans_sum': loans['total'] or 0.0, 'today_messages': today_row['message_count'] if today_row else 0, 'yesterday_messages': yesterday_row['message_count'] if yesterday_row else 0, } def regulate_inflation(self): inflation_rate = float(self.get_setting('inflation_rate')) with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute('SELECT SUM(balance) AS total_balance, COUNT(*) AS user_count FROM user_balances') result = cur.fetchone() total_supply = result['total_balance'] or 0.0 cur.execute('SELECT capital FROM central_bank WHERE id = 1') cb_result = cur.fetchone() cb_capital = cb_result['capital'] if cb_result else 1000.0 if inflation_rate > 10.0 and total_supply > 1000 and cb_capital > 100: buyback_amount = min(total_supply * 0.05, cb_capital * 0.1) self.update_central_bank(-buyback_amount, 'inflation', f'Anti-inflation: {buyback_amount:.2f} cm') new_tax_rate = min(20.0, 13.0 + (inflation_rate - 10.0) * 0.5) self.update_setting('tax_rate', str(new_tax_rate)) logger.info(f"Inflation regulation: bought back {buyback_amount:.2f} cm, tax={new_tax_rate}%") elif inflation_rate < 2.0 and total_supply < 500 and cb_capital > 200: emission_amount = cb_capital * 0.05 self.update_central_bank(-emission_amount, 'emission', f'Stimulus: {emission_amount:.2f} cm') new_tax_rate = max(10.0, 13.0 - (2.0 - inflation_rate) * 1.5) self.update_setting('tax_rate', str(new_tax_rate)) logger.info(f"Economic stimulus: emitted {emission_amount:.2f} cm, tax={new_tax_rate}%") def play_penis(self, user_id: int, display_name: str, now_ts: int, start_length: float, cooldown_seconds: int, min_delta: float, max_delta: float) -> dict: 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() current_balance = row['balance'] if row else start_length cur.execute( 'SELECT last_used_ts, display_name FROM penis_stats WHERE user_id = %s', (user_id,), ) ps_row = cur.fetchone() last_used_ts = ps_row['last_used_ts'] if ps_row else None if last_used_ts is not None: next_ts = int(last_used_ts) + cooldown_seconds if now_ts < next_ts: if ps_row and ps_row['display_name'] != display_name: wcur = conn.cursor() wcur.execute( 'UPDATE penis_stats SET display_name = %s WHERE user_id = %s', (display_name, user_id), ) return {'allowed': False, 'left_seconds': next_ts - now_ts} delta = round(random.uniform(min_delta, max_delta), 1) sign = random.choice([-1, 1]) new_balance = round(max(0.1, current_balance + sign * delta), 1) wcur = conn.cursor() wcur.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (%s, %s, 0.0, CURRENT_DATE) ON CONFLICT (user_id) DO UPDATE SET balance = %s ''', (user_id, new_balance, new_balance)) sign_label = f'+{delta:.1f}' if sign > 0 else f'-{delta:.1f}' wcur.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (%s, %s, %s, 'penis_game', %s) ''', (user_id, user_id, sign * delta, f'Игра /penis: {sign_label} см')) if ps_row is None: wcur.execute( 'INSERT INTO penis_stats (user_id, display_name, last_used_ts) VALUES (%s, %s, %s)', (user_id, display_name, now_ts), ) else: wcur.execute( 'UPDATE penis_stats SET display_name = %s, last_used_ts = %s WHERE user_id = %s', (display_name, now_ts, user_id), ) return {'allowed': True, 'new_balance': new_balance, 'delta': delta, 'sign': sign} def get_penis_top(self, limit: int) -> list: with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute(''' SELECT ub.user_id, ps.display_name, ub.balance FROM user_balances ub INNER JOIN penis_stats ps ON ub.user_id = ps.user_id ORDER BY ub.balance DESC, ub.user_id ASC LIMIT %s ''', (limit,)) rows = cur.fetchall() return [{'user_id': r['user_id'], 'display_name': r['display_name'], 'balance': r['balance']} for r in rows] def process_matured_deposits(self) -> int: with get_conn() as conn: cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) cur.execute(''' SELECT id, user_id, amount, interest_rate FROM deposits WHERE is_active = TRUE AND matures_at <= NOW() ''') matured = cur.fetchall() paid = 0 wcur = conn.cursor() for row in matured: dep_id = row['id'] user_id = row['user_id'] amount = row['amount'] rate = row['interest_rate'] payout = round(amount * (1 + rate / 100 * 1 / 365 * 30), 2) wcur.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (%s, %s, 0.0, CURRENT_DATE) ON CONFLICT (user_id) DO UPDATE SET balance = user_balances.balance + %s ''', (user_id, payout, payout)) wcur.execute(''' UPDATE central_bank SET capital = capital - %s, last_updated = CURRENT_TIMESTAMP WHERE id = 1 ''', (payout,)) wcur.execute('UPDATE deposits SET is_active = FALSE WHERE id = %s', (dep_id,)) wcur.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (0, %s, %s, 'deposit_payout', %s) ''', (user_id, payout, f'Выплата вклада #{dep_id}: {amount:.1f} + проценты = {payout:.2f} см')) paid += 1 if paid: logger.info(f"Matured deposits paid out: {paid}") return paid economy = EconomyManager()