fix(telegram): preserve incident history html output
This commit is contained in:
@@ -70,6 +70,7 @@ logger = structlog.get_logger(__name__)
|
||||
_TELEGRAM_BOT_URL_RE = re.compile(r"(api\.telegram\.org/bot)[^/\s]+")
|
||||
_INCIDENT_ID_RE = re.compile(r"\bINC-\d{8}-[A-Z0-9]{4,}\b")
|
||||
_CODE_REF_RE = re.compile(r"<code>([0-9a-f]{7,12})</code>", re.IGNORECASE)
|
||||
_TELEGRAM_HTML_CHUNK_LIMIT = 3600
|
||||
|
||||
|
||||
def _top_gateway_bucket(
|
||||
@@ -195,6 +196,34 @@ def _format_remediation_history_lines(history: dict[str, object] | None) -> list
|
||||
]
|
||||
|
||||
|
||||
def _telegram_html_chunks(lines: list[str], limit: int = _TELEGRAM_HTML_CHUNK_LIMIT) -> list[str]:
|
||||
"""Split HTML messages by complete lines so Telegram does not receive broken tags."""
|
||||
chunks: list[str] = []
|
||||
current: list[str] = []
|
||||
current_len = 0
|
||||
for raw_line in lines:
|
||||
line = str(raw_line)
|
||||
line_len = len(line) + 1
|
||||
if current and current_len + line_len > limit:
|
||||
chunks.append("\n".join(current))
|
||||
current = []
|
||||
current_len = 0
|
||||
if line_len > limit:
|
||||
chunks.append(line[:limit])
|
||||
continue
|
||||
current.append(line)
|
||||
current_len += line_len
|
||||
if current:
|
||||
chunks.append("\n".join(current))
|
||||
return chunks
|
||||
|
||||
|
||||
def _plain_text_from_html(text: str, limit: int = 3900) -> str:
|
||||
"""Fallback renderer for Telegram HTML parse failures."""
|
||||
plain = re.sub(r"</?[^>]+>", "", text)
|
||||
return html.unescape(plain)[:limit]
|
||||
|
||||
|
||||
def _sanitize_telegram_error(text: str) -> str:
|
||||
"""遮蔽 Telegram Bot URL 中的 token,避免例外字串污染 log / trace。"""
|
||||
return _TELEGRAM_BOT_URL_RE.sub(r"\1<redacted>", text)
|
||||
@@ -5366,7 +5395,10 @@ class TelegramGateway:
|
||||
error=str(remediation_exc),
|
||||
)
|
||||
|
||||
await self.send_notification("\n".join(lines))
|
||||
await self._send_html_line_message(
|
||||
lines,
|
||||
failure_context="incident_detail",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning("send_incident_detail_failed", incident_id=incident_id, error=str(e))
|
||||
@@ -5501,7 +5533,10 @@ class TelegramGateway:
|
||||
error=str(truth_exc),
|
||||
)
|
||||
|
||||
await self.send_notification("\n".join(lines))
|
||||
await self._send_html_line_message(
|
||||
lines,
|
||||
failure_context="incident_history",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning("send_incident_history_failed", incident_id=incident_id, error=str(e))
|
||||
@@ -5714,6 +5749,41 @@ class TelegramGateway:
|
||||
|
||||
return await self._send_request("sendMessage", payload)
|
||||
|
||||
async def _send_html_line_message(
|
||||
self,
|
||||
lines: list[str],
|
||||
*,
|
||||
chat_id: str | int | None = None,
|
||||
failure_context: str,
|
||||
) -> None:
|
||||
"""Send a multi-line HTML message without cutting Telegram tags in half."""
|
||||
chunks = _telegram_html_chunks(lines)
|
||||
for index, chunk in enumerate(chunks):
|
||||
try:
|
||||
await self._send_request(
|
||||
"sendMessage",
|
||||
{
|
||||
"chat_id": chat_id or self.alert_chat_id,
|
||||
"text": chunk,
|
||||
"parse_mode": "HTML",
|
||||
},
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"telegram_html_line_message_failed",
|
||||
failure_context=failure_context,
|
||||
chunk_index=index,
|
||||
chunk_count=len(chunks),
|
||||
error=str(exc),
|
||||
)
|
||||
await self._send_request(
|
||||
"sendMessage",
|
||||
{
|
||||
"chat_id": chat_id or self.alert_chat_id,
|
||||
"text": _plain_text_from_html(chunk),
|
||||
},
|
||||
)
|
||||
|
||||
async def send_alert_notification(
|
||||
self,
|
||||
text: str,
|
||||
|
||||
Reference in New Issue
Block a user