mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(telegram): use retry logic for sticker getFile calls (#32349)
The sticker code path called ctx.getFile() directly without retry, unlike the non-sticker media path which uses resolveTelegramFileWithRetry (3 attempts with jitter). This made sticker downloads vulnerable to transient Telegram API failures, particularly in group topics where file availability can be delayed. Refs #32326 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@ const MAX_MEDIA_BYTES = 10_000_000;
|
||||
const BOT_TOKEN = "tok123";
|
||||
|
||||
function makeCtx(
|
||||
mediaField: "voice" | "audio" | "photo" | "video" | "document" | "animation",
|
||||
mediaField: "voice" | "audio" | "photo" | "video" | "document" | "animation" | "sticker",
|
||||
getFile: TelegramContext["getFile"],
|
||||
opts?: { file_name?: string },
|
||||
): TelegramContext {
|
||||
@@ -79,6 +79,17 @@ function makeCtx(
|
||||
...(opts?.file_name && { file_name: opts.file_name }),
|
||||
};
|
||||
}
|
||||
if (mediaField === "sticker") {
|
||||
msg.sticker = {
|
||||
file_id: "stk1",
|
||||
file_unique_id: "ustk1",
|
||||
type: "regular",
|
||||
width: 512,
|
||||
height: 512,
|
||||
is_animated: false,
|
||||
is_video: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
message: msg as unknown as Message,
|
||||
me: {
|
||||
@@ -243,6 +254,45 @@ describe("resolveMedia getFile retry", () => {
|
||||
// Should retry transient errors.
|
||||
expect(result).not.toBeNull();
|
||||
});
|
||||
|
||||
it("retries getFile for stickers on transient failure", async () => {
|
||||
const getFile = vi
|
||||
.fn()
|
||||
.mockRejectedValueOnce(new Error("Network request for 'getFile' failed!"))
|
||||
.mockResolvedValueOnce({ file_path: "stickers/file_0.webp" });
|
||||
|
||||
fetchRemoteMedia.mockResolvedValueOnce({
|
||||
buffer: Buffer.from("sticker-data"),
|
||||
contentType: "image/webp",
|
||||
fileName: "file_0.webp",
|
||||
});
|
||||
saveMediaBuffer.mockResolvedValueOnce({
|
||||
path: "/tmp/file_0.webp",
|
||||
contentType: "image/webp",
|
||||
});
|
||||
|
||||
const ctx = makeCtx("sticker", getFile);
|
||||
const promise = resolveMedia(ctx, MAX_MEDIA_BYTES, BOT_TOKEN);
|
||||
await flushRetryTimers();
|
||||
const result = await promise;
|
||||
|
||||
expect(getFile).toHaveBeenCalledTimes(2);
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({ path: "/tmp/file_0.webp", placeholder: "<media:sticker>" }),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null for sticker when getFile exhausts retries", async () => {
|
||||
const getFile = vi.fn().mockRejectedValue(new Error("Network request for 'getFile' failed!"));
|
||||
|
||||
const ctx = makeCtx("sticker", getFile);
|
||||
const promise = resolveMedia(ctx, MAX_MEDIA_BYTES, BOT_TOKEN);
|
||||
await flushRetryTimers();
|
||||
const result = await promise;
|
||||
|
||||
expect(getFile).toHaveBeenCalledTimes(3);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveMedia original filename preservation", () => {
|
||||
|
||||
@@ -156,8 +156,8 @@ async function resolveStickerMedia(params: {
|
||||
}
|
||||
|
||||
try {
|
||||
const file = await ctx.getFile();
|
||||
if (!file.file_path) {
|
||||
const file = await resolveTelegramFileWithRetry(ctx);
|
||||
if (!file?.file_path) {
|
||||
logVerbose("telegram: getFile returned no file_path for sticker");
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user