diff --git a/CHANGELOG.md b/CHANGELOG.md index 547530996a9..edd541e5ba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Docs: https://docs.openclaw.ai - Agents/compaction: resolve `agents.defaults.compaction.model` consistently for manual `/compact` and other context-engine compaction paths, so engine-owned compaction uses the configured override model across runtime entrypoints. (#56710) Thanks @oliviareid-svg - Channels/session routing: move provider-specific session conversation grammar into plugin-owned session-key surfaces, preserving Telegram topic routing and Feishu scoped inheritance across bootstrap, model override, restart, and tool-policy paths. +### Fixes + +- Telegram/local Bot API: preserve media MIME types for absolute-path downloads so local audio files still trigger transcription and other MIME-based handling. (#54603) Thanks @jzakirov + ## 2026.3.31 ### Breaking diff --git a/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts b/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts index c7551d1a294..49f396e3f7b 100644 --- a/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts +++ b/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts @@ -40,7 +40,7 @@ const BOT_TOKEN = "tok123"; function makeCtx( mediaField: "voice" | "audio" | "photo" | "video" | "document" | "animation" | "sticker", getFile: TelegramContext["getFile"], - opts?: { file_name?: string }, + opts?: { file_name?: string; mime_type?: string }, ): TelegramContext { const msg: Record = { message_id: 1, @@ -48,7 +48,12 @@ function makeCtx( chat: { id: 1, type: "private" }, }; if (mediaField === "voice") { - msg.voice = { file_id: "v1", duration: 5, file_unique_id: "u1" }; + msg.voice = { + file_id: "v1", + duration: 5, + file_unique_id: "u1", + ...(opts?.mime_type && { mime_type: opts.mime_type }), + }; } if (mediaField === "audio") { msg.audio = { @@ -56,6 +61,7 @@ function makeCtx( duration: 5, file_unique_id: "u2", ...(opts?.file_name && { file_name: opts.file_name }), + ...(opts?.mime_type && { mime_type: opts.mime_type }), }; } if (mediaField === "photo") { @@ -74,6 +80,7 @@ function makeCtx( file_id: "d1", file_unique_id: "u4", ...(opts?.file_name && { file_name: opts.file_name }), + ...(opts?.mime_type && { mime_type: opts.mime_type }), }; } if (mediaField === "animation") { @@ -359,13 +366,18 @@ describe("resolveMedia getFile retry", () => { it("uses local absolute file paths directly for media downloads", async () => { const getFile = vi.fn().mockResolvedValue({ file_path: "/var/lib/telegram-bot-api/file.pdf" }); - const result = await resolveMedia(makeCtx("document", getFile), MAX_MEDIA_BYTES, BOT_TOKEN); + const result = await resolveMedia( + makeCtx("document", getFile, { mime_type: "application/pdf" }), + MAX_MEDIA_BYTES, + BOT_TOKEN, + ); expect(fetchRemoteMedia).not.toHaveBeenCalled(); expect(saveMediaBuffer).not.toHaveBeenCalled(); expect(result).toEqual( expect.objectContaining({ path: "/var/lib/telegram-bot-api/file.pdf", + contentType: "application/pdf", placeholder: "", }), ); diff --git a/extensions/telegram/src/bot/delivery.resolve-media.ts b/extensions/telegram/src/bot/delivery.resolve-media.ts index 38e9e962090..f0a256cf404 100644 --- a/extensions/telegram/src/bot/delivery.resolve-media.ts +++ b/extensions/telegram/src/bot/delivery.resolve-media.ts @@ -90,6 +90,17 @@ function resolveTelegramFileName(msg: TelegramContext["message"]): string | unde ); } +function resolveTelegramMimeType(msg: TelegramContext["message"]): string | undefined { + return ( + msg.audio?.mime_type ?? + msg.voice?.mime_type ?? + msg.video?.mime_type ?? + msg.document?.mime_type ?? + msg.animation?.mime_type ?? + undefined + ); +} + async function resolveTelegramFileWithRetry( ctx: TelegramContext, ): Promise<{ file_path?: string } | null> { @@ -152,10 +163,11 @@ async function downloadAndSaveTelegramFile(params: { transport: TelegramTransport; maxBytes: number; telegramFileName?: string; + mimeType?: string; apiRoot?: string; }) { if (path.isAbsolute(params.filePath)) { - return { path: params.filePath, contentType: undefined }; + return { path: params.filePath, contentType: params.mimeType }; } const apiBase = resolveTelegramApiBase(params.apiRoot); const url = `${apiBase}/file/bot${params.token}/${params.filePath}`; @@ -320,6 +332,7 @@ export async function resolveMedia( transport: resolveRequiredTelegramTransport(transport), maxBytes, telegramFileName: resolveTelegramFileName(msg), + mimeType: resolveTelegramMimeType(msg), apiRoot, }); const placeholder = resolveTelegramMediaPlaceholder(msg) ?? "";