""" Анти-спам — управление чёрным списком ссылок Только для администраторов """ import logging from aiogram import Router, F from aiogram.types import Message from aiogram.filters import Command from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func from database.db import AsyncSessionLocal from database.models import User, SpamWarning from services.antispam import AntiSpamService from utils.decorators import admin_only import config logger = logging.getLogger(__name__) router = Router() # ============================================================================= # КОМАНДЫ УПРАВЛЕНИЯ ЧЁРНЫМ СПИСКОМ # ============================================================================= @router.message(Command('add_domain')) @admin_only async def cmd_add_domain(message: Message): """ Добавить домен в чёрный список Использование: /add_domain spam.com """ args = message.text.split() if len(args) < 2: await message.answer( '❌ Использование:\n' '/add_domain spam.com\n\n' 'Домен будет добавлен в чёрный список.', parse_mode='HTML' ) return domain = args[1] async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) if antispam.add_domain(domain): await message.answer(f'✅ Домен {domain} добавлен в чёрный список', parse_mode='HTML') else: await message.answer(f'ℹ️ Домен {domain} уже в чёрном списке', parse_mode='HTML') @router.message(Command('remove_domain')) @admin_only async def cmd_remove_domain(message: Message): """ Удалить домен из чёрного списка Использование: /remove_domain spam.com """ args = message.text.split() if len(args) < 2: await message.answer( '❌ Использование:\n' '/remove_domain spam.com', parse_mode='HTML' ) return domain = args[1] async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) if antispam.remove_domain(domain): await message.answer(f'✅ Домен {domain} удалён из чёрного списка', parse_mode='HTML') else: await message.answer(f'ℹ️ Домен {domain} не найден в чёрном списке', parse_mode='HTML') @router.message(Command('add_pattern')) @admin_only async def cmd_add_pattern(message: Message): """ Добавить паттерн в чёрный список Использование: /add_pattern t.me/spam_ """ args = message.text.split(maxsplit=1) if len(args) < 2: await message.answer( '❌ Использование:\n' '/add_pattern t.me/spam_', parse_mode='HTML' ) return pattern = args[1] async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) if antispam.add_pattern(pattern): await message.answer(f'✅ Паттерн {pattern} добавлен в чёрный список', parse_mode='HTML') else: await message.answer(f'ℹ️ Паттерн {pattern} уже в чёрном списке', parse_mode='HTML') @router.message(Command('remove_pattern')) @admin_only async def cmd_remove_pattern(message: Message): """ Удалить паттерн из чёрного списка Использование: /remove_pattern t.me/spam_ """ args = message.text.split(maxsplit=1) if len(args) < 2: await message.answer( '❌ Использование:\n' '/remove_pattern t.me/spam_', parse_mode='HTML' ) return pattern = args[1] async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) if antispam.remove_pattern(pattern): await message.answer(f'✅ Паттерн {pattern} удалён из чёрного списка', parse_mode='HTML') else: await message.answer(f'ℹ️ Паттерн {pattern} не найден в чёрном списке', parse_mode='HTML') @router.message(Command('add_keyword')) @admin_only async def cmd_add_keyword(message: Message): """ Добавить ключевое слово в чёрный список Использование: /add_keyword выигрыш в лотерею """ args = message.text.split(maxsplit=1) if len(args) < 2: await message.answer( '❌ Использование:\n' '/add_keyword выигрыш в лотерею', parse_mode='HTML' ) return keyword = args[1] async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) if antispam.add_keyword(keyword): await message.answer(f'✅ Ключ {keyword} добавлен в чёрный список', parse_mode='HTML') else: await message.answer(f'ℹ️ Ключ {keyword} уже в чёрном списке', parse_mode='HTML') @router.message(Command('remove_keyword')) @admin_only async def cmd_remove_keyword(message: Message): """ Удалить ключевое слово из чёрного списка Использование: /remove_keyword выигрыш в лотерею """ args = message.text.split(maxsplit=1) if len(args) < 2: await message.answer( '❌ Использование:\n' '/remove_keyword выигрыш в лотерею', parse_mode='HTML' ) return keyword = args[1] async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) if antispam.remove_keyword(keyword): await message.answer(f'✅ Ключ {keyword} удалён из чёрного списка', parse_mode='HTML') else: await message.answer(f'ℹ️ Ключ {keyword} не найден в чёрном списке', parse_mode='HTML') # ============================================================================= # ПРОСМОТР ЧЁРНОГО СПИСКА # ============================================================================= @router.message(Command('domains')) @admin_only async def cmd_domains(message: Message): """Показать список запрещённых доменов""" async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) domains = antispam.get_all_domains() stats = antispam.get_stats() if not domains: await message.answer('📋 Чёрный список доменов пуст') return text = f'📋 Чёрный список доменов ({len(domains)})\n\n' for i, domain in enumerate(domains[:20], 1): # Показываем первые 20 text += f'{i}. {domain}\n' if len(domains) > 20: text += f'\n... и ещё {len(domains) - 20}' await message.answer(text) @router.message(Command('patterns')) @admin_only async def cmd_patterns(message: Message): """Показать список запрещённых паттернов""" async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) patterns = antispam.get_all_patterns() stats = antispam.get_stats() if not patterns: await message.answer('📋 Список запрещённых паттернов пуст') return text = f'📋 Запрещённые паттерны ({len(patterns)})\n\n' for i, pattern in enumerate(patterns[:20], 1): text += f'{i}. {pattern}\n' if len(patterns) > 20: text += f'\n... и ещё {len(patterns) - 20}' await message.answer(text) @router.message(Command('keywords')) @admin_only async def cmd_keywords(message: Message): """Показать список запрещённых ключевых слов""" async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) keywords = antispam.get_all_keywords() if not keywords: await message.answer('📋 Список запрещённых ключей пуст') return text = f'📋 Запрещённые ключевые слова ({len(keywords)})\n\n' for i, keyword in enumerate(keywords[:20], 1): text += f'{i}. {keyword}\n' if len(keywords) > 20: text += f'\n... и ещё {len(keywords) - 20}' await message.answer(text) @router.message(Command('antispam_stats')) @admin_only async def cmd_antispam_stats(message: Message): """Показать статистику анти-спама""" async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) stats = antispam.get_stats() # Считаем предупреждения stmt = select(func.count(SpamWarning.warning_id)).where(SpamWarning.is_active == True) result = await session.execute(stmt) active_warnings = result.scalar() # Считаем забаненных за спам stmt = select(func.count(User.user_id)).where(User.spam_warning_count >= stats['settings'].get('ban_after_warnings', 3)) result = await session.execute(stmt) banned_count = result.scalar() text = ( f'📊 Статистика анти-спама\n\n' f'📁 Чёрный список:\n' f'├─ Домены: {stats["domains_count"]}\n' f'├─ Паттерны: {stats["patterns_count"]}\n' f'└─ Ключевые слова: {stats["keywords_count"]}\n\n' f'⚠️ Предупреждения:\n' f'├─ Активные: {active_warnings}\n' f'└─ Забанено за спам: {banned_count}\n\n' f'⚙️ Настройки:\n' f'├─ Удалять сообщения: {"✅" if stats["settings"].get("delete_message") else "❌"}\n' f'├─ Предупреждать пользователя: {"✅" if stats["settings"].get("warn_user") else "❌"}\n' f'├─ Уведомлять админа: {"✅" if stats["settings"].get("notify_admin") else "❌"}\n' f'└─ Бан после {stats["settings"].get("ban_after_warnings", 3)} предупреждений' ) await message.answer(text) # ============================================================================= # ПРЕДУПРЕЖДЕНИЯ ПОЛЬЗОВАТЕЛЕЙ # ============================================================================= @router.message(Command('warnings')) @admin_only async def cmd_warnings(message: Message): """Показать пользователей с предупреждениями""" async with AsyncSessionLocal() as session: stmt = ( select(User) .where(User.spam_warning_count > 0) .order_by(User.spam_warning_count.desc()) .limit(20) ) result = await session.execute(stmt) users = list(result.scalars().all()) if not users: await message.answer('✅ Нет пользователей с предупреждениями') return text = '⚠️ Пользователи с предупреждениями\n\n' for user in users: name = user.full_name or f'user{user.user_id}' apt = user.apartment or '?' text += f'• {name} (кв. {apt}) — {user.spam_warning_count} предупр.\n' text += '\n💡 Для сброса: /clear_warnings ' await message.answer(text) @router.message(Command('clear_warnings')) @admin_only async def cmd_clear_warnings(message: Message): """ Сбросить предупреждения пользователя Использование: /clear_warnings """ args = message.text.split() if len(args) < 2: await message.answer( '❌ Использование:\n' '/clear_warnings 123456789', parse_mode='HTML' ) return try: user_id = int(args[1]) except ValueError: await message.answer('❌ Неверный ID пользователя') return async with AsyncSessionLocal() as session: # Сбрасываем счётчик user = await session.get(User, user_id) if user: user.spam_warning_count = 0 await session.commit() await message.answer(f'✅ Предупреждения пользователя {user_id} сброшены') else: await message.answer(f'❌ Пользователь {user_id} не найден') @router.message(Command('reload_antispam')) @admin_only async def cmd_reload_antispam(message: Message): """Перезагрузить конфигурацию анти-спама из файла""" async with AsyncSessionLocal() as session: antispam = AntiSpamService(session) antispam.reload_config() await message.answer('✅ Конфигурация анти-спама перезаряжена')