fix(telegram): link incident truth chain from alerts
Some checks failed
CD Pipeline / tests (push) Successful in 1m35s
Code Review / ai-code-review (push) Successful in 12s
CD Pipeline / build-and-deploy (push) Successful in 4m37s
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
Your Name
2026-05-31 23:08:01 +08:00
parent 920488c5ff
commit 356e4d41cc
4 changed files with 151 additions and 40 deletions

View File

@@ -27,7 +27,6 @@ import asyncio
import html
import time
from typing import TYPE_CHECKING, Any
from urllib.parse import quote
from uuid import UUID
import structlog
@@ -40,9 +39,10 @@ from src.plugins.mcp.gateway import GatewayContext, McpGateway, McpGatewayError
from src.plugins.mcp.interfaces import MCPToolResult
from src.services.approval_action_classifier import is_no_action_approval_action
from src.services.approval_db import get_approval_service, get_timeline_service
from src.services.awooop_deeplinks import incident_truth_chain_button_row
from src.services.executor import ExecutionResult, OperationType, get_executor
from src.services.operator_outcome import build_operator_outcome
from src.services.operation_parser import parse_operation_from_action
from src.services.operator_outcome import build_operator_outcome
if TYPE_CHECKING:
from src.services.notifications import ExecutionStatus
@@ -1046,24 +1046,19 @@ class ApprovalExecutionService:
km_info=km_info,
outcome=outcome,
)
reply_markup = {
"inline_keyboard": [[
{
"text": "🧭 AwoooP",
"url": (
"https://awoooi.wooo.work/zh-TW/awooop/runs"
f"?project_id=awoooi&incident_id={quote(approval.incident_id or '')}"
),
}
]]
}
truth_chain_row = incident_truth_chain_button_row(
approval.incident_id or ""
)
payload: dict[str, Any] = {
"chat_id": target_chat_id,
"text": text,
"parse_mode": "HTML",
"reply_markup": reply_markup,
"disable_web_page_preview": True,
}
if truth_chain_row:
payload["reply_markup"] = {
"inline_keyboard": [truth_chain_row],
}
if orig_msg_id is not None:
payload["reply_to_message_id"] = orig_msg_id

View File

@@ -0,0 +1,63 @@
"""Shared AwoooP operator deeplinks used by Telegram-facing services."""
from urllib.parse import quote
AWOOOP_WEB_BASE_URL = "https://awoooi.wooo.work"
def incident_runs_url(
incident_id: str,
*,
project_id: str = "awoooi",
locale: str = "zh-TW",
) -> str:
safe_project_id = quote(str(project_id or "awoooi"), safe="")
safe_incident_id = quote(str(incident_id or ""), safe="")
return (
f"{AWOOOP_WEB_BASE_URL}/{locale}/awooop/runs"
f"?project_id={safe_project_id}&incident_id={safe_incident_id}"
)
def incident_alerts_url(
incident_id: str,
*,
project_id: str = "awoooi",
locale: str = "zh-TW",
) -> str:
safe_project_id = quote(str(project_id or "awoooi"), safe="")
safe_incident_id = quote(str(incident_id or ""), safe="")
return (
f"{AWOOOP_WEB_BASE_URL}/{locale}/alerts"
f"?project_id={safe_project_id}&incident_id={safe_incident_id}"
)
def incident_truth_chain_button_row(
incident_id: str,
*,
project_id: str = "awoooi",
) -> list[dict[str, str]]:
if not incident_id:
return []
return [
{
"text": "🔎 真相鏈",
"url": incident_alerts_url(incident_id, project_id=project_id),
},
{
"text": "🧭 Runs",
"url": incident_runs_url(incident_id, project_id=project_id),
},
]
def incident_truth_chain_reply_markup(
incident_id: str,
*,
project_id: str = "awoooi",
) -> dict | None:
row = incident_truth_chain_button_row(incident_id, project_id=project_id)
if not row:
return None
return {"inline_keyboard": [row]}

View File

@@ -30,7 +30,6 @@ import os
import re
from dataclasses import dataclass
from datetime import UTC, datetime
from urllib.parse import quote
from uuid import NAMESPACE_URL, UUID, uuid5
import httpx
@@ -40,13 +39,19 @@ from opentelemetry import trace
from src.core.config import settings
from src.core.redis_client import get_redis
from src.services.awooop_ansible_audit_service import summarize_ansible_execution
from src.services.awooop_deeplinks import (
incident_alerts_url,
incident_runs_url,
incident_truth_chain_button_row,
incident_truth_chain_reply_markup,
)
from src.services.chat_manager import get_chat_manager
from src.services.operator_outcome import build_operator_outcome
from src.services.security_interceptor import (
NonceReplayError,
UserNotWhitelistedError,
get_security_interceptor,
)
from src.services.chat_manager import get_chat_manager
# =============================================================================
# Snooze/Silence Redis Keys (2026-03-27 P1 優化)
@@ -78,7 +83,6 @@ _AI_ADVISORY_CALLBACK_RE = re.compile(
)
_CODE_REF_RE = re.compile(r"<code>([0-9a-f]{7,12})</code>", re.IGNORECASE)
_TELEGRAM_HTML_CHUNK_LIMIT = 3600
_AWOOOP_WEB_BASE_URL = "https://awoooi.wooo.work"
_AWOOOP_SOURCE_ENVELOPE_EXTRA_KEY = "_awooop_source_envelope_extra"
@@ -249,27 +253,32 @@ def _format_remediation_history_lines(history: dict[str, object] | None) -> list
def _awooop_runs_url_for_incident(incident_id: str) -> str:
safe_incident_id = quote(str(incident_id or ""), safe="")
return (
f"{_AWOOOP_WEB_BASE_URL}/zh-TW/awooop/runs"
f"?project_id=awoooi&incident_id={safe_incident_id}"
)
return incident_runs_url(incident_id)
def _awooop_alerts_url_for_incident(incident_id: str) -> str:
return incident_alerts_url(incident_id)
def _awooop_runs_button_row(incident_id: str) -> list[dict[str, str]]:
if not incident_id:
return []
return [{
"text": "🧭 AwoooP",
"text": "🧭 Runs",
"url": _awooop_runs_url_for_incident(incident_id),
}]
def _awooop_truth_chain_button_row(incident_id: str) -> list[dict[str, str]]:
return incident_truth_chain_button_row(incident_id)
def _awooop_truth_chain_reply_markup(incident_id: str) -> dict | None:
return incident_truth_chain_reply_markup(incident_id)
def _awooop_runs_reply_markup(incident_id: str) -> dict | None:
row = _awooop_runs_button_row(incident_id)
if not row:
return None
return {"inline_keyboard": [row]}
return _awooop_truth_chain_reply_markup(incident_id)
def _latest_remediation_history_item(history: dict[str, object] | None) -> dict[str, object]:
@@ -3578,7 +3587,7 @@ class TelegramGateway:
tuning_nonce = self._security.generate_callback_nonce(approval_id, "tune")
buttons.append([{"text": "⚡ 執行自動調優", "callback_data": tuning_nonce}])
awooop_row = _awooop_runs_button_row(incident_id)
awooop_row = _awooop_truth_chain_button_row(incident_id)
if awooop_row:
buttons.append(awooop_row)
@@ -3627,7 +3636,7 @@ class TelegramGateway:
{"text": "📋 詳情", "callback_data": f"detail:{incident_id}"},
{"text": "🔕 忽略", "callback_data": silence_nonce},
])
awooop_row = _awooop_runs_button_row(incident_id)
awooop_row = _awooop_truth_chain_button_row(incident_id)
if awooop_row:
rows.append(awooop_row)
buttons = rows
@@ -3647,7 +3656,7 @@ class TelegramGateway:
{"text": "🔄 重診", "callback_data": f"reanalyze:{incident_id}"},
{"text": "📊 歷史", "callback_data": f"history:{incident_id}"},
])
awooop_row = _awooop_runs_button_row(incident_id)
awooop_row = _awooop_truth_chain_button_row(incident_id)
if awooop_row:
buttons.append(awooop_row)
@@ -4242,7 +4251,7 @@ class TelegramGateway:
{"text": "📋 詳情", "callback_data": f"detail:{incident_id}"},
{"text": "📊 歷史", "callback_data": f"history:{incident_id}"},
]]
awooop_row = _awooop_runs_button_row(incident_id)
awooop_row = _awooop_truth_chain_button_row(incident_id)
if awooop_row:
inline_keyboard.append(awooop_row)
keyboard = {"inline_keyboard": inline_keyboard}
@@ -6628,7 +6637,7 @@ class TelegramGateway:
{"text": "📊 歷史", "callback_data": f"history:{incident_id}"},
],
]
awooop_row = _awooop_runs_button_row(incident_id)
awooop_row = _awooop_truth_chain_button_row(incident_id)
if awooop_row:
inline_keyboard.append(awooop_row)
new_keyboard = {"inline_keyboard": inline_keyboard}
@@ -6997,7 +7006,7 @@ class TelegramGateway:
await self._send_html_line_message(
lines,
failure_context="incident_detail",
reply_markup=_awooop_runs_reply_markup(incident_id),
reply_markup=_awooop_truth_chain_reply_markup(incident_id),
incident_id=incident_id,
callback_action="detail",
km_stale_completion_summary=km_completion_summary,
@@ -7182,7 +7191,7 @@ class TelegramGateway:
await self._send_html_line_message(
lines,
failure_context="incident_history",
reply_markup=_awooop_runs_reply_markup(incident_id),
reply_markup=_awooop_truth_chain_reply_markup(incident_id),
incident_id=incident_id,
callback_action="history",
km_stale_completion_summary=km_completion_summary,
@@ -8580,7 +8589,7 @@ class TelegramGateway:
{"text": "📋 詳情", "callback_data": f"detail:{incident_id}"},
{"text": "📊 歷史", "callback_data": f"history:{incident_id}"},
]]
awooop_row = _awooop_runs_button_row(incident_id)
awooop_row = _awooop_truth_chain_button_row(incident_id)
if awooop_row:
info_buttons.append(awooop_row)
await self._send_request(