diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index eea36f89..0c1abb7c 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -1376,16 +1376,26 @@ class TelegramGateway: alert_category: 告警類別 (ADR-071-E: 決定 TYPE-3 按鈕組合) notification_type: 通知類型 (TYPE-1/2/3/4/4D) """ - # 2026-04-14 Claude Sonnet 4.6 首席架構師裁示: - # 原 _CATEGORY_BUTTONS (28 個分類按鈕) 全部下架 — 信任 > 功能 - # 盤查證據(統帥 2026-04-14 20:00 完整 audit): - # - callback_data 格式全錯 (3-part action:xxx:id 不符合 parser 4-part nonce / 2-part info) - # - 後端 0 個 handler(grep apps/api/src 無任何 dispatch elif 分支) - # - 從 2026-04-11 commit 325b3851 起死了 3 天 - # - 統帥今天真踩到:點「查程序」沒反應 → 信任破壞 - # 正式下架改用通用按鈕 (approve/reject/silence/detail/history/reanalyze) - # 補完計畫:MASTER 藍圖 Phase 5「分類按鈕完整化」(另立 ADR) - _CATEGORY_BUTTONS: dict[str, list[tuple[str, str]]] = {} # 臨時空,fallback 通用按鈕 + # 2026-04-14 Claude Sonnet 4.6 (Phase 5 Sprint 5.4): + # 從 callback_action_spec registry 動態產生按鈕(原 _CATEGORY_BUTTONS hardcode 已下架) + # 優點:新增按鈕只需改 yaml,callback_data 格式由 spec.callback_format 決定 + # 安全:yaml 裡每個 action 都有對應 MCP dispatcher handler(Sprint 5.2/5.3 實作) + from src.services.callback_dispatcher import list_actions_for_category as _list_actions + + def _build_category_buttons_for(category: str) -> list[tuple[str, str]]: + """從 registry 產生 (label, callback_data) list""" + actions = _list_actions(category) + btns: list[tuple[str, str]] = [] + for spec_it in actions: + emoji_label = f"{spec_it.emoji} {spec_it.label}".strip() + if spec_it.callback_format == "nonce": + # 寫類:產生 4-part nonce + cb = self._security.generate_callback_nonce(approval_id, spec_it.name) + else: + # 查類:2-part info + cb = f"{spec_it.name}:{incident_id}" + btns.append((emoji_label, cb)) + return btns # 產生 Nonce (防重放,用於寫操作) approve_nonce = self._security.generate_callback_nonce(approval_id, "approve") @@ -1394,11 +1404,13 @@ class TelegramGateway: is_type3 = notification_type in ("TYPE-3", NotificationType.TYPE_3, "") - if is_type3 and alert_category and alert_category in _CATEGORY_BUTTONS: + _dynamic_buttons = _build_category_buttons_for(alert_category) if alert_category else [] + + if is_type3 and _dynamic_buttons: # TYPE-3 動態操作按鈕:第一行為類別專屬操作 category_btns = [ {"text": text, "callback_data": cb_data} - for text, cb_data in _CATEGORY_BUTTONS[alert_category] + for text, cb_data in _dynamic_buttons ] # 每行最多 3 個,超過換行 rows = [category_btns[i:i+3] for i in range(0, len(category_btns), 3)]