fix: Telegram HTML 解析錯誤 + 簽核後內容保留

修復:
1. telegram_gateway.py - HTML 轉義 (html.escape) 防止 "Can't parse entities"
2. openclaw-state-machine.tsx - 簽核後顯示結果 2 秒再導航

問題根因:
- URL 和用戶輸入內容可能包含 <, >, & 破壞 HTML
- 簽核後立即刷新列表,已簽核項目消失

Memory: feedback_approval_preserve_content.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-26 15:32:23 +08:00
parent 505ceab895
commit b79e5f1a1a
2 changed files with 107 additions and 28 deletions

View File

@@ -17,9 +17,13 @@ SOUL.md 鐵律 (4.1 Telegram 訊息壓縮原則):
- 根因摘要: 100 字元
- 建議行動: 50 字元
- 總長度: 800 字元 (v7.0 擴展以容納 SignOz 區塊)
修復紀錄:
- 2026-03-26 Claude Code: 修復 HTML 解析錯誤 (Can't parse entities)
"""
import asyncio
import html
from dataclasses import dataclass
from datetime import UTC, datetime
@@ -156,31 +160,38 @@ class TelegramMessage:
# 自動生成事件編號
incident_id = self.incident_id or f"INC-{self.approval_id[:8].upper()}"
# SignOz URL (優先使用動態 URL)
# SignOz URL (優先使用動態 URL) - 必須 HTML 轉義防止解析錯誤
service_name = self.resource_name.split("-")[0] if "-" in self.resource_name else self.resource_name
signoz_url = self.signoz_trace_url or f"http://192.168.0.188:3301/traces?service={service_name}"
raw_url = self.signoz_trace_url or f"http://192.168.0.188:3301/traces?service={service_name}"
signoz_url = html.escape(raw_url, quote=True)
# SignOz 指標區塊
signoz_block = ""
if self.signoz_metrics:
signoz_block = f"━━━━━━━━━━━━━━━━━━━\n{self.signoz_metrics.format()}\n"
# HTML 轉義用戶輸入內容,防止 "Can't parse entities" 錯誤
safe_resource = html.escape(self.resource_name[:35])
safe_root_cause = html.escape(self.root_cause[:50])
safe_action = html.escape(self.suggested_action[:35])
safe_downtime = html.escape(self.estimated_downtime)
# 組裝訊息
message = (
f"═══════════════════════════\n"
f"{self.status_emoji} <b>{self.risk_level}</b> | {self.resource_name[:25]}\n"
f"{self.status_emoji} <b>{html.escape(self.risk_level)}</b> | {html.escape(self.resource_name[:25])}\n"
f"═══════════════════════════\n"
f"📋 <code>{incident_id}</code>\n"
f"🎯 資源: <code>{self.resource_name[:35]}</code>\n"
f"📋 <code>{html.escape(incident_id)}</code>\n"
f"🎯 資源: <code>{safe_resource}</code>\n"
f"━━━━━━━━━━━━━━━━━━━\n"
f"🤖 <b>AI 仲裁判定</b>\n"
f"👥 責任: {resp_display}\n"
f"📊 信心: {conf_emoji} {confidence_pct}%\n"
f"💡 原因: {self.root_cause[:50]}\n"
f"💡 原因: {safe_root_cause}\n"
f"{signoz_block}"
f"━━━━━━━━━━━━━━━━━━━\n"
f"🔧 建議: {self.suggested_action[:35]}\n"
f"⏱️ 停機: {self.estimated_downtime}\n"
f"🔧 建議: {safe_action}\n"
f"⏱️ 停機: {safe_downtime}\n"
f"🔍 <a href='{signoz_url}'>查看 SignOz Trace (±5min)</a>"
)