fix(telegram): land #31067 first-chunk voice-fallback reply refs (@xdanger)

Landed from contributor PR #31067 by @xdanger.

Co-authored-by: Kros Dai <xdanger@gmail.com>
This commit is contained in:
Peter Steinberger
2026-03-02 03:49:16 +00:00
parent 61ef76edb5
commit ede944371f
3 changed files with 21 additions and 3 deletions

View File

@@ -114,8 +114,10 @@ Docs: https://docs.openclaw.ai
- Signal/Sync message null-handling: treat `syncMessage` presence (including `null`) as sync envelope traffic so replayed sentTranscript payloads cannot bypass loop guards after daemon restart. Landed from contributor PR #31138 by @Sid-Qin. Thanks @Sid-Qin. - Signal/Sync message null-handling: treat `syncMessage` presence (including `null`) as sync envelope traffic so replayed sentTranscript payloads cannot bypass loop guards after daemon restart. Landed from contributor PR #31138 by @Sid-Qin. Thanks @Sid-Qin.
- Inbound metadata/Multi-account routing: include `account_id` in trusted inbound metadata so multi-account channel sessions can reliably disambiguate the receiving account in prompt context. Landed from contributor PR #30984 by @Stxle2. Thanks @Stxle2. - Inbound metadata/Multi-account routing: include `account_id` in trusted inbound metadata so multi-account channel sessions can reliably disambiguate the receiving account in prompt context. Landed from contributor PR #30984 by @Stxle2. Thanks @Stxle2.
- Web tools/RFC2544 fake-IP compatibility: allow RFC2544 benchmark range (`198.18.0.0/15`) for trusted web-tool fetch endpoints so proxy fake-IP networking modes do not trigger false SSRF blocks. Landed from contributor PR #31176 by @sunkinux. Thanks @sunkinux. - Web tools/RFC2544 fake-IP compatibility: allow RFC2544 benchmark range (`198.18.0.0/15`) for trusted web-tool fetch endpoints so proxy fake-IP networking modes do not trigger false SSRF blocks. Landed from contributor PR #31176 by @sunkinux. Thanks @sunkinux.
- Telegram/Voice fallback reply chunking: apply reply reference, quote text, and inline buttons only to the first fallback text chunk when voice delivery is blocked, preventing over-quoted multi-chunk replies. Landed from contributor PR #31067 by @xdanger. Thanks @xdanger.
- Feishu/System preview prompt leakage: stop enqueuing inbound Feishu message previews as system events so user preview text is not injected into later turns as trusted `System:` context. Landed from contributor PR #31209 by @stakeswky. Thanks @stakeswky. - Feishu/System preview prompt leakage: stop enqueuing inbound Feishu message previews as system events so user preview text is not injected into later turns as trusted `System:` context. Landed from contributor PR #31209 by @stakeswky. Thanks @stakeswky.
- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #29610, #30432, #30331, and #29501. Thanks @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff. - Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #29610, #30432, #30331, and #29501. Thanks @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff.
## Unreleased ## Unreleased
### Changes ### Changes

View File

@@ -459,20 +459,35 @@ describe("deliverReplies", () => {
text: "chunk-one\n\nchunk-two", text: "chunk-one\n\nchunk-two",
replyToId: "77", replyToId: "77",
audioAsVoice: true, audioAsVoice: true,
channelData: {
telegram: {
buttons: [[{ text: "Ack", callback_data: "ack" }]],
},
},
}, },
], ],
runtime, runtime,
bot, bot,
replyToMode: "first", replyToMode: "first",
replyQuoteText: "quoted context",
textLimit: 12, textLimit: 12,
}); });
expect(sendVoice).toHaveBeenCalledTimes(1); expect(sendVoice).toHaveBeenCalledTimes(1);
expect(sendMessage.mock.calls.length).toBeGreaterThanOrEqual(2); expect(sendMessage.mock.calls.length).toBeGreaterThanOrEqual(2);
expect(sendMessage.mock.calls[0][2]).toEqual( expect(sendMessage.mock.calls[0][2]).toEqual(
expect.objectContaining({
reply_to_message_id: 77,
reply_markup: {
inline_keyboard: [[{ text: "Ack", callback_data: "ack" }]],
},
}),
);
expect(sendMessage.mock.calls[1][2]).not.toEqual(
expect.objectContaining({ reply_to_message_id: 77 }), expect.objectContaining({ reply_to_message_id: 77 }),
); );
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_to_message_id"); expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_parameters");
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_markup");
}); });
it("rethrows non-VOICE_MESSAGES_FORBIDDEN errors from sendVoice", async () => { it("rethrows non-VOICE_MESSAGES_FORBIDDEN errors from sendVoice", async () => {

View File

@@ -682,15 +682,16 @@ async function sendTelegramVoiceFallbackText(opts: {
let appliedReplyTo = false; let appliedReplyTo = false;
for (let i = 0; i < chunks.length; i += 1) { for (let i = 0; i < chunks.length; i += 1) {
const chunk = chunks[i]; const chunk = chunks[i];
// Only apply reply reference, quote text, and buttons to the first chunk.
const replyToForChunk = !appliedReplyTo ? opts.replyToId : undefined; const replyToForChunk = !appliedReplyTo ? opts.replyToId : undefined;
await sendTelegramText(opts.bot, opts.chatId, chunk.html, opts.runtime, { await sendTelegramText(opts.bot, opts.chatId, chunk.html, opts.runtime, {
replyToMessageId: replyToForChunk, replyToMessageId: replyToForChunk,
replyQuoteText: opts.replyQuoteText, replyQuoteText: !appliedReplyTo ? opts.replyQuoteText : undefined,
thread: opts.thread, thread: opts.thread,
textMode: "html", textMode: "html",
plainText: chunk.text, plainText: chunk.text,
linkPreview: opts.linkPreview, linkPreview: opts.linkPreview,
replyMarkup: i === 0 ? opts.replyMarkup : undefined, replyMarkup: !appliedReplyTo ? opts.replyMarkup : undefined,
}); });
if (replyToForChunk) { if (replyToForChunk) {
appliedReplyTo = true; appliedReplyTo = true;