feat: Система автоматического подтверждения выигрышей с поддержкой множественных счетов
Some checks reported errors
continuous-integration/drone/push Build encountered an error
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
This commit is contained in:
370
main.py
370
main.py
@@ -16,8 +16,12 @@ import sys
|
||||
from src.core.config import BOT_TOKEN, ADMIN_IDS
|
||||
from src.core.database import async_session_maker, init_db
|
||||
from src.core.services import UserService, LotteryService, ParticipationService
|
||||
from src.core.models import User
|
||||
from src.handlers.admin_panel import admin_router
|
||||
from src.handlers.account_handlers import account_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.utils.async_decorators import (
|
||||
async_user_action, admin_async_action, db_operation,
|
||||
TaskManagerMiddleware, shutdown_task_manager,
|
||||
@@ -65,16 +69,19 @@ def is_admin(user_id: int) -> bool:
|
||||
def get_main_keyboard(is_admin_user: bool = False) -> InlineKeyboardMarkup:
|
||||
"""Главная клавиатура"""
|
||||
buttons = [
|
||||
[InlineKeyboardButton(text="🎲 Активные розыгрыши", callback_data="list_lotteries")],
|
||||
[InlineKeyboardButton(text="📝 Мои участия", callback_data="my_participations")],
|
||||
[InlineKeyboardButton(text="💳 Мой счёт", callback_data="my_account")]
|
||||
[InlineKeyboardButton(text="🎲 Активные розыгрыши", callback_data="list_lotteries")]
|
||||
]
|
||||
|
||||
if not is_admin_user:
|
||||
buttons.extend([
|
||||
[InlineKeyboardButton(text="📝 Мои участия", callback_data="my_participations")],
|
||||
[InlineKeyboardButton(text="💳 Мой счёт", callback_data="my_account")]
|
||||
])
|
||||
|
||||
if is_admin_user:
|
||||
buttons.extend([
|
||||
[InlineKeyboardButton(text="🔧 Админ-панель", callback_data="admin_panel")],
|
||||
[InlineKeyboardButton(text="➕ Создать розыгрыш", callback_data="create_lottery")],
|
||||
[InlineKeyboardButton(text="👑 Установить победителя", callback_data="set_winner")],
|
||||
[InlineKeyboardButton(text="📊 Статистика задач", callback_data="task_stats")]
|
||||
])
|
||||
|
||||
@@ -96,11 +103,29 @@ async def cmd_start(message: Message):
|
||||
# Устанавливаем права администратора, если пользователь в списке
|
||||
if message.from_user.id in ADMIN_IDS:
|
||||
await UserService.set_admin(session, message.from_user.id, True)
|
||||
|
||||
is_registered = user.is_registered
|
||||
|
||||
is_admin_user = is_admin(message.from_user.id)
|
||||
|
||||
welcome_text = f"Добро пожаловать, {message.from_user.first_name}! 🎉\n\n"
|
||||
welcome_text += "Это бот для проведения розыгрышей.\n\n"
|
||||
|
||||
# Для обычных пользователей - проверяем регистрацию
|
||||
if not is_admin_user and not is_registered:
|
||||
welcome_text += "⚠️ Для участия в розыгрышах необходимо пройти регистрацию.\n\n"
|
||||
|
||||
buttons = [
|
||||
[InlineKeyboardButton(text="📝 Зарегистрироваться", callback_data="start_registration")],
|
||||
[InlineKeyboardButton(text="🎲 Активные розыгрыши", callback_data="list_lotteries")]
|
||||
]
|
||||
|
||||
await message.answer(
|
||||
welcome_text,
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||
)
|
||||
return
|
||||
|
||||
welcome_text += "Выберите действие из меню ниже:"
|
||||
|
||||
if is_admin_user:
|
||||
@@ -196,9 +221,18 @@ async def show_lottery_details(callback: CallbackQuery):
|
||||
if winners:
|
||||
text += "\n\n🏆 Победители:\n"
|
||||
for winner in winners:
|
||||
# Используем новую систему отображения
|
||||
winner_display = format_winner_display(winner.user, lottery, show_sensitive_data=False)
|
||||
text += f"{winner.place}. {winner_display}\n"
|
||||
# Безопасное отображение победителя
|
||||
if winner.user:
|
||||
if winner.user.username:
|
||||
winner_display = f"@{winner.user.username}"
|
||||
else:
|
||||
winner_display = f"{winner.user.first_name}"
|
||||
elif winner.account_number:
|
||||
winner_display = f"Счет: {winner.account_number}"
|
||||
else:
|
||||
winner_display = "Участник"
|
||||
|
||||
text += f"{winner.place}. {winner_display} - {winner.prize}\n"
|
||||
else:
|
||||
text += f"\n🟢 Статус: {'Активен' if lottery.is_active else 'Неактивен'}"
|
||||
if is_participating:
|
||||
@@ -241,7 +275,8 @@ async def join_lottery(callback: CallbackQuery):
|
||||
await callback.answer("Ошибка получения данных пользователя", show_alert=True)
|
||||
return
|
||||
|
||||
success = await LotteryService.add_participant(session, lottery_id, user.id)
|
||||
# Используем правильный метод ParticipationService
|
||||
success = await ParticipationService.add_participant(session, lottery_id, user.id)
|
||||
|
||||
if success:
|
||||
await callback.answer("✅ Вы успешно присоединились к розыгрышу!", show_alert=True)
|
||||
@@ -252,6 +287,237 @@ async def join_lottery(callback: CallbackQuery):
|
||||
await show_lottery_details(callback)
|
||||
|
||||
|
||||
async def notify_winners_async(bot: Bot, lottery_id: int, results: dict):
|
||||
"""
|
||||
Асинхронно отправить уведомления победителям с кнопкой подтверждения
|
||||
Вызывается после проведения розыгрыша
|
||||
"""
|
||||
async with async_session_maker() as session:
|
||||
from src.core.registration_services import AccountService, WinnerNotificationService
|
||||
from src.core.models import Winner
|
||||
from sqlalchemy import select
|
||||
|
||||
# Получаем информацию о розыгрыше
|
||||
lottery = await LotteryService.get_lottery(session, lottery_id)
|
||||
if not lottery:
|
||||
return
|
||||
|
||||
# Получаем всех победителей из БД
|
||||
winners_result = await session.execute(
|
||||
select(Winner).where(Winner.lottery_id == lottery_id)
|
||||
)
|
||||
winners = winners_result.scalars().all()
|
||||
|
||||
for winner in winners:
|
||||
try:
|
||||
# Если у победителя есть account_number, ищем владельца
|
||||
if winner.account_number:
|
||||
owner = await AccountService.get_account_owner(session, winner.account_number)
|
||||
|
||||
if owner and owner.telegram_id:
|
||||
# Создаем токен верификации
|
||||
verification = await WinnerNotificationService.create_verification_token(
|
||||
session,
|
||||
winner.id
|
||||
)
|
||||
|
||||
# Формируем сообщение с кнопкой подтверждения
|
||||
message = (
|
||||
f"🎉 **Поздравляем! Ваш счет выиграл!**\n\n"
|
||||
f"🎯 Розыгрыш: {lottery.title}\n"
|
||||
f"🏆 Место: {winner.place}\n"
|
||||
f"🎁 Приз: {winner.prize}\n"
|
||||
f"💳 **Выигрышный счет: {winner.account_number}**\n\n"
|
||||
f"⏰ **У вас есть 24 часа для подтверждения!**\n\n"
|
||||
f"Нажмите кнопку ниже, чтобы подтвердить получение приза по этому счету.\n"
|
||||
f"Если вы не подтвердите в течение 24 часов, "
|
||||
f"приз будет разыгран заново.\n\n"
|
||||
f"ℹ️ Если у вас несколько выигрышных счетов, "
|
||||
f"подтвердите каждый из них отдельно."
|
||||
)
|
||||
|
||||
# Создаем кнопку подтверждения с указанием счета
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(
|
||||
text=f"✅ Подтвердить счет {winner.account_number}",
|
||||
callback_data=f"confirm_win_{winner.id}"
|
||||
)],
|
||||
[InlineKeyboardButton(
|
||||
text="📞 Связаться с администратором",
|
||||
url=f"tg://user?id={ADMIN_IDS[0]}"
|
||||
)]
|
||||
])
|
||||
|
||||
# Отправляем уведомление с кнопкой
|
||||
await bot.send_message(
|
||||
owner.telegram_id,
|
||||
message,
|
||||
reply_markup=keyboard,
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
|
||||
# Отмечаем, что уведомление отправлено
|
||||
winner.is_notified = True
|
||||
await session.commit()
|
||||
|
||||
logger.info(f"Отправлено уведомление победителю {owner.telegram_id} за счет {winner.account_number}")
|
||||
|
||||
# Если победитель - обычный пользователь (старая система)
|
||||
elif winner.user_id:
|
||||
user_result = await session.execute(
|
||||
select(User).where(User.id == winner.user_id)
|
||||
)
|
||||
user = user_result.scalar_one_or_none()
|
||||
|
||||
if user and user.telegram_id:
|
||||
message = (
|
||||
f"🎉 Поздравляем! Вы выиграли!\n\n"
|
||||
f"🎯 Розыгрыш: {lottery.title}\n"
|
||||
f"🏆 Место: {winner.place}\n"
|
||||
f"🎁 Приз: {winner.prize}\n\n"
|
||||
f"⏰ **У вас есть 24 часа для подтверждения!**\n\n"
|
||||
f"Нажмите кнопку ниже, чтобы подтвердить получение приза."
|
||||
)
|
||||
|
||||
keyboard = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(
|
||||
text="✅ Подтвердить получение приза",
|
||||
callback_data=f"confirm_win_{winner.id}"
|
||||
)]
|
||||
])
|
||||
|
||||
await bot.send_message(
|
||||
user.telegram_id,
|
||||
message,
|
||||
reply_markup=keyboard,
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
winner.is_notified = True
|
||||
await session.commit()
|
||||
|
||||
logger.info(f"Отправлено уведомление победителю {user.telegram_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при отправке уведомления победителю: {e}")
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("confirm_win_"))
|
||||
async def confirm_winner_response(callback: CallbackQuery):
|
||||
"""Обработка подтверждения выигрыша победителем"""
|
||||
winner_id = int(callback.data.split("_")[2])
|
||||
|
||||
async with async_session_maker() as session:
|
||||
from src.core.models import Winner
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
# Получаем выигрыш с загрузкой связанного розыгрыша
|
||||
winner_result = await session.execute(
|
||||
select(Winner)
|
||||
.options(joinedload(Winner.lottery))
|
||||
.where(Winner.id == winner_id)
|
||||
)
|
||||
winner = winner_result.scalar_one_or_none()
|
||||
|
||||
if not winner:
|
||||
await callback.answer("❌ Выигрыш не найден", show_alert=True)
|
||||
return
|
||||
|
||||
# Проверяем, не подтвержден ли уже этот конкретный счет
|
||||
if winner.is_claimed:
|
||||
await callback.message.edit_text(
|
||||
"✅ **Выигрыш этого счета уже подтвержден!**\n\n"
|
||||
f"🎯 Розыгрыш: {winner.lottery.title}\n"
|
||||
f"🏆 Место: {winner.place}\n"
|
||||
f"🎁 Приз: {winner.prize}\n"
|
||||
f"💳 Счет: {winner.account_number}\n\n"
|
||||
"Администратор свяжется с вами для передачи приза.",
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
return
|
||||
|
||||
# Проверяем, что подтверждает владелец именно ЭТОГО счета
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
|
||||
if winner.account_number:
|
||||
# Проверяем что счет принадлежит текущему пользователю
|
||||
from src.core.registration_services import AccountService
|
||||
owner = await AccountService.get_account_owner(session, winner.account_number)
|
||||
|
||||
if not owner or owner.telegram_id != callback.from_user.id:
|
||||
await callback.answer(
|
||||
f"❌ Счет {winner.account_number} вам не принадлежит",
|
||||
show_alert=True
|
||||
)
|
||||
return
|
||||
elif winner.user_id:
|
||||
# Старая логика для выигрышей без счета
|
||||
if not user or user.id != winner.user_id:
|
||||
await callback.answer("❌ Это не ваш выигрыш", show_alert=True)
|
||||
return
|
||||
|
||||
# Подтверждаем выигрыш ЭТОГО конкретного счета
|
||||
from datetime import datetime, timezone
|
||||
winner.is_claimed = True
|
||||
winner.claimed_at = datetime.now(timezone.utc)
|
||||
await session.commit()
|
||||
|
||||
# Обновляем сообщение с указанием счета
|
||||
confirmation_text = (
|
||||
"✅ **Выигрыш успешно подтвержден!**\n\n"
|
||||
f"🎯 Розыгрыш: {winner.lottery.title}\n"
|
||||
f"🏆 Место: {winner.place}\n"
|
||||
f"🎁 Приз: {winner.prize}\n"
|
||||
)
|
||||
|
||||
if winner.account_number:
|
||||
confirmation_text += f"💳 Счет: {winner.account_number}\n"
|
||||
|
||||
confirmation_text += (
|
||||
"\n🎊 Поздравляем! Администратор свяжется с вами "
|
||||
"для передачи приза в ближайшее время.\n\n"
|
||||
"Спасибо за участие!"
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
confirmation_text,
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
|
||||
# Уведомляем администраторов о подтверждении конкретного счета
|
||||
for admin_id in ADMIN_IDS:
|
||||
try:
|
||||
admin_msg = (
|
||||
f"✅ **Победитель подтвердил получение приза!**\n\n"
|
||||
f"🎯 Розыгрыш: {winner.lottery.title}\n"
|
||||
f"🏆 Место: {winner.place}\n"
|
||||
f"🎁 Приз: {winner.prize}\n"
|
||||
)
|
||||
|
||||
# Обязательно показываем счет
|
||||
if winner.account_number:
|
||||
admin_msg += f"<EFBFBD> **Подтвержденный счет: {winner.account_number}**\n\n"
|
||||
|
||||
if user:
|
||||
admin_msg += f"👤 Владелец: {user.first_name}"
|
||||
if user.username:
|
||||
admin_msg += f" (@{user.username})"
|
||||
admin_msg += f"\n🎫 Клубная карта: {user.club_card_number}\n"
|
||||
if user.phone:
|
||||
admin_msg += f"📱 Телефон: {user.phone}\n"
|
||||
|
||||
await callback.bot.send_message(admin_id, admin_msg, parse_mode="Markdown")
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(
|
||||
f"Победитель {callback.from_user.id} подтвердил выигрыш {winner_id} "
|
||||
f"(счет: {winner.account_number})"
|
||||
)
|
||||
|
||||
await callback.answer("✅ Выигрыш подтвержден!", show_alert=True)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("conduct_"))
|
||||
async def conduct_lottery(callback: CallbackQuery):
|
||||
"""Провести розыгрыш"""
|
||||
@@ -262,6 +528,11 @@ async def conduct_lottery(callback: CallbackQuery):
|
||||
lottery_id = int(callback.data.split("_")[1])
|
||||
|
||||
async with async_session_maker() as session:
|
||||
lottery = await LotteryService.get_lottery(session, lottery_id)
|
||||
if not lottery:
|
||||
await callback.answer("❌ Розыгрыш не найден", show_alert=True)
|
||||
return
|
||||
|
||||
results = await LotteryService.conduct_draw(session, lottery_id)
|
||||
|
||||
if not results:
|
||||
@@ -271,14 +542,26 @@ async def conduct_lottery(callback: CallbackQuery):
|
||||
text = "🎉 Розыгрыш завершен!\n\n🏆 Победители:\n\n"
|
||||
|
||||
for place, winner_info in results.items():
|
||||
user = winner_info['user']
|
||||
user_obj = winner_info['user']
|
||||
prize = winner_info['prize']
|
||||
|
||||
# Используем новую систему отображения
|
||||
winner_display = format_winner_display(user, lottery, show_sensitive_data=False)
|
||||
# Безопасное отображение победителя
|
||||
if hasattr(user_obj, 'username') and user_obj.username:
|
||||
winner_display = f"@{user_obj.username}"
|
||||
elif hasattr(user_obj, 'first_name'):
|
||||
winner_display = f"{user_obj.first_name}"
|
||||
elif hasattr(user_obj, 'account_number'):
|
||||
winner_display = f"Счет: {user_obj.account_number}"
|
||||
else:
|
||||
winner_display = "Участник"
|
||||
|
||||
text += f"{place}. {winner_display}\n"
|
||||
text += f" 🎁 {prize}\n\n"
|
||||
|
||||
# Отправляем уведомления победителям асинхронно
|
||||
asyncio.create_task(notify_winners_async(callback.bot, lottery_id, results))
|
||||
text += "📨 Уведомления отправляются победителям...\n"
|
||||
|
||||
await callback.message.edit_text(
|
||||
text,
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
|
||||
@@ -339,6 +622,11 @@ async def process_lottery_prizes(message: Message, state: FSMContext):
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
||||
|
||||
if not user:
|
||||
await message.answer("❌ Ошибка получения данных пользователя")
|
||||
await state.clear()
|
||||
return
|
||||
|
||||
data = await state.get_data()
|
||||
lottery = await LotteryService.create_lottery(
|
||||
session,
|
||||
@@ -529,7 +817,7 @@ async def show_my_participations(callback: CallbackQuery):
|
||||
@router.callback_query(F.data == "my_account")
|
||||
@db_operation()
|
||||
async def show_my_account(callback: CallbackQuery):
|
||||
"""Показать информацию о счёте пользователя"""
|
||||
"""Показать информацию о счетах пользователя"""
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
|
||||
@@ -537,27 +825,44 @@ async def show_my_account(callback: CallbackQuery):
|
||||
await callback.answer("Пользователь не найден", show_alert=True)
|
||||
return
|
||||
|
||||
text = "💳 **Ваш клиентский счёт**\n\n"
|
||||
# Проверяем регистрацию
|
||||
if not user.is_registered:
|
||||
text = "❌ **Вы не зарегистрированы**\n\n"
|
||||
text += "Пройдите регистрацию для доступа к счетам"
|
||||
|
||||
await callback.message.edit_text(
|
||||
text,
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text="📝 Зарегистрироваться", callback_data="start_registration")],
|
||||
[InlineKeyboardButton(text="🔙 Главное меню", callback_data="back_to_main")]
|
||||
]),
|
||||
parse_mode="Markdown"
|
||||
)
|
||||
return
|
||||
|
||||
if user.account_number:
|
||||
# Показываем маскированный номер для безопасности
|
||||
from src.utils.account_utils import mask_account_number
|
||||
masked = mask_account_number(user.account_number, show_last_digits=6)
|
||||
text += f"📋 Номер счёта: `{masked}`\n"
|
||||
text += f"✅ Статус: Активен\n\n"
|
||||
text += "ℹ️ Счёт используется для идентификации в розыгрышах"
|
||||
# Получаем счета пользователя
|
||||
from src.core.registration_services import AccountService
|
||||
accounts = await AccountService.get_user_accounts(session, user.id)
|
||||
|
||||
text = "💳 **Ваши счета**\n\n"
|
||||
|
||||
if accounts:
|
||||
text += f"🎫 Клубная карта: `{user.club_card_number}`\n"
|
||||
text += f"<EFBFBD> Код верификации: `{user.verification_code}`\n\n"
|
||||
text += f"**Счета ({len(accounts)}):**\n\n"
|
||||
|
||||
for i, acc in enumerate(accounts, 1):
|
||||
status = "✅ Активен" if acc.is_active else "❌ Неактивен"
|
||||
text += f"{i}. `{acc.account_number}`\n"
|
||||
text += f" {status}\n\n"
|
||||
|
||||
text += "ℹ️ Счета используются для участия в розыгрышах"
|
||||
else:
|
||||
text += "❌ Счёт не привязан\n\n"
|
||||
text += "Привяжите счёт для участия в розыгрышах"
|
||||
text += f"🎫 Клубная карта: `{user.club_card_number}`\n\n"
|
||||
text += "❌ У вас нет счетов\n\n"
|
||||
text += "Обратитесь к администратору для добавления счетов"
|
||||
|
||||
buttons = []
|
||||
|
||||
if user.account_number:
|
||||
buttons.append([InlineKeyboardButton(text="🔄 Изменить счёт", callback_data="change_account")])
|
||||
else:
|
||||
buttons.append([InlineKeyboardButton(text="➕ Привязать счёт", callback_data="add_account")])
|
||||
|
||||
buttons.append([InlineKeyboardButton(text="🔙 Главное меню", callback_data="back_to_main")])
|
||||
buttons = [[InlineKeyboardButton(text="🔙 Главное меню", callback_data="back_to_main")]]
|
||||
|
||||
await callback.message.edit_text(
|
||||
text,
|
||||
@@ -700,7 +1005,10 @@ async def main():
|
||||
await set_commands()
|
||||
|
||||
# Подключение роутеров
|
||||
dp.include_router(account_router) # Роутер для работы со счетами (приоритетный)
|
||||
dp.include_router(registration_router) # Роутер регистрации (первый)
|
||||
dp.include_router(admin_account_router) # Роутер админских команд для счетов
|
||||
dp.include_router(redraw_router) # Роутер повторного розыгрыша
|
||||
dp.include_router(account_router) # Роутер для работы со счетами
|
||||
dp.include_router(router)
|
||||
dp.include_router(admin_router)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user