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

@@ -42,6 +42,7 @@ async def detect_account_input(message: Message, state: FSMContext):
"""
Обнаружение ввода счетов в сообщении
Активируется только для администраторов
Извлекает номер клубной карты и определяет владельца
"""
if not is_admin(message.from_user.id):
return
@@ -52,16 +53,82 @@ async def detect_account_input(message: Message, state: FSMContext):
if not accounts:
return # Счета не обнаружены, пропускаем
# Сохраняем счета в состоянии
await state.update_data(detected_accounts=accounts)
# Извлекаем номера клубных карт из последних 4 цифр каждого счета
from ..core.services import UserService
from ..core.registration_services import AccountService
# Формируем сообщение
accounts_text = "\n".join([f"{acc}" for acc in accounts])
async with async_session_maker() as session:
accounts_with_owners = []
for account in accounts:
# Извлекаем только номер счета (без карты если есть)
parts = account.split()
account_number = parts[-1] if parts else account
# Извлекаем последние 4 цифры (номер клубной карты)
digits_only = account_number.replace('-', '').replace(' ', '')
if len(digits_only) >= 4:
club_card = digits_only[-4:] # Последние 4 цифры
# Ищем пользователя по номеру клубной карты
user = await UserService.get_user_by_club_card(session, club_card)
if user:
owner_info = f"@{user.username}" if user.username else user.first_name
accounts_with_owners.append({
'account': account,
'club_card': club_card,
'owner': owner_info,
'user_id': user.id
})
else:
accounts_with_owners.append({
'account': account,
'club_card': club_card,
'owner': None,
'user_id': None
})
else:
# Счет неверного формата
accounts_with_owners.append({
'account': account,
'club_card': None,
'owner': None,
'user_id': None
})
# Сохраняем счета в состоянии
await state.update_data(
detected_accounts=accounts,
accounts_with_owners=accounts_with_owners
)
# Формируем сообщение с владельцами
accounts_text_parts = []
for item in accounts_with_owners:
account = item['account']
club_card = item['club_card']
owner = item['owner']
if owner:
line = f"{account}{owner} (карта: {club_card})"
elif club_card:
line = f"{account} (карта: {club_card}, владелец не найден)"
else:
line = f"{account} (неверный формат)"
accounts_text_parts.append(line)
accounts_text = "\n".join(accounts_text_parts)
count = len(accounts)
# Подсчёт найденных владельцев
owners_found = sum(1 for item in accounts_with_owners if item['owner'])
text = (
f"🔍 <b>Обнаружен ввод счет{'а' if count == 1 else 'ов'}</b>\n\n"
f"Найдено: <b>{count}</b>\n\n"
f"Найдено: <b>{count}</b>\n"
f"Владельцев определено: <b>{owners_found}</b>\n\n"
f"{accounts_text}\n\n"
f"Выберите действие:"
)