Files
remnawave-bedolaga-telegram…/app/handlers/admin/bulk_ban.py
T
Fringg 1f0fef114b refactor: complete structlog migration with contextvars, kwargs, and logging hardening
- Add ContextVarsMiddleware for automatic user_id/chat_id/username binding
  via structlog contextvars (aiogram) and http_method/http_path (FastAPI)
- Use bound_contextvars() context manager instead of clear_contextvars()
  to safely restore previous state instead of wiping all context
- Register ContextVarsMiddleware as outermost middleware (before GlobalError)
  so all error logs include user context
- Replace structlog.get_logger() with structlog.get_logger(__name__) across
  270 calls in 265 files for meaningful logger names
- Switch wrapper_class from BoundLogger to make_filtering_bound_logger()
  for pre-processor level filtering (performance optimization)
- Migrate 1411 %-style positional arg logger calls to structlog kwargs
  style across 161 files via AST script
- Migrate log_rotation_service.py from stdlib logging to structlog
- Add payment module prefixes to TelegramNotifierProcessor.IGNORED_LOGGER_PREFIXES
  and ExcludePaymentFilter.PAYMENT_MODULES to prevent payment data leaking
  to Telegram notifications and general log files
- Fix LoggingMiddleware: add from_user null-safety for channel posts,
  switch time.time() to time.monotonic() for duration measurement
- Remove duplicate logger assignments in purchase.py, config.py,
  inline.py, and admin/payments.py
2026-02-16 09:18:12 +03:00

165 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Обработчики команд для массовой блокировки пользователей
"""
import structlog
from aiogram import types
from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession
from app.database.models import User
from app.services.bulk_ban_service import bulk_ban_service
from app.states import AdminStates
from app.utils.decorators import admin_required, error_handler
logger = structlog.get_logger(__name__)
@admin_required
@error_handler
async def start_bulk_ban_process(callback: types.CallbackQuery, db_user: User, state: FSMContext):
"""
Начало процесса массовой блокировки пользователей
"""
await callback.message.edit_text(
'🛑 <b>Массовая блокировка пользователей</b>\n\n'
'Введите список Telegram ID для блокировки.\n\n'
'<b>Форматы ввода:</b>\n'
'• По одному ID на строку\n'
'• Через запятую\n'
'• Через пробел\n\n'
'Пример:\n'
'<code>123456789\n'
'987654321\n'
'111222333</code>\n\n'
'Или:\n'
'<code>123456789, 987654321, 111222333</code>\n\n'
'Для отмены используйте команду /cancel',
parse_mode='HTML',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='❌ Отмена', callback_data='admin_users')]]
),
)
await state.set_state(AdminStates.waiting_for_bulk_ban_list)
await callback.answer()
@admin_required
@error_handler
async def process_bulk_ban_list(message: types.Message, db_user: User, state: FSMContext, db: AsyncSession):
"""
Обработка списка Telegram ID и выполнение массовой блокировки
"""
if not message.text:
await message.answer(
'❌ Отправьте текстовое сообщение со списком Telegram ID',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='🔙 Назад', callback_data='admin_users')]]
),
)
return
input_text = message.text.strip()
if not input_text:
await message.answer(
'❌ Введите корректный список Telegram ID',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='🔙 Назад', callback_data='admin_users')]]
),
)
return
# Парсим ID из текста
try:
telegram_ids = await bulk_ban_service.parse_telegram_ids_from_text(input_text)
except Exception as e:
logger.error('Ошибка парсинга Telegram ID', error=e)
await message.answer(
'❌ Ошибка при обработке списка ID. Проверьте формат ввода.',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='🔙 Назад', callback_data='admin_users')]]
),
)
return
if not telegram_ids:
await message.answer(
'❌ Не найдено корректных Telegram ID в списке',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='🔙 Назад', callback_data='admin_users')]]
),
)
return
if len(telegram_ids) > 1000: # Ограничение на количество ID за раз
await message.answer(
f'❌ Слишком много ID в списке ({len(telegram_ids)}). Максимум: 1000',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='🔙 Назад', callback_data='admin_users')]]
),
)
return
# Выполняем массовую блокировку
try:
successfully_banned, not_found, error_ids = await bulk_ban_service.ban_users_by_telegram_ids(
db=db,
admin_user_id=db_user.id,
telegram_ids=telegram_ids,
reason='Массовая блокировка администратором',
bot=message.bot,
notify_admin=True,
admin_name=db_user.full_name,
)
# Подготавливаем сообщение с результатами
result_text = '✅ <b>Массовая блокировка завершена</b>\n\n'
result_text += '📊 <b>Результаты:</b>\n'
result_text += f'✅ Успешно заблокировано: {successfully_banned}\n'
result_text += f'❌ Не найдено: {not_found}\n'
result_text += f'💥 Ошибок: {len(error_ids)}\n\n'
result_text += f'📈 Всего обработано: {len(telegram_ids)}'
if successfully_banned > 0:
result_text += f'\n🎯 Процент успеха: {round((successfully_banned / len(telegram_ids)) * 100, 1)}%'
# Добавляем информацию об ошибках, если есть
if error_ids:
result_text += '\n\n⚠️ <b>Telegram ID с ошибками:</b>\n'
result_text += f'<code>{", ".join(map(str, error_ids[:10]))}</code>' # Показываем первые 10
if len(error_ids) > 10:
result_text += f' и еще {len(error_ids) - 10}...'
await message.answer(
result_text,
parse_mode='HTML',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='👥 К пользователям', callback_data='admin_users')]]
),
)
except Exception as e:
logger.error('Ошибка при выполнении массовой блокировки', error=e)
await message.answer(
'❌ Произошла ошибка при выполнении массовой блокировки',
reply_markup=types.InlineKeyboardMarkup(
inline_keyboard=[[types.InlineKeyboardButton(text='🔙 Назад', callback_data='admin_users')]]
),
)
await state.clear()
def register_bulk_ban_handlers(dp):
"""
Регистрация обработчиков команд для массовой блокировки
"""
# Обработчик команды начала массовой блокировки
dp.callback_query.register(start_bulk_ban_process, lambda c: c.data == 'admin_bulk_ban_start')
# Обработчик текстового сообщения с ID для блокировки
dp.message.register(process_bulk_ban_list, AdminStates.waiting_for_bulk_ban_list)