import sqlite3 import json import logging import os from datetime import datetime, timedelta from pathlib import Path from typing import Dict, List, Optional, Tuple import requests logger = logging.getLogger(__name__) # Пути к базам данных ECONOMY_DB_PATH = os.getenv("ECONOMY_DB_PATH", "/db/economy.sqlite3") class EconomyManager: def __init__(self): self.init_db() def init_db(self): """Инициализация базы данных экономики""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Таблица балансов пользователей cursor.execute(''' CREATE TABLE IF NOT EXISTS user_balances ( user_id INTEGER PRIMARY KEY, balance REAL DEFAULT 0.0, daily_income REAL DEFAULT 0.0, last_daily_reset DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # Таблица вкладов cursor.execute(''' CREATE TABLE IF NOT EXISTS deposits ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, amount REAL, interest_rate REAL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, matures_at TIMESTAMP, is_active BOOLEAN DEFAULT 1 ) ''') # Таблица кредитов cursor.execute(''' CREATE TABLE IF NOT EXISTS loans ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, amount REAL, interest_rate REAL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, due_at TIMESTAMP, is_repaid BOOLEAN DEFAULT 0 ) ''') # Таблица транзакций cursor.execute(''' CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, from_user_id INTEGER, to_user_id INTEGER, amount REAL, transaction_type TEXT, description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # Таблица налогов cursor.execute(''' CREATE TABLE IF NOT EXISTS taxes ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, amount REAL, tax_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # Таблица глобальных настроек экономики cursor.execute(''' CREATE TABLE IF NOT EXISTS economy_settings ( key TEXT PRIMARY KEY, value TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # Таблица ежедневной активности пользователей cursor.execute(''' CREATE TABLE IF NOT EXISTS daily_activity ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, activity_date DATE, message_count INTEGER DEFAULT 0, last_activity_time TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, activity_date) ) ''') # Таблица Центрального Банка cursor.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) ) ''') # Вставка настроек по умолчанию 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(): cursor.execute('INSERT OR IGNORE INTO economy_settings (key, value) VALUES (?, ?)', (key, value)) # Инициализация Центрального Банка cursor.execute('INSERT OR IGNORE INTO central_bank (id, capital) VALUES (1, 1000.0)') conn.commit() conn.close() def get_user_balance(self, user_id: int) -> float: """Получить баланс пользователя""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() cursor.execute('SELECT balance FROM user_balances WHERE user_id = ?', (user_id,)) result = cursor.fetchone() conn.close() return result[0] if result else 0.0 def update_balance(self, user_id: int, amount: float, transaction_type: str, description: str = ""): """Обновить баланс пользователя""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Вставляем или обновляем баланс cursor.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (?, ?, 0.0, date('now')) ON CONFLICT(user_id) DO UPDATE SET balance = balance + ? ''', (user_id, amount, amount)) # Записываем транзакцию cursor.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (?, ?, ?, ?, ?) ''', (user_id, user_id, amount, transaction_type, description)) conn.commit() conn.close() 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) transfer_limit = from_balance * 0.2 # 20% от капитала if amount > transfer_limit: return False if from_balance < amount: return False conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Обновляем балансы cursor.execute(''' UPDATE user_balances SET balance = balance - ? WHERE user_id = ? ''', (amount, from_user_id)) cursor.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (?, ?, 0.0, date('now')) ON CONFLICT(user_id) DO UPDATE SET balance = balance + ? ''', (to_user_id, amount, amount)) # Записываем транзакцию cursor.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (?, ?, ?, ?, ?) ''', (from_user_id, to_user_id, amount, 'transfer', description)) conn.commit() conn.close() 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: return False if balance < amount: return False central_bank_rate = float(self.get_setting('central_bank_rate')) matures_at = datetime.now() + timedelta(days=days) conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Списываем деньги со счета cursor.execute('UPDATE user_balances SET balance = balance - ? WHERE user_id = ?', (amount, user_id)) # Создаем вклад cursor.execute(''' INSERT INTO deposits (user_id, amount, interest_rate, matures_at) VALUES (?, ?, ?, ?) ''', (user_id, amount, central_bank_rate, matures_at)) conn.commit() conn.close() 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) conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Начисляем деньги на счет cursor.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (?, ?, 0.0, date('now')) ON CONFLICT(user_id) DO UPDATE SET balance = balance + ? ''', (user_id, amount, amount)) # Создаем кредит cursor.execute(''' INSERT INTO loans (user_id, amount, interest_rate, due_at) VALUES (?, ?, ?, ?) ''', (user_id, amount, loan_rate, due_at)) conn.commit() conn.close() 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: """Погасить кредит""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() cursor.execute('SELECT amount, interest_rate, is_repaid FROM loans WHERE id = ? AND user_id = ?', (loan_id, user_id)) loan = cursor.fetchone() if not loan or loan[2]: # Кредит не найден или уже погашен conn.close() return False amount, rate, _ = loan total_repayment = amount * (1 + rate / 100) balance = self.get_user_balance(user_id) if balance < total_repayment: conn.close() return False # Списываем деньги cursor.execute('UPDATE user_balances SET balance = balance - ? WHERE user_id = ?', (total_repayment, user_id)) # Отмечаем кредит как погашенный cursor.execute('UPDATE loans SET is_repaid = 1 WHERE id = ?', (loan_id,)) conn.commit() conn.close() 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 conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Получаем всех пользователей с ежедневным доходом cursor.execute(''' SELECT user_id, daily_income FROM user_balances WHERE daily_income > 0 AND (last_daily_reset != date('now') OR last_daily_reset IS NULL) ''') users = cursor.fetchall() total_taxes_collected = 0.0 for user_id, daily_income in users: tax_amount = daily_income * tax_rate if tax_amount > 0: # Списываем налог cursor.execute( 'UPDATE user_balances SET balance = balance - ?, daily_income = 0, last_daily_reset = date(\'now\') WHERE user_id = ?', (tax_amount, user_id) ) # Записываем налог cursor.execute('INSERT INTO taxes (user_id, amount, tax_date) VALUES (?, ?, date("now"))', (user_id, tax_amount)) total_taxes_collected += tax_amount # Переводим собранные налоги в Центральный Банк if total_taxes_collected > 0: cursor.execute('UPDATE central_bank SET capital = capital + ?, total_taxes_collected = total_taxes_collected + ? WHERE id = 1', (total_taxes_collected, total_taxes_collected)) conn.commit() conn.close() logger.info(f"Daily taxes collected: {total_taxes_collected:.2f} cm from {len(users)} users") def count_message(self, user_id: int): """Учесть сообщение пользователя""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() today = datetime.now().strftime('%Y-%m-%d') # Вставляем или обновляем счетчик сообщений за сегодня cursor.execute(''' INSERT INTO daily_activity (user_id, activity_date, message_count, last_activity_time) VALUES (?, ?, 1, CURRENT_TIMESTAMP) ON CONFLICT(user_id, activity_date) DO UPDATE SET message_count = message_count + 1, last_activity_time = CURRENT_TIMESTAMP ''', (user_id, today)) conn.commit() conn.close() 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 conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Получаем всех пользователей с активностью за вчерашний день yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d') cursor.execute(''' SELECT user_id, message_count FROM daily_activity WHERE activity_date = ? AND message_count > 0 ''', (yesterday,)) users = cursor.fetchall() total_paid = 0 for user_id, message_count in users: reward = message_count * reward_rate if reward > 0: # Обновляем баланс cursor.execute(''' INSERT INTO user_balances (user_id, balance, daily_income, last_daily_reset) VALUES (?, ?, 0.0, date('now')) ON CONFLICT(user_id) DO UPDATE SET balance = balance + ?, daily_income = daily_income + ? ''', (user_id, reward, reward, reward)) # Записываем транзакцию cursor.execute(''' INSERT INTO transactions (from_user_id, to_user_id, amount, transaction_type, description) VALUES (?, ?, ?, ?, ?) ''', (user_id, user_id, reward, 'activity_reward', f'Вознаграждение за {message_count} сообщений')) total_paid += reward # Обновляем дату последней выплаты cursor.execute('UPDATE economy_settings SET value = ? WHERE key = ?', (today, 'last_activity_payout')) conn.commit() conn.close() logger.info(f"Activity rewards paid: {total_paid:.2f} cm to {len(users)} users") return len(users) def get_central_bank_stats(self) -> Dict: """Получить статистику Центрального Банка""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() cursor.execute('SELECT capital, total_taxes_collected, total_inflation_adjustments, total_emissions FROM central_bank WHERE id = 1') result = cursor.fetchone() conn.close() if result: return { 'capital': result[0], 'total_taxes_collected': result[1], 'total_inflation_adjustments': result[2], 'total_emissions': result[3] } else: 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 = ""): """Обновить капитал Центрального Банка""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Обновляем капитал cursor.execute(''' UPDATE central_bank SET capital = capital + ?, last_updated = CURRENT_TIMESTAMP WHERE id = 1 ''', (capital_change,)) # Обновляем соответствующую статистику if transaction_type == 'tax': cursor.execute('UPDATE central_bank SET total_taxes_collected = total_taxes_collected + ? WHERE id = 1', (abs(capital_change),)) elif transaction_type == 'inflation': cursor.execute('UPDATE central_bank SET total_inflation_adjustments = total_inflation_adjustments + ? WHERE id = 1', (abs(capital_change),)) elif transaction_type == 'emission': cursor.execute('UPDATE central_bank SET total_emissions = total_emissions + ? WHERE id = 1', (abs(capital_change),)) conn.commit() conn.close() logger.info(f"Central bank updated: {capital_change} ({transaction_type})") def emit_money(self, amount: float, reason: str = "") -> bool: """Эмиссия денег - добавление денег в систему из ЦБ""" cb_stats = self.get_central_bank_stats() if cb_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: """Полное обнуление всех балансов""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Проверяем кулдаун cursor.execute('SELECT value FROM economy_settings WHERE key = "last_vanomasa"') last_vanomasa = cursor.fetchone() if last_vanomasa: last_date = datetime.strptime(last_vanomasa[0], '%Y-%m-%d') cooldown_days = int(self.get_setting('vanomasa_cooldown')) if datetime.now() - last_date < timedelta(days=cooldown_days): conn.close() return False # Обнуляем все балансы cursor.execute('UPDATE user_balances SET balance = 0, daily_income = 0') # Записываем дату ваномаса cursor.execute('INSERT OR REPLACE INTO economy_settings (key, value, updated_at) VALUES ("last_vanomasa", date("now"), CURRENT_TIMESTAMP)') conn.commit() conn.close() logger.warning("VANOMASA: All balances reset to zero!") return True def get_setting(self, key: str) -> str: """Получить настройку экономики""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() cursor.execute('SELECT value FROM economy_settings WHERE key = ?', (key,)) result = cursor.fetchone() conn.close() return result[0] if result else '0' def update_setting(self, key: str, value: str): """Обновить настройку экономики""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() cursor.execute('INSERT OR REPLACE INTO economy_settings (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)', (key, value)) conn.commit() conn.close() def get_user_stats(self, user_id: int) -> Dict: """Получить статистику пользователя""" conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Баланс cursor.execute('SELECT balance FROM user_balances WHERE user_id = ?', (user_id,)) row = cursor.fetchone() balance = row[0] if row else 0.0 # Активные вклады cursor.execute('SELECT COUNT(*), SUM(amount) FROM deposits WHERE user_id = ? AND is_active = 1', (user_id,)) deposits = cursor.fetchone() # Активные кредиты cursor.execute('SELECT COUNT(*), SUM(amount) FROM loans WHERE user_id = ? AND is_repaid = 0', (user_id,)) loans = cursor.fetchone() # Получаем статистику сообщений за сегодня today = datetime.now().strftime('%Y-%m-%d') cursor.execute('SELECT message_count FROM daily_activity WHERE user_id = ? AND activity_date = ?', (user_id, today)) today_messages = cursor.fetchone() # Получаем статистику сообщений за вчера yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d') cursor.execute('SELECT message_count FROM daily_activity WHERE user_id = ? AND activity_date = ?', (user_id, yesterday)) yesterday_messages = cursor.fetchone() conn.close() return { 'balance': balance, 'active_deposits_count': deposits[0] or 0, 'active_deposits_sum': deposits[1] or 0.0, 'active_loans_count': loans[0] or 0, 'active_loans_sum': loans[1] or 0.0, 'today_messages': today_messages[0] if today_messages else 0, 'yesterday_messages': yesterday_messages[0] if yesterday_messages else 0 } def regulate_inflation(self): """Регулирование инфляции через Центральный Банк""" inflation_rate = float(self.get_setting('inflation_rate')) total_supply = 0.0 user_count = 0 conn = sqlite3.connect(ECONOMY_DB_PATH) cursor = conn.cursor() # Получаем общую массу денег и количество пользователей cursor.execute('SELECT SUM(balance), COUNT(*) FROM user_balances') result = cursor.fetchone() if result[0]: total_supply = result[0] user_count = result[1] # Получаем статистику ЦБ cursor.execute('SELECT capital FROM central_bank WHERE id = 1') cb_result = cursor.fetchone() cb_capital = cb_result[0] if cb_result else 1000.0 conn.close() # Если инфляция высокая, ЦБ может скупать деньги с рынка if inflation_rate > 10.0 and total_supply > 1000 and cb_capital > 100: # ЦБ скупает деньги для борьбы с инфляцией buyback_amount = min(total_supply * 0.05, cb_capital * 0.1) # 5% от массы или 10% от капитала ЦБ # Уменьшаем капитал ЦБ (скупка денег) self.update_central_bank(-buyback_amount, 'inflation', f'Anti-inflation measures: buying back {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: CB bought back {buyback_amount:.2f} cm, tax rate increased to {new_tax_rate}%") # Если дефляция (низкая инфляция) и много денег в ЦБ, можно делать эмиссию elif inflation_rate < 2.0 and total_supply < 500 and cb_capital > 200: emission_amount = cb_capital * 0.05 # 5% от капитала ЦБ # Эмиссия денег через ЦБ self.update_central_bank(-emission_amount, 'emission', f'Economic stimulus: emitting {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: CB emitted {emission_amount:.2f} cm, tax rate decreased to {new_tax_rate}%") # Глобальный экземпляр economy = EconomyManager()