feat: read-only WhatsApp connector (Baileys) as a channel source - #245
Open
oxedom wants to merge 5 commits into
Open
feat: read-only WhatsApp connector (Baileys) as a channel source#245oxedom wants to merge 5 commits into
oxedom wants to merge 5 commits into
Conversation
Ports the whatsapp-sidecar core from the moomacha repo into the daemon, running in-process next to the Telegram gateway (server/whatsapp/ mirrors server/telegram/). One Baileys connection per account, read-only by construction (no send path, no read receipts, no presence); every inbound message is normalized and posted to a channel board on the home cabinet. Off unless WHATSAPP_ACCOUNTS is set in .cabinet.env; a chokidar watch enables/reconfigures it live, and an owner.json pid marker keeps two daemons from fighting over the same session files. QR pairing prints in the daemon log and is mirrored to a runtime file for a future UI card. Session keys persist under <data>/.agents/.whatsapp/store/<id>/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Baileys lazily require()s link-preview-js, jimp, and sharp for link previews and media re-encoding — paths the read-only WhatsApp gateway never invokes and deps we don't install. esbuild resolves requires eagerly, so mark them external to keep electron:prep building. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds WHATSAPP_PAIRING_PHONE support (requestPairingCode instead of QR) and a Settings UI card polling connection status, for validating the onboarding flow before deciding whether to build this properly.
- Fix Baileys 6.x getPlatformId bug that broke pairing-code linking
(charCode sent instead of the platform enum, WhatsApp closes with an
immediate 401). Same workaround as moomacha's whatsapp-sidecar this
connector was ported from. Guard against requesting more than one
code per Connect click, and delay the request slightly so it doesn't
race the socket handshake.
- Add WhatsApp as a native integration card in the Integrations Hub
(logo, blurb, capabilities) with its own settings panel: phone
number in, live-polled pairing code / connected state out, and an
unlink action that clears the saved session.
- New routes: POST /api/whatsapp/connect, POST /api/whatsapp/disconnect,
GET /api/whatsapp/status — backed by a status file the daemon's
AccountConnection mirrors on every connection.update.
Known issue found during manual testing: the gateway's .cabinet.env
chokidar watcher (server/whatsapp/gateway.ts) stops reacting to file
changes after the first reconcile in a given daemon process — Telegram's
identical watcher keeps working on the same file, so this looks specific
to our watcher instance, not a general chokidar/inotify limit. Live
Connect/Disconnect clicks may silently no-op until the daemon restarts;
verified pairing works when WHATSAPP_ACCOUNTS/WHATSAPP_PAIRING_PHONE are
already set in .cabinet.env before boot (goes through the direct
reconcile("daemon boot") call instead of the watcher). Needs a follow-up
fix before this ships — the watcher failure mode is silent
(envWatcher.on("error") swallows it) which will confuse users the same
way it confused this test session.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a read-only WhatsApp connection that runs inside the Cabinet daemon, structured exactly like the Telegram gateway (
server/whatsapp/mirrorsserver/telegram/). The core (normalize + bus + per-account Baileys connection) is ported from thewhatsapp-sidecarpackage in the moomacha repo; the sidecar's loopback HTTP/SSE layer was dropped because the daemon is Node and consumes the message bus in-process.WHATSAPP_ACCOUNTSis set in.cabinet.env(idorid:Label, comma-separated). A chokidar watch applies changes live, no daemon restart.sendMessagepath exists; the connection never marks chats read and never sends presence. Nothing inbound can drive Cabinet.#whatsapp) viachannels-manager.<data>/.agents/.runtime/whatsapp/qr-<id>.txtso a future settings card can render it. Session keys persist under<data>/.agents/.whatsapp/store/<id>/.owner.jsonpid marker (same pattern as Telegram) prevents two daemons (dev + packaged) from corrupting the same Baileys session.See
docs/WHATSAPP_CONNECTOR.mdfor setup and the ToS caveat (Baileys speaks the unofficial WhatsApp Web protocol — this is an opt-in personal connector; paired accounts carry a nonzero ban risk).Verification
npx tsx --test test/whatsapp-*.test.ts(normalize, bus, config parsing) — all passtsc --noEmitandeslintcleanNext steps (in rough order)
cli-proxy-connector-card.tsx): configure accounts, show per-account connection status, and render the pairing QR from the runtime file instead of making users dig through daemon logs. Needs a small daemon endpoint exposing account status + QR.server/telegram/router.ts) is the template. Needs a deliberate gating design first (which chats/senders may trigger runs; fail-closed like Telegram's allowlist).@whiskeysockets/baileyscleanly and measure size impact before a release.🤖 Generated with Claude Code