import asyncio import logging import sqlite3 import os from datetime import datetime import pytz from aiogram import Bot, Dispatcher, types, F from aiogram.filters import Command from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup # --- НАСТРОЙКИ --- # Если запускаешь на ПК, можешь временно заменить os.getenv на свой токен в кавычках TOKEN = os.getenv("BOT_TOKEN") or "8670682692:AAFM0jiSTY7z30O-PKkUZgj8QSpKX-GwHyo" ADMIN_ID = 1753037099 CHANNEL_ID = "@TWIXER_MUSIC" # Юзернейм канала с @ CHANNEL_URL = "https://t.me/TWIXER_MUSIC" MOSCOW_TZ = pytz.timezone('Europe/Moscow') # --- ИНИЦИАЛИЗАЦИЯ БАЗЫ ДАННЫХ --- def init_db(): conn = sqlite3.connect('database.db') cur = conn.cursor() # Таблица пользователей cur.execute('''CREATE TABLE IF NOT EXISTS users (user_id INTEGER PRIMARY KEY, username TEXT, received INTEGER, date TEXT)''') # Таблица настроек (для ID файла и счетчика) cur.execute('''CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)''') # Установка начального счетчика скачиваний cur.execute("INSERT OR IGNORE INTO settings (key, value) VALUES ('downloads', '0')") conn.commit() conn.close() def get_setting(key): conn = sqlite3.connect('database.db') cur = conn.cursor() cur.execute("SELECT value FROM settings WHERE key=?", (key,)) res = cur.fetchone() conn.close() return res[0] if res else None def update_setting(key, value): conn = sqlite3.connect('database.db') cur = conn.cursor() cur.execute("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", (key, value)) conn.commit() conn.close() def log_user(user_id, username, received): conn = sqlite3.connect('database.db') cur = conn.cursor() now = datetime.now(MOSCOW_TZ).strftime("%d.%m.%Y %H:%M") cur.execute("INSERT OR REPLACE INTO users VALUES (?, ?, ?, ?)", (user_id, username, received, now)) conn.commit() conn.close() def get_all_users(): conn = sqlite3.connect('database.db') cur = conn.cursor() cur.execute("SELECT * FROM users") data = cur.fetchall() conn.close() return data # --- ИНИЦИАЛИЗАЦИЯ БОТА --- bot = Bot(token=TOKEN) dp = Dispatcher() class AdminStates(StatesGroup): waiting_for_file = State() # Клавиатура подписки def get_sub_kb(): return InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="1. Подписаться на TWIXER", url=CHANNEL_URL)], [InlineKeyboardButton(text="2. Получить Драм Кит", callback_data="check_sub")] ]) # --- ОБРАБОТЧИКИ --- @dp.message(Command("start")) async def start_handler(message: types.Message): await message.answer( f"Привет, {message.from_user.first_name}!\n" f"Чтобы получить данный драм кит, нужно быть подписанным на канал TWIXER", reply_markup=get_sub_kb() ) @dp.callback_query(F.data == "check_sub") async def check_sub_callback(callback: types.CallbackQuery): user_id = callback.from_user.id try: # Проверяем статус в канале chat_member = await bot.get_chat_member(chat_id=CHANNEL_ID, user_id=user_id) if chat_member.status in ['member', 'administrator', 'creator']: file_id = get_setting("drum_kit_file_id") if not file_id: await callback.message.answer("Админ еще не загрузил файл в бота.") await callback.answer() return # Увеличиваем счетчик count = int(get_setting("downloads") or 0) + 1 update_setting("downloads", str(count)) # Логируем пользователя log_user(user_id, callback.from_user.username, 1) # Отправляем файл await callback.message.answer_document( file_id, caption=f"Драм кит успешно получен!\n\n🔥 Общее количество скачиваний: {count}" ) await callback.answer("Файл отправлен!") else: # Если не подписан await callback.answer("Вы не подписаны на канал!", show_alert=True) await callback.message.answer( "К сожалению вы не подписаны на канал.\n" "Чтобы получить драм кит, надо быть подписанным на канал!", reply_markup=get_sub_kb() ) except Exception as e: logging.error(e) await callback.answer("Ошибка! Убедитесь, что бот добавлен в админы канала.", show_alert=True) # --- АДМИН-КОМАНДЫ --- @dp.message(Command("admin")) async def admin_menu(message: types.Message): if message.from_user.id != ADMIN_ID: return text = ( "⚙️ Меню администратора:\n\n" "/admin - Показать список команд\n" "/FileDK - Загрузить/изменить файл драм кита\n" "/Stata - Посмотреть статистику и подписчиков" ) await message.answer(text) @dp.message(Command("FileDK")) async def admin_file_dk(message: types.Message, state: FSMContext): if message.from_user.id != ADMIN_ID: return await message.answer("Пришлите файл (документ), который бот будет выдавать пользователям.") await state.set_state(AdminStates.waiting_for_file) @dp.message(AdminStates.waiting_for_file, F.document) async def save_file_id(message: types.Message, state: FSMContext): file_id = message.document.file_id update_setting("drum_kit_file_id", file_id) await message.answer("✅ Файл успешно сохранен! Теперь пользователи будут получать его.") await state.clear() @dp.message(Command("Stata")) async def admin_stata(message: types.Message): if message.from_user.id != ADMIN_ID: return users = get_all_users() if not users: await message.answer("Статистика пока пуста.") return report = "📊 Детальная статистика:\n\n" for uid, name, received, date in users: # Проверяем статус подписки в реальном времени try: member = await bot.get_chat_member(chat_id=CHANNEL_ID, user_id=uid) sub_status = "подписан" if member.status in ['member', 'administrator', 'creator'] else "не подписан" except: sub_status = "неизвестно (бот заблокирован?)" rec_status = "получил(-а) файл" if received == 1 else "не получил(-а)" username = f"@{name}" if name else f"ID: {uid}" report += f"👤 {username} {sub_status} {rec_status}\n📅 {date}\n\n" if len(report) > 3800: # Лимит сообщения в ТГ await message.answer(report) report = "" if report: await message.answer(report) # --- ЗАПУСК --- async def main(): init_db() logging.basicConfig(level=logging.INFO) print("Бот запущен!") await dp.start_polling(bot) if __name__ == "__main__": try: asyncio.run(main()) except (KeyboardInterrupt, SystemExit): logging.info("Бот остановлен")