From ad120687b30b78655b68ff9657ac78b3e1275f00 Mon Sep 17 00:00:00 2001 From: kyx236 Date: Sun, 18 Jan 2026 01:16:20 +0800 Subject: [PATCH] feat(logger, telegram_bot): Add real-time Telegram logging capability - Add TelegramLogHandler class to stream logs to Telegram in real-time - Implement enable_telegram_logging() method to activate Telegram log streaming - Implement disable_telegram_logging() method to deactivate Telegram log streaming - Implement is_telegram_logging_enabled() method to check logging status - Add /logs_live command to enable real-time log push notifications - Add /logs_stop command to disable real-time log push notifications - Update help text to document new real-time logging commands - Remove obsolete team.json.example file - Enables administrators to monitor system logs in real-time through Telegram bot --- logger.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++ team.json.example | 40 ----------------------------------- telegram_bot.py | 38 +++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 40 deletions(-) delete mode 100644 team.json.example diff --git a/logger.py b/logger.py index 00d86b1..d3bb4a1 100644 --- a/logger.py +++ b/logger.py @@ -72,6 +72,38 @@ class FileFormatter(logging.Formatter): return f"[{timestamp}] [{level}] {icon}{record.getMessage()}" +class TelegramLogHandler(logging.Handler): + """Telegram 实时日志处理器""" + + def __init__(self, callback, level=logging.INFO): + """初始化 Telegram 日志处理器 + + Args: + callback: 回调函数,接收 (message: str, level: str) 参数 + level: 日志级别 + """ + super().__init__(level) + self.callback = callback + self.setFormatter(FileFormatter()) + + def emit(self, record): + """发送日志记录到 Telegram""" + try: + msg = self.format(record) + # 映射日志级别到 BotNotifier 的级别 + level_map = { + logging.DEBUG: "debug", + logging.INFO: "info", + logging.WARNING: "warning", + logging.ERROR: "error", + logging.CRITICAL: "error" + } + level = level_map.get(record.levelno, "info") + self.callback(msg, level) + except Exception: + self.handleError(record) + + class Logger: """统一日志输出 (基于 Python logging 模块)""" @@ -113,6 +145,7 @@ class Logger: self.name = name self.use_color = use_color self.enable_file_log = enable_file_log + self._telegram_handler = None # Telegram 日志处理器 # 从环境变量读取日志级别,默认 INFO if level is None: @@ -162,6 +195,27 @@ class Logger: return self.ICONS.get(icon, icon) return "" + def enable_telegram_logging(self, callback, level: int = logging.INFO): + """启用 Telegram 实时日志 + + Args: + callback: 回调函数,接收 (message: str, level: str) 参数 + level: 日志级别,默认 INFO + """ + if self._telegram_handler is None: + self._telegram_handler = TelegramLogHandler(callback, level) + self._logger.addHandler(self._telegram_handler) + + def disable_telegram_logging(self): + """禁用 Telegram 实时日志""" + if self._telegram_handler is not None: + self._logger.removeHandler(self._telegram_handler) + self._telegram_handler = None + + def is_telegram_logging_enabled(self) -> bool: + """检查 Telegram 实时日志是否启用""" + return self._telegram_handler is not None + def info(self, msg: str, icon: str = None, indent: int = 0): """信息日志""" prefix = " " * indent diff --git a/team.json.example b/team.json.example deleted file mode 100644 index 0f9ac9e..0000000 --- a/team.json.example +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "_comment": "格式1 (旧格式): 通过 https://chatgpt.com/api/auth/session 获取授权信息", - "_comment2": "登录 ChatGPT Team 账号后访问此链接获取完整的 session 信息", - "user": { - "id": "user-xxxxxxxxxxxxxxxxxxxxxxxx", - "email": "your-email@example.com", - "idp": "auth0", - "iat": 0, - "mfa": false - }, - "expires": "2026-01-01T00:00:00.000Z", - "account": { - "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - "planType": "team", - "structure": "workspace", - "workspaceType": null, - "organizationId": "org-xxxxxxxxxxxxxxxxxxxxxxxx", - "isDelinquent": false, - "gracePeriodId": null - }, - "accessToken": "eyJhbGciOiJSUzI1NiIs...(your access token)", - "authProvider": "openai" - }, - { - "_comment": "格式2 (新格式-有Token): 简化配置,只需邮箱、密码和Token", - "_comment2": "适用于已有 accessToken 的情况", - "account": "team-owner@example.com", - "password": "YourPassword@2025", - "token": "eyJhbGciOiJSUzI1NiIs...(your access token)", - "authorized": false, - "account_id": "" - }, - { - "_comment": "格式3 (新格式-无Token): 只需邮箱和密码,程序会自动登录获取Token", - "_comment2": "适用于没有 accessToken 的情况,程序启动时会自动登录", - "account": "team-owner2@example.com", - "password": "YourPassword@2025" - } -] diff --git a/telegram_bot.py b/telegram_bot.py index e304691..40a108a 100644 --- a/telegram_bot.py +++ b/telegram_bot.py @@ -102,6 +102,8 @@ class ProvisionerBot: ("run_all", self.cmd_run_all), ("stop", self.cmd_stop), ("logs", self.cmd_logs), + ("logs_live", self.cmd_logs_live), + ("logs_stop", self.cmd_logs_stop), ("dashboard", self.cmd_dashboard), ("import", self.cmd_import), ("stock", self.cmd_stock), @@ -204,6 +206,8 @@ class ProvisionerBot: /team <n> - 查看第 n 个 Team 处理详情 /config - 查看系统配置 /logs [n] - 查看最近 n 条日志 +/logs_live - 启用实时日志推送 +/logs_stop - 停止实时日志推送 🚀 任务控制: /run <n> - 开始处理第 n 个 Team @@ -849,6 +853,40 @@ class ProvisionerBot: except Exception as e: await update.message.reply_text(f"❌ 读取日志失败: {e}") + @admin_only + async def cmd_logs_live(self, update: Update, context: ContextTypes.DEFAULT_TYPE): + """启用实时日志推送""" + from logger import log + + if log.is_telegram_logging_enabled(): + await update.message.reply_text("📡 实时日志已经在运行中") + return + + # 启用 Telegram 日志推送 + def log_callback(message: str, level: str): + """日志回调函数""" + if self.notifier: + self.notifier.queue_message(message, level) + + log.enable_telegram_logging(log_callback) + await update.message.reply_text( + "✅ 实时日志已启用\n" + "所有日志将实时推送到此聊天\n" + "使用 /logs_stop 停止推送" + ) + + @admin_only + async def cmd_logs_stop(self, update: Update, context: ContextTypes.DEFAULT_TYPE): + """停止实时日志推送""" + from logger import log + + if not log.is_telegram_logging_enabled(): + await update.message.reply_text("📭 实时日志未启用") + return + + log.disable_telegram_logging() + await update.message.reply_text("✅ 实时日志已停止") + @admin_only async def cmd_dashboard(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """查看 S2A 仪表盘统计"""