From ecc65be6e1517e6444f5bd90a04a6cd5f04e9eb3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 7 May 2026 00:33:27 +0800 Subject: [PATCH] fix(telegram): route direct sends through gateway --- apps/api/src/services/ai_rate_limiter.py | 42 +++++++++---------- apps/api/src/services/approval_execution.py | 6 +-- .../src/services/post_execution_verifier.py | 6 +-- docs/LOGBOOK.md | 1 + .../TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md | 1 + 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/api/src/services/ai_rate_limiter.py b/apps/api/src/services/ai_rate_limiter.py index c4213557..0c02d030 100644 --- a/apps/api/src/services/ai_rate_limiter.py +++ b/apps/api/src/services/ai_rate_limiter.py @@ -274,14 +274,13 @@ class AIRateLimiter: try: from src.core.config import settings + from src.services.telegram_gateway import get_telegram_gateway target_chat_id = settings.SRE_GROUP_CHAT_ID or settings.OPENCLAW_TG_CHAT_ID if not settings.OPENCLAW_TG_BOT_TOKEN or not target_chat_id: logger.warning("telegram_not_configured_for_cost_alert") return - import httpx - message = ( f"🚨🚨🚨 AI 成本超限警報 🚨🚨🚨\n\n" f"Provider: {provider.upper()}\n" @@ -292,15 +291,15 @@ class AIRateLimiter: f"redis-cli DEL ai_rate:total_cost:{provider}" ) - async with httpx.AsyncClient(timeout=10.0) as client: - await client.post( - f"https://api.telegram.org/bot{settings.OPENCLAW_TG_BOT_TOKEN}/sendMessage", - json={ - "chat_id": target_chat_id, - "text": message, - "parse_mode": "HTML", - }, - ) + gateway = get_telegram_gateway() + await gateway._send_request( + "sendMessage", + { + "chat_id": target_chat_id, + "text": message, + "parse_mode": "HTML", + }, + ) logger.error( "ai_cost_alert_sent", @@ -327,13 +326,12 @@ class AIRateLimiter: try: from src.core.config import settings + from src.services.telegram_gateway import get_telegram_gateway target_chat_id = settings.SRE_GROUP_CHAT_ID or settings.OPENCLAW_TG_CHAT_ID if not settings.OPENCLAW_TG_BOT_TOKEN or not target_chat_id: return - import httpx - limit = COST_LIMITS[provider]["total_cost_usd"] remaining = limit - current_cost @@ -345,15 +343,15 @@ class AIRateLimiter: f"接近上限,請注意監控!" ) - async with httpx.AsyncClient(timeout=10.0) as client: - await client.post( - f"https://api.telegram.org/bot{settings.OPENCLAW_TG_BOT_TOKEN}/sendMessage", - json={ - "chat_id": target_chat_id, - "text": message, - "parse_mode": "HTML", - }, - ) + gateway = get_telegram_gateway() + await gateway._send_request( + "sendMessage", + { + "chat_id": target_chat_id, + "text": message, + "parse_mode": "HTML", + }, + ) logger.warning( "ai_cost_warning_sent", diff --git a/apps/api/src/services/approval_execution.py b/apps/api/src/services/approval_execution.py index 4952cf6e..1387e6a4 100644 --- a/apps/api/src/services/approval_execution.py +++ b/apps/api/src/services/approval_execution.py @@ -838,9 +838,9 @@ class ApprovalExecutionService: f"{km_info}" ) - await gateway._http_client.post( - f"https://api.telegram.org/bot{settings.OPENCLAW_TG_BOT_TOKEN}/sendMessage", - json={ + await gateway._send_request( + "sendMessage", + { "chat_id": target_chat_id, "text": text, "parse_mode": "HTML", diff --git a/apps/api/src/services/post_execution_verifier.py b/apps/api/src/services/post_execution_verifier.py index b32b55b0..25661759 100644 --- a/apps/api/src/services/post_execution_verifier.py +++ b/apps/api/src/services/post_execution_verifier.py @@ -350,9 +350,9 @@ async def _send_rollback_proposal_alert( ) target_chat_id = _settings.SRE_GROUP_CHAT_ID or _settings.OPENCLAW_TG_CHAT_ID - await gateway._http_client.post( - f"https://api.telegram.org/bot{_settings.OPENCLAW_TG_BOT_TOKEN}/sendMessage", - json={ + await gateway._send_request( + "sendMessage", + { "chat_id": target_chat_id, "text": text, "parse_mode": "HTML", diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index e66c5b7d..aeddc943 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -4399,6 +4399,7 @@ HTTP: - 沒有正式 `run_id` 的 legacy 發送使用穩定 soft run id:`awoooi:legacy-telegram:{chat_id}:{provider_message_id}` 的 UUIDv5。 - legacy 訊息先映射成有限分類:`approval_request`、`final`、`error`,供 AwoooP Run / Timeline 後續彙整。 - 鏡像採 fail-open:DB / RLS / schema 或 Channel Hub 失敗時只寫 `telegram_outbound_mirror_failed`,不得影響 Telegram 原本送達。 +- 成本告警、審批執行結果、自愈 rollback 提案三條 direct Bot API 發送改為走 `TelegramGateway._send_request()`,發送目標與內容不變,但會進 outbound mirror。 ### 驗證 diff --git a/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md b/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md index 1107c0f4..107d3436 100644 --- a/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md +++ b/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md @@ -39,6 +39,7 @@ Telegram 不應是完整執行日誌,也不應承載所有 AI 推理細節。T - `append_incident_update()` 對同一 incident 的相同狀態做 5 分鐘 Redis 去重。 - `append_incident_update()` 對相同的「AI 自動修復失敗 / AI 診斷工具失敗」摘要增加 10 分鐘跨 incident 去重;每個 incident 仍會移除原卡危險按鈕,但 Telegram 不再重複 reply 同一個失敗摘要。 - `TelegramGateway._send_request()` 對成功送出的 legacy `sendMessage` 增加 AwoooP `awooop_outbound_message` 鏡像。鏡像失敗只記錄 `telegram_outbound_mirror_failed`,不能影響 Telegram 正常送達。 +- 成本告警、審批執行結果、自愈 rollback 提案已由 direct Bot API 改走 `TelegramGateway._send_request()`,避免繞過 outbound mirror。 - 既有 `詳情 / 重診 / 歷史` 按鈕保留,讓 Telegram 保持輕量,細節回到控制台。 ## 後續建議