"""Nexus — Telegram Bot Notification Module Sends alerts/recovery/system messages via Telegram Bot API. """ import logging import httpx from datetime import datetime, timezone from server.config import settings logger = logging.getLogger("nexus.telegram") # Bot API base URL TELEGRAM_API_BASE = "https://api.telegram.org" # Reusable httpx client — avoids TCP connection churn during alert storms _http_client: httpx.AsyncClient | None = None async def _get_client() -> httpx.AsyncClient: global _http_client if _http_client is None or _http_client.is_closed: _http_client = httpx.AsyncClient(timeout=10.0) return _http_client async def close_client(): """Close the shared httpx client — call during app shutdown""" global _http_client if _http_client and not _http_client.is_closed: await _http_client.aclose() _http_client = None async def send_telegram(message: str) -> bool: """Send a Telegram message via Bot API. Returns True if sent successfully, False if skipped or failed. Silently skips if TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID is not configured. """ if not settings.TELEGRAM_BOT_TOKEN or not settings.TELEGRAM_CHAT_ID: return False # Not configured — silent skip try: client = await _get_client() resp = await client.post( f"{TELEGRAM_API_BASE}/bot{settings.TELEGRAM_BOT_TOKEN}/sendMessage", json={ "chat_id": settings.TELEGRAM_CHAT_ID, "text": message, "parse_mode": "HTML", }, ) if resp.status_code == 200: logger.info(f"Telegram sent: {message[:50]}...") return True else: logger.error(f"Telegram API error: {resp.status_code} {resp.text[:100]}") return False except Exception as e: logger.error(f"Telegram send failed: {e}") return False async def send_telegram_alert(server_name: str, alert_type: str, value: float) -> bool: """Send server alert notification""" now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") return await send_telegram( f"🔴 告警\n" f"服务器 {server_name} {alert_type} 使用率 {value:.1f}%\n" f"时间: {now}" ) async def send_telegram_recovery(server_name: str, metric: str, value: float) -> bool: """Send server recovery notification""" now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") return await send_telegram( f"🟢 恢复\n" f"服务器 {server_name} {metric} 已恢复正常 ({value:.1f}%)\n" f"时间: {now}" ) async def send_telegram_system_alert(message: str) -> bool: """Send system-level alert (Python crash, Redis disconnect, etc.)""" now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") return await send_telegram( f"⚠️ 系统告警\n" f"{message}\n" f"时间: {now}" ) async def send_telegram_restart_success() -> bool: """Notify that Python backend has successfully restarted""" now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") return await send_telegram( f"🟢 Nexus后端已恢复\n" f"Python服务自动重启成功\n" f"时间: {now}" ) async def send_telegram_restart_failed() -> bool: """Notify that Python backend restart failed — needs human intervention""" now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC") return await send_telegram( f"🔴 Nexus后端重启失败!\n" f"请人工介入检查\n" f"时间: {now}" )