Skip to content

Commit 6237cfc

Browse files
committed
fix: finish telegram reply fallback landing (openclaw#52524) (thanks @moltbot886)
1 parent b12dc4d commit 6237cfc

File tree

6 files changed

+55
-9
lines changed

6 files changed

+55
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ Docs: https://docs.openclaw.ai
103103
- Gateway/restart: defer externally signaled unmanaged restarts through the in-process idle drain, and preserve the restored subagent run as remap fallback during orphan recovery so resumed sessions do not duplicate work. (#47719) Thanks @joeykrug.
104104
- Control UI/session routing: preserve established external delivery routes when webchat views or sends in externally originated sessions, so subagent completions still return to the original channel instead of the dashboard. (#47797) Thanks @brokemac79.
105105
- Configure/startup: move outbound send-deps resolution into a lightweight helper so `openclaw configure` no longer stalls after the banner while eagerly loading channel plugins. (#46301) Thanks @scoootscooob.
106+
- Mattermost/threading: honor `replyToMode: "off"` for already-threaded inbound posts so threaded follow-ups can fall back to top-level replies when configured. (#52543) Thanks @RichardCao.
107+
- Telegram/replies: set `allow_sending_without_reply` on reply-targeted sends and media-error notices so deleted parent messages no longer drop otherwise valid replies. (#52524) Thanks @moltbot886.
106108
- CLI/startup: lazy-load channel add and root help startup paths to trim avoidable RSS and help latency on constrained hosts. (#46784) Thanks @vincentkoc.
107109
- CLI/onboarding: import static provider definitions directly for onboarding model/config helpers so those paths no longer pull provider discovery just for built-in defaults. (#47467) Thanks @vincentkoc.
108110
- CLI/configure: clarify fresh-setup memory-search warnings so they say semantic recall needs at least one embedding provider, and scope the initial model allowlist picker to the provider selected in configure. Thanks @vincentkoc.

extensions/telegram/src/bot-handlers.runtime.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,8 +1016,10 @@ export const registerTelegramHandlers = ({
10161016
runtime,
10171017
fn: () =>
10181018
bot.api.sendMessage(chatId, `⚠️ File too large. Maximum size is ${limitMb}MB.`, {
1019-
reply_to_message_id: msg.message_id,
1020-
allow_sending_without_reply: true,
1019+
reply_parameters: {
1020+
message_id: msg.message_id,
1021+
allow_sending_without_reply: true,
1022+
},
10211023
}),
10221024
}).catch(() => {});
10231025
}
@@ -1030,8 +1032,10 @@ export const registerTelegramHandlers = ({
10301032
runtime,
10311033
fn: () =>
10321034
bot.api.sendMessage(chatId, "⚠️ Failed to download media. Please try again.", {
1033-
reply_to_message_id: msg.message_id,
1034-
allow_sending_without_reply: true,
1035+
reply_parameters: {
1036+
message_id: msg.message_id,
1037+
allow_sending_without_reply: true,
1038+
},
10351039
}),
10361040
}).catch(() => {});
10371041
return;

extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,12 @@ describe("createTelegramBot channel_post media", () => {
231231
expect(sendMessageSpy).toHaveBeenCalledWith(
232232
1234,
233233
"⚠️ Failed to download media. Please try again.",
234-
{ reply_to_message_id: 411 },
234+
{
235+
reply_parameters: {
236+
message_id: 411,
237+
allow_sending_without_reply: true,
238+
},
239+
},
235240
);
236241
expect(replySpy).not.toHaveBeenCalled();
237242
} finally {

extensions/telegram/src/bot/delivery.test.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ describe("deliverReplies", () => {
636636
expect.any(String),
637637
expect.objectContaining({
638638
reply_to_message_id: 500,
639+
allow_sending_without_reply: true,
639640
}),
640641
);
641642
expect(sendMessage).toHaveBeenCalledWith(
@@ -744,6 +745,7 @@ describe("deliverReplies", () => {
744745
expect(sendMessage.mock.calls[0][2]).toEqual(
745746
expect.objectContaining({
746747
reply_to_message_id: 77,
748+
allow_sending_without_reply: true,
747749
reply_markup: {
748750
inline_keyboard: [[{ text: "Ack", callback_data: "ack" }]],
749751
},
@@ -799,7 +801,10 @@ describe("deliverReplies", () => {
799801
expect(sendMessage.mock.calls.length).toBeGreaterThanOrEqual(2);
800802
// First chunk should have reply_to_message_id
801803
expect(sendMessage.mock.calls[0][2]).toEqual(
802-
expect.objectContaining({ reply_to_message_id: 700 }),
804+
expect.objectContaining({
805+
reply_to_message_id: 700,
806+
allow_sending_without_reply: true,
807+
}),
803808
);
804809
// Second chunk should NOT have reply_to_message_id
805810
expect(sendMessage.mock.calls[1][2]).not.toHaveProperty("reply_to_message_id");
@@ -826,7 +831,12 @@ describe("deliverReplies", () => {
826831
expect(sendMessage.mock.calls.length).toBeGreaterThanOrEqual(2);
827832
// Both chunks should have reply_to_message_id
828833
for (const call of sendMessage.mock.calls) {
829-
expect(call[2]).toEqual(expect.objectContaining({ reply_to_message_id: 800 }));
834+
expect(call[2]).toEqual(
835+
expect.objectContaining({
836+
reply_to_message_id: 800,
837+
allow_sending_without_reply: true,
838+
}),
839+
);
830840
}
831841
});
832842

@@ -854,7 +864,10 @@ describe("deliverReplies", () => {
854864
expect(sendPhoto).toHaveBeenCalledTimes(2);
855865
// First media should have reply_to_message_id
856866
expect(sendPhoto.mock.calls[0][2]).toEqual(
857-
expect.objectContaining({ reply_to_message_id: 900 }),
867+
expect.objectContaining({
868+
reply_to_message_id: 900,
869+
allow_sending_without_reply: true,
870+
}),
858871
);
859872
// Second media should NOT have reply_to_message_id
860873
expect(sendPhoto.mock.calls[1][2]).not.toHaveProperty("reply_to_message_id");

extensions/telegram/src/draft-stream.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,24 @@ describe("createTelegramDraftStream", () => {
218218
);
219219
});
220220

221+
it("keeps allow_sending_without_reply on message previews that target a reply", async () => {
222+
const api = createMockDraftApi();
223+
const stream = createDraftStream(api, {
224+
thread: { id: 42, scope: "dm" },
225+
previewTransport: "message",
226+
replyToMessageId: 411,
227+
});
228+
229+
stream.update("Hello");
230+
await stream.flush();
231+
232+
expect(api.sendMessage).toHaveBeenCalledWith(123, "Hello", {
233+
message_thread_id: 42,
234+
reply_to_message_id: 411,
235+
allow_sending_without_reply: true,
236+
});
237+
});
238+
221239
it("materializes draft previews using rendered HTML text", async () => {
222240
const api = createMockDraftApi();
223241
const stream = createDraftStream(api, {

extensions/telegram/src/draft-stream.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ export function createTelegramDraftStream(params: {
125125
const threadParams = buildTelegramThreadParams(params.thread);
126126
const replyParams =
127127
params.replyToMessageId != null
128-
? { ...threadParams, reply_to_message_id: params.replyToMessageId, allow_sending_without_reply: true }
128+
? {
129+
...threadParams,
130+
reply_to_message_id: params.replyToMessageId,
131+
allow_sending_without_reply: true,
132+
}
129133
: threadParams;
130134
const resolvedDraftApi = prefersDraftTransport
131135
? resolveSendMessageDraftApi(params.api)

0 commit comments

Comments
 (0)