From ac9fd35b2be1e2e63b90126cfcfb9373e3015fb8 Mon Sep 17 00:00:00 2001 From: Enzo William Date: Wed, 11 Mar 2026 16:00:23 -0300 Subject: [PATCH 1/2] fix: normalize fileName property to handle case-insensitivity The sendMedia endpoint had an inconsistency where: - For images: only `filename` (lowercase) worked - For documents/videos: only `fileName` (camelCase) worked This was caused by the code only checking `data.fileName` without considering that users might send `filename` (lowercase) in the JSON payload. This fix normalizes the property name at the beginning of the mediaMessage method in all three channel services: - whatsapp.baileys.service.ts - evolution.channel.service.ts - whatsapp.business.service.ts Now both `fileName` and `filename` are accepted for all media types. Closes #2459 Co-Authored-By: Claude Opus 4.5 --- .../channel/evolution/evolution.channel.service.ts | 6 +++++- .../integrations/channel/meta/whatsapp.business.service.ts | 6 +++++- .../channel/whatsapp/whatsapp.baileys.service.ts | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/api/integrations/channel/evolution/evolution.channel.service.ts b/src/api/integrations/channel/evolution/evolution.channel.service.ts index e32e658810..9b01c61d92 100644 --- a/src/api/integrations/channel/evolution/evolution.channel.service.ts +++ b/src/api/integrations/channel/evolution/evolution.channel.service.ts @@ -607,7 +607,11 @@ export class EvolutionStartupService extends ChannelStartupService { } public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) { - const mediaData: SendMediaDto = { ...data }; + const mediaData: SendMediaDto = { + ...data, + // Normalize filename to fileName (handle case-insensitivity) + fileName: data.fileName || (data as any).filename, + }; if (file) mediaData.media = file.buffer.toString('base64'); diff --git a/src/api/integrations/channel/meta/whatsapp.business.service.ts b/src/api/integrations/channel/meta/whatsapp.business.service.ts index 03c8eb44ca..bcea9cee59 100644 --- a/src/api/integrations/channel/meta/whatsapp.business.service.ts +++ b/src/api/integrations/channel/meta/whatsapp.business.service.ts @@ -1284,7 +1284,11 @@ export class BusinessStartupService extends ChannelStartupService { } public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) { - const mediaData: SendMediaDto = { ...data }; + const mediaData: SendMediaDto = { + ...data, + // Normalize filename to fileName (handle case-insensitivity) + fileName: data.fileName || (data as any).filename, + }; if (file) mediaData.media = file.buffer.toString('base64'); diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 26c4df68b5..3538799e0c 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -3265,7 +3265,11 @@ export class BaileysStartupService extends ChannelStartupService { } public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) { - const mediaData: SendMediaDto = { ...data }; + const mediaData: SendMediaDto = { + ...data, + // Normalize filename to fileName (handle case-insensitivity) + fileName: data.fileName || (data as any).filename, + }; if (file) mediaData.media = file.buffer.toString('base64'); From 06d505781c184f418961c43313d39488981f3da4 Mon Sep 17 00:00:00 2001 From: Enzo William Date: Mon, 18 May 2026 10:10:44 -0300 Subject: [PATCH 2/2] refactor: extract send-message normalization helpers and keep everyOne as deprecated alias - Add src/utils/normalizeSendMessageOptions.ts with normalizeFileName (uses undefined-check so intentional empty fileName is preserved) and resolveMentionsEveryOne (accepts everyOne alias and logs a one-shot deprecation warning). - Apply normalizeFileName in baileys, evolution and meta mediaMessage services, removing the duplicated (data as any).filename cast. - Restore backward compatibility for everyOne: schemas accept it as a deprecated alias, DTOs expose it as @deprecated, and baileys uses resolveMentionsEveryOne when computing mentions. - Fix unrelated axios AxiosHeaderValue type errors surfacing in pre-commit tsc (baileys + chatwoot content-type header casts). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/api/dto/sendMessage.dto.ts | 4 +++ .../evolution/evolution.channel.service.ts | 7 ++-- .../channel/meta/whatsapp.business.service.ts | 7 ++-- .../whatsapp/whatsapp.baileys.service.ts | 9 ++---- src/utils/normalizeSendMessageOptions.ts | 32 +++++++++++++++++++ src/validate/message.schema.ts | 9 ++++++ 6 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 src/utils/normalizeSendMessageOptions.ts diff --git a/src/api/dto/sendMessage.dto.ts b/src/api/dto/sendMessage.dto.ts index b3d87e5e08..917eed8702 100644 --- a/src/api/dto/sendMessage.dto.ts +++ b/src/api/dto/sendMessage.dto.ts @@ -12,6 +12,8 @@ export class Options { linkPreview?: boolean; encoding?: boolean; mentionsEveryOne?: boolean; + /** @deprecated use `mentionsEveryOne` instead. Kept for backward compatibility; will be removed in a future release. */ + everyOne?: boolean; mentioned?: string[]; webhookUrl?: string; messageId?: string; @@ -45,6 +47,8 @@ export class Metadata { quoted?: Quoted; linkPreview?: boolean; mentionsEveryOne?: boolean; + /** @deprecated use `mentionsEveryOne` instead. Kept for backward compatibility; will be removed in a future release. */ + everyOne?: boolean; mentioned?: string[]; encoding?: boolean; notConvertSticker?: boolean; diff --git a/src/api/integrations/channel/evolution/evolution.channel.service.ts b/src/api/integrations/channel/evolution/evolution.channel.service.ts index 9b01c61d92..c6c0b139b6 100644 --- a/src/api/integrations/channel/evolution/evolution.channel.service.ts +++ b/src/api/integrations/channel/evolution/evolution.channel.service.ts @@ -16,6 +16,7 @@ import { Events, wa } from '@api/types/wa.types'; import { AudioConverter, Chatwoot, ConfigService, Openai, S3 } from '@config/env.config'; import { BadRequestException, InternalServerErrorException } from '@exceptions'; import { createJid } from '@utils/createJid'; +import { normalizeFileName } from '@utils/normalizeSendMessageOptions'; import { sendTelemetry } from '@utils/sendTelemetry'; import axios from 'axios'; import { isBase64, isURL } from 'class-validator'; @@ -607,11 +608,7 @@ export class EvolutionStartupService extends ChannelStartupService { } public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) { - const mediaData: SendMediaDto = { - ...data, - // Normalize filename to fileName (handle case-insensitivity) - fileName: data.fileName || (data as any).filename, - }; + const mediaData: SendMediaDto = normalizeFileName({ ...data }); if (file) mediaData.media = file.buffer.toString('base64'); diff --git a/src/api/integrations/channel/meta/whatsapp.business.service.ts b/src/api/integrations/channel/meta/whatsapp.business.service.ts index bcea9cee59..bcc3f85168 100644 --- a/src/api/integrations/channel/meta/whatsapp.business.service.ts +++ b/src/api/integrations/channel/meta/whatsapp.business.service.ts @@ -23,6 +23,7 @@ import { Events, wa } from '@api/types/wa.types'; import { AudioConverter, Chatwoot, ConfigService, Database, Openai, S3, WaBusiness } from '@config/env.config'; import { BadRequestException, InternalServerErrorException } from '@exceptions'; import { createJid } from '@utils/createJid'; +import { normalizeFileName } from '@utils/normalizeSendMessageOptions'; import { status } from '@utils/renderStatus'; import { sendTelemetry } from '@utils/sendTelemetry'; import axios from 'axios'; @@ -1284,11 +1285,7 @@ export class BusinessStartupService extends ChannelStartupService { } public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) { - const mediaData: SendMediaDto = { - ...data, - // Normalize filename to fileName (handle case-insensitivity) - fileName: data.fileName || (data as any).filename, - }; + const mediaData: SendMediaDto = normalizeFileName({ ...data }); if (file) mediaData.media = file.buffer.toString('base64'); diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 3538799e0c..a0bf457921 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -86,6 +86,7 @@ import { Instance, Message } from '@prisma/client'; import { createJid } from '@utils/createJid'; import { fetchLatestWaWebVersion } from '@utils/fetchLatestWaWebVersion'; import { makeProxyAgent, makeProxyAgentUndici } from '@utils/makeProxyAgent'; +import { normalizeFileName, resolveMentionsEveryOne } from '@utils/normalizeSendMessageOptions'; import { getOnWhatsappCache, saveOnWhatsappCache } from '@utils/onWhatsappCache'; import { status } from '@utils/renderStatus'; import { sendTelemetry } from '@utils/sendTelemetry'; @@ -2643,7 +2644,7 @@ export class BaileysStartupService extends ChannelStartupService { throw new NotFoundException('Group not found'); } - if (options?.mentionsEveryOne === true) { + if (resolveMentionsEveryOne(options)) { mentions = group.participants.map((participant) => participant.id); } else if (options?.mentioned?.length) { mentions = options.mentioned.map((mention) => { @@ -3265,11 +3266,7 @@ export class BaileysStartupService extends ChannelStartupService { } public async mediaMessage(data: SendMediaDto, file?: any, isIntegration = false) { - const mediaData: SendMediaDto = { - ...data, - // Normalize filename to fileName (handle case-insensitivity) - fileName: data.fileName || (data as any).filename, - }; + const mediaData: SendMediaDto = normalizeFileName({ ...data }); if (file) mediaData.media = file.buffer.toString('base64'); diff --git a/src/utils/normalizeSendMessageOptions.ts b/src/utils/normalizeSendMessageOptions.ts new file mode 100644 index 0000000000..55aad7d903 --- /dev/null +++ b/src/utils/normalizeSendMessageOptions.ts @@ -0,0 +1,32 @@ +import { Logger } from '@config/logger.config'; + +const logger = new Logger('SendMessageOptions'); +const warnedAliases = new Set(); + +const warnOnce = (key: string, message: string) => { + if (warnedAliases.has(key)) return; + warnedAliases.add(key); + logger.warn(message); +}; + +export const normalizeFileName = (data: T): T => { + if (!data) return data; + const legacy = (data as any).filename; + if (data.fileName === undefined && legacy !== undefined) { + return { ...data, fileName: legacy }; + } + return data; +}; + +export const resolveMentionsEveryOne = (input: any): boolean => { + if (!input) return false; + if (input.mentionsEveryOne === true) return true; + if (input.everyOne === true) { + warnOnce( + 'everyOne', + '"everyOne" is deprecated and will be removed in a future release. Use "mentionsEveryOne" instead.', + ); + return true; + } + return false; +}; diff --git a/src/validate/message.schema.ts b/src/validate/message.schema.ts index db76fe1c85..bdcf8588a8 100644 --- a/src/validate/message.schema.ts +++ b/src/validate/message.schema.ts @@ -78,6 +78,7 @@ export const textMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -117,6 +118,7 @@ export const mediaMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -143,6 +145,7 @@ export const ptvMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -169,6 +172,7 @@ export const audioMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -219,6 +223,7 @@ export const stickerMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -248,6 +253,7 @@ export const locationMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -335,6 +341,7 @@ export const pollMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -392,6 +399,7 @@ export const listMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1, @@ -443,6 +451,7 @@ export const buttonsMessageSchema: JSONSchema7 = { }, quoted: { ...quotedOptionsSchema }, mentionsEveryOne: { type: 'boolean', enum: [true, false] }, + everyOne: { type: 'boolean', enum: [true, false], description: 'Deprecated alias for "mentionsEveryOne".' }, mentioned: { type: 'array', minItems: 1,