From 561c1d806bcac03c9435142c2a1e7ca799909b1c Mon Sep 17 00:00:00 2001 From: OG T Date: Sun, 12 Apr 2026 18:39:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(adr-075):=20Phase=202=20=E2=80=94=20TYPE-8?= =?UTF-8?q?M=20=E9=A3=9B=E8=BC=AA/=E5=91=8A=E8=AD=A6=E9=8F=88=E8=B7=AF?= =?UTF-8?q?=E5=81=A5=E5=BA=B7=E9=80=9A=E7=9F=A5=E6=A0=BC=E5=BC=8F=E8=88=87?= =?UTF-8?q?=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 send_meta_alert() — ⚙️ META SYSTEM 卡片(觸發診斷/查看面板/靜默) decision_manager 新增 TYPE-8M elif 分支(在 TYPE-4D 後) _alert_category 提取提前至 if 鏈前,三個分支共用 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/services/decision_manager.py | 20 ++++++-- apps/api/src/services/telegram_gateway.py | 61 +++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/apps/api/src/services/decision_manager.py b/apps/api/src/services/decision_manager.py index 8f397e3d..76e4e121 100644 --- a/apps/api/src/services/decision_manager.py +++ b/apps/api/src/services/decision_manager.py @@ -227,6 +227,11 @@ async def _push_decision_to_telegram( decision_state=_decision_state, ) + # 2026-04-12 ogt: ADR-075 — 從 Incident 提取 alert_category/notification_type(各分支共用) + _alert_category = getattr(incident, "alert_category", "") or "" + _notification_type = getattr(incident, "notification_type", "") or (_notif_type.value if _notif_type else "") + _alertname = incident.signals[0].labels.get("alertname", "MetaSystemAlert") if incident.signals else "MetaSystemAlert" + if _notif_type == NotificationType.TYPE_1: # 純資訊通知 — 無按鈕 tg_result = await gateway.send_info_notification( @@ -244,11 +249,20 @@ async def _push_decision_to_telegram( resource_name=target[:50], diff_summary=description[:500], ) + elif _notif_type.value == "TYPE-8M" or _alert_category in ("alertchain_health", "flywheel_health"): + # TYPE-8M:飛輪/告警鏈路健康異常,發到個人 DM(不發群組) + tg_result = await gateway.send_meta_alert( + incident_id=incident.incident_id, + approval_id=approval_id, + alertname=_alertname, + alert_category=_alert_category, + diagnosis=reasoning[:100] if reasoning else description[:100], + severity_level=risk_level, + system_impact=description[:150] if description else "", + probable_cause=reasoning[:100] if reasoning else "", + ) else: # TYPE-2 / TYPE-3 / TYPE-4 都走 send_approval_card(按鈕組合由 alert_category 決定) - # 2026-04-12 ogt: ADR-075 斷點 A 修復 — 從 Incident 提取 alert_category/notification_type - _alert_category = getattr(incident, "alert_category", "") or "" - _notification_type = getattr(incident, "notification_type", "") or _notif_type.value if _notif_type else "" tg_result = await gateway.send_approval_card( approval_id=approval_id, risk_level=risk_level, diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index 489b73a4..b9aaa6f7 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -1867,6 +1867,67 @@ class TelegramGateway: }, ) + # ========================================================================= + # ADR-075: TYPE-8M Meta-System 告警(飛輪/告警鏈路健康) + # 2026-04-12 ogt + # ========================================================================= + + async def send_meta_alert( + self, + incident_id: str, + approval_id: str, + alertname: str, + alert_category: str, # "flywheel_health" or "alertchain_health" + diagnosis: str, + severity_level: str = "critical", + system_impact: str = "", + probable_cause: str = "", + ) -> dict: + """ + TYPE-8M Meta-System 告警 — 飛輪或告警鏈路自身健康異常。 + + 適用: FlywheelPlaybookZero / AlertChainBroken_* 等 + 按鈕: 固定 3 個([觸發診斷] [查看面板] [靜默]) + 只發個人 DM,不發群組(雙頻道路由規則)。 + """ + severity_emoji = "🔴" if severity_level == "critical" else "🟠" + category_label = "飛輪核心異常" if alert_category == "flywheel_health" else "告警鏈路異常" + + text = ( + f"⚙️ META SYSTEM | {severity_emoji} {category_label}\n" + f"━━━━━━━━━━━━━━━━━━━\n" + f"📋 {html.escape(incident_id)}\n" + f"🚨 異常元件:{html.escape(alertname)}\n" + f"🎯 診斷結果:{html.escape(diagnosis[:100])}\n" + ) + if system_impact: + text += f"\n🧠 系統影響\n{html.escape(system_impact[:150])}\n" + if probable_cause: + text += f"└─ 可能根因:{html.escape(probable_cause[:100])}\n" + + silence_nonce = self._security.generate_callback_nonce(approval_id, "silence") + keyboard = { + "inline_keyboard": [ + [ + {"text": "🔄 觸發診斷", "callback_data": f"action:flywheel_diag:{incident_id}"}, + {"text": "📊 查看面板", "callback_data": f"action:flywheel_dashboard:{incident_id}"}, + ], + [ + {"text": "🔕 靜默 1h", "callback_data": silence_nonce}, + ], + ] + } + + return await self._make_request( + "sendMessage", + { + "chat_id": settings.OPENCLAW_TG_CHAT_ID, + "text": text, + "parse_mode": "HTML", + "reply_markup": keyboard, + }, + ) + # ========================================================================= # 新訊息發送方法 (2026-03-29 ogt: ADR-038) # =========================================================================