Skip to content

feat(engine): Baileys media/location/contact sends (slice 2a)#307

Merged
rmyndharis merged 5 commits into
mainfrom
feat/baileys-media-sends
Jun 18, 2026
Merged

feat(engine): Baileys media/location/contact sends (slice 2a)#307
rmyndharis merged 5 commits into
mainfrom
feat/baileys-media-sends

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Summary

Slice 2a of the Baileys engine: un-gates the storeless outbound sends — image, video, audio, document, sticker, location, and contact — so ENGINE_TYPE=baileys can send rich content, not just text.

Stacked on #299 (base branch feat/baileys-engine-minimal). This PR's diff shows only the slice-2a commits; once #299 merges, I'll retarget the base to main.

What's added

  • Media sends (sendImage/Video/Audio/Document/Sticker): each resolves MediaInput.data (Buffer / base64 / http(s) URL) to a Buffer and emits the Baileys sock.sendMessage(jid, content) shape (image/video carry caption+mimetype, audio ptt:false, document fileName, sticker bare buffer).
  • Location (sendLocationMessage) → { location: { degreesLatitude, degreesLongitude, name, address } }.
  • Contact (sendContactMessage) → a minimal WhatsApp vCard (FN + TEL;waid=…), with CR/LF stripped from name/number to prevent vCard line-injection.
  • getFeatures() now advertises media-messages, location-messages, contact-messages.

Security: SSRF-guarded URL media

Baileys, given { image: { url } }, would fetch the URL itself with no SSRF guard. So URL media goes through a new neutral helper src/common/media/load-remote-media.tsloadRemoteMediaBuffer(url) — that:

  • runs the existing audited assertSafeFetchUrl before any socket opens (internal/reserved hosts → SsrfBlockedError),
  • enforces a byte cap while streaming the body (Content-Length can be absent/wrong),
  • applies a timeout and redirect: 'error' (a followed 3xx could reach an internal target),

then hands Baileys a Buffer — a raw { url } is never passed to the lib. Same guard, cap, timeout, and env knobs (MEDIA_DOWNLOAD_MAX_BYTES, MEDIA_DOWNLOAD_TIMEOUT_MS) as the whatsapp-web.js path. No new dependency (Node global fetch).

Still out of scope (→ slice 2b)

reply/forward/react/delete remain gated to HTTP 501 — they need a self-maintained store mapping our neutral message id → the Baileys message key/proto (Baileys 6.7.23 ships none). That store is the next slice.

Testing

  • 579 unit + 6 e2e green; backend lint 0 / build clean; dashboard build clean / lint 0 errors.
  • New coverage: loadRemoteMediaBuffer (SSRF block-before-fetch, streaming cap, redirect, mimetype); per-method adapter send-shape tests (Buffer/base64/URL resolution, URL→guarded fetch, audio ptt, document fileName, location mapping, vCard waid + CRLF-injection guard, caller-mimetype-wins); the e2e boot gate's feature list synced to the expanded set.
  • Engine-neutral boundary intact: Baileys types/shapes stay inside the adapter; loadRemoteMediaBuffer returns a neutral { data, mimetype }.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant