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
- Добавлено уведомление о задержке при отправке >250 счетов - Реализовано массовое удаление счетов через /remove_account - Исправлен flood control с задержкой 500ms между сообщениями - Callback.answer() перенесён в начало для предотвращения timeout - Добавлена обработка TelegramRetryAfter с повторной попыткой
This commit is contained in:
@@ -438,31 +438,70 @@ async def skip_lottery_add(callback: CallbackQuery, state: FSMContext):
|
|||||||
@admin_only
|
@admin_only
|
||||||
async def remove_account_command(message: Message):
|
async def remove_account_command(message: Message):
|
||||||
"""
|
"""
|
||||||
Деактивировать счет
|
Деактивировать счет(а)
|
||||||
Формат: /remove_account <account_number>
|
Формат: /remove_account <account_number1> [account_number2] [account_number3] ...
|
||||||
|
Можно указать несколько счетов через пробел для массового удаления
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parts = message.text.split()
|
parts = message.text.split()
|
||||||
if len(parts) != 2:
|
if len(parts) < 2:
|
||||||
await message.answer(
|
await message.answer(
|
||||||
"❌ Неверный формат команды\n\n"
|
"❌ Неверный формат команды\n\n"
|
||||||
"Используйте: /remove_account <account_number>"
|
"Используйте: /remove_account <account_number1> [account_number2] ...\n\n"
|
||||||
|
"Примеры:\n"
|
||||||
|
"• /remove_account 12-34-56-78-90-12-34\n"
|
||||||
|
"• /remove_account 12-34-56-78-90-12-34 98-76-54-32-10-98-76"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
account_number = parts[1]
|
account_numbers = parts[1:] # Все аргументы после команды
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with async_session_maker() as session:
|
results = {
|
||||||
success = await AccountService.deactivate_account(session, account_number)
|
'success': [],
|
||||||
|
'not_found': [],
|
||||||
|
'errors': []
|
||||||
|
}
|
||||||
|
|
||||||
if success:
|
async with async_session_maker() as session:
|
||||||
await message.answer(f"✅ Счет {account_number} деактивирован")
|
for account_number in account_numbers:
|
||||||
|
try:
|
||||||
|
success = await AccountService.deactivate_account(session, account_number)
|
||||||
|
if success:
|
||||||
|
results['success'].append(account_number)
|
||||||
|
else:
|
||||||
|
results['not_found'].append(account_number)
|
||||||
|
except Exception as e:
|
||||||
|
results['errors'].append((account_number, str(e)))
|
||||||
|
|
||||||
|
# Формируем отчёт
|
||||||
|
response_parts = []
|
||||||
|
|
||||||
|
if results['success']:
|
||||||
|
response_parts.append(
|
||||||
|
f"✅ *Деактивировано счетов: {len(results['success'])}*\n"
|
||||||
|
+ "\n".join(f"• `{acc}`" for acc in results['success'])
|
||||||
|
)
|
||||||
|
|
||||||
|
if results['not_found']:
|
||||||
|
response_parts.append(
|
||||||
|
f"❌ *Не найдено счетов: {len(results['not_found'])}*\n"
|
||||||
|
+ "\n".join(f"• `{acc}`" for acc in results['not_found'])
|
||||||
|
)
|
||||||
|
|
||||||
|
if results['errors']:
|
||||||
|
response_parts.append(
|
||||||
|
f"⚠️ *Ошибки при обработке: {len(results['errors'])}*\n"
|
||||||
|
+ "\n".join(f"• `{acc}`: {err}" for acc, err in results['errors'])
|
||||||
|
)
|
||||||
|
|
||||||
|
if not response_parts:
|
||||||
|
await message.answer("❌ Не удалось обработать ни один счет")
|
||||||
else:
|
else:
|
||||||
await message.answer(f"❌ Счет {account_number} не найден")
|
await message.answer("\n\n".join(response_parts), parse_mode="Markdown")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await message.answer(f"❌ Ошибка: {str(e)}")
|
await message.answer(f"❌ Критическая ошибка: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.message(Command("verify_winner"))
|
@router.message(Command("verify_winner"))
|
||||||
@@ -707,6 +746,8 @@ async def user_info_command(message: Message):
|
|||||||
@router.callback_query(F.data == "view_my_accounts")
|
@router.callback_query(F.data == "view_my_accounts")
|
||||||
async def view_my_accounts_callback(callback: CallbackQuery):
|
async def view_my_accounts_callback(callback: CallbackQuery):
|
||||||
"""Показать все счета пользователя"""
|
"""Показать все счета пользователя"""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with async_session_maker() as session:
|
async with async_session_maker() as session:
|
||||||
# Получаем пользователя
|
# Получаем пользователя
|
||||||
@@ -726,6 +767,19 @@ async def view_my_accounts_callback(callback: CallbackQuery):
|
|||||||
await callback.answer("У вас нет счетов", show_alert=True)
|
await callback.answer("У вас нет счетов", show_alert=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Отвечаем на callback сразу, чтобы не было timeout
|
||||||
|
await callback.answer("⏳ Загружаю ваши счета...")
|
||||||
|
|
||||||
|
# Если счетов много - предупреждаем о задержке
|
||||||
|
batches_count = (len(accounts) + 49) // 50 # Округление вверх
|
||||||
|
if batches_count > 5:
|
||||||
|
await callback.message.answer(
|
||||||
|
f"📊 Найдено счетов: *{len(accounts)}*\n"
|
||||||
|
f"📤 Отправка {batches_count} сообщений с задержкой (~{batches_count//2} сек)\n\n"
|
||||||
|
f"⏳ _Пожалуйста, подождите. Бот не завис._",
|
||||||
|
parse_mode="Markdown"
|
||||||
|
)
|
||||||
|
|
||||||
# Формируем сообщение с пагинацией (по 50 счетов на сообщение)
|
# Формируем сообщение с пагинацией (по 50 счетов на сообщение)
|
||||||
BATCH_SIZE = 50
|
BATCH_SIZE = 50
|
||||||
for i in range(0, len(accounts), BATCH_SIZE):
|
for i in range(0, len(accounts), BATCH_SIZE):
|
||||||
@@ -736,9 +790,22 @@ async def view_my_accounts_callback(callback: CallbackQuery):
|
|||||||
status = "✅" if acc.is_active else "❌"
|
status = "✅" if acc.is_active else "❌"
|
||||||
text += f"{status} `{acc.account_number}`\n"
|
text += f"{status} `{acc.account_number}`\n"
|
||||||
|
|
||||||
await callback.message.answer(text, parse_mode="Markdown")
|
try:
|
||||||
|
await callback.message.answer(text, parse_mode="Markdown")
|
||||||
await callback.answer("✅ Список счетов отправлен")
|
# Задержка между сообщениями для избежания flood control
|
||||||
|
if i + BATCH_SIZE < len(accounts):
|
||||||
|
await asyncio.sleep(0.5) # 500ms между сообщениями
|
||||||
|
except Exception as send_error:
|
||||||
|
# Если flood control - ждём дольше
|
||||||
|
if "Flood control" in str(send_error) or "Too Many Requests" in str(send_error):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
await callback.message.answer(text, parse_mode="Markdown")
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await callback.answer(f"❌ Ошибка: {str(e)}", show_alert=True)
|
# Не используем callback.answer в except - может быть timeout
|
||||||
|
try:
|
||||||
|
await callback.message.answer(f"❌ Ошибка: {str(e)}")
|
||||||
|
except:
|
||||||
|
pass # Игнорируем если не получилось отправить
|
||||||
|
|||||||
Reference in New Issue
Block a user