fix(telegram): deduplicate repeated failure updates
All checks were successful
Code Review / ai-code-review (push) Successful in 10s
CD Pipeline / tests (push) Successful in 1m4s
CD Pipeline / build-and-deploy (push) Successful in 3m47s
CD Pipeline / post-deploy-checks (push) Successful in 1m20s

This commit is contained in:
Your Name
2026-05-06 22:36:44 +08:00
parent 886657473e
commit c5964fbcd3
4 changed files with 133 additions and 4 deletions

View File

@@ -53,6 +53,8 @@ SNOOZE_TTL_SECONDS = 30 * 60 # 30 分鐘
SILENCE_TTL_SECONDS = 60 * 60 # 1 小時
INCIDENT_UPDATE_DEDUP_PREFIX = "awoooi:tg_update_dedup:" # {incident_id}:{status_hash}
INCIDENT_UPDATE_DEDUP_TTL_SECONDS = 5 * 60 # 5 分鐘內相同狀態不重複洗版
INCIDENT_UPDATE_GLOBAL_FAILURE_DEDUP_PREFIX = "awoooi:tg_update_global_failure_dedup:"
INCIDENT_UPDATE_GLOBAL_FAILURE_DEDUP_TTL_SECONDS = 10 * 60 # 相同失敗摘要跨 incident 10 分鐘只推一次
# 2026-04-01 Claude Code: Long Polling 分散式 Leader Election
# 防止多 Pod 同時 getUpdates → 409 Conflict 互搶問題
@@ -69,6 +71,14 @@ def _sanitize_telegram_error(text: str) -> str:
"""遮蔽 Telegram Bot URL 中的 token避免例外字串污染 log / trace。"""
return _TELEGRAM_BOT_URL_RE.sub(r"\1<redacted>", text)
def _is_noisy_failure_update(status_line: str) -> bool:
"""判斷是否屬於容易跨 incident 洗版的失敗摘要。"""
return (
"AI 自動修復失敗" in status_line
or "AI 診斷工具失敗" in status_line
)
# 2026-04-27 Claude Sonnet 4.6: B3 — LLM 動態 Telegram 按鈕 Feature Flag
# true → 優先使用 ActionPlan.recommended_actions 動態生成按鈕
# false → 維持現有 callback_action_spec.yaml 路徑(預設,向下相容)
@@ -4539,6 +4549,33 @@ class TelegramGateway:
error=str(exc),
)
suppress_reply = False
if _is_noisy_failure_update(status_line):
# 不同 incident 若卡在同一個自動修復/診斷失敗摘要Telegram 只推第一則;
# 每個 incident 仍會繼續移除原卡危險按鈕,完整細節交給 timeline / AwoooP。
global_hash = hashlib.sha1(status_line.encode("utf-8")).hexdigest()[:16]
global_dedup_key = f"{INCIDENT_UPDATE_GLOBAL_FAILURE_DEDUP_PREFIX}{global_hash}"
try:
was_global_set = await redis.set(
global_dedup_key,
incident_id,
ex=INCIDENT_UPDATE_GLOBAL_FAILURE_DEDUP_TTL_SECONDS,
nx=True,
)
suppress_reply = not bool(was_global_set)
if suppress_reply:
logger.info(
"append_incident_update_global_failure_dedup_suppressed",
incident_id=incident_id,
dedup_key=global_dedup_key,
)
except Exception as exc:
logger.warning(
"append_incident_update_global_failure_dedup_failed",
incident_id=incident_id,
error=str(exc),
)
# Step 1: 取得原始訊息文字Telegram Bot API 不提供讀取原文,只能在 editMessageText 裡重建)
# 策略: 只追加 status_line不讀取原文Telegram edit 要傳完整新文字)
# 所以先用 editMessageReplyMarkup 換按鈕,再 sendMessage 同 chat 以 reply 方式追加狀態
@@ -4565,6 +4602,9 @@ class TelegramGateway:
except TelegramGatewayError as e:
logger.warning("append_incident_update_edit_buttons_failed", message_id=message_id, error=str(e))
if suppress_reply:
return True
# Step 2: Reply 原訊息追加狀態(保留原文不動,以 reply 方式延續)
try:
await self._send_request("sendMessage", {