From 628099b4430768ad10725144fd880123901caea7 Mon Sep 17 00:00:00 2001
From: itexpert228 <67105314+fdaser1337@users.noreply.github.com>
Date: Fri, 10 Apr 2026 12:37:16 +0300
Subject: [PATCH 1/3] delete govnshe ebanoe
---
games/uwu.py | 87 ----------------------------------------------------
1 file changed, 87 deletions(-)
delete mode 100644 games/uwu.py
diff --git a/games/uwu.py b/games/uwu.py
deleted file mode 100644
index 634e4f1..0000000
--- a/games/uwu.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import aiohttp
-from aiogram import types
-import config
-import logging
-import random
-
-logger = logging.getLogger(__name__)
-
-CAPTIONS = [
- "Ня :3",
- "uwu"
-]
-
-async def handle_uwu_cmd(message: types.Message):
- args = message.text.split(maxsplit=1)
-
- if len(args) > 1 and args[1].strip() == "-h":
- help_msg = (
- "🐈 Справка по тегам e621:\n\n"
- "Перечислите любые теги через пробел после команды.\n"
- "• rating: rating:safe (безопасные), rating:questionable, rating:explicit (NSFW)\n"
- "• score: score:>500 (искать популярные посты с рейтингом больше 500)\n"
- "• Исключить тег: поставьте минус, например -fox\n\n"
- "Пример: /uwu wolf -rating:explicit score:>100\n"
- "По умолчанию: rating:safe score:>500 -animated"
- )
- await message.reply(help_msg, parse_mode="HTML")
- return
-
- tags = "rating:safe score:>500 -animated"
- if len(args) > 1:
- tags = args[1].strip()
-
- tags += " order:random"
-
- url = "https://e621.net/posts.json"
- params = {
- "tags": tags,
- "limit": 1
- }
-
- auth = None
- if config.E621_LOGIN and config.E621_API_KEY:
- auth = aiohttp.BasicAuth(config.E621_LOGIN, config.E621_API_KEY)
-
- headers = {
- "User-Agent": f"Sex Bomba TG Bot/1.0 (by {config.E621_LOGIN or 'unknown'} on e621)"
- }
-
- try:
- async with aiohttp.ClientSession() as session:
- async with session.get(url, params=params, headers=headers, auth=auth) as resp:
- if resp.status != 200:
- logger.error(f"e621 API Error: {resp.status}")
- await message.reply("Не удалось получить картинку от сервера :(")
- return
- data = await resp.json()
-
- posts = data.get("posts", [])
- if not posts:
- await message.reply("Ничего не найдено по этим тегам :(")
- return
-
- post = posts[0]
- file_url = post.get("file", {}).get("url")
- ext = post.get("file", {}).get("ext", "png")
- if not file_url:
- await message.reply("У найденного поста нет прямого URL изображения :(")
- return
-
- caption = random.choice(CAPTIONS)
- if ext in ("gif", "webm", "mp4"):
- # Для анимаций: отправляем caption отдельным сообщением,
- # а саму гифку/видео через URL напрямую — так Telegram
- # корректно показывает её как анимацию, а не как файл
- await message.answer(caption)
- await message.answer_animation(animation=file_url)
- elif ext in ("png", "jpg", "jpeg", "webp"):
- input_file = types.URLInputFile(file_url)
- await message.answer_photo(photo=input_file, caption=caption)
- else:
- input_file = types.URLInputFile(file_url)
- await message.answer_document(document=input_file, caption=caption)
-
- except Exception as e:
- logger.exception("Error in uwu command")
- await message.reply("Произошла ошибка при обращении к API :(")
From 833ec9cf9ac9b0321b1a4658becf044c3149813a Mon Sep 17 00:00:00 2001
From: itexpert228 <67105314+fdaser1337@users.noreply.github.com>
Date: Fri, 24 Apr 2026 12:12:59 +0300
Subject: [PATCH 2/3] =?UTF-8?q?=D1=81=D0=B8=D1=81=D0=B8=D0=BF=D0=B8=D1=81?=
=?UTF-8?q?=D0=B8=20=D1=8D=D0=BA=D0=BE=D0=BD=D0=BE=D0=BC=D0=B8=D0=BA=D0=B0?=
=?UTF-8?q?=20=D1=81=D0=BF=D0=B5=D1=80=D0=BC=D0=B0=20=D0=B3=D0=BE=D0=B2?=
=?UTF-8?q?=D0=BD=D0=BE=20=D0=B6=D0=BE=D0=BF=D0=B0=D0=B0=D0=B0=D0=B0=D0=B0?=
=?UTF-8?q?=20=D1=81=D0=B5=D0=B5=D0=B5=D0=B5=D0=BA=D1=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
DEPLOYMENT.md | 106 ++++++++
README.md | 42 ++-
docker-compose.yaml | 2 +
games/casino.py | 31 +--
games/economy.py | 633 ++++++++++++++++++++++++++++++++++++++++++++
main.py | 304 +++++++++++++++++++++
6 files changed, 1097 insertions(+), 21 deletions(-)
create mode 100644 DEPLOYMENT.md
create mode 100644 games/economy.py
diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md
new file mode 100644
index 0000000..88356f1
--- /dev/null
+++ b/DEPLOYMENT.md
@@ -0,0 +1,106 @@
+# Инструкция по развертыванию бота
+
+## 🚀 Быстрый старт
+
+### 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`
+
+Бот готов к работе! 🎉
diff --git a/README.md b/README.md
index d586f05..0da5c5d 100644
--- a/README.md
+++ b/README.md
@@ -35,10 +35,11 @@ docker compose down
Для ставок нужен `ODDS_API_KEY` (бесплатно на https://the-odds-api.com).
Контейнер хранит состояние в `./db`:
-- `penis_stats.sqlite3`
-- `chat_history.sqlite3`
-- `polychaetsi_stats.json`
-- `bets.sqlite3`
+- `penis_stats.sqlite3` — статистика игры /penis
+- `chat_history.sqlite3` — история для ИИ-общения
+- `polychaetsi_stats.json` — статистика слова "получается"
+- `bets.sqlite3` — ставки на спорт
+- `economy.sqlite3` — экономическая система (балансы, вклады, кредиты)
## Команды
@@ -60,6 +61,39 @@ docker compose down
- `/mybets` — мои активные ставки.
- `/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` лежит в корне.
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 7432aa9..3e3796f 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -11,5 +11,7 @@ services:
PENIS_DB_PATH: /db/penis_stats.sqlite3
CHAT_HISTORY_DB_PATH: /db/chat_history.sqlite3
POLYCHAETSI_STATS_PATH: /db/polychaetsi_stats.json
+ BETS_DB_PATH: /db/bets.sqlite3
+ ECONOMY_DB_PATH: /db/economy.sqlite3
init: true
restart: unless-stopped
diff --git a/games/casino.py b/games/casino.py
index 6c4a534..036e723 100644
--- a/games/casino.py
+++ b/games/casino.py
@@ -3,6 +3,7 @@ import sqlite3
import logging
import config
+from .economy import economy
logger = logging.getLogger(__name__)
@@ -69,14 +70,8 @@ def update_user_length(user_id: int, delta: float) -> float | None:
def play_casino(user_id: int, bet: float) -> str:
- current = get_user_length(user_id)
- if current is None:
- return "Ты ещё ни разу не крутил /penis, братуха. Сначала заведи счёт."
-
- # Проверяем отрицательный баланс
- if current <= 0:
- return "🚫 С кредитом в казино не впускают, братуха. Иди наращивай через /penis."
-
+ current = economy.get_user_balance(user_id)
+
# Проверяем максимум ставки
if bet > config.CASINO_MAX_BET:
return f"Максимальная ставка — {config.CASINO_MAX_BET} см, не жадничай."
@@ -96,9 +91,11 @@ def play_casino(user_id: int, bet: float) -> str:
min_mult, max_mult = MULTIPLIERS[matches]
multiplier = round(random.uniform(min_mult, max_mult), 1)
winnings = round(bet * multiplier, 1)
- new_length = update_user_length(user_id, winnings)
- if new_length is None:
- return "Ошибка БД. Попробуй позже."
+
+ # Обновляем баланс в экономической системе
+ economy.update_balance(user_id, winnings, 'casino_win', f'Выигрыш в казино x{multiplier}')
+
+ new_balance = economy.get_user_balance(user_id)
if matches == 3:
return (
@@ -106,22 +103,22 @@ def play_casino(user_id: int, bet: float) -> str:
f"🎉 ДЖЕКПОТ!!! Три одинаковых!\n"
f"Множитель: x{multiplier}\n"
f"Выигрыш: +{winnings:.1f} см\n"
- f"📏 Текущий размер: {new_length:.1f} см"
+ f"� Баланс: {new_balance:.1f} см"
)
return (
f"🎰 {slots_display}\n\n"
f"✅ Выигрыш! Две совпали.\n"
f"Множитель: x{multiplier}\n"
f"Выигрыш: +{winnings:.1f} см\n"
- f"📏 Текущий размер: {new_length:.1f} см"
+ f"� Баланс: {new_balance:.1f} см"
)
else:
- new_length = update_user_length(user_id, -bet)
- if new_length is None:
- return "Ошибка БД. Попробуй позже."
+ # Обновляем баланс в экономической системе
+ 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_length:.1f} см"
+ f"� Баланс: {new_balance:.1f} см"
)
diff --git a/games/economy.py b/games/economy.py
new file mode 100644
index 0000000..2da55c3
--- /dev/null
+++ b/games/economy.py
@@ -0,0 +1,633 @@
+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()
diff --git a/main.py b/main.py
index 7891bde..17669df 100644
--- a/main.py
+++ b/main.py
@@ -38,6 +38,7 @@ from games.betting import (
resolve_match_outcome,
settle_bets,
)
+from games.economy import economy
from zparser import get_military_data
import config
@@ -738,6 +739,12 @@ def play_penis(user_id: int, user_name: str | None = None, now: datetime | None
(display_name, new_length, now_ts, user_id),
)
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:
logger.exception("Failed to process /penis")
return False, "Ошибка БД. Попробуй позже."
@@ -1141,6 +1148,11 @@ async def handle_keywords(message: Message):
global _last_autoreply_ts
if not message.text or message.text.startswith('/'):
return
+
+ # Считаем сообщение для экономической системы
+ if message.from_user:
+ await asyncio.to_thread(economy.count_message, message.from_user.id)
+
text_lower = message.text.lower()
chat_id = message.chat.id
user_name = (message.from_user.first_name or "кент") if message.from_user else "кент"
@@ -1210,6 +1222,243 @@ async def handle_penis_casino_cmd(message: Message):
result = await asyncio.to_thread(play_casino, message.from_user.id, bet)
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**
+
+🤖 **Децентрализованная система:**
+• ЦБ работает полностью автоматически
+• Никакого ручного вмешательства
+• Все решения принимаются по алгоритмам
+
+� **Сбор налогов:**
+• 13% налог автоматически поступает в ЦБ
+• Налоги собираются каждый час
+• Капитал ЦБ растет за счет налогов
+
+📈 **Борьба с инфляцией:**
+• Если инфляция > 10%: ЦБ скупает деньги
+• Увеличивает налоги до 20% максимум
+• Скупает до 5% от массы денег
+
+� **Стимуляция экономики:**
+• Если инфляция < 2%: ЦБ проводит эмиссию
+• Уменьшает налоги до 10% минимум
+• Эмиссия до 5% от капитала ЦБ
+
+⚖️ **Баланс системы:**
+• ЦБ не может обанкротиться (учредительный капитал)
+• Автоматическая стабилизация экономики
+• Прозрачная статистика для всех
+"""
+ await message.answer(text, parse_mode=None)
+
async def handle_gadanie_cmd(message: Message):
parts = message.text.split(maxsplit=1)
if len(parts) < 2 or not parts[1].strip():
@@ -1419,6 +1668,20 @@ def main():
dp.message.register(handle_gen_mem, Command("gen_mem"))
dp.message.register(handle_uwu_cmd, Command("uwu"))
dp.message.register(handle_nude_cmd, Command("nude"))
+
+ # Экономические команды
+ 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)
async def on_startup(bot: Bot):
@@ -1449,6 +1712,13 @@ def main():
BotCommand(command="svodka", description="СВО: итоги"),
BotCommand(command="uwu", description="Случайная картинка с e621"),
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 = (
BotCommandScopeDefault(),
@@ -1459,6 +1729,40 @@ def main():
await bot.set_my_commands(commands, scope=scope)
except Exception:
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)
From 3f416e9fe059a9a0f5ad8e7e776cf44e591b3c8c Mon Sep 17 00:00:00 2001
From: itexpert228 <67105314+fdaser1337@users.noreply.github.com>
Date: Fri, 24 Apr 2026 12:15:31 +0300
Subject: [PATCH 3/3] Revert "delete govnshe ebanoe"
This reverts commit 628099b4430768ad10725144fd880123901caea7.
---
games/uwu.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100644 games/uwu.py
diff --git a/games/uwu.py b/games/uwu.py
new file mode 100644
index 0000000..634e4f1
--- /dev/null
+++ b/games/uwu.py
@@ -0,0 +1,87 @@
+import aiohttp
+from aiogram import types
+import config
+import logging
+import random
+
+logger = logging.getLogger(__name__)
+
+CAPTIONS = [
+ "Ня :3",
+ "uwu"
+]
+
+async def handle_uwu_cmd(message: types.Message):
+ args = message.text.split(maxsplit=1)
+
+ if len(args) > 1 and args[1].strip() == "-h":
+ help_msg = (
+ "🐈 Справка по тегам e621:\n\n"
+ "Перечислите любые теги через пробел после команды.\n"
+ "• rating: rating:safe (безопасные), rating:questionable, rating:explicit (NSFW)\n"
+ "• score: score:>500 (искать популярные посты с рейтингом больше 500)\n"
+ "• Исключить тег: поставьте минус, например -fox\n\n"
+ "Пример: /uwu wolf -rating:explicit score:>100\n"
+ "По умолчанию: rating:safe score:>500 -animated"
+ )
+ await message.reply(help_msg, parse_mode="HTML")
+ return
+
+ tags = "rating:safe score:>500 -animated"
+ if len(args) > 1:
+ tags = args[1].strip()
+
+ tags += " order:random"
+
+ url = "https://e621.net/posts.json"
+ params = {
+ "tags": tags,
+ "limit": 1
+ }
+
+ auth = None
+ if config.E621_LOGIN and config.E621_API_KEY:
+ auth = aiohttp.BasicAuth(config.E621_LOGIN, config.E621_API_KEY)
+
+ headers = {
+ "User-Agent": f"Sex Bomba TG Bot/1.0 (by {config.E621_LOGIN or 'unknown'} on e621)"
+ }
+
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url, params=params, headers=headers, auth=auth) as resp:
+ if resp.status != 200:
+ logger.error(f"e621 API Error: {resp.status}")
+ await message.reply("Не удалось получить картинку от сервера :(")
+ return
+ data = await resp.json()
+
+ posts = data.get("posts", [])
+ if not posts:
+ await message.reply("Ничего не найдено по этим тегам :(")
+ return
+
+ post = posts[0]
+ file_url = post.get("file", {}).get("url")
+ ext = post.get("file", {}).get("ext", "png")
+ if not file_url:
+ await message.reply("У найденного поста нет прямого URL изображения :(")
+ return
+
+ caption = random.choice(CAPTIONS)
+ if ext in ("gif", "webm", "mp4"):
+ # Для анимаций: отправляем caption отдельным сообщением,
+ # а саму гифку/видео через URL напрямую — так Telegram
+ # корректно показывает её как анимацию, а не как файл
+ await message.answer(caption)
+ await message.answer_animation(animation=file_url)
+ elif ext in ("png", "jpg", "jpeg", "webp"):
+ input_file = types.URLInputFile(file_url)
+ await message.answer_photo(photo=input_file, caption=caption)
+ else:
+ input_file = types.URLInputFile(file_url)
+ await message.answer_document(document=input_file, caption=caption)
+
+ except Exception as e:
+ logger.exception("Error in uwu command")
+ await message.reply("Произошла ошибка при обращении к API :(")