fix(telegram): land #34983 from @HOYALIM

Landed from contributor PR #34983 by @HOYALIM.

Co-authored-by: Ho Lim <subhoya@gmail.com>
This commit is contained in:
Peter Steinberger
2026-03-08 00:52:56 +00:00
parent d6d04f361e
commit 4869e24915
3 changed files with 49 additions and 0 deletions

View File

@@ -320,6 +320,7 @@ Docs: https://docs.openclaw.ai
- Exec approvals/config fallback: inherit `ask` from `exec-approvals.json` when `tools.exec.ask` is unset, so local full/off defaults no longer fall back to `on-miss` for exec tool and `nodes run`. Landed from contributor PR #29187 by @Bartok9. Thanks @Bartok9.
- Exec approvals/allow-always shell scripts: persist and match script paths for wrapper invocations like `bash scripts/foo.sh` while still blocking `-c`/`-s` wrapper bypasses. Landed from contributor PR #35137 by @yuweuii. Thanks @yuweuii.
- Queue/followup dedupe across drain restarts: dedupe queued redelivery `message_id` values after queue recreation so busy-session followups no longer duplicate on replayed inbound events. Landed from contributor PR #33168 by @rylena. Thanks @rylena.
- Telegram/preview-final edit idempotence: treat `message is not modified` errors during preview finalization as delivered so partial-stream final replies do not fall back to duplicate sends. Landed from contributor PR #34983 by @HOYALIM. Thanks @HOYALIM.
## 2026.3.2

View File

@@ -146,6 +146,30 @@ describe("createLaneTextDeliverer", () => {
expect(harness.log).toHaveBeenCalledWith(expect.stringContaining("treating as delivered"));
});
it("treats 'message is not modified' preview edit errors as delivered", async () => {
const harness = createHarness({ answerMessageId: 999 });
harness.editPreview.mockRejectedValue(
new Error(
"400: Bad Request: message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message",
),
);
const result = await harness.deliverLaneText({
laneName: "answer",
text: "Hello final",
payload: { text: "Hello final" },
infoKind: "final",
});
expect(result).toBe("preview-finalized");
expect(harness.editPreview).toHaveBeenCalledTimes(1);
expect(harness.sendPayload).not.toHaveBeenCalled();
expect(harness.markDelivered).toHaveBeenCalledTimes(1);
expect(harness.log).toHaveBeenCalledWith(
expect.stringContaining('edit returned "message is not modified"; treating as delivered'),
);
});
it("falls back to normal delivery when editing an existing preview fails", async () => {
const harness = createHarness({ answerMessageId: 999 });
harness.editPreview.mockRejectedValue(new Error("500: preview edit failed"));

View File

@@ -2,6 +2,23 @@ import type { ReplyPayload } from "../auto-reply/types.js";
import type { TelegramInlineButtons } from "./button-types.js";
import type { TelegramDraftStream } from "./draft-stream.js";
const MESSAGE_NOT_MODIFIED_RE =
/400:\s*Bad Request:\s*message is not modified|MESSAGE_NOT_MODIFIED/i;
function isMessageNotModifiedError(err: unknown): boolean {
const text =
typeof err === "string"
? err
: err instanceof Error
? err.message
: typeof err === "object" && err && "description" in err
? typeof err.description === "string"
? err.description
: ""
: "";
return MESSAGE_NOT_MODIFIED_RE.test(text);
}
export type LaneName = "answer" | "reasoning";
export type DraftLaneState = {
@@ -216,6 +233,13 @@ export function createLaneTextDeliverer(params: CreateLaneTextDelivererParams) {
params.markDelivered();
return true;
} catch (err) {
if (isMessageNotModifiedError(err)) {
params.log(
`telegram: ${args.laneName} preview ${args.context} edit returned "message is not modified"; treating as delivered`,
);
params.markDelivered();
return true;
}
if (args.treatEditFailureAsDelivered) {
params.log(
`telegram: ${args.laneName} preview ${args.context} edit failed after stop-created flush; treating as delivered (${String(err)})`,