From c0f3509d3958111060fc8f69041214485dfa38ca Mon Sep 17 00:00:00 2001 From: OG T Date: Sun, 19 Apr 2026 14:26:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(drift-card):=20Drift=20Diff=20HTTP=20400=20?= =?UTF-8?q?=E2=80=94=20item-by-item=20=E7=B4=AF=E8=A8=88=E9=95=B7=E5=BA=A6?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=88=87=E6=96=B7=20HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 統帥回報 14:18 點 [查看 Diff] 收到 'Drift Diff 查詢失敗: HTTP error: 400' 真因 (telegram_gateway.py:2087 _send_drift_diff_detail): - report_id=7ffe78ae 有 48 items,單筆 git_value 最長 1794 字 (env array) - 累計 _full 遠超 4096,執行 _full[:3950] 截斷 - 截斷可能切在 HTML tag 中間 (... 或 < entity 中間) - Telegram parse_mode='HTML' 拒絕不完整 HTML → 400 修復: - item-by-item 累計長度,單個 item 算 _block 長度+1 - 預留 3800 上限 (4096 - 250 buffer 給 header + '… 還有 X 項' 提示) - 確保 _full 永遠是完整 HTML 結構 驗證: 下次 drift report 出現 + 統帥點 [查看 Diff] 應正常顯示 (本 session 的下個 cycle) Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/api/src/services/telegram_gateway.py | 44 ++++++++++++++++------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index 32b7f212..9f28f5de 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -2100,26 +2100,44 @@ class TelegramGateway: }) return - _lines = [f"📊 完整 Drift Diff{html.escape(report_id)}"] - _lines.append(f"Namespace: {html.escape(_rpt.namespace)}") - _lines.append(f"HIGH×{_rpt.high_count} MEDIUM×{_rpt.medium_count} INFO×{_rpt.info_count}") - _lines.append("━" * 20) - for i, _item in enumerate(_rpt.items[:50], 1): + # 2026-04-19 ogt + Claude Opus 4.7: 修 HTTP 400 真因 + # 原邏輯: _full[:3950] 切在 HTML tag/entity 中間 → Telegram parse_mode HTML 拒絕 + # 修法: item-by-item 累計長度,超過 3800 就停,確保完整 HTML 結構 + # (3800 留 250 buffer 給 header + 截斷提示) + _MAX_LEN = 3800 + + _header = [ + f"📊 完整 Drift Diff{html.escape(report_id)}", + f"Namespace: {html.escape(_rpt.namespace)}", + f"HIGH×{_rpt.high_count} MEDIUM×{_rpt.medium_count} INFO×{_rpt.info_count}", + "━" * 20, + ] + _lines = list(_header) + _used_len = sum(len(s) + 1 for s in _header) + _shown = 0 + + for _item in _rpt.items: _level = getattr(_item.drift_level, "value", str(_item.drift_level)) _emoji = "🔴" if _level == "high" else ("🟡" if _level == "medium" else "⚪") _field = (_item.field_path or "")[:80] _git = str(_item.git_value)[:40] if _item.git_value is not None else "(未設)" _k8s = str(_item.actual_value)[:40] if _item.actual_value is not None else "(未設)" - _lines.append(f"{_emoji} {html.escape(_field)}") - _lines.append(f" Git: {html.escape(_git)}") - _lines.append(f" K8s: {html.escape(_k8s)}") - if len(_rpt.items) > 50: - _lines.append(f"… 還有 {len(_rpt.items) - 50} 項未顯示") + _block = ( + f"{_emoji} {html.escape(_field)}\n" + f" Git: {html.escape(_git)}\n" + f" K8s: {html.escape(_k8s)}" + ) + if _used_len + len(_block) + 1 > _MAX_LEN: + break + _lines.append(_block) + _used_len += len(_block) + 1 + _shown += 1 + + _remaining = len(_rpt.items) - _shown + if _remaining > 0: + _lines.append(f"… 還有 {_remaining} 項未顯示") _full = "\n".join(_lines) - # Telegram 訊息上限 4096 字元 - if len(_full) > 4000: - _full = _full[:3950] + "\n… (截斷)" await self._send_request("sendMessage", { "chat_id": settings.OPENCLAW_TG_CHAT_ID,