Skip to content

Changelog

Manaa-Soft edited this page Oct 22, 2026 · 16 revisions

0.15.0 (2026-07-28)

Added

  • WhatsApp Catalog Product DocType — links ERPNext Items to WhatsApp catalog with auto-fetch of name, description, image, and price from Item
  • 6 whitelisted methods in catalog.py: get_catalog_products, get_catalog_product, sync_catalog_products, send_product_to_chat_direct, send_catalog_to_chat, get_openwa_catalog_info
  • Automatic fallback — when OpenWA catalog endpoints return 501, sends richly formatted text+image messages
  • Client-side JS — auto-fetch from Item on link select, Sync and Send to Chat buttons
  • 10 test classes in test_catalog.py covering sync, send, fallback, and summary flows
  • Docs — API_REFERENCE (catalog section), ARCHITECTURE, FLOWS (Flow 12), IMPROVEMENTS

Fixed

  • Optimistic concurrency error ("has been modified after you have opened it") — replaced frappe.db.set_value() with doc.save() in _sync_single_product() so Frappe's modified timestamp stays in sync
  • Auto-fetch on re-save — _auto_fetch_item_fields() now only runs for new documents (__islocal=1), preventing validate from touching existing doc fields during sync saves

0.16.0 (2026-07-28)

Added

  • send_product_to_customer — new method to send a catalog product directly to a Customer's WhatsApp number (resolves Contact → phone → chat ID)

Removed

  • Catalog sync removed entirely — removed sync_catalog_products, sync_single_product, get_openwa_catalog_info since OpenWA does not support WhatsApp catalog operations
  • No more error logs from catalog sync background attempts
  • DocType controller and JS simplified — no Sync button, no sync status workflow
  • API reference reduced — catalog section now documents 5 methods instead of 6

Changed

  • send_single_product_to_chat renamed to send_product_to_chat — always uses fallback text+image (never attempts native catalog send); send_product_to_chat_direct now delegates to send_product_to_chat

Fixed

  • No more 501 errors from OpenWA catalog stubs polluting server logs

Changelog

All notable changes to OpenWA Bridge are documented here.

Format: Keep a Changelog Versioning: Semantic Versioning


[Unreleased] — feature/improvements branch

Added

  • OpenWA Event Log DocType — persists non-message webhook events (group.join, group.leave, group.update, call.received, status.received, message.reaction, webhook.delivery_failure) instead of only logging them
  • Inbound event persistence — _log_event() helper; group/call/status handlers now insert Event Log rows (best-effort, never breaks webhook processing)
  • Reactions persistence — message.reaction events stored on the WhatsApp Message doc in openwa_reactions JSON field as [{emoji, sender, timestamp}] with advance-only dedupe per (emoji, sender)
  • Webhook DLQ replay — check_webhook_delivery_failures() reads OpenWA delivery-failures (limit 50/session), writes Event Log rows deduped by idempotency key, and replays via replay_webhooks(); wired into daily(); 403 (non-ADMIN key) skipped silently
  • Scheduled send — scheduled_at (Datetime) on OpenWA Outbox holds entries in Pending until due; openwa_scheduled_at custom field on WhatsApp Message propagated in after_insert; processor skips future-scheduled entries without bumping attempts
  • Base64 media outbound — image/video/audio/document sends accept {base64, mimetype, filename} in addition to {link} (mirrors sticker pattern), with a clear error when neither is supplied
  • Send-as-PDF — "Send To Whatsapp" dialog can send the open document as a PDF via OpenWA send-document; render_doc_as_pdf() helper (Chrome → wkhtmltopdf fallback), whitelisted send_document_pdf() method, send-time PDF rendering in the outbox worker (always current, retry-safe), openwa_send_pdf / openwa_print_format / openwa_pdf_filename custom fields, unit tests
  • Media reply workaround — OpenWA POST /messages/reply is text-only, so media replies send the media unquoted then a text reply quoting the returned media messageId
  • Docs — Tier 2 sections in API_REFERENCE, KNOWN_ISSUES, CUSTOM_FIELDS, IMPROVEMENTS
  • Contact statuses API — get_contact_statuses to get statuses from a specific contact
  • Status media streaming — get_status_media to stream stored status image/video
  • Channel subscribe/unsubscribe — subscribe_channel (via invite code) and unsubscribe_channel APIs
  • Channel details — GET /channels/:channelId endpoint documented
  • Message reactions — get_message_reactions API to retrieve reactions for a message
  • Batch cancel — cancel_batch API to stop a running bulk batch
  • kind field — inbound messages now carry kind discriminator (individual, group, channel, status, broadcast)
  • Typing indicator documentation — noted OpenWA's built-in SIMULATE_TYPING redundancy with bridge indicator
  • Message editing — edit_message API + edit content_type for outbound message edits
  • Status/Stories posting — post_status_text, post_status_image, post_status_video, get_statuses for WhatsApp status updates
  • Call handling — reject_call API + call.received webhook event logging
  • Chat management — mark_chat_read, mark_chat_unread, get_chat_history APIs
  • Full-text search — search_messages API for searching across all sessions
  • Group management — list_groups, create_group, add_group_participants, remove_group_participants, promote_group_admins, demote_group_admins, set_group_name, leave_group APIs
  • Label management — list_labels, add_label_to_chat, remove_label_from_chat for WhatsApp Business labels
  • Batch messaging with progress — send_bulk_with_progress async batch API with progress tracking
  • Profile management — set_profile_name, set_profile_status, set_profile_picture APIs
  • Session stats — get_session_stats for aggregate session statistics
  • Channels/Newsletters — list_channels, get_channel_messages for WhatsApp Channels
  • Typing indicators in send flow — auto-send typing indicator before each message (non-blocking)
  • message.edited webhook handler — updates WhatsApp Message body when sent message is edited
  • session.reconnect_loop webhook handler — alerts when session is stuck in reconnect loop
  • group.join/group.leave webhook handlers — logs group membership changes
  • group.update webhook handler — logs group metadata changes
  • call.received webhook handler — logs incoming call events
  • Contact management API — check_whatsapp_number, block_contact, unblock_contact
  • Typing indicators — send_typing_indicator (typing/recording/paused states)
  • Bulk messaging — send_bulk_openwa via OpenWA /messages/send-bulk
  • Message forwarding — forward_message via OpenWA /messages/forward
  • Message deletion — delete_message with optional revoke (delete for everyone)
  • Pairing code authentication — request_pairing_code as alternative to QR scanning
  • Sticker messages — send_sticker (URL or base64) + sticker content type
  • @mention support — auto-extracts @NNNN@c.us JIDs from text messages
  • Extended webhook events — message.sent, message.revoked, message.reaction, message.edited, session.qr, session.authenticated, session.disconnected, session.reconnect_loop, group.join, group.leave, group.update, call.received
  • Inbound Communication/Lead auto-creation — creates Communication doc linked to Contact; auto-creates Lead+Contact for new numbers
  • Outbox cleanup scheduler — daily cleanup of old outbox entries
  • Circuit breaker — blocks sends after N consecutive failures per account
  • Two-phase notification pattern — sync outbox creation + async background send
  • Inbound webhook hardening — always HTTP 200, HMAC lenient/strict mode, idempotency, rate limiting
  • Auto-sync webhook secret — saving WhatsApp Account auto-syncs secret to OpenWA
  • Session health check — hourly/daily scheduler restarts disconnected sessions
  • Pre-send session verification — auto-restarts sessions before sending messages
  • Dynamic image headers — renders documents as PNG via Print Format + PyMuPDF
  • SSRF validation — URL validation warns on private/internal IPs
  • Role-based access — whitelisted methods check Frappe permissions
  • Configurable per-account settings — HMAC strict, API timeout, session start timeout, rate limit, circuit breaker, max outbox attempts
  • WhatsApp Workspace — shortcuts to all key DocTypes
  • Comprehensive test suite — 40+ unit and integration tests
  • HTTP connection pooling — module-level requests.Session() for all API calls
  • Distributed lock — Redis-based lock prevents duplicate outbox processing
  • Replay webhooks API — fetches messages from OpenWA and creates WhatsApp Message docs
  • Outbox dashboard API — system health, success rates, queue depth, circuit breaker status
  • Architecture Decision Records — 5 ADRs documenting key design decisions
  • Uninstall cleanup — before_uninstall() deletes OpenWA sessions and webhooks
  • Outbound send idempotency — single-recipient sends now carry an Idempotency-Key header (derived from the WhatsApp Message doc name by build_idempotency_key()); header images use a distinct -hdr-suffixed key so the image and its caption are deduplicated independently. Key threaded through openwa_api() and both direct-HTTP send paths (whatsapp_message._send_via_openwa, tasks._send_dynamic_header_for_outbox).
  • message.receipt webhook event — opt-in per-recipient delivery/read/played receipts for group and status-broadcast sends (_handle_message_receipt, persisted via _log_event with contact=participant); subscribed explicitly in OpenWA (not delivered to the * wildcard). Added to the webhook event list in Settings and the OpenWA Event Log event-type Select.
  • Actionable OpenWA HTTP errors — openwa_error_message() parses the JSON error body (message/error/code) instead of a bare raise_for_status(), so engine 400/503 and vote-poll 400 POLL_OPTION_NOT_FOUND surface the real reason; still raises requests.HTTPError so existing except blocks keep working.

Fixed

  • Unit test file did not compile — every MagicMock(get_password.return_value=...) used Python 3.8-removed dotted-name keyword args (SyntaxError), so test_account.py never parsed and no pytest run could include it. Replaced with mock.get_password.return_value = "..." assignments across all 55 test mocks; the suite now compiles and lint/typecheck can analyze it.
  • Stale session recovery — when a session is deleted from OpenWA dashboard, the Frappe form now detects it (returns not_found status) and shows a "Setup OpenWA (New Session)" button instead of getting stuck
  • reset_openwa_session method — new whitelisted method to clear stale openwa_session_id + cache
  • get_openwa_session_status distinguishes 404 from unreachable — returns not_found when server is reachable but session is gone, error when server is down
  • Session disconnects after 1-2 minutes — health check now handles failed sessions with force-kill → delete+recreate flow; recommended MEDIA_DOWNLOAD_ENABLED=false to prevent media flood crashes
  • Health check force-kill — hourly health check now tries force-kill (keeps auth, no QR rescan) before falling back to delete+recreate for failed sessions
  • Duplicate message bug — notification flow with dynamic header sent duplicate messages
  • Broken test imports — corrected import paths and frappe cache parameter names
  • RedisWrapper.set_value() only_set unsupported — changed to get_value + set_value pattern
  • UnboundLocalError in outbox processor — account referenced before definition
  • Template sent as raw Meta dict — stored entire Meta payload instead of rendered text
  • Dynamic header skipped template — outbox returned early after image send
  • Jinja sent as raw template — set template on doc causing wrong send path
  • requests not defined — bare requests.post() calls missed during connection pooling migration
  • Template vs text send guard — changed from if self.template to if self.use_template and self.template
  • Jinja send type bypassed OpenWA — fell through to parent's Meta API
  • Empty vars sent to OpenWA — bulk messages with empty template_variables caused 500
  • get_account_setting treats 0 as falsy — changed to val if val is not None
  • time.sleep() blocking workers — reduced polling from 20s to 3s
  • Outbox race condition — added Redis-based distributed lock
  • frappe.db.commit() anti-pattern — removed explicit commit from inbound webhook
  • "Password not found" error — account resolution before _is_openwa_account() check
  • frappe.msgprint() in background — replaced with frappe.logger().info()
  • N+1 queries in health check — cached account loading
  • Documentation inaccuracies — FLOWS.md, ARCHITECTURE.md, DEPLOYMENT.md, CUSTOM_FIELDS.md corrected
  • send_document_pdf missing read-permission check — the whitelisted method accepted any caller-supplied document and rendered/sent it later as the worker user; it now rejects the send unless the caller can read the reference document (frappe.has_permission).
  • Phantom stats methods removed — get_overview_stats / get_message_stats were documented and tested but never implemented (the real /api/stats/* routes require an ADMIN + unscoped key the bridge does not hold); removed from tests, docs and the wiki. get_session_stats (GET /api/sessions/stats/overview) remains.
  • Multi-chunk bulk total inflation — send_bulk_openwa summed counts against the full recipient list for every chunk; each chunk is now sized against its own batch.
  • Unbounded delivery-failure dedupe scan — _known_delivery_failure_keys now filters Event Log rows to the last 30 days.
  • scheduled_at string comparison — after_insert normalizes a possible string scheduled_at via frappe.utils.get_datetime before comparing.
  • Statistics / setup docs corrected — Statistics section documents the real unscoped /api/stats/* routes; setup_openwa_session example shows the actual qr_ready/ready contract.

Changed

  • OpenWA v0.19-v0.23 API contract support — bridge follows the OpenWA v0.23.7 API surface:
    • send_catalog_message deprecated — POST /messages/send-catalog was removed in v0.19 and answers 501 on every engine; the method now returns a clear error pointing to send_product_message (product card, Baileys) / send_product_to_chat / send_catalog_to_chat (text+image / text-summary fallbacks) instead of calling the dead endpoint.
    • mark_chat_read accepts an optional message_ids argument — comma-separated list forwarded as the v0.23 messageIds array (max 100, Baileys) for per-message read receipts; omitted → the whole chat is marked read as before.
    • New message endpoints: vote_poll (POST /messages/vote-poll), pin_message (POST /messages/pin, validated durationSeconds 86400/604800/2592000), unpin_message (POST /messages/unpin), star_message (POST /messages/star), get_chat_media (GET /messages/:chatId/:messageId/media).
    • New chat/session endpoints: archive_chat, mute_chat, pin_chat (POST /chats/archive|mute|pin), clear_chat_messages (DELETE /chats/:chatId/messages), get_session_proxy / set_session_proxy (per-session egress proxy GET/PATCH /proxy).
    • New group endpoints: get_group_membership_requests, approve_group_membership_requests, reject_group_membership_requests (GET/POST /groups/:groupId/membership-requests[/approve|/reject]; empty participant list approves/rejects all).
    • New status endpoint: post_status_voice (POST /status/send-voice, URL or base64).
    • New channel endpoints: create_channel (POST /channels), mute_channel (POST /channels/:channelId/mute), demote_channel_admin (POST /channels/:channelId/admins/demote), transfer_channel_ownership (POST /channels/:channelId/owner/transfer).
    • The v0.19 uniform {sessionId} route change requires no bridge change — relative paths are appended to /api/sessions/{session_id} by openwa_api().
  • OpenWA v0.23.5-v0.23.7 refinements:
    • Inbound commerce messages (0.23.5) — OpenWA now types order / product on message.received instead of bodyless unknown; openwa_type_to_frappe maps both to content_type = "order".
    • Inbound prompt taps (0.23.6+) — business-prompt button/list replies arrive as type: "text" with a button { id, text? } field; _handle_inbound_message stores the tapped button text (or id) as the message body when the body is empty.
    • New click_button method (0.23.6) — POST /messages/click-button sends a structured reply against a stored prompt (Baileys; whatsapp-web.js answers 501).
    • Catalog docs corrected — OpenWA exposes read-only catalog endpoints plus send-product; the fallback-product path is now described accurately.
  • OpenWA v0.24-v0.25 API contract support — bridge tracks the current OpenWA main (v0.25.0):
    • Outbound single-recipient sends carry an Idempotency-Key header (v0.25) — see Added. OpenWA replays the stored response within 24 h instead of sending twice and returns Idempotent-Replayed: true on replay.
    • message.ack / message.failed payloads carry chatId (v0.25) — _handle_status_update reads it and _find_whatsapp_message() uses it as a fallback for messages whose stored to is blank.
    • New opt-in message.receipt webhook event (v0.25) — see Added.
    • Webhook receivers must deduplicate on X-OpenWA-Idempotency-Key (v0.25 upgrade note) — the inbound endpoint already reads it.
  • Settings moved to WhatsApp Account — 7 settings moved from global to per-account
  • WhatsApp Account custom fields — expanded from 9 to 16 fields
  • OpenWA Bridge Settings — reduced from 10 to 3 fields (only global settings remain)
  • All requests.get/post calls — replaced with _http_session for connection pooling
  • All get_password("openwa_api_key") calls — replaced with get_api_key() helper
  • All _is_openwa_account() definitions — consolidated to single function in utils.py

[0.1.0] — 2026-07-01

Added

  • Initial release
  • OpenWA integration via override_doctype_class
  • Outbound message routing (text, image, video, audio, document, location, contact, poll, reaction, reply, template)
  • Inbound message reception via webhook
  • Template sync to OpenWA
  • Notification system (Jinja, Template, Dynamic Image Header)
  • QR code setup flow
  • Auto-reconnect scheduled tasks

Clone this wiki locally