mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-05-02 10:46:25 +00:00
- Add pyproject.toml with uv and ruff configuration - Pin Python version to 3.13 via .python-version - Add Makefile commands: lint, format, fix - Apply ruff formatting to entire codebase - Remove unused imports (base64 in yookassa/simple_subscription) - Update .gitignore for new config files
170 lines
7.3 KiB
Python
170 lines
7.3 KiB
Python
"""
|
||
Модуль для массовой блокировки пользователей по списку Telegram ID
|
||
"""
|
||
|
||
import logging
|
||
|
||
from aiogram import Bot
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.database.crud.user import get_user_by_telegram_id
|
||
from app.database.models import UserStatus
|
||
from app.services.admin_notification_service import AdminNotificationService
|
||
from app.services.user_service import UserService
|
||
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class BulkBanService:
|
||
"""
|
||
Сервис для массовой блокировки пользователей по списку Telegram ID
|
||
"""
|
||
|
||
def __init__(self):
|
||
self.user_service = UserService()
|
||
|
||
async def ban_users_by_telegram_ids(
|
||
self,
|
||
db: AsyncSession,
|
||
admin_user_id: int,
|
||
telegram_ids: list[int],
|
||
reason: str = 'Заблокирован администратором по списку',
|
||
bot: Bot = None,
|
||
notify_admin: bool = True,
|
||
admin_name: str = 'Администратор',
|
||
) -> tuple[int, int, list[int]]:
|
||
"""
|
||
Массовая блокировка пользователей по Telegram ID
|
||
|
||
Args:
|
||
db: Асинхронная сессия базы данных
|
||
admin_user_id: ID администратора, который осуществляет блокировку
|
||
telegram_ids: Список Telegram ID для блокировки
|
||
reason: Причина блокировки
|
||
bot: Бот для отправки уведомлений
|
||
notify_admin: Отправлять ли уведомления администратору
|
||
admin_name: Имя администратора для логирования
|
||
|
||
Returns:
|
||
Кортеж из (успешно заблокированных, не найденных, список ID с ошибками)
|
||
"""
|
||
successfully_banned = 0
|
||
not_found_users = []
|
||
error_ids = []
|
||
|
||
for telegram_id in telegram_ids:
|
||
try:
|
||
# Получаем пользователя по Telegram ID
|
||
user = await get_user_by_telegram_id(db, telegram_id)
|
||
|
||
if not user:
|
||
logger.warning(f'Пользователь с Telegram ID {telegram_id} не найден')
|
||
not_found_users.append(telegram_id)
|
||
continue
|
||
|
||
# Проверяем, что пользователь не заблокирован уже
|
||
if user.status == UserStatus.BLOCKED.value:
|
||
logger.info(f'Пользователь {telegram_id} уже заблокирован')
|
||
continue
|
||
|
||
# Блокируем пользователя
|
||
ban_success = await self.user_service.block_user(db, user.id, admin_user_id, reason)
|
||
|
||
if ban_success:
|
||
successfully_banned += 1
|
||
logger.info(f'Пользователь {telegram_id} успешно заблокирован')
|
||
|
||
# Отправляем уведомление пользователю, если возможно
|
||
if bot:
|
||
try:
|
||
await bot.send_message(
|
||
chat_id=telegram_id,
|
||
text=(
|
||
f'🚫 <b>Ваш аккаунт заблокирован</b>\n\n'
|
||
f'Причина: {reason}\n\n'
|
||
f'Если вы считаете, что блокировка произошла ошибочно, '
|
||
f'обратитесь в поддержку.'
|
||
),
|
||
parse_mode='HTML',
|
||
)
|
||
except Exception as e:
|
||
logger.warning(f'Не удалось отправить уведомление пользователю {telegram_id}: {e}')
|
||
else:
|
||
logger.error(f'Не удалось заблокировать пользователя {telegram_id}')
|
||
error_ids.append(telegram_id)
|
||
|
||
except Exception as e:
|
||
logger.error(f'Ошибка при блокировке пользователя {telegram_id}: {e}')
|
||
error_ids.append(telegram_id)
|
||
|
||
# Отправляем уведомление администратору
|
||
if notify_admin and bot:
|
||
try:
|
||
admin_notification_service = AdminNotificationService(bot)
|
||
await admin_notification_service.send_bulk_ban_notification(
|
||
admin_user_id, successfully_banned, len(not_found_users), len(error_ids), admin_name
|
||
)
|
||
except Exception as e:
|
||
logger.error(f'Ошибка при отправке уведомления администратору: {e}')
|
||
|
||
logger.info(
|
||
f'Массовая блокировка завершена: успешно={successfully_banned}, '
|
||
f'не найдено={len(not_found_users)}, ошибки={len(error_ids)}'
|
||
)
|
||
|
||
return successfully_banned, len(not_found_users), error_ids
|
||
|
||
async def parse_telegram_ids_from_text(self, text: str) -> list[int]:
|
||
"""
|
||
Парсит Telegram ID из текста. Поддерживает различные форматы:
|
||
- по одному ID на строку
|
||
- через запятую
|
||
- через пробелы
|
||
- с @username (если username соответствует формату ID)
|
||
"""
|
||
if not text:
|
||
return []
|
||
|
||
# Удаляем лишние пробелы и разбиваем по переносам строк
|
||
lines = text.strip().split('\n')
|
||
ids = []
|
||
|
||
for line in lines:
|
||
# Убираем комментарии и лишние пробелы
|
||
line = line.strip()
|
||
if not line or line.startswith('#'):
|
||
continue
|
||
|
||
# Разбиваем строку по запятым или пробелам
|
||
tokens = line.replace(',', ' ').split()
|
||
|
||
for token in tokens:
|
||
token = token.strip()
|
||
|
||
# Убираем символ @ если присутствует
|
||
token = token.removeprefix('@')
|
||
|
||
# Проверяем, является ли токен числом (Telegram ID)
|
||
try:
|
||
telegram_id = int(token)
|
||
if telegram_id > 0: # Telegram ID должны быть положительными
|
||
ids.append(telegram_id)
|
||
except ValueError:
|
||
# Пропускаем, если не является числом
|
||
continue
|
||
|
||
# Убираем дубликаты, сохранив порядок
|
||
unique_ids = []
|
||
seen = set()
|
||
for tid in ids:
|
||
if tid not in seen:
|
||
unique_ids.append(tid)
|
||
seen.add(tid)
|
||
|
||
return unique_ids
|
||
|
||
|
||
# Создаем глобальный экземпляр сервиса
|
||
bulk_ban_service = BulkBanService()
|