""" Bot module для PyGuardian Telegram бот для управления системой защиты """ import asyncio import logging from datetime import datetime, timedelta from typing import Dict, List, Optional, Callable from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes from telegram.constants import ParseMode import ipaddress logger = logging.getLogger(__name__) class TelegramBot: """Telegram бот для управления PyGuardian""" def __init__(self, config: Dict, storage, firewall_manager, attack_detector, security_manager=None, session_manager=None, password_manager=None): self.token = config.get('bot_token') self.admin_id = config.get('admin_id') self.storage = storage self.firewall_manager = firewall_manager self.attack_detector = attack_detector self.security_manager = security_manager self.session_manager = session_manager self.password_manager = password_manager if not self.token or not self.admin_id: raise ValueError("Не указан токен бота или admin_id в конфигурации") self.application = None self.running = False def _check_admin(self, user_id: int) -> bool: """Проверка прав администратора""" return user_id == self.admin_id async def _send_unauthorized_message(self, update: Update) -> None: """Отправка сообщения о недостатке прав""" await update.message.reply_text( "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", parse_mode=ParseMode.MARKDOWN ) async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /start""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return welcome_message = """ 🛡️ *PyGuardian Protection System* Добро пожаловать! Система активна и готова к работе. *Доступные команды:* • `/status` - Текущая статистика • `/top10` - Топ атакующих IP • `/details ` - Информация по IP • `/ban ` - Ручная блокировка • `/unban ` - Разблокировать IP • `/list` - Список забаненных IP • `/help` - Справка *Системная информация:* • Мониторинг: auth.log • Firewall: {backend} • База данных: Активна """.format(backend=self.firewall_manager.backend.upper()) await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /status""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return try: # Получаем статистику stats = await self.storage.get_daily_stats() top_attackers = await self.storage.get_top_attackers(limit=5) banned_ips = await self.storage.get_banned_ips() # Формируем сообщение message = f""" 📊 *Статистика за сегодня* 🚨 *Атаки:* {stats['today']['attacks']} {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера 🌐 *Уникальных IP:* {stats['today']['unique_ips']} 🔒 *Активных банов:* {stats['active_bans']} ✅ *Успешных входов:* {stats['today']['successful_logins']} 🔝 *Топ атакующих:* """ for i, attacker in enumerate(top_attackers, 1): message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" if not top_attackers: message += "Сегодня атак не обнаружено 🎉\n" # Добавляем клавиатуру keyboard = [ [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( message, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup ) except Exception as e: logger.error(f"Ошибка в команде status: {e}") await update.message.reply_text( f"❌ Ошибка получения статистики: {str(e)}" ) async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /top10""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return try: top_attackers = await self.storage.get_top_attackers(limit=10, days=1) if not top_attackers: await update.message.reply_text( "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", parse_mode=ParseMode.MARKDOWN ) return message = "📈 *Топ-10 атакующих IP за сутки*\n\n" for i, attacker in enumerate(top_attackers, 1): emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" message += f"{emoji} `{attacker['ip']}`\n" message += f" 📊 {attacker['attempts']} попыток\n" message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" if attacker['attack_types']: message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" message += "\n" # Добавляем кнопку для получения деталей keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( message, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup ) except Exception as e: logger.error(f"Ошибка в команде top10: {e}") await update.message.reply_text( f"❌ Ошибка получения топ атакующих: {str(e)}" ) async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /details """ if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return if not context.args: await update.message.reply_text( "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", parse_mode=ParseMode.MARKDOWN ) return ip = context.args[0] # Валидация IP try: ipaddress.ip_address(ip) except ValueError: await update.message.reply_text( f"❌ Некорректный IP-адрес: `{ip}`", parse_mode=ParseMode.MARKDOWN ) return try: details = await self.storage.get_ip_details(ip) message = f"🔍 *Детали для IP:* `{ip}`\n\n" if details['total_attempts'] == 0: message += "ℹ️ Атак от данного IP не обнаружено" else: message += f"📊 *Общая статистика:*\n" message += f"• Всего попыток: {details['total_attempts']}\n" message += f"• Первая атака: {details['first_seen']}\n" message += f"• Последняя атака: {details['last_seen']}\n" if details['attack_types']: message += f"• Типы атак: {', '.join(details['attack_types'])}\n" if details['usernames']: message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" # Статус бана if details['is_banned']: ban_info = details['ban_info'] message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" message += f"• Причина: {ban_info['reason']}\n" message += f"• Заблокирован: {ban_info['banned_at']}\n" message += f"• Разбан: {ban_info['unban_at']}\n" message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" else: message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" # Последние попытки if details['recent_attempts']: message += f"\n📋 *Последние попытки:*\n" for attempt in details['recent_attempts'][:5]: message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" # Кнопки управления keyboard = [] if details['is_banned']: keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) else: keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) if keyboard: reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( message, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup ) else: await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) except Exception as e: logger.error(f"Ошибка в команде details: {e}") await update.message.reply_text( f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" ) async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /ban """ if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return if not context.args: await update.message.reply_text( "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", parse_mode=ParseMode.MARKDOWN ) return ip = context.args[0] # Валидация IP try: ipaddress.ip_address(ip) except ValueError: await update.message.reply_text( f"❌ Некорректный IP-адрес: `{ip}`", parse_mode=ParseMode.MARKDOWN ) return try: success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") if success: await update.message.reply_text( f"✅ IP `{ip}` успешно заблокирован", parse_mode=ParseMode.MARKDOWN ) else: await update.message.reply_text( f"❌ Не удалось заблокировать IP `{ip}`", parse_mode=ParseMode.MARKDOWN ) except Exception as e: logger.error(f"Ошибка в команде ban: {e}") await update.message.reply_text( f"❌ Ошибка блокировки IP {ip}: {str(e)}" ) async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /unban """ if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return if not context.args: await update.message.reply_text( "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", parse_mode=ParseMode.MARKDOWN ) return ip = context.args[0] # Валидация IP try: ipaddress.ip_address(ip) except ValueError: await update.message.reply_text( f"❌ Некорректный IP-адрес: `{ip}`", parse_mode=ParseMode.MARKDOWN ) return try: success = await self.attack_detector.process_unban(ip) if success: await update.message.reply_text( f"✅ IP `{ip}` успешно разблокирован", parse_mode=ParseMode.MARKDOWN ) else: await update.message.reply_text( f"❌ Не удалось разблокировать IP `{ip}`", parse_mode=ParseMode.MARKDOWN ) except Exception as e: logger.error(f"Ошибка в команде unban: {e}") await update.message.reply_text( f"❌ Ошибка разблокировки IP {ip}: {str(e)}" ) async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /list""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return try: banned_ips = await self.storage.get_banned_ips() if not banned_ips: await update.message.reply_text( "📋 *Список забаненных IP*\n\nСписок пуст 🎉", parse_mode=ParseMode.MARKDOWN ) return message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 emoji = "🔴" if ban['manual'] else "🟡" message += f"{emoji} `{ban['ip']}`\n" message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" message += f" 🕐 {ban['banned_at']}\n" if ban['attempts'] > 0: message += f" 📊 {ban['attempts']} попыток\n" message += "\n" if len(banned_ips) > 15: message += f"\n... и еще {len(banned_ips) - 15} IP" # Добавляем кнопки управления keyboard = [ [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] ] reply_markup = InlineKeyboardMarkup(keyboard) await update.message.reply_text( message, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup ) except Exception as e: logger.error(f"Ошибка в команде list: {e}") await update.message.reply_text( f"❌ Ошибка получения списка забаненных IP: {str(e)}" ) async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Команда /help""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return help_text = """ 🛡️ *PyGuardian - Справка* *Основные команды:* • `/start` - Запуск и приветствие • `/status` - Текущая статистика системы • `/top10` - Топ-10 атакующих IP за сутки • `/details ` - Подробная информация по IP • `/ban ` - Ручная блокировка IP • `/unban ` - Разблокировка IP • `/list` - Список всех забаненных IP *🚨 Управление компромиссами:* • `/compromises` - Список обнаруженных взломов • `/sessions` - Активные SSH сессии • `/kick ` - Завершить сессию пользователя • `/generate_password ` - Сгенерировать пароль • `/set_password ` - Установить пароль *ℹ️ Информация:* • `/help` - Эта справка *Системная информация:* • Мониторинг auth.log в реальном времени • Автоматическая блокировка при превышении лимита • Поддержка iptables и nftables • Автоматический разбан по таймеру • 🚨 СКРЫТНАЯ реакция на взломы • Уведомления о критических событиях *Безопасность:* • Доступ только для авторизованных пользователей • Белый список IP для исключений • Защита от ложных срабатываний • Логирование всех действий *Поддержка:* @your_support_bot """ await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Обработчик кнопок""" query = update.callback_query await query.answer() if not self._check_admin(query.from_user.id): await query.message.reply_text("❌ Нет доступа") return data = query.data try: if data == "refresh_status": # Обновляем статус await self.status_command(query, context) elif data == "show_top10": # Показываем топ-10 await self.top10_command(query, context) elif data == "show_banned": # Показываем список банов await self.list_command(query, context) elif data == "refresh_banned": # Обновляем список банов await self.list_command(query, context) elif data == "cleanup_expired": # Очищаем истекшие баны await self.attack_detector.check_expired_bans() await query.message.reply_text("✅ Очистка истекших банов выполнена") elif data.startswith("ban_"): ip = data[4:] success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") if success: await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) else: await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) elif data.startswith("unban_"): ip = data[6:] success = await self.attack_detector.process_unban(ip) if success: await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) else: await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) elif data.startswith("details_"): ip = data[8:] # Отправляем детали как новое сообщение context.args = [ip] await self.details_command(query, context) except Exception as e: logger.error(f"Ошибка в обработчике кнопок: {e}") await query.message.reply_text(f"❌ Ошибка: {str(e)}") async def send_notification(self, message: str) -> None: """Отправка уведомления администратору""" try: if self.application and self.running: await self.application.bot.send_message( chat_id=self.admin_id, text=message, parse_mode=ParseMode.MARKDOWN ) except Exception as e: logger.error(f"Ошибка отправки уведомления: {e}") async def start_bot(self) -> None: """Запуск бота""" try: self.application = Application.builder().token(self.token).build() # Добавляем обработчики команд self.application.add_handler(CommandHandler("start", self.start_command)) self.application.add_handler(CommandHandler("status", self.status_command)) self.application.add_handler(CommandHandler("top10", self.top10_command)) self.application.add_handler(CommandHandler("details", self.details_command)) self.application.add_handler(CommandHandler("ban", self.ban_command)) self.application.add_handler(CommandHandler("unban", self.unban_command)) self.application.add_handler(CommandHandler("list", self.list_command)) self.application.add_handler(CommandHandler("help", self.help_command)) # Новые команды для управления компромиссами и сессиями self.application.add_handler(CommandHandler("compromises", self.compromises_command)) self.application.add_handler(CommandHandler("sessions", self.sessions_command)) self.application.add_handler(CommandHandler("kick", self.kick_command)) self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) self.application.add_handler(CommandHandler("set_password", self.set_password_command)) # Добавляем обработчик кнопок self.application.add_handler(CallbackQueryHandler(self.button_callback)) self.running = True logger.info("Telegram бот запущен") # Запускаем бота await self.application.initialize() await self.application.start() # Уведомляем о запуске await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") # Держим бота работающим await self.application.updater.start_polling() except Exception as e: logger.error(f"Ошибка запуска Telegram бота: {e}") self.running = False async def stop_bot(self) -> None: """Остановка бота""" try: if self.application and self.running: # Уведомляем об остановке await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") await self.application.updater.stop() await self.application.stop() await self.application.shutdown() self.running = False logger.info("Telegram бот остановлен") except Exception as e: logger.error(f"Ошибка остановки Telegram бота: {e}") async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """GET compromises Показать список обнаруженных компромиссов""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return try: if not self.security_manager: await update.message.reply_text( "⚠️ Менеджер безопасности не доступен", parse_mode='Markdown' ) return compromises = await self.storage.get_compromises() if not compromises: await update.message.reply_text( "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", parse_mode='Markdown' ) return response = "🚨 *Обнаруженные компромиссы*\n\n" for comp in compromises[:10]: # Показываем только 10 последних timestamp = comp['timestamp'] ip = comp['ip_address'] user = comp['username'] action = comp['action_taken'] evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" response += f"🚨 `{timestamp}`\n" response += f" • IP: `{ip}`\n" response += f" • User: `{user}`\n" response += f" • Действие: {action}\n" response += f" • Доказательства: {evidence}...\n\n" if len(compromises) > 10: response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" await update.message.reply_text(response, parse_mode='Markdown') except Exception as e: await update.message.reply_text( f"❌ Ошибка: {e}", parse_mode='Markdown' ) logger.error(f"Ошибка при выполнении команды /compromises: {e}") async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """GET sessions Показать активные SSH сессии""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return try: if not self.session_manager: await update.message.reply_text( "⚠️ Менеджер сессий не доступен", parse_mode='Markdown' ) return sessions = self.session_manager.get_active_sessions() if not sessions: await update.message.reply_text( "🎉 *Активные SSH сессии отсутствуют*", parse_mode='Markdown' ) return response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" for session in sessions: pid = session.get('pid', 'Неизвестно') user = session.get('username', 'Неизвестно') tty = session.get('tty', 'Неизвестно') start_time = session.get('start_time', 'Неизвестно') remote_ip = session.get('remote_ip', 'Неизвестно') response += f"💻 PID: `{pid}`\n" response += f" • User: `{user}`\n" response += f" • TTY: `{tty}`\n" response += f" • IP: `{remote_ip}`\n" response += f" • Start: `{start_time}`\n\n" response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" await update.message.reply_text(response, parse_mode='Markdown') except Exception as e: await update.message.reply_text( f"❌ Ошибка: {e}", parse_mode='Markdown' ) logger.error(f"Ошибка при выполнении команды /sessions: {e}") async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """KICK user/pid Завершить сессию пользователя""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return if not context.args: await update.message.reply_text( "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", parse_mode='Markdown' ) return try: if not self.session_manager: await update.message.reply_text( "⚠️ Менеджер сессий не доступен", parse_mode='Markdown' ) return target = context.args[0] # Проверяем, является ли target PID (число) if target.isdigit(): result = self.session_manager.terminate_session(pid=int(target)) action = f"PID {target}" else: result = self.session_manager.terminate_session(username=target) action = f"user {target}" if result: response = f"✅ *Сессия завершена*\n\n" response += f"💻 Target: `{action}`\n" response += f"ℹ️ Сессия была успешно завершена" # Логируем действие logger.info(f"Сессия завершена через Telegram бот: {action}") else: response = f"❌ *Не удалось завершить сессию*\n\n" response += f"💻 Target: `{action}`\n" response += f"ℹ️ Сессия может быть уже завершена или не существовать" await update.message.reply_text(response, parse_mode='Markdown') except Exception as e: await update.message.reply_text( f"❌ Ошибка: {e}", parse_mode='Markdown' ) logger.error(f"Ошибка при выполнении команды /kick: {e}") async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" if not self._check_admin(update.effective_user.id): await self._send_unauthorized_message(update) return if not context.args: await update.message.reply_text( "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", parse_mode='Markdown' ) return try: if not self.password_manager: await update.message.reply_text( "⚠️ Менеджер паролей не доступен", parse_mode='Markdown' ) return username = context.args[0] # Проверяем, существует ли пользователь user_exists = self.password_manager.user_exists(username) if not user_exists: await update.message.reply_text( f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", parse_mode='Markdown' ) return new_password = self.password_manager.generate_password() success = self.password_manager.set_user_password(username, new_password) if success: response = f"✅ *Пароль успешно сгенерирован*\n\n" response += f"💻 User: `{username}`\n" response += f"🔐 Password: `{new_password}`\n\n" response += "⚠️ *ВНИМАНИЕ:*\n" response += "• Сохраните пароль в надёжном месте\n" response += "• Это сообщение будет удалено через 5 минут" # Логируем действие (без пароля!) logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") # Отправляем сообщение и планируем его удаление message = await update.message.reply_text(response, parse_mode='Markdown') # Планируем удаление через 5 минут (безопасность) context.job_queue.run_once( self._delete_message, 300, # 5 минут data={'chat_id': message.chat_id, 'message_id': message.message_id} ) else: response = f"❌ *Не удалось установить пароль*\n\n" response += f"💻 User: `{username}`\n" response += f"ℹ️ Проверьте права доступа и существование пользователя" await update.message.reply_text(response, parse_mode='Markdown') except Exception as e: await update.message.reply_text( f"❌ Ошибка: {e}", parse_mode='Markdown' ) logger.error(f"Ошибка при выполнении команды /generate_password: {e}") async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """SET PASSWORD Установить пароль для пользователя""" if not await self._check_access(update): return if len(context.args) < 2: await update.message.reply_text( "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", parse_mode='Markdown' ) return try: if not self.password_manager: await update.message.reply_text( "⚠️ Менеджер паролей не доступен", parse_mode='Markdown' ) return username = context.args[0] password = context.args[1] # Проверяем, существует ли пользователь user_exists = self.password_manager.user_exists(username) if not user_exists: await update.message.reply_text( f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", parse_mode='Markdown' ) return # Проверяем сложность пароля if len(password) < 8: await update.message.reply_text( "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", parse_mode='Markdown' ) return success = self.password_manager.set_user_password(username, password) if success: response = f"✅ *Пароль успешно установлен*\n\n" response += f"💻 User: `{username}`\n" response += f"ℹ️ Пароль изменён успешно\n\n" response += "⚠️ *ВНИМАНИЕ:*\n" response += "• Это сообщение будет удалено через 5 минут" # Логируем действие (без пароля!) logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") # Отправляем сообщение и планируем его удаление message = await update.message.reply_text(response, parse_mode='Markdown') # Планируем удаление через 5 минут (безопасность) context.job_queue.run_once( self._delete_message, 300, # 5 минут data={'chat_id': message.chat_id, 'message_id': message.message_id} ) else: response = f"❌ *Не удалось установить пароль*\n\n" response += f"💻 User: `{username}`\n" response += f"ℹ️ Проверьте права доступа и существование пользователя" await update.message.reply_text(response, parse_mode='Markdown') except Exception as e: await update.message.reply_text( f"❌ Ошибка: {e}", parse_mode='Markdown' ) logger.error(f"Ошибка при выполнении команды /set_password: {e}") async def _delete_message(self, context): """Helper метод для удаления сообщения (безопасность)""" try: data = context.job.data await context.bot.delete_message( chat_id=data['chat_id'], message_id=data['message_id'] ) except Exception as e: logger.warning(f"Не удалось удалить сообщение: {e}") class NotificationManager: """Менеджер уведомлений""" def __init__(self, bot: TelegramBot): self.bot = bot async def on_ip_banned(self, ban_info: Dict) -> None: """Уведомление о блокировке IP""" try: emoji = "🔴" if not ban_info['auto'] else "🟡" ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" message = f"""{emoji} *{ban_type}* 🚫 IP: `{ban_info['ip']}` 📝 Причина: {ban_info['reason']} """ if ban_info['attempts'] > 0: message += f"📊 Попыток: {ban_info['attempts']}\n" message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" await self.bot.send_notification(message) except Exception as e: logger.error(f"Ошибка отправки уведомления о бане: {e}") async def on_ip_unbanned(self, unban_info: Dict) -> None: """Уведомление о разблокировке IP""" try: emoji = "🟢" if unban_info['auto'] else "🔓" unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" message = f"""{emoji} *{unban_type}* ✅ IP: `{unban_info['ip']}` ⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ await self.bot.send_notification(message) except Exception as e: logger.error(f"Ошибка отправки уведомления о разбане: {e}") async def on_critical_attack(self, ip: str, attempts: int) -> None: """Уведомление о критической атаке""" try: message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* 🚨 IP: `{ip}` 📊 Попыток: {attempts} за последние 5 минут 🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Рекомендуется ручная проверка! """ await self.bot.send_notification(message) except Exception as e: logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") async def on_system_error(self, error: str) -> None: """Уведомление о системной ошибке""" try: message = f"""❌ *СИСТЕМНАЯ ОШИБКА* 🔧 Описание: {error} 🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} Требует внимания администратора! """ await self.bot.send_notification(message) except Exception as e: logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}")