Skip to content

feat(receipt): expose the 'offline' attr on the Receipt event - #780

Merged
jlucaso1 merged 1 commit into
mainfrom
protocol/receipt-offline-attr
Jun 8, 2026
Merged

feat(receipt): expose the 'offline' attr on the Receipt event#780
jlucaso1 merged 1 commit into
mainfrom
protocol/receipt-offline-attr

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

WA Web parses offline on incoming receipts (incomingMsgReceiptParser: offline = maybeAttrString("offline")) and on retries (retryRequestParser: hasAttr("offline")). Receipts/retries delivered while the server flushes its offline queue on reconnect carry this marker. We never read it and Event::Receipt had no field for it, so a consumer couldn't distinguish a live receipt from one drained out of the offline queue — relevant for resume metrics and for not re-firing receipt-driven logic on stale acks.

This parses the offline attribute in handle_receipt and adds Receipt.offline (set on both the aggregated per-user and simple receipt paths). Added a test dispatching a receipt with and without the attr.

Breaking for Receipt struct-literal constructors (pre-1.0); in-tree constructors (handle_receipt + the retry tests) are updated. Consumers that match on Event::Receipt fields are unaffected.

WA Web parses 'offline' on incoming receipts (incomingMsgReceiptParser:
offline = maybeAttrString) and retries; receipts drained from the server's
offline queue on reconnect carry it. We never read it, so a consumer
couldn't tell a live receipt from one replayed out of the offline queue
(relevant for resume metrics and for not re-firing receipt-driven logic on
stale acks). Parse the attr in handle_receipt and add Receipt.offline.
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: be94eca9-eb10-4e3f-b499-9535e35b33d8

📥 Commits

Reviewing files that changed from the base of the PR and between 26c6d00 and c38b25a.

📒 Files selected for processing (3)
  • src/receipt.rs
  • src/retry.rs
  • wacore/src/types/events.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Receipt events now include an offline status indicator, distinguishing between receipts delivered live and those retrieved from the offline queue upon reconnection. This enables better tracking of message delivery timing and status.

Walkthrough

Receipt events now carry an offline boolean field derived from the server's <receipt> stanza attribute, indicating whether the receipt was drained from the offline queue on reconnect rather than delivered live. Parsing is applied consistently to both aggregated (per-<user>) and simple receipt dispatch paths, with tests verifying both cases.

Changes

Receipt offline attribute

Layer / File(s) Summary
Receipt event contract
wacore/src/types/events.rs
Receipt struct adds offline: bool field documenting offline-queue drain semantics matching the WA Web parser behavior.
Receipt parsing and emission
src/receipt.rs
Client::handle_receipt reads the optional offline attribute from incoming stanzas and includes the boolean flag in dispatched Receipt events for both aggregated and simple receipt paths.
Receipt offline test coverage
src/receipt.rs, src/retry.rs
New tokio::test verifies that receipts with offline="1" produce Receipt.offline == true and live receipts without the attribute produce false; existing test fixtures updated to include the required field.

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • oxidezap/whatsapp-rust#708: Also modifies Client::handle_receipt and the Receipt event dispatching logic in src/receipt.rs, extending receipt event semantics.
  • oxidezap/whatsapp-rust#313: Introduces e2e offline-event tests that verify receipt delivery behavior after disconnect/reconnect, directly testing the same offline receipt semantics being added here.
  • oxidezap/whatsapp-rust#519: Modifies both Client::handle_receipt and the Receipt struct shape in wacore::types::events, overlapping on the receipt dispatch path and event serialization.

Suggested labels

breaking-change, api-design


Look, the change is straightforward and necessary—you're propagating the offline attribute from the stanza through to the Receipt event, which is the right architectural choice. The parsing logic is clean, both emission paths are covered, and the test coverage validates the expected behavior. No surprises here. Make sure the offline semantics are documented clearly for users of this API so they understand the distinction between drained offline receipts and live ones. This is a public API change, so the breaking-change label is appropriate.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(receipt): expose the 'offline' attr on the Receipt event' accurately and concisely describes the primary change: adding the offline attribute to Receipt events.
Description check ✅ Passed The description clearly explains the motivation (WA Web parses offline but crate never exposed it), implementation details (parsing in handle_receipt, adding Receipt.offline field), and testing coverage. It is directly related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch protocol/receipt-offline-attr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Benchmark Results

67 unchanged benchmark(s)
Benchmark Current Baseline Change
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message() 2,925 2,925 +0.0%
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message() 8,446 8,446 +0.0%
reporting_token_benchmark::key_derivation_group::bench_key_derivation 31,317 31,317 +0.0%
reporting_token_benchmark::token_calculation_group::bench_token_calculation 13,827 13,827 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple() 49,485 49,485 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended() 55,001 55,001 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message() 1,679 1,679 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message() 4,393 4,393 +0.0%
send_receive_benchmark::dm_send::bench_dm_send text:setup_dm_send() 113,073 112,962 +0.1%
send_receive_benchmark::dm_recv::bench_dm_recv text:setup_dm_recv() 1,656,730 1,656,734 -0.0%
send_receive_benchmark::group_send::bench_group_send group_10:setup_group_send_10() 651,840 651,789 +0.0%
send_receive_benchmark::group_send::bench_group_send group_50:setup_group_send_50() 875,757 875,770 -0.0%
send_receive_benchmark::group_send::bench_group_send group_256:setup_group_send_256() 2,083,597 2,083,588 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_10:setup_group_skdm_10() 749,079 748,897 +0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_50:setup_group_skdm_50() 1,326,084 1,329,883 -0.3%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_256:setup_group_skdm_256() 4,369,906 4,372,432 -0.1%
send_receive_benchmark::group_recv::bench_group_recv text:setup_group_recv() 516,172 517,808 -0.3%
binary_benchmark::marshal_group::bench_marshal_allocating 45,381 45,381 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_allocating 45,431 45,431 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_allocating 66,334 66,334 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer 43,492 43,492 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer_vec_writer 45,487 45,487 +0.0%
binary_benchmark::marshal_group::bench_marshal_long_string 4,945 4,945 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_long_string 4,976 4,976 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_long_string 6,747 6,747 +0.0%
binary_benchmark::marshal_group::bench_marshal_huge_bytes_allocating 528,544 528,544 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_huge_bytes_allocating 528,165 528,165 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_huge_bytes_allocating 529,411 529,411 +0.0%
binary_benchmark::marshal_group::bench_marshal_many_children_allocating 5,417,732 5,417,732 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_many_children_allocating 5,362,047 5,362,047 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_many_children_allocating 13,276,365 13,276,365 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled() 1,850 1,850 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled() 29,217 29,217 +0.0%
binary_benchmark::unpack_group::bench_unpack_uncompressed 618 618 +0.0%
binary_benchmark::unpack_group::bench_unpack_compressed 672,890 672,890 +0.0%
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled() 3,736 3,736 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled() 3,840 3,840 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled() 48,274 48,274 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto small:setup_small_marshaled() 3,866 3,866 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto large:setup_large_marshaled() 48,335 48,335 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact small:setup_small_marshaled() 5,206 5,206 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact large:setup_large_marshaled() 66,659 66,659 +0.0%
binary_benchmark::child_iteration_group::bench_get_children_by_tag 310,312 310,312 +0.0%
binary_benchmark::jid_optimization_group::bench_jid_to_owned_access jid_access:setup_jid_heavy_marshaled() 8,291 8,291 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u32 254 254 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u32 91 91 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u64 292 292 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u64 137 137 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_i64 317 317 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_i64 145 145 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_loop_100_u64 27,425 27,425 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_loop_100_u64 10,725 10,725 +0.0%
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users() 4,142,446 4,135,118 +0.2%
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session() 100,133 100,133 +0.0%
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message() 4,264,189 4,264,189 +0.0%
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session() 100,399 100,399 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender() 210,262 210,262 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution() 496,908 496,921 -0.0%
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message() 511,148 510,239 +0.2%
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data() 11,980,720 11,976,296 +0.0%
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message() 2,466,138 2,466,138 +0.0%
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message() 4,939,502 4,922,932 +0.3%
libsignal_benchmark::signature_group::bench_key_generation keygen 2,043,397 2,043,397 +0.0%
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions() 37,414 37,404 +0.0%
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages() 3,617,967 3,617,967 +0.0%
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session() 230,658 230,658 +0.0%
libsignal_benchmark::session_optimization_group::bench_message_key_eviction eviction:setup_message_key_eviction() 9,980,959 9,980,959 +0.0%
No significant changes detected.

@jlucaso1
jlucaso1 merged commit 16dca20 into main Jun 8, 2026
11 checks passed
@jlucaso1
jlucaso1 deleted the protocol/receipt-offline-attr branch June 8, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant