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 :(") -- 2.45.2 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) -- 2.45.2 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 :(") -- 2.45.2