feat: доработки функционала бота

1. Подтверждение запуска розыгрыша:
   - Показ диалога с информацией об участниках и призах
   - Кнопки 'Да, провести' и 'Отмена'
   - Индикатор загрузки при проведении

2. Удаление сообщений администратором:
   - Команда /delete для удаления сообщений бота (ответ на сообщение)
   - Callback кнопка delete_message
   - Новый роутер message_admin_router

3. Определение владельцев счетов:
   - Извлечение номера клубной карты (последние 4 цифры)
   - Поиск владельца по club_card_number
   - Отображение владельца в списке обнаруженных счетов
   - Метод UserService.get_user_by_club_card()

4. Тестирование производительности:
   - Скрипт generate_test_accounts.py
   - Генерация файлов с 100, 500, 1000, 2000, 5000 счетов
   - Готовые тестовые файлы для проверки

5. Улучшения парсинга:
   - Обработка текста из кабинета с мусорными данными
   - Построчный парсинг с разбором по пробелам
   - Поддержка формата 'Viposnova  16-11-2025 22:19:36  17-24-66-42-38-31-53  0.00    2918'

6. Исправления багов:
   - AttributeError при отображении победителей без user_id
   - Безопасная обработка winner.user == None
This commit is contained in:
2025-11-17 10:42:41 +09:00
parent 65b550f8c8
commit 79eb66cf51
13 changed files with 8951 additions and 34 deletions

View File

@@ -0,0 +1,65 @@
"""
Хэндлеры для управления сообщениями администратором
"""
import logging
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery
from aiogram.filters import Command
from ..core.config import ADMIN_IDS
logger = logging.getLogger(__name__)
message_admin_router = Router(name="message_admin")
def is_admin(user_id: int) -> bool:
"""Проверка, является ли пользователь администратором"""
return user_id in ADMIN_IDS
@message_admin_router.message(Command("delete"))
async def delete_replied_message(message: Message):
"""
Удаление сообщения по команде /delete
Работает только если команда является ответом на сообщение бота
"""
if not is_admin(message.from_user.id):
await message.answer("❌ Недостаточно прав")
return
if not message.reply_to_message:
await message.answer("⚠️ Ответьте на сообщение бота командой /delete чтобы удалить его")
return
if message.reply_to_message.from_user.id != message.bot.id:
await message.answer("⚠️ Можно удалять только сообщения бота")
return
try:
# Удаляем сообщение бота
await message.reply_to_message.delete()
# Удаляем команду
await message.delete()
logger.info(f"Администратор {message.from_user.id} удалил сообщение {message.reply_to_message.message_id}")
except Exception as e:
logger.error(f"Ошибка при удалении сообщения: {e}")
await message.answer(f"Не удалось удалить сообщение: {str(e)}")
@message_admin_router.callback_query(F.data == "delete_message")
async def delete_message_callback(callback: CallbackQuery):
"""
Удаление сообщения по нажатию кнопки
"""
if not is_admin(callback.from_user.id):
await callback.answer("❌ Недостаточно прав", show_alert=True)
return
try:
await callback.message.delete()
await callback.answer("✅ Сообщение удалено")
logger.info(f"Администратор {callback.from_user.id} удалил сообщение {callback.message.message_id} кнопкой")
except Exception as e:
logger.error(f"Ошибка при удалении сообщения: {e}")
await callback.answer(f"❌ Ошибка: {str(e)}", show_alert=True)