fix(awooop): mirror telegram outbound messages
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 1m4s
CD Pipeline / build-and-deploy (push) Successful in 3m57s
CD Pipeline / post-deploy-checks (push) Successful in 1m27s

This commit is contained in:
Your Name
2026-05-07 00:23:32 +08:00
parent 012cd27b4a
commit 9365bdab93
4 changed files with 139 additions and 0 deletions

View File

@@ -29,6 +29,7 @@ import os
import re
from dataclasses import dataclass
from datetime import UTC, datetime
from uuid import NAMESPACE_URL, UUID, uuid5
import httpx
import structlog
@@ -79,6 +80,26 @@ def _is_noisy_failure_update(status_line: str) -> bool:
or "AI 診斷工具失敗" in status_line
)
def _legacy_outbound_run_id(chat_id: str, provider_message_id: str) -> UUID:
"""Legacy Telegram 發送尚未有 run_id 時,產生穩定 soft run_id 供 Channel Hub 串接。"""
return uuid5(NAMESPACE_URL, f"awoooi:legacy-telegram:{chat_id}:{provider_message_id}")
def _infer_outbound_message_type(text: str, payload: dict) -> str:
"""將既有 Telegram 訊息映射成 AwoooP outbound_message 的有限分類。"""
if "reply_to_message_id" in payload:
if "失敗" in text or "錯誤" in text or "FAILED" in text:
return "error"
return "final"
if payload.get("reply_markup"):
return "approval_request"
if "ACTION REQUIRED" in text or "待審" in text or "審批" in text:
return "approval_request"
if "失敗" in text or "錯誤" in text or "FAILED" in text:
return "error"
return "final"
# 2026-04-27 Claude Sonnet 4.6: B3 — LLM 動態 Telegram 按鈕 Feature Flag
# true → 優先使用 ActionPlan.recommended_actions 動態生成按鈕
# false → 維持現有 callback_action_spec.yaml 路徑(預設,向下相容)
@@ -1511,6 +1532,11 @@ class TelegramGateway:
result_val = result.get("result")
if isinstance(result_val, dict) and "message_id" in result_val:
span.set_attribute("telegram.message_id", result_val["message_id"])
await self._mirror_outbound_message(
method=method,
payload=payload,
provider_message_id=str(result_val["message_id"]),
)
span.set_status(trace.Status(trace.StatusCode.OK))
return result
@@ -1541,6 +1567,52 @@ class TelegramGateway:
)
raise TelegramGatewayError(safe_error) from None
async def _mirror_outbound_message(
self,
*,
method: str,
payload: dict,
provider_message_id: str,
) -> None:
"""將 legacy Telegram 出站訊息鏡像到 AwoooP不改變實際發送行為。"""
if method != "sendMessage":
return
chat_id = str(payload.get("chat_id") or "")
text = str(payload.get("text") or payload.get("caption") or "")
if not chat_id or not text:
return
try:
from src.core.context import get_current_project_id
from src.db.base import get_db_context
from src.services.channel_hub import record_outbound_message
project_id = get_current_project_id() or "awoooi"
run_id = _legacy_outbound_run_id(chat_id, provider_message_id)
async with get_db_context(project_id) as db:
await record_outbound_message(
db,
project_id=project_id,
run_id=run_id,
channel_type="telegram",
channel_chat_id=chat_id,
message_type=_infer_outbound_message_type(text, payload),
content=text,
provider_message_id=provider_message_id,
send_status="sent",
triggered_by_state="legacy_gateway",
is_shadow=False,
)
except Exception as exc:
logger.warning(
"telegram_outbound_mirror_failed",
method=method,
chat_id=chat_id,
provider_message_id=provider_message_id,
error=str(exc),
)
async def _build_inline_keyboard(
self,
approval_id: str,