Skip to content

feat(engine): Baileys engine — minimal slice (link + text send/receive)#299

Merged
rmyndharis merged 12 commits into
mainfrom
feat/baileys-engine-minimal
Jun 18, 2026
Merged

feat(engine): Baileys engine — minimal slice (link + text send/receive)#299
rmyndharis merged 12 commits into
mainfrom
feat/baileys-engine-minimal

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Summary

Adds a second, browser-free WhatsApp engine to OpenWA: @whiskeysockets/baileys (WebSocket/Noise protocol, no Chromium). This is slice 1 of a decomposed effort — a minimal working engine that links a session and sends/receives text through the unchanged engine-neutral pipeline. Everything outside that minimal set is honestly capability-gated to HTTP 501.

Selectable via ENGINE_TYPE=baileys. The whatsapp-web.js engine and the default ENGINE_TYPE are untouched (backward compatible), and the work builds directly on the opaque per-engine-config foundation (#296): the factory passes only neutral fields + an opaque context.config blob each engine reads its own namespace from.

In scope (the minimal working engine)

  • Lifecycle: initialize / disconnect / logout / destroy
  • Auth: useMultiFileAuthState (per-session dir under BAILEYS_AUTH_DIR), QR via connection.update.qr, requestPairingCode, creds persisted on every creds.update
  • Connection mapping → neutral callbacks: qronQRCode/QR_READY, openonReady+phone/pushName/READY, close→ terminal on loggedOut (no reconnect) vs. auto-reconnect on recoverable closes (Baileys emits restartRequired right after pairing — reconnect is required for linking to complete), guarded against reconnecting after an intentional close
  • Send text: sendTextMessagesock.sendMessage(jid, { text })
  • Recipient resolution: checkNumberExists / getNumberId via sock.onWhatsApp (powers send-to-number + the dashboard MessageTester)
  • Typing presence: sendChatStatesock.sendPresenceUpdate (so the default-on anti-ban typing path works)
  • Inbound: messages.upsertonMessage (incoming) / onMessageCreate (fromMe), messages.updateonMessageAck, all mapped to the neutral IncomingMessage / DeliveryStatus by a new baileys-message-mapper (the sole Baileys-scheme→neutral boundary)

Out of scope → gated to HTTP 501

Media/location/contact/sticker sends, reply/forward/react/delete, contacts/groups/chats/history/seen, labels/catalog/channels/status — every one throws the new typed EngineNotSupportedError (→ 501). No self-maintained store in this slice (that's the largest piece of net-new infra; it unlocks most of the above in later slices). getFeatures() advertises only ['text-messages', 'typing-indicator'].

⚠️ Breaking: Node.js ≥ 20.19 now required

@whiskeysockets/baileys is ESM-only ("type": "module"). OpenWA is CommonJS, so it require()s the lib at startup, and because the engine factory statically imports the Baileys plugin, the lib loads on every boot — even with the default whatsapp-web.js engine. require()-of-ESM needs Node ≥ 20.19 (and 22.12+); on older Node the app would fail to boot with ERR_REQUIRE_ESM. This PR declares engines.node >= 20.19.0 and documents it (CI + Docker already run Node 22). Operators on EOL Node 18 must upgrade. → targets v0.3.0 per the SemVer policy.

Alternative considered: lazy-import() the Baileys lib so wwjs-only users keep Node 18. Deferred in favor of declaring the floor (Node 18 is EOL since April 2025). Happy to switch to the lazy-load approach if you'd prefer to preserve the older floor — say the word before merge.

Notes

  • Dependency pinned exactly to @whiskeysockets/baileys@6.7.23 (stable 6.x), not npm's latest (7.0.0-rc13, a pre-release). Core APIs are unchanged across 6→7; the adapter + baileys.types.ts boundary isolates a future bump.
  • Proxy is gated on this engine for slice 1 (Baileys proxying needs an http/socks agent dependency); a configured proxyUrl is logged-and-ignored and documented in .env.example.
  • Baileys' own JID schemes (@s.whatsapp.net / @g.us / @lid / status@broadcast) and proto types stay strictly inside the adapter + mapper + types files — nothing leaks into the neutral pipeline, webhooks, or dashboard.

Testing

  • 566 unit tests + 6 e2e green; backend lint 0 / build clean; dashboard build clean / lint 0 errors.
  • New coverage: baileys-message-mapper (neutral shaping, @lid/group/fromMe/status), the adapter (QR/open/close + reconnect-vs-terminal, gating, text send, recipient resolution, presence, inbound fan-out, pairing-code guard), the plugin (opaque-config read + fallback + minimal feature set), and a factory-registration test.
  • E2E boot gate (test/baileys-engine.e2e-spec.ts): boots the full AppModule with ENGINE_TYPE=baileys and asserts the engine is selected + the plugin enabled — without connecting a socket. (This is also the gate that catches DI module-load cycles.)
  • The CommonJS app's runtime require() of the ESM lib was verified directly on Node 22 (all symbols resolve); the jest stub for Baileys is a test-environment workaround only (ts-jest can't load type: module), not a masked runtime break.

Next slices

  • Slice 2: media/location/contact/sticker sends + reply/forward/react/delete (introduces the message store)
  • Slice 3: contacts + groups + chats/history/seen (store-backed)

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