From c3cb338937d2283b7359b353bb23bedf3d813f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zuri=20=E2=9C=A6?= Date: Mon, 26 Jan 2026 14:40:09 -0300 Subject: [PATCH] feat(whatsapp): extract mentions from text and captions WhatsApp's Baileys library requires a separate mentions array in the message payload for @mentions to work properly. Without this array, text like '@5511999999999' appears as plain text instead of a clickable mention. This change automatically extracts phone numbers in the format @<10-15 digits> from the message text AND media captions, adding them to the mentions array as JIDs (e.g., '5511999999999@s.whatsapp.net'). Supported message types: - Text messages - Image with caption - Video with caption - Document with caption Fixes mentions in group chats when using the message tool. --- src/web/inbound/send-api.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/web/inbound/send-api.ts b/src/web/inbound/send-api.ts index 06860e896..bdbd74221 100644 --- a/src/web/inbound/send-api.ts +++ b/src/web/inbound/send-api.ts @@ -19,6 +19,17 @@ export function createWebSendApi(params: { sendOptions?: ActiveWebSendOptions, ): Promise<{ messageId: string }> => { const jid = toWhatsappJid(to); + + const mentions: string[] = []; + if (text) { + const phoneRegex = /@(\d{10,15})/g; + let match: RegExpExecArray | null; + while ((match = phoneRegex.exec(text)) !== null) { + mentions.push(`${match[1]}@s.whatsapp.net`); + } + } + const mentionSpread = mentions.length > 0 ? { mentions } : {}; + let payload: AnyMessageContent; if (mediaBuffer && mediaType) { if (mediaType.startsWith("image/")) { @@ -26,7 +37,8 @@ export function createWebSendApi(params: { image: mediaBuffer, caption: text || undefined, mimetype: mediaType, - }; + ...mentionSpread, + } as AnyMessageContent; } else if (mediaType.startsWith("audio/")) { payload = { audio: mediaBuffer, ptt: true, mimetype: mediaType }; } else if (mediaType.startsWith("video/")) { @@ -36,17 +48,19 @@ export function createWebSendApi(params: { caption: text || undefined, mimetype: mediaType, ...(gifPlayback ? { gifPlayback: true } : {}), - }; + ...mentionSpread, + } as AnyMessageContent; } else { payload = { document: mediaBuffer, fileName: "file", caption: text || undefined, mimetype: mediaType, - }; + ...mentionSpread, + } as AnyMessageContent; } } else { - payload = { text }; + payload = mentions.length > 0 ? ({ text, mentions } as AnyMessageContent) : { text }; } const result = await params.sock.sendMessage(jid, payload); const accountId = sendOptions?.accountId ?? params.defaultAccountId;