mirror of https://github.com/openclaw/openclaw.git
fix(telegram): fallback to plain text when threaded markdown renders empty
Minimal fix path for Telegram empty-text failures in threaded replies. - fallback to plain text when formatted htmlText is empty - retry plain text on parse/empty-text API errors - add focused regression test for threaded mode case Related: #25091 Supersedes alternative fix path in #17629 if maintainers prefer minimal scope.
This commit is contained in:
parent
00de3ca833
commit
51b3e23680
|
|
@ -244,6 +244,40 @@ describe("deliverReplies", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("falls back to plain text when markdown renders to empty HTML in threaded mode", async () => {
|
||||
const runtime = { error: vi.fn(), log: vi.fn() };
|
||||
const sendMessage = vi.fn(async (_chatId: string, text: string) => {
|
||||
if (text === "") {
|
||||
throw new Error("400: Bad Request: message text is empty");
|
||||
}
|
||||
return {
|
||||
message_id: 6,
|
||||
chat: { id: "123" },
|
||||
};
|
||||
});
|
||||
const bot = { api: { sendMessage } } as unknown as Bot;
|
||||
|
||||
await deliverReplies({
|
||||
replies: [{ text: ">" }],
|
||||
chatId: "123",
|
||||
token: "tok",
|
||||
runtime,
|
||||
bot,
|
||||
replyToMode: "off",
|
||||
textLimit: 4000,
|
||||
thread: { id: 42, scope: "forum" },
|
||||
});
|
||||
|
||||
expect(sendMessage).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessage).toHaveBeenCalledWith(
|
||||
"123",
|
||||
">",
|
||||
expect.objectContaining({
|
||||
message_thread_id: 42,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("uses reply_to_message_id when quote text is provided", async () => {
|
||||
const runtime = createRuntime();
|
||||
const sendMessage = vi.fn().mockResolvedValue({
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ const TELEGRAM_MEDIA_SSRF_POLICY = {
|
|||
allowedHostnames: ["api.telegram.org"],
|
||||
allowRfc2544BenchmarkRange: true,
|
||||
};
|
||||
const EMPTY_TEXT_ERR_RE = /message text is empty/i;
|
||||
|
||||
export async function deliverReplies(params: {
|
||||
replies: ReplyPayload[];
|
||||
|
|
@ -553,6 +554,30 @@ async function sendTelegramText(
|
|||
const linkPreviewOptions = linkPreviewEnabled ? undefined : { is_disabled: true };
|
||||
const textMode = opts?.textMode ?? "markdown";
|
||||
const htmlText = textMode === "html" ? text : markdownToTelegramHtml(text);
|
||||
const fallbackText = opts?.plainText ?? text;
|
||||
const hasFallbackText = fallbackText.trim().length > 0;
|
||||
const sendPlainFallback = async () => {
|
||||
if (!hasFallbackText) {
|
||||
return undefined;
|
||||
}
|
||||
const res = await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
fn: () =>
|
||||
bot.api.sendMessage(chatId, fallbackText, {
|
||||
...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}),
|
||||
...(opts?.replyMarkup ? { reply_markup: opts.replyMarkup } : {}),
|
||||
...baseParams,
|
||||
}),
|
||||
});
|
||||
return res.message_id;
|
||||
};
|
||||
|
||||
// Markdown can occasionally render to empty HTML (for example syntax-only chunks).
|
||||
// Telegram rejects those sends, so fall back to plain text early.
|
||||
if (!htmlText.trim()) {
|
||||
return await sendPlainFallback();
|
||||
}
|
||||
try {
|
||||
const res = await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
|
|
@ -570,21 +595,9 @@ async function sendTelegramText(
|
|||
return res.message_id;
|
||||
} catch (err) {
|
||||
const errText = formatErrorMessage(err);
|
||||
if (PARSE_ERR_RE.test(errText)) {
|
||||
runtime.log?.(`telegram HTML parse failed; retrying without formatting: ${errText}`);
|
||||
const fallbackText = opts?.plainText ?? text;
|
||||
const res = await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
runtime,
|
||||
fn: () =>
|
||||
bot.api.sendMessage(chatId, fallbackText, {
|
||||
...(linkPreviewOptions ? { link_preview_options: linkPreviewOptions } : {}),
|
||||
...(opts?.replyMarkup ? { reply_markup: opts.replyMarkup } : {}),
|
||||
...baseParams,
|
||||
}),
|
||||
});
|
||||
runtime.log?.(`telegram sendMessage ok chat=${chatId} message=${res.message_id} (plain)`);
|
||||
return res.message_id;
|
||||
if (PARSE_ERR_RE.test(errText) || EMPTY_TEXT_ERR_RE.test(errText)) {
|
||||
runtime.log?.(`telegram formatted send failed; retrying without formatting: ${errText}`);
|
||||
return await sendPlainFallback();
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue