Some checks reported errors
continuous-integration/drone/push Build encountered an error
Основные изменения: ✨ Новые функции: - Система регистрации пользователей с множественными счетами - Автоматическое подтверждение выигрышей через inline-кнопки - Механизм переигровки для неподтвержденных выигрышей (24 часа) - Подтверждение на уровне счетов (каждый счет подтверждается отдельно) - Скрипт полной очистки базы данных 🔧 Технические улучшения: - Исправлена ошибка MissingGreenlet при lazy loading (добавлен joinedload/selectinload) - Добавлено поле claimed_at для отслеживания времени подтверждения - Пакетное добавление счетов с выбором розыгрыша - Проверка владения конкретным счетом при подтверждении 📚 Документация: - docs/AUTO_CONFIRM_SYSTEM.md - Полная документация системы подтверждения - docs/ACCOUNT_BASED_CONFIRMATION.md - Подтверждение на уровне счетов - docs/REGISTRATION_SYSTEM.md - Система регистрации - docs/ADMIN_COMMANDS.md - Команды администратора - docs/CLEAR_DATABASE.md - Очистка БД - docs/QUICK_GUIDE.md - Быстрое начало - docs/UPDATE_LOG.md - Журнал обновлений 🗄️ База данных: - Миграция 003: Таблицы accounts, winner_verifications - Миграция 004: Поле claimed_at в таблице winners - Скрипт scripts/clear_database.py для полной очистки 🎮 Новые команды: Админские: - /check_unclaimed <lottery_id> - Проверка неподтвержденных выигрышей - /redraw <lottery_id> - Повторный розыгрыш - /add_accounts - Пакетное добавление счетов - /list_accounts <telegram_id> - Список счетов пользователя Пользовательские: - /register - Регистрация с вводом данных - /my_account - Просмотр своих счетов - Callback confirm_win_{id} - Подтверждение выигрыша 🛠️ Makefile: - make clear-db - Очистка всех данных из БД (с подтверждением) 🔒 Безопасность: - Проверка владения счетом при подтверждении - Защита от подтверждения чужих счетов - Независимое подтверждение каждого выигрышного счета 📊 Логика работы: 1. Пользователь регистрируется и добавляет счета 2. Счета участвуют в розыгрыше 3. Победители получают уведомление с кнопкой подтверждения 4. Каждый счет подтверждается отдельно (24 часа на подтверждение) 5. Неподтвержденные выигрыши переигрываются через /redraw
151 lines
6.2 KiB
Python
151 lines
6.2 KiB
Python
"""Обработчики для регистрации пользователей"""
|
||
from aiogram import Router, F
|
||
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
|
||
from aiogram.filters import Command, StateFilter
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.fsm.state import State, StatesGroup
|
||
|
||
from src.core.database import async_session_maker
|
||
from src.core.registration_services import RegistrationService, AccountService
|
||
from src.core.services import UserService
|
||
|
||
|
||
router = Router()
|
||
|
||
|
||
class RegistrationStates(StatesGroup):
|
||
"""Состояния для процесса регистрации"""
|
||
waiting_for_club_card = State()
|
||
waiting_for_phone = State()
|
||
|
||
|
||
@router.callback_query(F.data == "start_registration")
|
||
async def start_registration(callback: CallbackQuery, state: FSMContext):
|
||
"""Начать процесс регистрации"""
|
||
text = (
|
||
"📝 Регистрация в системе\n\n"
|
||
"Для участия в розыгрышах необходимо зарегистрироваться.\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_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(
|
||
"📱 Теперь введите ваш номер телефона\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 = None if message.text.strip() == "-" else message.text.strip()
|
||
|
||
data = await state.get_data()
|
||
club_card_number = data['club_card_number']
|
||
|
||
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
|
||
)
|
||
|
||
text = (
|
||
"✅ Регистрация завершена!\n\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)
|