c9a99f4fb3
代码修复: - web/agent/agent.py: datetime.utcnow() → datetime.now(timezone.utc) - requirements.txt: 移除 paramiko==3.5.0 (已无活跃引用) - 删除 server/infrastructure/ssh/pool.py (DEPRECATED, 无引用) 连接池三层对齐 (config.py/.env.example/session.py): - DB_POOL_SIZE 100→160, DB_MAX_OVERFLOW 100→120 - 基于 MySQL max_connections=400, install.php 公式 文档修复 (21项): - P0: 硬编码域名/IP替换为配置变量占位符 - P1: 10个过时设计文档加归档标注 (引用旧文件结构) - P2: step-3-webssh报告 paramiko引用修正 - 6份审查报告连接池参数勘误 100/100→160/120 - ECC安全报告 EC5 datetime.utcnow 标记已修复 - docs/README.md 文档索引重写 - docs/memory/mem_nexus_overview.md 移除硬编码凭证 - docs/project/tech-stack-inventory.md paramiko标记已移除 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
112 lines
3.6 KiB
Python
112 lines
3.6 KiB
Python
"""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"🔴 <b>告警</b>\n"
|
|
f"服务器 <b>{server_name}</b> {alert_type} 使用率 <b>{value:.1f}%</b>\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"🟢 <b>恢复</b>\n"
|
|
f"服务器 <b>{server_name}</b> {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"⚠️ <b>系统告警</b>\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"🟢 <b>Nexus后端已恢复</b>\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"🔴 <b>Nexus后端重启失败!</b>\n"
|
|
f"请人工介入检查\n"
|
|
f"时间: {now}"
|
|
) |