Skip to content

Engine pluggability: decouple whatsapp-web.js specifics from the engine-neutral layer #265

@rmyndharis

Description

@rmyndharis

Context

OpenWA has a pluggable engine architecture — the seam is IWhatsAppEngine (neutral types), and only the adapter layer (src/engine/adapters/*, src/plugins/engines/whatsapp-web-js/*) should know whatsapp-web.js. A code audit found several places outside that seam that assume whatsapp-web.js specifics and would break (or silently misbehave) under a different engine (e.g. Baileys, which uses @s.whatsapp.net JIDs and a different ack/presence/type model).

PR #264 already addressed the lightweight items (engine-neutral typing via sendChatState, mark-chat-read JID validation relaxed, real engine library version surfaced). This issue tracks the remaining, larger decouplings — each is its own PR to keep regression surface contained.

Findings (ranked)

🔴 High — delivery ack scale leaks whatsapp-web.js integers end-to-end

The raw wwebjs MessageAck integer (-1..4) flows through the neutral interface (MessageResult.ack, onMessageAck(id, ack: number)), is interpreted in message-status.util.ts (ackToMessageStatus) and session.service.ts (ack < 0message.failed), embedded in webhook payloads + idempotency keys, re-emitted by events.gateway.ts, and re-mapped again in the dashboard (Chats.tsx statusMap, useWebSocket.ts). Baileys uses a different/offset integer scale → every delivery/read state would mis-map.

  • Fix: introduce a neutral DeliveryStatus enum (pending|sent|delivered|read|played|failed) at the seam; each adapter maps its native codes; services/webhook/ws/dashboard consume the neutral string.

🔴 High — IncomingMessage.type is the raw wwebjs MessageTypes string

message-mapper.ts assigns type: msg.type (chat/ptt/voice/revoked/…) directly; the dashboard switches on those tokens (Chats.tsx media render, 'ptt'/'voice', and a latent 'chat' vs 'text' mismatch to verify).

  • Fix: normalize to a neutral MessageType union/enum at the adapter boundary ('chat' → 'text', 'ptt'/'voice' → 'voice', …); consumers switch on the neutral type.

🟠 Medium — JID construction outside the adapter

  • contact.controller.ts builds ${number}@c.us in the check-number response.
  • dashboard/MessageTester.tsx builds …@c.us client-side.
  • session.service.ts hardcodes status@broadcast.
  • Fix: let the engine canonicalize numbers → JIDs (checkNumberExists returns the resolved id); surface an isStatusBroadcast/chatType flag instead of string-matching.

🟠 Medium — Puppeteer/browser config baked into the neutral engine contract

infra.controller.ts, EngineCreateOptions, and plugin.createEngine pass headless/puppeteerArgs/executablePath to every engine — meaningless for a non-browser engine.

  • Fix: pass only neutral fields generically; deliver engine-specific config via each plugin's own (declared) config schema (opaque bag).

🟠 Medium — EngineFactory hard-registers only the wwebjs plugin

registerBuiltInEngines() registers a single engine and create() resolves by type, but there's no real registry for a second engine.

  • Fix: a registry (Map<engineType, IEnginePlugin>) seeded by each built-in engine module, selectable by config.

Notes

  • Audit verified: there are zero raw import 'whatsapp-web.js' statements outside the adapter layer — the leaks are semantic (values/shapes/JIDs), not direct imports.
  • Swagger example strings using @c.us are documentation-only (low priority; genericize opportunistically).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions