github rmyndharis/OpenWA v0.15.0

4 hours ago

Added

  • CHAT_MEDIA_ARCHIVE_OUTBOUND archives media this account sent — a sub-flag of CHAT_MEDIA_ARCHIVE_ENABLED (off by default), so a sent attachment gets the same durable file copy, S3 portability and TTL retention inbound media already had. Refs #1165.

  • Group membership requests: list, approve, reject, and a group.join_request event — the join-approval queue wwebjs/Baileys both expose is now surfaced: GET/POST .../groups/:groupId/membership-requests[/approve|/reject] on both engines, plus a webhook/socket event when someone asks to join an administered group. Refs #1164 (discussion).

  • Own global presence: PUT /sessions/:id/presence — appear online or offline on both engines; an always-online headless bot suppresses the phone's own notifications, and available: false hands them back. Connection-scoped (re-issue after a reconnect). Refs #871.

  • Blocklist read: GET /sessions/:sessionId/contacts/blocked — the missing read half of the block/unblock endpoints, as a bare array of neutral contact ids on both engines; on Baileys an unanswered query is a 503, never an empty list.

  • Upstream capability-surface drift guardscripts/check-upstream-surface.mjs (in test:scripts) diffs the installed engines' Client/socket methods and event maps against a reviewed snapshot, so an engine bump that ships new capabilities fails CI until the delta is reviewed instead of sitting unnoticed.

  • The media download route now serves media sent by the accountGET /messages/:chatId/:messageId/media falls back to the inline copy stored on the message row when no archived file exists, which covers outbound messages (never archived) and inbound messages whose archived file retention has purged.

Changed

  • ⚠️ Breaking (behavior). Engine operations during a WhatsApp Web page reload answer 409 instead of a raw 500 — WhatsApp Web reloads its own page (measured ~5 minutes after a fresh pairing, among others) and whatsapp-web.js re-injects into it; for that bounded window every engine route now answers the documented retryable 409 naming the reload, where it previously surfaced raw TypeError 500s — and the six chat write routes (read/unread/archive/unarchive/typing/clear) answered 200 {success:false}. Retry after the session re-emits ready. Typed 4xx also no longer count toward the send breaker, so a reload can no longer latch its 15-minute cooldown.

  • GET /sessions/{id}/chats now documents and answers the 503 a dead page deserves — the one read route that still surfaced a page-transport death as a raw 500, now split exactly like its siblings.

  • 125 routes now document a status they could already answer — 91 gained the 409 an unconnected session answers, plus 400 on six sends, 403 on six group and channel writes, 404 on eleven, and 501 on eleven more. A generated client had no branch for any of them.

  • Five routes now document a 503 they have been answering for releases — the four group participant writes since 0.14.5, and listing chats by label since 0.14.0. There the 503 is what separates "WhatsApp never answered" from the per-participant refusals a 200 reports inside results.

Fixed

  • A URL-based send no longer discards bytes the gateway already downloaded — merging its metadata onto the engine's own-send echo replaced that echo's real payload with the URL string, so a whatsapp-web.js URL send rendered as a bare marker after a reload.

  • A bulk media send lost its attachment when the engine echo won the persist race — the batch write collided on UNIQUE(sessionId, waMessageId) and was swallowed into a warning, leaving only the echo's row, which on a Baileys API send carries a media-less marker; it now merges onto that row like the single-send path already did.

  • A WhatsApp Web page reload no longer kills a starting session — a navigation landing during the first injection rejects Client.initialize() before whatsapp-web.js has installed its own re-inject handler, and the adapter's error channel is terminal, so one reload parked the session FAILED with nobody retrying (on start and on reconnect alike); the engine now retries that launch once, within the init deadline, before declaring failure.

  • The liveness watchdog no longer tears down a session that is healing itself — a post-ready page navigation makes getState() reject until WhatsApp Web reboots, which read as two failed probes and a teardown of a page whatsapp-web.js was about to re-inject; the probe now grants a bounded post-navigation grace (never for a logout navigation, and capped per episode so a reload loop cannot mask a genuinely dead page).

  • Three shared status descriptions said the wrong thing on the routes that borrowed themsend-text's 501 is specifically a caller-supplied customLinkPreview, which only Baileys accepts, not a wholesale gap in the engine; POST /channels/subscribe takes an invite code and no channel id, so its 404 is an unresolvable invite; and the 400 on six send routes named the unreachable recipient as though it were the only cause, while omitting body validation and an inactive session, and cited a 404 meaning that four of those six routes do not declare.

  • Four contract corrections from a review of this cycle's own work — the 409 description said the session had no engine when the guard actually fires on an engine that exists but is not ready (an unstarted session answers 400); send-bulk declared a 409 it cannot produce, since the batch drains after the handler has answered 202; the nine catalog and status routes declared that 409 but not the 404 their own service throws for an unstarted session; and the logout example showed a null pushName and connectedAt beside a populated lastActive, though logout clears only phone.

  • Fifteen statements in the codebase that were not true — comments, a test name and an architecture sketch still described the own-send echo by its pre-0.10.0 behaviour: that whatsapp-web.js echoes carry no media, that the engines are at parity in carrying none, and that the echo never writes the database.

  • An API key's session allow-list showed ids that can never matchallowedSessions is matched by exact equality against the session id, but the example was ['session-uuid-1', 'session-uuid-2'], which would scope a key to nothing and deny every request. It now shows real UUIDs.

  • Seven published examples showed values the API cannot produce — a sess_-prefixed session id that ParseUUIDPipe rejects, two truncated UUIDs, a logout example missing the required engineLoaded, a readiness probe keyed on database rather than mainDatabase/dataDatabase, a send-text capability no engine declares, and the whatsapp-web.js id form on a Baileys-only route.

Don't miss a new OpenWA release

NewReleases is sending notifications on new releases.