"""Обработчики для регистрации пользователей""" from aiogram import Router, F from aiogram.types import Message, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup from aiogram.filters import Command, StateFilter from src.filters.case_insensitive import CaseInsensitiveCommand from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup import logging from src.core.database import async_session_maker from src.core.registration_services import RegistrationService, AccountService from src.core.services import UserService logger = logging.getLogger(__name__) router = Router() # Служебные слова, которые нельзя использовать как никнейм FORBIDDEN_NICKNAMES = [ 'привет', 'здравствуйте', 'добрый', 'день', 'вечер', 'утро', 'спасибо', 'пожалуйста', 'извините', 'до свидания', 'пока', 'admin', 'administrator', 'moderator', 'bot', 'system', 'hello', 'hi', 'thanks', 'please', 'sorry', 'goodbye', 'bye' ] def validate_nickname(nickname: str) -> tuple[bool, str]: """ Валидация никнейма Returns: (valid, error_message) """ nickname = nickname.strip() # Проверка длины if len(nickname) < 2: return False, "❌ Никнейм слишком короткий (минимум 2 символа)" if len(nickname) > 20: return False, "❌ Никнейм слишком длинный (максимум 20 символов)" # Проверка на служебные слова nickname_lower = nickname.lower() for forbidden in FORBIDDEN_NICKNAMES: if forbidden in nickname_lower: import random suggestion = f"{nickname[:3]}{random.randint(10, 99)}" return False, f"❌ Это похоже на приветствие или служебное слово.\n\nПридумайте уникальный никнейм (например: {suggestion})" # Проверка на команды if nickname.startswith('/'): return False, "❌ Никнейм не может начинаться с '/'" return True, "" class RegistrationStates(StatesGroup): """Состояния для процесса регистрации""" waiting_for_nickname = State() waiting_for_club_card = State() waiting_for_phone = State() @router.callback_query(F.data == "start_registration") async def start_registration(callback: CallbackQuery, state: FSMContext): """Начать процесс регистрации""" logger.info(f"Получен запрос на регистрацию от пользователя {callback.from_user.id}") text = ( "📝 Регистрация в системе\n\n" "Для участия в розыгрышах необходимо зарегистрироваться.\n\n" "Шаг 1 из 3: Придумайте никнейм\n\n" "🎭 Введите ваш никнейм для чата:\n" "• От 2 до 20 символов\n" "• Может содержать буквы, цифры, пробелы\n" "• Это имя будут видеть другие участники" ) await callback.message.edit_text( text, reply_markup=InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text="❌ Отмена", callback_data="back_to_main")] ]) ) await state.set_state(RegistrationStates.waiting_for_nickname) @router.message(StateFilter(RegistrationStates.waiting_for_nickname)) async def process_nickname(message: Message, state: FSMContext): """Обработка никнейма""" nickname = message.text.strip() # Валидация никнейма valid, error_msg = validate_nickname(nickname) if not valid: await message.answer( f"{error_msg}\n\n" "Попробуйте другой вариант:" ) return # Сохраняем никнейм await state.update_data(nickname=nickname) await message.answer( f"✅ Отлично! Ваш никнейм: {nickname}\n\n" "Шаг 2 из 3: Клубная карта\n\n" "📝 Введите номер вашей клубной карты:" ) await state.set_state(RegistrationStates.waiting_for_club_card) @router.message(StateFilter(RegistrationStates.waiting_for_club_card)) async def process_club_card(message: Message, state: FSMContext): """Обработка номера клубной карты""" club_card_number = message.text.strip() # Проверяем, не занята ли карта async with async_session_maker() as session: existing_user = await RegistrationService.get_user_by_club_card(session, club_card_number) if existing_user: await message.answer( f"❌ Клубная карта {club_card_number} уже зарегистрирована.\n\n" "Если это ваша карта, обратитесь к администратору." ) await state.clear() return await state.update_data(club_card_number=club_card_number) await message.answer( "Шаг 3 из 3: Телефон\n\n" "📱 Введите ваш номер телефона\n" "(или отправьте '-' чтобы пропустить):" ) await state.set_state(RegistrationStates.waiting_for_phone) @router.message(StateFilter(RegistrationStates.waiting_for_phone)) async def process_phone(message: Message, state: FSMContext): """Обработка номера телефона""" phone_input = message.text.strip() # Проверяем, не отправил ли пользователь просто "-" if phone_input == "-": phone = None else: # Валидируем телефон: не должно быть пустых или некорректных значений if not phone_input: await message.answer( "❌ Неверный номер телефона.\n\n" "Пожалуйста, введите корректный номер или отправьте '-' чтобы пропустить." ) return phone = phone_input data = await state.get_data() club_card_number = data['club_card_number'] nickname = data.get('nickname') try: async with async_session_maker() as session: user = await RegistrationService.register_user( session, telegram_id=message.from_user.id, club_card_number=club_card_number, phone=phone ) # Обновляем никнейм пользователя if nickname: user.nickname = nickname await session.commit() await session.refresh(user) text = ( "✅ Регистрация завершена!\n\n" f"🎭 Никнейм: {user.nickname}\n" f"🎫 Клубная карта: {user.club_card_number}\n" f"🔑 Ваш код верификации: **{user.verification_code}**\n\n" "⚠️ Сохраните этот код! Он понадобится для подтверждения выигрыша.\n\n" "Теперь вы можете участвовать в розыгрышах!" ) await message.answer(text, parse_mode="Markdown") await state.clear() except ValueError as e: await message.answer(f"❌ Ошибка регистрации: {str(e)}") await state.clear() except Exception as e: await message.answer(f"❌ Произошла ошибка: {str(e)}") await state.clear() @router.message(Command("my_code")) async def show_verification_code(message: Message): """Показать код верификации пользователя""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, message.from_user.id) if not user or not user.is_registered: await message.answer( "❌ Вы не зарегистрированы в системе.\n\n" "Для регистрации отправьте /start и выберите 'Регистрация'" ) return text = ( "🔑 Ваш код верификации:\n\n" f"**{user.verification_code}**\n\n" "Этот код используется для подтверждения выигрыша.\n" "Сообщите его администратору при получении приза." ) await message.answer(text, parse_mode="Markdown") @router.message(Command("my_accounts")) async def show_user_accounts(message: Message): """Показать счета пользователя""" async with async_session_maker() as session: user = await UserService.get_user_by_telegram_id(session, message.from_user.id) if not user or not user.is_registered: await message.answer("❌ Вы не зарегистрированы в системе") return accounts = await AccountService.get_user_accounts(session, user.id) if not accounts: await message.answer( "У вас пока нет привязанных счетов.\n\n" "Счета добавляются администратором." ) return text = f"💳 Ваши счета (Клубная карта: {user.club_card_number}):\n\n" for i, account in enumerate(accounts, 1): status = "✅" if account.is_active else "❌" text += f"{i}. {status} {account.account_number}\n" await message.answer(text)