fix(telegram): handle expired callback queries gracefully

Parse Telegram HTTP error bodies so the real description is logged, fall back to a normal message when answerCallbackQuery rejects an expired callback ID, and remove inline buttons after a successful action.
This commit is contained in:
kairee committed 2026-10-31 21:59:04 +08:00
1 parent e2e4dc3336
commit 2d42d8ed55
3 files changed
+96 -8

No files matched your search

+28 -2
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import html
import json
import urllib.error
import urllib.parse
import urllib.request
from typing import Any, Mapping, Protocol, Sequence
@@ -27,8 +28,18 @@ class UrllibTelegramTransport:
data=urllib.parse.urlencode(data).encode("utf-8"),
method="POST",
)
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.loads(response.read().decode("utf-8", "replace"))
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.loads(response.read().decode("utf-8", "replace"))
except urllib.error.HTTPError as exc:
# Telegram 对业务错误也返回 4xx + JSON 描述;保留描述便于定位。
body = exc.read().decode("utf-8", "replace")
try:
return json.loads(body)
except json.JSONDecodeError as parse_error:
raise TelegramError(
f"Telegram HTTP {exc.code}: {body[:200]}"
) from parse_error
def format_duration(seconds: int) -> str:
@@ -142,3 +153,18 @@ class TelegramClient:
"answerCallbackQuery",
{"callback_query_id": callback_id, "text": text[:200]},
)
def edit_reply_markup(
self, message_id: int, *, reply_markup: Mapping[str, Any] | None = None
) -> Any:
"""移除或替换原消息的按钮;空 keyboard 表示移除。"""
return self._call(
"editMessageReplyMarkup",
{
"chat_id": self.chat_id,
"message_id": int(message_id),
"reply_markup": json.dumps(
reply_markup or {"inline_keyboard": []}, ensure_ascii=False
),
},
)