Files
new_lottery_bot/main.py
Andrew K. Choi 782f702327
Some checks failed
continuous-integration/drone/pr Build is failing
Fix registration button handling and add debug logging
- Improve btn_registration handler to directly set state instead of creating fake callback
- Add /register command handler for registration
- Add text-based registration triggers ('регистрация', 'регистр', 'register')
- Add debug logging to handle_start to track registration status
- Ensure registration button is shown correctly for unregistered users
2026-03-07 08:55:35 +09:00

328 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Новая модульная версия main.py с применением SOLID принципов
"""
import asyncio
import logging
from contextlib import asynccontextmanager
from aiogram import Bot, Dispatcher, Router, F
from aiogram.types import Message, CallbackQuery
from aiogram.filters import Command
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.fsm.context import FSMContext
from src.filters.case_insensitive import CaseInsensitiveCommand
from src.core.config import BOT_TOKEN
from src.core.database import async_session_maker
from src.core.scheduler import bot_scheduler
from src.container import container
from src.interfaces.base import IBotController
from src.middlewares.activity import ActivityMiddleware
from src.handlers.admin_panel import admin_router
from src.handlers.registration_handlers import router as registration_router
from src.handlers.admin_account_handlers import router as admin_account_router
from src.handlers.redraw_handlers import router as redraw_router
from src.handlers.chat_handlers import router as chat_router
from src.handlers.admin_chat_handlers import router as admin_chat_router
from src.handlers.account_handlers import account_router
from src.handlers.message_management import message_admin_router
from src.handlers.p2p_chat import router as p2p_chat_router
from src.handlers.help_handlers import router as help_router
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Создание бота и диспетчера
bot = Bot(token=BOT_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(storage=storage)
router = Router()
# Middleware для логирования всех callback'ов
@dp.callback_query.middleware()
async def log_callback_middleware(handler, event, data):
"""Middleware для логирования всех callback запросов"""
logger.warning(f"🔔 MIDDLEWARE CALLBACK: data='{event.data}', user_id={event.from_user.id}")
result = await handler(event, data)
logger.warning(f"🔔 MIDDLEWARE CALLBACK HANDLED: data='{event.data}', result={result}")
return result
@asynccontextmanager
async def get_controller():
"""Контекстный менеджер для получения контроллера с БД сессией"""
async with async_session_maker() as session:
scoped_container = container.create_scoped_container(session)
controller = scoped_container.get(IBotController)
yield controller
# === COMMAND HANDLERS ===
@router.message(CaseInsensitiveCommand("start"))
async def cmd_start(message: Message):
"""Обработчик команды /start (регистронезависимо)"""
async with get_controller() as controller:
await controller.handle_start(message)
# === TEXT BUTTON HANDLERS ===
@router.message(F.text == "🎰 Розыгрыши")
async def btn_lotteries(message: Message):
"""Обработчик кнопки 'Розыгрыши'"""
from src.core.database import async_session_maker
from src.repositories.implementations import LotteryRepository, ParticipationRepository
from src.display.message_formatter import MessageFormatterImpl
from src.components.ui import KeyboardBuilderImpl
from src.core.services import UserService
from src.core.config import ADMIN_IDS
async with async_session_maker() as session:
lottery_repo = LotteryRepository(session)
participation_repo = ParticipationRepository(session)
lotteries = await lottery_repo.get_active()
if not lotteries:
await message.answer("❌ Нет активных розыгрышей")
return
text = "🎲 **Активные розыгрыши:**\n\n"
formatter = MessageFormatterImpl()
for lottery in lotteries:
participants_count = await participation_repo.get_count_by_lottery(lottery.id)
lottery_info = formatter.format_lottery_info(lottery, participants_count)
text += lottery_info + "\n" + "="*30 + "\n\n"
# Получаем информацию о регистрации пользователя
user_service = UserService(session)
user = await user_service.get_or_create_user(
telegram_id=message.from_user.id,
username=message.from_user.username,
first_name=message.from_user.first_name,
last_name=message.from_user.last_name
)
keyboard_builder = KeyboardBuilderImpl()
keyboard = keyboard_builder.get_main_keyboard(
is_admin=message.from_user.id in ADMIN_IDS,
is_registered=user.is_registered
)
await message.answer(
text,
reply_markup=keyboard,
parse_mode="Markdown"
)
@router.message(F.text == "💬 Чат")
async def btn_chat(message: Message, state: FSMContext):
"""Обработчик кнопки 'Чат'"""
from src.handlers.chat_handlers import enter_chat
await enter_chat(message, state)
@router.message(F.text == "📝 Регистрация")
async def btn_registration(message: Message, state: FSMContext):
"""Обработчик кнопки 'Регистрация'"""
from src.handlers.registration_handlers import RegistrationStates
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
logger.info(f"User {message.from_user.id} pressed Registration button")
text = (
"📝 Регистрация в системе\n\n"
"Для участия в розыгрышах необходимо зарегистрироваться.\n\n"
"Шаг 1 из 3: Придумайте никнейм\n\n"
"🎭 Введите ваш никнейм для чата:\n"
"• От 2 до 20 символов\n"
"• Может содержать буквы, цифры, пробелы\n"
"• Это имя будут видеть другие участники"
)
await message.answer(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="❌ Отмена", callback_data="back_to_main")]
])
)
await state.set_state(RegistrationStates.waiting_for_nickname)
@router.message(CaseInsensitiveCommand("register"))
async def cmd_register(message: Message, state: FSMContext):
"""Обработчик команды /register (регистронезависимо)"""
await btn_registration(message, state)
@router.message(F.text.lower().in_(["регистрация", "регистр", "register"]))
async def text_registration(message: Message, state: FSMContext):
"""Обработчик текста для регистрации"""
await btn_registration(message, state)
@router.message(F.text == "🔑 Мой код")
async def btn_my_code(message: Message):
"""Обработчик кнопки 'Мой код'"""
from src.handlers.registration_handlers import show_verification_code
await show_verification_code(message)
@router.message(F.text == "<EFBFBD> Мои логины")
async def btn_my_accounts(message: Message):
"""Обработчик кнопки 'Мои логины'"""
from src.handlers.registration_handlers import show_user_accounts
await show_user_accounts(message)
@router.message(F.text == "❓ Справка")
async def btn_help(message: Message):
"""Обработчик кнопки 'Справка'"""
from src.handlers.help_handlers import show_help_main
await show_help_main(message)
@router.message(F.text == "⚙️ Админ панель")
async def btn_admin(message: Message):
"""Обработчик кнопки 'Админ панель'"""
await cmd_admin(message)
@router.message(F.text == "🚪 Выйти из чата")
async def btn_exit_chat(message: Message, state: FSMContext):
"""Обработчик кнопки 'Выйти из чата'"""
from src.handlers.chat_handlers import exit_chat
await exit_chat(message, state)
@router.message(F.text == "🏠 Главная")
async def btn_main_menu(message: Message):
"""Обработчик кнопки 'Главная'"""
await cmd_start(message)
@router.message(CaseInsensitiveCommand("admin"))
async def cmd_admin(message: Message):
"""Обработчик команды /admin (регистронезависимо) - перенаправляет в admin_panel"""
from src.core.config import ADMIN_IDS
from src.core.database import async_session_maker
from src.core.models import User
from sqlalchemy import select
# Проверяем, является ли пользователь главным администратором из .env
user_id = message.from_user.id
is_super_admin = user_id in ADMIN_IDS
# Проверяем, является ли пользователь назначенным администратором
is_assigned_admin = False
if not is_super_admin:
async with async_session_maker() as session:
user = await session.execute(
select(User).where(User.telegram_id == user_id)
)
user = user.scalar_one_or_none()
is_assigned_admin = user and user.is_admin
# Если не администратор ни того, ни другого типа
if not (is_super_admin or is_assigned_admin):
await message.answer("❌ Недостаточно прав для доступа к админ панели")
return
# Отправляем сообщение с кнопкой админ панели
from src.components.ui import KeyboardBuilderImpl
kb = KeyboardBuilderImpl()
keyboard = kb.get_admin_keyboard()
text = "⚙️ **Панель администратора**\n\n"
text += "Выберите раздел для управления:"
await message.answer(text, reply_markup=keyboard, parse_mode="Markdown")
# === CALLBACK HANDLERS ===
@router.callback_query(F.data == "active_lotteries")
async def active_lotteries_handler(callback: CallbackQuery):
"""Обработчик показа активных розыгрышей"""
async with get_controller() as controller:
await controller.handle_active_lotteries(callback)
@router.callback_query(F.data == "back_to_main")
async def back_to_main_handler(callback: CallbackQuery):
"""Обработчик возврата в главное меню"""
async with get_controller() as controller:
await controller.handle_start(callback.message)
# Функции обрабатываются в:
# - admin_panel.py: создание розыгрышей, управление пользователями, счетами, чатом, статистика
# - registration_handlers.py: регистрация пользователей
# - admin_account_handlers.py: управление счетами
# - admin_chat_handlers.py: управление чатом
# - chat_handlers.py: пользовательский чат
# === FALLBACK HANDLERS ===
# Обработка неизвестных callback и сообщений происходит в соответствующих роутерах
async def main():
"""Главная функция запуска бота"""
logger.info("Запуск бота...")
# Подключаем middleware для отслеживания активности
dp.message.middleware(ActivityMiddleware())
dp.callback_query.middleware(ActivityMiddleware())
# Подключаем роутеры в правильном порядке
# 1. Основной роутер main.py с базовыми командами (/start, /help, /admin)
dp.include_router(router)
# 2. Специфичные роутеры
dp.include_router(message_admin_router) # Управление сообщениями администратором
dp.include_router(admin_router) # Админ панель - самая высокая специфичность
dp.include_router(registration_router) # Регистрация
dp.include_router(admin_account_router) # Админские команды счетов
dp.include_router(admin_chat_router) # Админские команды чата
dp.include_router(redraw_router) # Повторные розыгрыши
dp.include_router(p2p_chat_router) # P2P чат между пользователями
dp.include_router(help_router) # Справка и помощь
# 3. Chat router для broadcast (обрабатывает обычные сообщения)
dp.include_router(chat_router) # Пользовательский чат (broadcast всем) - РАНЬШЕ account_router
# 4. Account router для обнаружения счетов (обрабатывает сообщения со счетами от админов)
dp.include_router(account_router) # Обнаружение счетов для админов - ПОСЛЕ chat_router
# Запускаем планировщик задач
bot_scheduler.start()
logger.info("Планировщик задач запущен")
# Запускаем polling
try:
logger.info("Бот запущен")
await dp.start_polling(bot)
except Exception as e:
logger.error(f"Ошибка при запуске бота: {e}")
finally:
# Останавливаем планировщик
bot_scheduler.shutdown()
await bot.session.close()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
logger.info("Бот остановлен пользователем")
except Exception as e:
logger.error(f"Критическая ошибка: {e}")