Compare commits

..

No commits in common. "0f8df917019103197de3d54cabea590fb5b524cf" and "5847e0e06ad56a7f954fd4eb9556059604267074" have entirely different histories.

6 changed files with 23 additions and 1097 deletions

View file

@ -1,106 +0,0 @@
# Инструкция по развертыванию бота
## 🚀 Быстрый старт
### 1. Клонирование и подготовка
```bash
git clone <репозиторий>
cd bot_tg
```
### 2. Настройка переменных окружения
Создайте файл `.env` с необходимыми переменными:
```env
BOT_TOKEN=your_telegram_bot_token
LLAMA_API_URL=http://your-llama-server:8080 # опционально
ODDS_API_KEY=your_odds_api_key # опционально для ставок
```
### 3. Запуск через Docker Compose
```bash
# Сборка и запуск
docker compose up --build -d
# Просмотр логов
docker compose logs -f bot
# Остановка
docker compose down
```
## 📁 Структура проекта
```
bot_tg/
├── main.py # основной файл бота
├── games/
│ ├── economy.py # экономическая система
│ ├── casino.py # казино
│ ├── betting.py # ставки на спорт
│ └── ...
├── AI/ # ИИ-модули
├── maket/ # макеты для мемов
├── db/ # базы данных (создаются автоматически)
├── Dockerfile
├── docker-compose.yaml
├── requirements.txt
└── .env # переменные окружения
```
## 🗄️ Базы данных
Автоматически создаются в папке `./db`:
- `penis_stats.sqlite3` - статистика игры /penis
- `chat_history.sqlite3` - история для ИИ-общения
- `polychaetsi_stats.json` - статистика слова "получается"
- `bets.sqlite3` - ставки на спорт
- `economy.sqlite3` - экономическая система (ЦБ, вклады, кредиты)
## 🏛️ Экономическая система
**Основная валюта:** сантиметры (см)
**Команды экономики:**
- `/balance` - баланс и статистика
- `/deposit [сумма] [дни]` - открыть вклад
- `/loan [сумма] [дни]` - взять кредит
- `/transfer @username [сумма]` - перевести деньги
- `/cb_stats` - статистика Центрального Банка
- `/central_bank` - детальная статистика ЦБ
- `/cb_rules` - правила работы ЦБ
**Центральный Банк:** децентрализованная система с капиталом 1000 см
## 🐛 Поиск проблем
### Проверка логов
```bash
docker compose logs bot
```
### Перезапуск бота
```bash
docker compose restart bot
```
### Проверка статуса контейнера
```bash
docker compose ps
```
## ⚠️ Важные моменты
1. **Все базы данных хранятся в `./db`** - эта папка монтируется в контейнер
2. **Бот работает в часовом поясе Europe/Moscow**
3. **Центральный Банк полностью автоматический** - никакого ручного управления
4. **Ежедневные выплаты за активность** - в 00:00 по МСК
5. **Налоги 13%** - автоматически поступают в ЦБ
## 🔧 Первоначальная настройка
1. Получите токен бота у @BotFather
2. Добавьте бота в чат (если нужен)
3. Настройте `.env` файл
4. Запустите через `docker compose up --build -d`
Бот готов к работе! 🎉

View file

@ -37,12 +37,11 @@ docker compose down
Для ставок нужен `ODDS_API_KEY` (бесплатно на https://the-odds-api.com). Для ставок нужен `ODDS_API_KEY` (бесплатно на https://the-odds-api.com).
Контейнер хранит состояние в `./db`: Контейнер хранит состояние в `./db`:
- `penis_stats.sqlite3` — статистика игры /penis - `penis_stats.sqlite3`
- `chat_history.sqlite3` — история для ИИ-общения - `chat_history.sqlite3`
- `polychaetsi_stats.json` — статистика слова "получается" - `polychaetsi_stats.json`
- `bets.sqlite3` — ставки на спорт - `bets.sqlite3`
- `economy.sqlite3` — экономическая система (балансы, вклады, кредиты) - `nude_history.json`
- `nude_history.json` — история запросов /nude
## Команды ## Команды
@ -64,39 +63,6 @@ docker compose down
- `/mybets` — мои активные ставки. - `/mybets` — мои активные ставки.
- `/svodka` — сводка СВО. - `/svodka` — сводка СВО.
## Экономика ZOV OS 💰
Основная валюта — сантиметры (см), получаемые из игры `/penis`.
**Команды экономики:**
- `/balance` — показать баланс и статистику
- `/deposit <сумма> <дни>` — открыть вклад по ставке ЦБ РФ
- `/loan <сумма> <дни>` — взять кредит под 15% годовых
- `/transfer @username <сумма>` — перевести деньги (лимит 20% от капитала)
- `/cb_stats` — статистика Центрального Банка
- `/central_bank` — детальная статистика ЦБ
- `/cb_rules` — правила работы ЦБ
- `/vanomasa` — полное обнуление всех балансов (админам)
**Фичи экономики:**
- Вклады по ставке ЦБ РФ (7.5% годовых)
- Кредиты под 15% годовых
- Налог 13% с ежедневного дохода
- Автоматическое регулирование инфляции
- Фоновый сбор налогов каждый час
- Заработок на сообщениях: 0.01 см за каждое сообщение
- Ежедневные выплаты в 00:00 по МСК за активность
**Центральный Банк ZOV OS 🏛️ (децентрализованный):**
- Учредительный капитал: 1000 см
- Полностью автоматическая работа без вмешательства
- Все налоги автоматически поступают в ЦБ
- Алгоритмическое регулирование инфляции
- Автоматическая эмиссия при дефляции
- Автоматическая скупка денег при инфляции
- Прозрачная статистика для всех пользователей
- Никакого ручного управления - система работает по правилам
## Макеты и шрифт ## Макеты и шрифт
- Шрифт `impact.ttf` лежит в корне. - Шрифт `impact.ttf` лежит в корне.

View file

@ -11,8 +11,7 @@ services:
PENIS_DB_PATH: /db/penis_stats.sqlite3 PENIS_DB_PATH: /db/penis_stats.sqlite3
CHAT_HISTORY_DB_PATH: /db/chat_history.sqlite3 CHAT_HISTORY_DB_PATH: /db/chat_history.sqlite3
POLYCHAETSI_STATS_PATH: /db/polychaetsi_stats.json POLYCHAETSI_STATS_PATH: /db/polychaetsi_stats.json
BETS_DB_PATH: /db/bets.sqlite3 BET_DB_PATH: /db/bets.sqlite3
ECONOMY_DB_PATH: /db/economy.sqlite3
LLAMA_API_URL: ${LLAMA_API_URL:-https://mirror.porno4free.ru/zovos-ai/} LLAMA_API_URL: ${LLAMA_API_URL:-https://mirror.porno4free.ru/zovos-ai/}
LLAMA_FORCE_DISABLE_THINKING: ${LLAMA_FORCE_DISABLE_THINKING:-0} LLAMA_FORCE_DISABLE_THINKING: ${LLAMA_FORCE_DISABLE_THINKING:-0}
LLAMA_LOG_THINKING: ${LLAMA_LOG_THINKING:-1} LLAMA_LOG_THINKING: ${LLAMA_LOG_THINKING:-1}

View file

@ -3,7 +3,6 @@ import sqlite3
import logging import logging
import config import config
from .economy import economy
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -74,8 +73,14 @@ def update_user_length(user_id: int, delta: float) -> float | None:
def play_casino(user_id: int, bet: float) -> str: def play_casino(user_id: int, bet: float) -> str:
current = economy.get_user_balance(user_id) current = get_user_length(user_id)
if current is None:
return "Ты ещё ни разу не крутил /penis, братуха. Сначала заведи счёт."
# Проверяем отрицательный баланс
if current <= 0:
return "🚫 С кредитом в казино не впускают, братуха. Иди наращивай через /penis."
# Проверяем максимум ставки # Проверяем максимум ставки
if bet > config.CASINO_MAX_BET: if bet > config.CASINO_MAX_BET:
return f"Максимальная ставка — {config.CASINO_MAX_BET} см, не жадничай." return f"Максимальная ставка — {config.CASINO_MAX_BET} см, не жадничай."
@ -95,11 +100,9 @@ def play_casino(user_id: int, bet: float) -> str:
min_mult, max_mult = MULTIPLIERS[matches] min_mult, max_mult = MULTIPLIERS[matches]
multiplier = round(random.uniform(min_mult, max_mult), 1) multiplier = round(random.uniform(min_mult, max_mult), 1)
winnings = round(bet * multiplier, 1) winnings = round(bet * multiplier, 1)
new_length = update_user_length(user_id, winnings)
# Обновляем баланс в экономической системе if new_length is None:
economy.update_balance(user_id, winnings, 'casino_win', f'Выигрыш в казино x{multiplier}') return "Ошибка БД. Попробуй позже."
new_balance = economy.get_user_balance(user_id)
if matches == 3: if matches == 3:
return ( return (
@ -107,22 +110,22 @@ def play_casino(user_id: int, bet: float) -> str:
f"🎉 ДЖЕКПОТ!!! Три одинаковых!\n" f"🎉 ДЖЕКПОТ!!! Три одинаковых!\n"
f"Множитель: x{multiplier}\n" f"Множитель: x{multiplier}\n"
f"Выигрыш: +{winnings:.1f} см\n" f"Выигрыш: +{winnings:.1f} см\n"
f"<EFBFBD> Баланс: {new_balance:.1f} см" f"📏 Текущий размер: {new_length:.1f} см"
) )
return ( return (
f"🎰 {slots_display}\n\n" f"🎰 {slots_display}\n\n"
f"✅ Выигрыш! Две совпали.\n" f"✅ Выигрыш! Две совпали.\n"
f"Множитель: x{multiplier}\n" f"Множитель: x{multiplier}\n"
f"Выигрыш: +{winnings:.1f} см\n" f"Выигрыш: +{winnings:.1f} см\n"
f"<EFBFBD> Баланс: {new_balance:.1f} см" f"📏 Текущий размер: {new_length:.1f} см"
) )
else: else:
# Обновляем баланс в экономической системе new_length = update_user_length(user_id, -bet)
economy.update_balance(user_id, -bet, 'casino_loss', f'Проигрыш в казино -{bet} см') if new_length is None:
new_balance = economy.get_user_balance(user_id) return "Ошибка БД. Попробуй позже."
return ( return (
f"🎰 {slots_display}\n\n" f"🎰 {slots_display}\n\n"
f"❌ Мимо, братуха.\n" f"❌ Мимо, братуха.\n"
f"Проигрыш: -{bet:.1f} см\n" f"Проигрыш: -{bet:.1f} см\n"
f"<EFBFBD> Баланс: {new_balance:.1f} см" f"📏 Текущий размер: {new_length:.1f} см"
) )

View file

@ -1,633 +0,0 @@
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,))
balance = cursor.fetchone()[0] if cursor.fetchone() 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()
conn.close()
# Получаем статистику сообщений за сегодня
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_capital = cursor.fetchone()[0] if cursor.fetchone() 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()

303
main.py
View file

@ -39,7 +39,6 @@ from games.betting import (
resolve_match_outcome, resolve_match_outcome,
settle_bets, settle_bets,
) )
from games.economy import economy
from zparser import get_military_data from zparser import get_military_data
import config import config
@ -776,12 +775,6 @@ def play_penis(user_id: int, user_name: str | None = None, now: datetime | None
(display_name, new_length, now_ts, user_id), (display_name, new_length, now_ts, user_id),
) )
conn.commit() conn.commit()
# Обновляем баланс в экономической системе
if sign > 0:
economy.update_balance(user_id, delta, 'penis_game', f'Выигрыш в игре +{delta} см')
else:
economy.update_balance(user_id, delta, 'penis_game', f'Проигрыш в игре {delta} см')
except sqlite3.Error: except sqlite3.Error:
logger.exception("Failed to process /penis") logger.exception("Failed to process /penis")
return False, "Ошибка БД. Попробуй позже." return False, "Ошибка БД. Попробуй позже."
@ -1208,11 +1201,6 @@ _autoreply_disabled_chats: set[int] = set()
async def handle_keywords(message: Message): async def handle_keywords(message: Message):
if not message.text or message.text.startswith('/'): if not message.text or message.text.startswith('/'):
return return
# Считаем сообщение для экономической системы
if message.from_user:
await asyncio.to_thread(economy.count_message, message.from_user.id)
text_lower = message.text.lower() text_lower = message.text.lower()
chat_id = message.chat.id chat_id = message.chat.id
user_name = (message.from_user.first_name or "кент") if message.from_user else "кент" user_name = (message.from_user.first_name or "кент") if message.from_user else "кент"
@ -1268,243 +1256,6 @@ async def handle_penis_casino_cmd(message: Message):
result = await asyncio.to_thread(play_casino, message.from_user.id, bet) result = await asyncio.to_thread(play_casino, message.from_user.id, bet)
await message.reply(result, parse_mode=None) await message.reply(result, parse_mode=None)
# --- Экономические команды ---
async def handle_balance_cmd(message: Message):
"""Показать баланс пользователя"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
balance = economy.get_user_balance(message.from_user.id)
stats = await asyncio.to_thread(economy.get_user_stats, message.from_user.id)
cb_stats = await asyncio.to_thread(economy.get_central_bank_stats)
text = f"""💰 **Твой баланс:** {balance:.1f} см
📊 **Статистика:**
Активные вклады: {stats['active_deposits_count']} шт. ({stats['active_deposits_sum']:.1f} см)
Активные кредиты: {stats['active_loans_count']} шт. ({stats['active_loans_sum']:.1f} см)
Сообщений сегодня: {stats['today_messages']} шт.
Сообщений вчера: {stats['yesterday_messages']} шт.
Ожидаемый доход: {stats['yesterday_messages'] * 0.01:.2f} см
🏛 **Центральный Банк:**
Капитал ЦБ: {cb_stats['capital']:.1f} см
Собрано налогов: {cb_stats['total_taxes_collected']:.1f} см
💡 **Команды экономики:**
/balance - показать баланс
/deposit [сумма] [дни] - открыть вклад
/loans - показать кредиты
/loan [сумма] [дни] - взять кредит
/repay [ID] - погасить кредит
/transfer @username [сумма] - перевести деньги
/cb_stats - статистика ЦБ
/vanomasa - полное обнуление (для админов)
💬 **Заработок на сообщениях:**
0.01 см за каждое сообщение
Выплата в 00:00 по МСК
"""
await message.answer(text, parse_mode=None)
async def handle_deposit_cmd(message: Message):
"""Открыть вклад"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
parts = message.text.split(maxsplit=2)
if len(parts) < 3:
await message.reply("Формат: /deposit [сумма] [дни]\nПример: /deposit 50 30", parse_mode=None)
return
try:
amount = float(parts[1].replace(",", "."))
days = int(parts[2])
except ValueError:
await message.reply("Сумма должна быть числом, дни - целым числом.", parse_mode=None)
return
if amount <= 0 or days <= 0:
await message.reply("Сумма и дни должны быть положительными.", parse_mode=None)
return
success = await asyncio.to_thread(economy.create_deposit, message.from_user.id, amount, days)
if success:
rate = economy.get_setting('central_bank_rate')
await message.reply(f"✅ Вклад открыт!\n💰 Сумма: {amount:.1f} см\n📅 Срок: {days} дней\n📈 Ставка: {rate}% годовых", parse_mode=None)
else:
await message.reply("Не удалось открыть вклад. Проверь баланс и минимальную сумму.", parse_mode=None)
async def handle_loans_cmd(message: Message):
"""Показать кредиты"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
# Здесь можно добавить логику показа кредитов пользователя
await message.reply("📋 Твои кредиты:\n\n(функция в разработке)", parse_mode=None)
async def handle_loan_cmd(message: Message):
"""Взять кредит"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
parts = message.text.split(maxsplit=2)
if len(parts) < 3:
await message.reply("Формат: /loan [сумма] [дни]\nПример: /loan 50 30", parse_mode=None)
return
try:
amount = float(parts[1].replace(",", "."))
days = int(parts[2])
except ValueError:
await message.reply("Сумма должна быть числом, дни - целым числом.", parse_mode=None)
return
if amount <= 0 or days <= 0:
await message.reply("Сумма и дни должны быть положительными.", parse_mode=None)
return
success = await asyncio.to_thread(economy.create_loan, message.from_user.id, amount, days)
if success:
rate = economy.get_setting('loan_interest_rate')
await message.reply(f"✅ Кредит выдан!\n💰 Сумма: {amount:.1f} см\n📅 Срок: {days} дней\n📈 Ставка: {rate}% годовых", parse_mode=None)
else:
await message.reply("Не удалось взять кредит. Проверь лимиты.", parse_mode=None)
async def handle_transfer_cmd(message: Message):
"""Перевод денег другому пользователю"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
parts = message.text.split(maxsplit=2)
if len(parts) < 3:
await message.reply("Формат: /transfer @username [сумма]\nПример: /transfer @user123 10.5", parse_mode=None)
return
username = parts[1].lstrip('@')
try:
amount = float(parts[2].replace(",", "."))
except ValueError:
await message.reply("Сумма должна быть числом.", parse_mode=None)
return
if amount <= 0:
await message.reply("Сумма должна быть положительной.", parse_mode=None)
return
# Здесь нужно найти user_id по username
# Для простоты примера, пока покажем сообщение об ошибке
await message.reply("🔍 Поиск пользователя...\n(функция поиска пользователей будет добавлена)", parse_mode=None)
async def handle_vanomasa_cmd(message: Message):
"""Полное обнуление (только для админов)"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
# Проверка на админа (простая реализация)
if message.from_user.id not in [123456789]: # Заменить на реальные ID админов
await message.reply("❌ Команда доступна только администраторам!", parse_mode=None)
return
success = await asyncio.to_thread(economy.vanomasa)
if success:
await message.reply("💥 **ВАНОМАСА!** 💥\n\nВсе балансы обнулены!", parse_mode=None)
else:
await message.reply("❌ Ваномаса еще не доступна. Подождите.", parse_mode=None)
async def handle_central_bank_cmd(message: Message):
"""Статистика Центрального Банка"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
cb_stats = await asyncio.to_thread(economy.get_central_bank_stats)
text = f"""🏛️ **Центральный Банк ZOV OS** (децентрализованный)
💰 **Капитал ЦБ:** {cb_stats['capital']:.2f} см
📊 **Статистика операций:**
Всего собрано налогов: {cb_stats['total_taxes_collected']:.2f} см
Регулирование инфляции: {cb_stats['total_inflation_adjustments']:.2f} см
Эмиссия денег: {cb_stats['total_emissions']:.2f} см
🤖 **Автоматическое регулирование:**
ЦБ работает по алгоритмам без вмешательства
Налоги автоматически поступают в ЦБ
Инфляция регулируется автоматически
Эмиссия при дефляции, скупка при инфляции
💡 **Команды ЦБ:**
/central_bank - детальная статистика
/cb_stats - краткая статистика
/cb_rules - правила работы ЦБ
"""
async def handle_cb_stats_cmd(message: Message):
"""Статистика ЦБ для всех пользователей"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
cb_stats = await asyncio.to_thread(economy.get_central_bank_stats)
text = f"""🏛️ **Центральный Банк ZOV OS** (децентрализованный)
💰 **Капитал ЦБ:** {cb_stats['capital']:.2f} см
📊 **Операции ЦБ:**
Собрано налогов: {cb_stats['total_taxes_collected']:.2f} см
Антиинфляция: {cb_stats['total_inflation_adjustments']:.2f} см
Эмиссия: {cb_stats['total_emissions']:.2f} см
🔹 ЦБ работает автоматически по алгоритмам
🔹 Учредительный капитал: 1000 см
🔹 Все налоги поступают в ЦБ
🔹 Никакого ручного управления
"""
await message.answer(text, parse_mode=None)
async def handle_cb_rules_cmd(message: Message):
"""Правила работы Центрального Банка"""
if not message.from_user:
await message.answer("Команда доступна только пользователям.")
return
text = f"""📖 **Правила Центрального Банка ZOV OS**
🤖 **Децентрализованная система:**
ЦБ работает полностью автоматически
Никакого ручного вмешательства
Все решения принимаются по алгоритмам
<EFBFBD> **Сбор налогов:**
13% налог автоматически поступает в ЦБ
Налоги собираются каждый час
Капитал ЦБ растет за счет налогов
📈 **Борьба с инфляцией:**
Если инфляция > 10%: ЦБ скупает деньги
Увеличивает налоги до 20% максимум
Скупает до 5% от массы денег
<EFBFBD> **Стимуляция экономики:**
Если инфляция < 2%: ЦБ проводит эмиссию
Уменьшает налоги до 10% минимум
Эмиссия до 5% от капитала ЦБ
**Баланс системы:**
ЦБ не может обанкротиться (учредительный капитал)
Автоматическая стабилизация экономики
Прозрачная статистика для всех
"""
await message.answer(text, parse_mode=None)
async def handle_gadanie_cmd(message: Message): async def handle_gadanie_cmd(message: Message):
parts = message.text.split(maxsplit=1) parts = message.text.split(maxsplit=1)
if len(parts) < 2 or not parts[1].strip(): if len(parts) < 2 or not parts[1].strip():
@ -1716,19 +1467,6 @@ def main():
dp.message.register(handle_uwu_cmd, Command("uwu")) dp.message.register(handle_uwu_cmd, Command("uwu"))
dp.message.register(handle_nude_cmd, Command("nude")) dp.message.register(handle_nude_cmd, Command("nude"))
dp.message.register(handle_photo_message, _is_watch_image_message) dp.message.register(handle_photo_message, _is_watch_image_message)
# Экономические команды
dp.message.register(handle_balance_cmd, Command("balance"))
dp.message.register(handle_deposit_cmd, Command("deposit"))
dp.message.register(handle_loans_cmd, Command("loans"))
dp.message.register(handle_loan_cmd, Command("loan"))
dp.message.register(handle_transfer_cmd, Command("transfer"))
dp.message.register(handle_vanomasa_cmd, Command("vanomasa"))
# Команды Центрального Банка (децентрализованные)
dp.message.register(handle_central_bank_cmd, Command("central_bank"))
dp.message.register(handle_cb_stats_cmd, Command("cb_stats"))
dp.message.register(handle_cb_rules_cmd, Command("cb_rules"))
dp.message.register(handle_keywords, F.text) dp.message.register(handle_keywords, F.text)
dp.message.register(handle_unmatched_message_debug) dp.message.register(handle_unmatched_message_debug)
@ -1761,13 +1499,6 @@ def main():
BotCommand(command="svodka", description="СВО: итоги"), BotCommand(command="svodka", description="СВО: итоги"),
BotCommand(command="uwu", description="Случайная картинка с e621"), BotCommand(command="uwu", description="Случайная картинка с e621"),
BotCommand(command="nude", description="Голые женщины из открытых источников"), BotCommand(command="nude", description="Голые женщины из открытых источников"),
BotCommand(command="balance", description="💰 баланс и статистика"),
BotCommand(command="deposit", description="💎 открыть вклад"),
BotCommand(command="loan", description="💵 взять кредит"),
BotCommand(command="transfer", description="💸 перевести деньги"),
BotCommand(command="cb_stats", description="🏛️ статистика ЦБ"),
BotCommand(command="central_bank", description="🏛️ детальная статистика ЦБ"),
BotCommand(command="cb_rules", description="📖 правила ЦБ"),
] ]
scopes = ( scopes = (
BotCommandScopeDefault(), BotCommandScopeDefault(),
@ -1778,40 +1509,6 @@ def main():
await bot.set_my_commands(commands, scope=scope) await bot.set_my_commands(commands, scope=scope)
except Exception: except Exception:
logger.warning("Не удалось выставить команды, но бот продолжит работу.") logger.warning("Не удалось выставить команды, но бот продолжит работу.")
# Запуск фоновых задач экономики
asyncio.create_task(economy_background_tasks())
async def economy_background_tasks():
"""Фоновые задачи для экономики"""
while True:
try:
# Проверяем текущее время по МСК
from datetime import datetime
from zoneinfo import ZoneInfo
moscow_time = datetime.now(ZoneInfo("Europe/Moscow"))
current_hour = moscow_time.hour
current_minute = moscow_time.minute
# Ежедневный сбор налогов (каждый час проверяем)
await asyncio.to_thread(economy.collect_daily_taxes)
# Регулирование инфляции (раз в день)
await asyncio.to_thread(economy.regulate_inflation)
# Выплаты за активность в 00:00 по МСК
if current_hour == 0 and current_minute == 0:
users_paid = await asyncio.to_thread(economy.pay_daily_activity_rewards)
if users_paid > 0:
logger.info(f"Activity rewards paid to {users_paid} users at 00:00 MSK")
logger.info(f"Economy background tasks completed at {moscow_time.strftime('%H:%M:%S MSK')}")
except Exception as e:
logger.exception(f"Economy background task failed: {e}")
# Ждём 1 минуту до следующей проверки (для точности в 00:00)
await asyncio.sleep(60)
dp.startup.register(on_startup) dp.startup.register(on_startup)