-
Notifications
You must be signed in to change notification settings - Fork 5.2k
feat: add typebot file support & fix whatsapp active link preview #2347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
…preview - Fix linkPreview logic in Baileys to default to true - Add support for 'file' and 'embed' types in Typebot integration - Ensure correct media type detection for PDFs and docs
Reviewer's GuideAdds Typebot support for sending file/embed blocks as WhatsApp media/audio and introduces active URL scraping to reliably generate WhatsApp link previews, wiring the preview context into the Baileys send flow while preserving explicit linkPreview disabling. Sequence diagram for WhatsApp active link preview generationsequenceDiagram
actor User
participant ClientApp
participant BaileysStartupService
participant LinkPreviewJS
participant WhatsAppServer
User ->> ClientApp: Send text message with URL
ClientApp ->> BaileysStartupService: sendMessage(sender, message, options, group)
activate BaileysStartupService
BaileysStartupService ->> BaileysStartupService: determine linkPreview flag
alt linkPreview explicitly disabled
BaileysStartupService ->> WhatsAppServer: send message without previewContext
else linkPreview enabled or undefined
BaileysStartupService ->> BaileysStartupService: generateLinkPreview(text)
activate BaileysStartupService
BaileysStartupService ->> LinkPreviewJS: getLinkPreview(url, headers)
LinkPreviewJS -->> BaileysStartupService: previewData(title, description, images)
BaileysStartupService ->> BaileysStartupService: build externalAdReply context
deactivate BaileysStartupService
BaileysStartupService ->> WhatsAppServer: send message with previewContext
end
WhatsAppServer -->> User: Deliver message with rich link preview
Sequence diagram for Typebot file and embed message handling to WhatsAppsequenceDiagram
actor TypebotFlow
participant TypebotService
participant WhatsAppInstance
participant WhatsAppServer
TypebotFlow ->> TypebotService: Emit outgoing message
TypebotService ->> TypebotService: Inspect message.type
alt message.type is file or embed
TypebotService ->> TypebotService: Extract content.url and content.name
TypebotService ->> TypebotService: mediaType = getMediaType(mediaUrl)
TypebotService ->> TypebotService: Derive fileName if missing
alt mediaType is audio
TypebotService ->> WhatsAppInstance: audioWhatsapp(number, delay, encoding, audioUrl)
else other media type
TypebotService ->> WhatsAppInstance: mediaMessage(number, delay, mediatype, mediaUrl, fileName)
end
TypebotService ->> TypebotService: sendTelemetry(/message/sendMedia)
else other message types
TypebotService ->> WhatsAppInstance: Existing handling for text, image, etc.
end
WhatsAppInstance ->> WhatsAppServer: Forward WhatsApp message
WhatsAppServer -->> TypebotFlow: Message delivered to end user
Updated class diagram for BaileysStartupService and TypebotService message handlingclassDiagram
class BaileysStartupService {
+sendMessage(sender:string, message:any, options:any, group:any, previewContext:any) Promise~any~
-generateLinkPreview(text:string) Promise~any~
}
class TypebotService {
+handleTypebotMessage(instance:any, session:any, message:any, settings:any) Promise~any~
-getMediaType(mediaUrl:string) string
}
TypebotService --> BaileysStartupService : uses for WhatsApp delivery
class LinkPreviewJS {
+getLinkPreview(url:string, options:any) Promise~any~
}
BaileysStartupService --> LinkPreviewJS : generates link previews using
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've found 3 issues, and left some high level feedback:
- The link preview payload from
generateLinkPreviewis shaped as{ externalAdReply: ... }but is later spread directly into the message options/relay call, so it never ends up undercontextInfo.externalAdReplyas described in the PR; consider wrapping it undercontextInfo(e.g.,{ contextInfo: { externalAdReply: ... } }) where Baileys expects it. - In
generateLinkPreview, the error logging interpolates the error into a template string, which may lose stack details; prefer passing the error object directly to the logger (e.g.,this.logger.error('Error generating link preview', error)) for better diagnostics.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The link preview payload from `generateLinkPreview` is shaped as `{ externalAdReply: ... }` but is later spread directly into the message options/relay call, so it never ends up under `contextInfo.externalAdReply` as described in the PR; consider wrapping it under `contextInfo` (e.g., `{ contextInfo: { externalAdReply: ... } }`) where Baileys expects it.
- In `generateLinkPreview`, the error logging interpolates the error into a template string, which may lose stack details; prefer passing the error object directly to the logger (e.g., `this.logger.error('Error generating link preview', error)`) for better diagnostics.
## Individual Comments
### Comment 1
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:2230-2233` </location>
<code_context>
+ private async generateLinkPreview(text: string) {
+ try {
+ const linkRegex = /https?:\/\/[^\s]+/;
+ const match = text.match(linkRegex);
+
+ if (!match) return undefined;
</code_context>
<issue_to_address>
**suggestion:** URL regex is likely to capture trailing punctuation and break previews for common cases.
In natural text, this pattern will often capture trailing punctuation like `)`, `,`, or `.` (e.g., `"Check this (https://example.com)."`), causing preview failures. Please either restrict the regex to exclude common trailing punctuation or post-process the match (e.g., trim `.,);]`) or, ideally, use a URL parser for more robust handling.
```suggestion
if (!match) return undefined;
// Trim common trailing punctuation that may follow URLs in natural text
let url = match[0].replace(/[.,);\]]+$/u, '');
if (!url) return undefined;
const previewData = await getLinkPreview(url, {
```
</issue_to_address>
### Comment 2
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:2546` </location>
<code_context>
unsigned: false,
},
disappearingMode: { initiator: 0 },
+ ...previewContext,
};
messageSent = await this.sendMessage(
</code_context>
<issue_to_address>
**issue (bug_risk):** The spread structure of `previewContext` may not match the message format expected by Baileys here.
`generateLinkPreview` returns `{ externalAdReply: { ... } }`, but this is being spread at the root of the message object. If Baileys expects link previews under `contextInfo.externalAdReply`, this structure may be ignored. Consider shaping `previewContext` into the exact structure Baileys requires at this call site (e.g., wrapping it under `contextInfo`) instead of spreading the helper output directly.
</issue_to_address>
### Comment 3
<location> `src/api/integrations/chatbot/typebot/services/typebot.service.ts:371-399` </location>
<code_context>
sendTelemetry('/message/sendWhatsAppAudio');
}
+ if (message.type === 'file' || message.type === 'embed') {
+ const mediaUrl = message.content.url;
+ const mediaType = this.getMediaType(mediaUrl);
+
+ if (mediaType === 'audio') {
+ await instance.audioWhatsapp(
+ {
+ number: session.remoteJid,
+ delay: settings?.delayMessage || 1000,
+ encoding: true,
+ audio: mediaUrl,
+ },
+ false,
+ );
+ } else {
+ await instance.mediaMessage(
+ {
+ number: session.remoteJid,
+ delay: settings?.delayMessage || 1000,
+ mediatype: mediaType || 'document',
+ media: mediaUrl,
+ fileName: message.content.name || 'document.pdf',
+ },
+ null,
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Assumptions about `message.content.url`/`name` and defaulting to `document.pdf` may be brittle.
This branch assumes `message.content.url` always exists for both `file` and `embed`, and hard-codes `document.pdf` when `name` is missing, which will be wrong for many embeds or non-PDF files. Consider guarding against missing `content`/`url` (no-op or log), deriving the filename from the URL, or using a more generic/default name (possibly based on `mediaType`).
```suggestion
if (message.type === 'file' || message.type === 'embed') {
const content = message.content as { url?: string; name?: string } | undefined;
if (!content?.url) {
// No media URL available; skip sending and record telemetry for diagnostics
sendTelemetry('/message/sendMediaMissingUrl');
return;
}
const mediaUrl = content.url;
const mediaType = this.getMediaType(mediaUrl);
let fileName = content.name;
if (!fileName) {
let derivedName: string | undefined;
try {
const urlObj = new URL(mediaUrl);
const path = urlObj.pathname || '';
const candidate = path.split('/').pop() || '';
if (candidate && candidate.includes('.')) {
derivedName = candidate;
}
} catch {
// Ignore URL parsing failures; fallback to generic names below
}
if (derivedName) {
fileName = derivedName;
} else if (mediaType && mediaType !== 'document') {
fileName = `media.${mediaType}`;
} else {
fileName = 'attachment';
}
}
if (mediaType === 'audio') {
await instance.audioWhatsapp(
{
number: session.remoteJid,
delay: settings?.delayMessage || 1000,
encoding: true,
audio: mediaUrl,
},
false,
);
} else {
await instance.mediaMessage(
{
number: session.remoteJid,
delay: settings?.delayMessage || 1000,
mediatype: mediaType || 'document',
media: mediaUrl,
fileName,
},
null,
false,
);
}
sendTelemetry('/message/sendMedia');
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
…ng, Typebot integration, and comprehensive API functionalities for chat, group, and business profiles.
…tions, messages, groups, profiles, and integrating with Chatwoot, OpenAI, and S3.
…ryption, and channel fetching
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:2259-2262` </location>
<code_context>
+ // showAdAttribution: true // Removed to prevent "Sent via ad" label
+ }
+ };
+ } catch (error) {
+ this.logger.error(`Error generating link preview: ${error}`);
+ return undefined;
+ }
</code_context>
<issue_to_address>
**suggestion:** Logging the error object via template string may lose stack/context information.
Instead of interpolating `error` into the string (which will typically emit `"[object Object]"`), pass it as a separate argument so the logger can include stack and metadata, e.g.:
```ts
} catch (error) {
this.logger.error('Error generating link preview', error);
return undefined;
}
```
```suggestion
} catch (error) {
this.logger.error('Error generating link preview', error);
return undefined;
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
📋 Description
This PR addresses two critical issues related to message handling in Typebot and Link Previews in WhatsApp (Baileys):
Typebot Integration Enhancement:
embedmessage types in TypebotService.WhatsApp Link Preview Fix (Active Scraping):
linkPreviewfunctionality which was failing to generate previews for text messages containing links.link-preview-jswithin BaileysStartupService.linkPreviewis enabled, the system now actively fetches metadata (title, description, thumbnail) from the URL and injects it into the message'scontextInfo.externalAdReply. This ensures rich previews appear consistently, even for plain text links.showAdAttribution: trueto prevent the "Sent via ad" label, ensuring a native look.🔗 Related Issue
Closes # (If there is an issue number, add it here)
🧪 Type of Change
🧪 Testing
Scenarios Verified:
Testing https://google.com) → Received with a rich link preview card (Title, Description, Thumbnail).📸 Screenshots (if applicable)
✅ Checklist
📝 Additional Notes
The
link-preview-jslibrary was already a dependency, so no new packages were added. The logic for link preview handles failures gracefully (falls back to sending without preview if scraping fails).Summary by Sourcery
Enhance Typebot WhatsApp integration with richer media support and enable reliable active link previews for WhatsApp messages.
New Features:
fileandembedblocks by sending them as appropriate WhatsApp media or documents, inferring filenames when needed.Bug Fixes: