Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 20 Mar 21:16
· 475 commits to main since this release

What's New in v0.4.0

This is a major release with 70+ PRs since v0.3.0, bringing runtime flexibility, new feature areas, significant performance work, and many protocol fixes.

Highlights

  • Runtime-agnostic core (#393) — Tokio is no longer a hard dependency. A new Runtime trait allows plugging in custom runtimes (useful for WASM, embedded, or alternative async runtimes). BotBuilder now uses a typestate pattern requiring .with_runtime().
  • Newsletter (Channel) support (#390) — Full CRUD: create, join, leave, update, list, get metadata, send messages, reactions, paginated history, and live update events.
  • Community support (#392) — Create/deactivate communities, link/unlink subgroups, query metadata, fetch participants. MEX (GraphQL) queries included.
  • Group invites, membership requests & privacy (#396) — Join via invite link (with approval handling), approve/reject membership requests, set member add mode, individual privacy settings, default disappearing messages, business profile queries.
  • Pluggable cache backends (#381) — New CacheStore trait lets you plug in Redis, Memcached, or any custom cache backend per cache type.
  • Bundled SQLite (#364) — bundled-sqlite feature (default-on) eliminates the need for system-level SQLite.
  • Media resilience (#361, #356) — Host failover, resumable uploads (≥5 MiB), download URL re-derivation on 404/410, and automatic retry on 401/403 with auth refresh.
  • DB-backed sent message retry (#360) — Sent messages stored in SQLite instead of memory. RSS baseline dropped from 93.9 MiB to 37.9 MiB.
  • Configurable caches (#334) — All cache TTLs and capacities are now configurable via CacheConfig.
  • Stable Rust support (#314, #319) — portable_simd is opt-out via the simd feature flag. All unsafe removed from the binary decoder.

Performance

Massive allocation reduction across hot paths (#375#380, #382, #385, #387):

  • Cow<'static, str> for Jid.server, Node.tag, and Attrs keys
  • Granular cache patching instead of invalidate+refetch
  • Single-allocation protocol address strings
  • History sync streaming and RAM optimization (#362)
  • Pre-allocated collections throughout

Connection Reliability

  • WhatsApp Web-matching keepalive (15s ping, 20s dead socket, Fibonacci backoff) (#341)
  • Stream error handling: 401 (logged out), 409 (stream replaced), 429 (rate limited) (#353)
  • 20s transport connect timeout (#366)
  • AtomicBool is_connected() preventing silent ack drops (#383)
  • Deterministic message ordering replacing 500ms re-queue hack (#328)

New Events

  • GroupUpdate (replaces GroupInfoUpdate) — granular per-action group notifications
  • ContactUpdated, ContactNumberChanged, ContactSyncRequested
  • DisappearingModeChanged, NewsletterLiveUpdate
  • PictureUpdate (updated fields), StarUpdate, PushNameUpdate, SelfPushNameUpdated
  • StreamReplaced, BusinessStatusUpdate, DeviceListUpdate

Bug Fixes

  • Prekey ID collision causing ~5.7% message decryption failures during offline sync (#322)
  • DeviceSentMessage not unwrapped before dispatch — self-sent messages appeared empty (#337)
  • AD_JID binary encoding writing incorrect domain_type (#391)
  • App state race condition in storage (#332)
  • Ack type attribute misalignment causing server disconnections (#365)

⚠️ Breaking Changes

BotBuilder requires .with_runtime() (#393)

// Before (v0.3)
let bot = BotBuilder::new()
    .with_backend(backend)
    .with_transport(transport)
    .with_http_client(http)
    .build(handler);

// After (v0.4)
use whatsapp_rust::TokioRuntime;
let bot = BotBuilder::new()
    .with_backend(backend)
    .with_transport(transport)
    .with_http_client(http)
    .with_runtime(TokioRuntime)  // NEW — required
    .build(handler);

Bot::run() returns BotHandle instead of JoinHandle (#393)

// Before
let join_handle: tokio::task::JoinHandle<()> = bot.run().await?;
join_handle.await?;

// After
let handle: BotHandle = bot.run().await?;
handle.wait().await; // dropping BotHandle aborts the task

Jid.server is now Cow<'static, str> (#376)

Code directly accessing jid.server as String needs updating. .to_string() or .as_ref() will work.

NodeBuilder::new() requires &'static str (#385)

Use NodeBuilder::new_dynamic(string) for runtime-generated tags.

NodeValue API simplified (#386)

Removed: as_jid(), to_string_value(), string().
Use: as_str() -> Cow<str>, to_jid() -> Option<Jid>, or PartialEq<str> comparisons.

ProtocolStore trait has new required methods (#360)

If you implement ProtocolStore yourself (custom storage backend), add:

  • store_sent_message()
  • take_sent_message()
  • delete_expired_sent_messages()

GroupInfoUpdate event replaced by GroupUpdate (#327)

Pattern match on Event::GroupUpdate instead of Event::GroupInfoUpdate.

PictureUpdate event fields changed (#316)

Fields are now: author: Option<Jid>, removed: bool, picture_id: Option<String>.

What's Changed (auto generated)

  • test: add tests e2e with bartender by @jlucaso1 in #303
  • Feat improve group tests by @jlucaso1 in #304
  • feat: add set name and status by @jlucaso1 in #305
  • feat: node waiter by @jlucaso1 in #307
  • test: add upload and download e2e tests by @jlucaso1 in #309
  • chore(deps): bump ghash from 0.5.1 to 0.6.0 by @dependabot[bot] in #308
  • fix(wacore): use Vec in key_pair_serde::deserialize to fix JSON roundtrip by @Devansh-bit in #311
  • feat: action state by @jlucaso1 in #310
  • feat: signal cache by @jlucaso1 in #312
  • feat(wacore-binary): stable Rust support with scalar SIMD fallback by @adolfousier in #314
  • test: e2e offline events testing by @jlucaso1 in #313
  • feat: receipts e2e tests by @jlucaso1 in #315
  • feat: profile complete by @jlucaso1 in #316
  • Tests split to improve paralelism by @jlucaso1 in #317
  • refactor: improve macro derive by @jlucaso1 in #318
  • feat: SIMD opt-out, remove all unsafe from decoder, fix LTHash endianness by @jlucaso1 in #319
  • feat: improve notification status handling by @jlucaso1 in #320
  • fix: improve disconnection handling by @jlucaso1 in #321
  • fix: prekey ID collision and signal cache bypass causing decryption failures by @jlucaso1 in #322
  • Fix more improvements decrypt by @jlucaso1 in #323
  • refactor: improve error typesafety by @jlucaso1 in #325
  • Chore increase prekeys by @jlucaso1 in #324
  • perf: bound cache by @jlucaso1 in #326
  • feat: add more events by @jlucaso1 in #327
  • fix: replace 500ms re-queue hack with deterministic message ordering by @jlucaso1 in #328
  • Add Claude Code GitHub Workflow by @jlucaso1 in #329
  • test: improve e2e test by @jlucaso1 in #331
  • fix: app state race storage by @jlucaso1 in #332
  • fix: handle different type of ping by @jlucaso1 in #333
  • feat: add configurable cache TTL and capacity via CacheConfig by @jlucaso1 in #334
  • fix: signal keepalive loop shutdown on connection cleanup by @jlucaso1 in #335
  • fix: unwrap DeviceSentMessage before dispatch by @jlucaso1 in #337
  • feat: merge messageContextInfo when unwrapping DeviceSentMessage by @jlucaso1 in #339
  • fix: keepalive timeout detection and pong id handling by @jlucaso1 in #340
  • feat: match WhatsApp Web keepalive, dead socket, and reconnect behavior by @jlucaso1 in #341
  • feat: handle stream error codes 401, 409, 429 by @jlucaso1 in #353
  • feat: handle disappearing_mode notification type by @jlucaso1 in #354
  • Handle contacts notifications for contact updates, number changes, and sync requests by @Copilot in #358
  • Retry media upload/download on 401/403 with forced media auth refresh by @Copilot in #356
  • Add presence unsubscribe API and re-subscribe tracked contacts on reconnect by @Copilot in #355
  • Add offline sync timeout fallback and offline sequence validation by @Copilot in #357
  • feat: DB-backed sent message retry + configurable cache reductions by @jlucaso1 in #360
  • feat: media host failover, resumable upload, download URL re-derivation by @jlucaso1 in #361
  • perf: history sync RAM optimization + fix protocol mismatches causing disconnects by @jlucaso1 in #362
  • fix: remove unecessary offline sequence by @jlucaso1 in #363
  • feat: bundled sqlite by @jlucaso1 in #364
  • fix: align ack type attribute handling with whatsmeow/WA Web by @jlucaso1 in #365
  • fix: add 20s transport connect timeout matching WA Web by @jlucaso1 in #366
  • feat: handle enc_rekey_retry receipt type for VoIP call re-keying by @jlucaso1 in #371
  • feat: add version rollback protection to app state sync by @jlucaso1 in #374
  • chore(deps): bump once_cell from 1.21.3 to 1.21.4 by @dependabot[bot] in #373
  • chore(deps): bump diesel from 2.3.6 to 2.3.7 by @dependabot[bot] in #372
  • perf: reduce heap allocations in signal cache and server_jid by @jlucaso1 in #375
  • perf: Cow<'static, str> for Jid server and hot-path optimizations by @jlucaso1 in #376
  • perf: eliminate allocations in app state sync and pre-allocate collections by @jlucaso1 in #377
  • perf: reduce allocations in history sync decrypt and LazyConversation by @jlucaso1 in #378
  • perf: use NodeBuilder chaining and jid_attr in send/receipt hot paths by @jlucaso1 in #379
  • perf: single-allocation to_protocol_address_string() for session lock keys by @jlucaso1 in #380
  • feat: pluggable cache store adapter for custom backends by @jlucaso1 in #381
  • perf: granular cache patching instead of invalidate+refetch by @jlucaso1 in #382
  • fix: replace try_lock() in is_connected() with AtomicBool to prevent silent ack drops by @jlucaso1 in #383
  • perf: remove unnecessary locks identified by WA Web comparison by @jlucaso1 in #384
  • perf: use Cow<'static, str> for Node tags and Attrs keys by @jlucaso1 in #385
  • refactor: simplify NodeValue API to 2 methods, fix AttrParser JID bug by @jlucaso1 in #386
  • perf: reduce hot path allocations on send and receive paths by @jlucaso1 in #387
  • feat: newsletter (channel) support — full CRUD, messaging, reactions, live updates by @jlucaso1 in #390
  • fix: write correct domain_type in AD_JID binary encoding by @jlucaso1 in #391
  • feat: community support — full CRUD, subgroup management, queries by @jlucaso1 in #392
  • refactor: runtime agnostic by @jlucaso1 in #393
  • chore: update rand deps and dedupe by @jlucaso1 in #394
  • feat: group invite, membership requests, privacy, business profiles & fixes by @jlucaso1 in #396

New Contributors

Full Changelog: v0.3.0...v0.4.0