diff --git a/src/config/types.telegram.ts b/src/config/types.telegram.ts index 6e2aba3583d..52fa1bb24cb 100644 --- a/src/config/types.telegram.ts +++ b/src/config/types.telegram.ts @@ -185,6 +185,8 @@ export type TelegramTopicConfig = { allowFrom?: Array; /** Optional system prompt snippet for this topic. */ systemPrompt?: string; + /** If true, skip automatic voice-note transcription for mention detection in this topic. */ + disableAudioPreflight?: boolean; }; export type TelegramGroupConfig = { @@ -204,6 +206,8 @@ export type TelegramGroupConfig = { allowFrom?: Array; /** Optional system prompt snippet for this group. */ systemPrompt?: string; + /** If true, skip automatic voice-note transcription for mention detection in this group. */ + disableAudioPreflight?: boolean; }; export type TelegramDirectConfig = { diff --git a/src/telegram/bot-message-context.audio-transcript.test.ts b/src/telegram/bot-message-context.audio-transcript.test.ts index 4e6a06132a7..90a3c28720e 100644 --- a/src/telegram/bot-message-context.audio-transcript.test.ts +++ b/src/telegram/bot-message-context.audio-transcript.test.ts @@ -41,4 +41,53 @@ describe("buildTelegramMessageContext audio transcript body", () => { expect(ctx?.ctxPayload?.Body).toContain("hey bot please help"); expect(ctx?.ctxPayload?.Body).not.toContain(""); }); + + it("skips preflight transcription when disableAudioPreflight is true", async () => { + transcribeFirstAudioMock.mockClear(); + + const ctx = await buildTelegramMessageContext({ + primaryCtx: { + message: { + message_id: 2, + chat: { id: -1001234567891, type: "supergroup", title: "Test Group 2" }, + date: 1700000100, + from: { id: 43, first_name: "Bob" }, + voice: { file_id: "voice-2" }, + }, + me: { id: 7, username: "bot" }, + } as never, + allMedia: [{ path: "/tmp/voice2.ogg", contentType: "audio/ogg" }], + storeAllowFrom: [], + options: { forceWasMentioned: true }, + bot: { + api: { + sendChatAction: vi.fn(), + setMessageReaction: vi.fn(), + }, + } as never, + cfg: { + agents: { defaults: { model: "anthropic/claude-opus-4-5", workspace: "/tmp/openclaw" } }, + channels: { telegram: {} }, + messages: { groupChat: { mentionPatterns: ["\\bbot\\b"] } }, + } as never, + account: { accountId: "default" } as never, + historyLimit: 0, + groupHistories: new Map(), + dmPolicy: "open", + allowFrom: [], + groupAllowFrom: [], + ackReactionScope: "off", + logger: { info: vi.fn() }, + resolveGroupActivation: () => true, + resolveGroupRequireMention: () => true, + resolveTelegramGroupConfig: () => ({ + groupConfig: { requireMention: true, disableAudioPreflight: true }, + topicConfig: undefined, + }), + }); + + expect(ctx).not.toBeNull(); + expect(transcribeFirstAudioMock).not.toHaveBeenCalled(); + expect(ctx?.ctxPayload?.Body).toContain(""); + }); }); diff --git a/src/telegram/bot-message-context.ts b/src/telegram/bot-message-context.ts index bca275ee2cc..6216c801441 100644 --- a/src/telegram/bot-message-context.ts +++ b/src/telegram/bot-message-context.ts @@ -393,11 +393,19 @@ export const buildTelegramMessageContext = async ({ let bodyText = rawBody; const hasAudio = allMedia.some((media) => media.contentType?.startsWith("audio/")); + const disableAudioPreflight = + firstDefined(topicConfig?.disableAudioPreflight, groupConfig?.disableAudioPreflight) === true; + // Preflight audio transcription for mention detection in groups // This allows voice notes to be checked for mentions before being dropped let preflightTranscript: string | undefined; const needsPreflightTranscription = - isGroup && requireMention && hasAudio && !hasUserText && mentionRegexes.length > 0; + isGroup && + requireMention && + hasAudio && + !hasUserText && + mentionRegexes.length > 0 && + !disableAudioPreflight; if (needsPreflightTranscription) { try {