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) # =========================================================================