pub struct Client {
pub group_cache: Mutex<Option<Arc<TypedCache<Jid, Arc<GroupInfo>>>>>,
pub enable_auto_reconnect: Arc<AtomicBool>,
pub custom_enc_handlers: OnceLock<HashMap<String, Arc<dyn EncHandler>>>,
pub http_client: Arc<dyn HttpClient>,
/* private fields */
}Expand description
A single WhatsApp session: the connection, the Signal state, and every protocol operation built on top of them.
This is the low-level entry point. Build one with
ClientBuilder, which
takes the four platform dependencies (storage backend, transport factory,
HTTP client, async runtime) and validates them at runtime. Most applications
should use Bot instead and reach the client through
Bot::client; Client is what remains when you
need to drive the lifecycle yourself, from an FFI host, or from a wrapper
that cannot express typestate generics.
The client is always used behind an Arc (most methods take self: &Arc<Self>)
and is cheap to clone and share across tasks.
§Lifecycle
Client::run owns the session: it connects, keeps the socket alive, and
reconnects with backoff until Client::disconnect is called or the device
is logged out. Client::connect performs a single connection attempt
without the supervision loop, for hosts that manage retries themselves.
§Events
Everything the server reports (messages, receipts, pairing progress,
connection state) is delivered as an Event
on the event bus. Register a handler with Client::subscribe (explicit
EventInterest filter) or
Client::subscribe_handler.
§Sending
Client::send_message covers the common path;
Client::send_message_with_options takes a SendOptions
for message-id pinning, ephemeral expiration, and cache freshness. Domain
operations hang off accessors such as Client::groups, Client::contacts,
and Client::presence.
Fields§
§group_cache: Mutex<Option<Arc<TypedCache<Jid, Arc<GroupInfo>>>>>§enable_auto_reconnect: Arc<AtomicBool>§custom_enc_handlers: OnceLock<HashMap<String, Arc<dyn EncHandler>>>Custom handlers for encrypted message types. Set once at Bot::build and
immutable afterward, so the receive hot path reads it with a plain
OnceLock::get (no lock) and no per-node guard acquisition.
http_client: Arc<dyn HttpClient>HTTP client for making HTTP requests (media upload/download, version fetching)
Implementations§
Source§impl Client
impl Client
Sourcepub fn subscribe(
&self,
interest: EventInterest,
handler: Arc<dyn EventHandler>,
) -> Subscription
pub fn subscribe( &self, interest: EventInterest, handler: Arc<dyn EventHandler>, ) -> Subscription
Subscribe an external event handler with an explicit event filter.
Sourcepub fn subscribe_handler(&self, handler: Arc<dyn EventHandler>) -> Subscription
pub fn subscribe_handler(&self, handler: Arc<dyn EventHandler>) -> Subscription
Subscribe using the handler’s current registration-time interest hint.
Sourcepub fn acquire_raw_node_forwarding(self: &Arc<Self>) -> RawNodeLease
pub fn acquire_raw_node_forwarding(self: &Arc<Self>) -> RawNodeLease
Acquire raw decoded stanza forwarding for one consumer.
Event::RawNode remains enabled until every acquired lease is dropped.
Sourcepub fn set_skip_history_sync(&self, enabled: bool)
pub fn set_skip_history_sync(&self, enabled: bool)
Enable or disable skipping of history sync notifications at runtime.
When enabled, the client will acknowledge incoming history sync notifications but will not download or process the data.
Sourcepub fn skip_history_sync_enabled(&self) -> bool
pub fn skip_history_sync_enabled(&self) -> bool
Returns true if history sync notifications are currently being skipped.
Sourcepub fn set_wanted_pre_key_count(&self, count: usize)
pub fn set_wanted_pre_key_count(&self, count: usize)
Set how many one-time pre-keys are generated per upload batch.
Defaults to WA Web’s UPLOAD_KEYS_COUNT (812). Call before connecting; it takes effect on the next pre-key upload. The value is clamped to the protocol-safe range at upload time, so out-of-range values are coerced (and logged) rather than rejected here.
Sourcepub fn wanted_pre_key_count(&self) -> usize
pub fn wanted_pre_key_count(&self) -> usize
Returns the configured pre-key upload batch size (the raw value, before the upload-time clamp).
Sourcepub fn set_resend_rate_limit(&self, burst: u32, refill_per_min: u32)
pub fn set_resend_rate_limit(&self, burst: u32, refill_per_min: u32)
Retune the per-chat outbound resend rate limiter live (no reconnect).
Outbound resends to a chat are bounded by a token bucket: burst is the
instantaneous allowance and refill_per_min the sustained ceiling per
chat. This caps the aggregate resend rate that WhatsApp’s anti-abuse
penalizes during a PN to LID migration fan-out, while throttled devices
still recover via the fresh-SKDM mark. A burst of 0 disables the limiter.
Takes effect on each chat’s next retry; a lowered burst clamps a live
bucket on its next access.
Sourcepub fn set_retry_admission(&self, policy: Arc<dyn RetryAdmission>) -> bool
pub fn set_retry_admission(&self, policy: Arc<dyn RetryAdmission>) -> bool
Register a RetryAdmission policy: an opt-in gate that can drop inbound
group/status retry receipts from other accounts before any repair work
runs. Unset (the default) admits every receipt, matching WhatsApp Web,
with zero overhead on the receive path.
Set once, before connecting; a later call is ignored and returns false
(the already-registered policy stays in effect). Live tuning belongs
inside the policy itself (e.g. atomics), not in re-registration. See
examples/retry_quarantine.rs.
Sourcepub fn stats(&self) -> StatsSnapshot
pub fn stats(&self) -> StatsSnapshot
Cumulative wire I/O and activity counters for this client session.
Always available, no feature gate: recording costs one relaxed atomic add per wire frame. Byte counts are post-noise wire bytes (frame headers and AEAD tags included; handshake and TLS/WebSocket overhead excluded), so two clients in one process can be compared directly.
Sourcepub async fn memory_report(&self) -> MemoryReport
pub async fn memory_report(&self) -> MemoryReport
Entry counts plus estimated retained heap bytes for the client’s
internal collections. See MemoryReport for the semantics of the
byte figures.
On-demand only: walks the in-process caches under their locks when
called, costs nothing otherwise. Counts are approximate (caches may
have pending evictions); call run_pending_tasks() on individual
caches first if you need exact counts.
Sourcepub async fn resource_report(&self) -> ResourceReport
pub async fn resource_report(&self) -> ResourceReport
Unified per-session resource estimate: the client’s own collections
(Client::memory_report) plus the components that live outside the
Client and dominate real per-session RAM — the storage backend’s page
cache, the transport’s buffers + TLS/noise state, the HTTP client’s pool
— and, when a AllocMeter is installed
(with_alloc_meter), an allocation-churn snapshot.
On-demand only, no hot-path cost. Each out-of-client figure is best
effort: a component reports only what it can introspect, so
ResourceReport::total_estimated_bytes is a lower bound (see its
docs for which parts are exact vs. estimated). No PII — sizes and counts
only. Send, so multi-session consumers can await it off a worker.
Sourcepub fn persistence_manager(&self) -> Arc<PersistenceManager> ⓘ
pub fn persistence_manager(&self) -> Arc<PersistenceManager> ⓘ
Get access to the PersistenceManager for this client. This is useful for multi-account scenarios to get the device ID.
Sourcepub fn pn(&self) -> Option<Jid>
pub fn pn(&self) -> Option<Jid>
This device’s phone-number JID, or None before pairing completes.
Sourcepub fn wait_for_node(&self, filter: NodeFilter) -> Receiver<Arc<OwnedNodeRef>> ⓘ
pub fn wait_for_node(&self, filter: NodeFilter) -> Receiver<Arc<OwnedNodeRef>> ⓘ
Register a waiter for an incoming node matching the given filter.
Returns a receiver that resolves when a matching node arrives. The waiter starts buffering immediately, so register it before performing the action that triggers the expected node.
When multiple waiters match the same node, each matching waiter receives a clone of the node (broadcast within a single resolve pass).
§Example
let waiter = client.wait_for_node(
NodeFilter::tag("notification").attr("type", "w:gp2"),
);
client.groups().add_participants(&group_jid, &[jid_c]).await?;
let node = waiter.await.expect("notification arrived");Sourcepub fn wait_for_sent_node(&self, filter: NodeFilter) -> Receiver<Arc<Node>> ⓘ
pub fn wait_for_sent_node(&self, filter: NodeFilter) -> Receiver<Arc<Node>> ⓘ
Register a waiter for an outgoing node before it is encrypted and sent.
This is intended for tests and diagnostics that need to inspect the raw
stanza built by the client, such as asserting whether <tctoken> or
<cstoken> was attached.
Source§impl Client
impl Client
Sourcepub async fn flush_pending_signal_state(
&self,
) -> Result<(), SignalMaintenanceError>
pub async fn flush_pending_signal_state( &self, ) -> Result<(), SignalMaintenanceError>
Force any pending write-behind Signal cache state to the backend, returning once the flush completes (or fails).
The live receive path schedules a coalesced flush (see signal_flush.rs)
instead of writing through, and lease-covered sends do the same. Only a
send that raises a session or sender-key counter lease flushes
synchronously. On success, the
backend normally trails the cache by about the coalescing window, but
that is not a hard wall-clock bound — the timer can slip under runtime
starvation and the flush can wait on locks or slow/failing storage (a
backend outage extends it until the retry loop succeeds). Use this to
settle durability deterministically before reading persisted state or
ahead of a non-graceful shutdown — and check the returned Result, as a
failure leaves state pending.
Call from a control task, never from inside an event handler or an
InboundDurabilityHook: during an offline-sync drain those run while
the processing permit is held, and settling routes through that same
permit — re-entering it would deadlock.
Source§impl Client
impl Client
Sourcepub async fn process_sync_task(self: &Arc<Self>, task: MajorSyncTask)
pub async fn process_sync_task(self: &Arc<Self>, task: MajorSyncTask)
Public entry point for processing MajorSyncTask from the sync channel.
pub async fn clean_dirty_bits(&self, bit: DirtyBit) -> Result<(), IqError>
Source§impl Client
impl Client
pub async fn set_passive(&self, passive: bool) -> Result<(), IqError>
pub async fn fetch_props(&self) -> Result<(), IqError>
pub async fn fetch_privacy_settings( &self, ) -> Result<PrivacySettingsResponse, IqError>
Sourcepub async fn set_privacy_setting(
&self,
category: PrivacyCategory,
value: PrivacyValue,
) -> Result<SetPrivacySettingResponse, IqError>
pub async fn set_privacy_setting( &self, category: PrivacyCategory, value: PrivacyValue, ) -> Result<SetPrivacySettingResponse, IqError>
Set a privacy setting.
Use PrivacyCategory::is_valid_value
to check valid combinations.
§Example
use wacore::iq::privacy::{PrivacyCategory, PrivacyValue};
client.set_privacy_setting(PrivacyCategory::Last, PrivacyValue::Contacts).await?;Sourcepub async fn set_privacy_disallowed_list(
&self,
category: PrivacyCategory,
update: DisallowedListUpdate,
) -> Result<SetPrivacySettingResponse, IqError>
pub async fn set_privacy_disallowed_list( &self, category: PrivacyCategory, update: DisallowedListUpdate, ) -> Result<SetPrivacySettingResponse, IqError>
Set a privacy setting to contact_blacklist with a disallowed list update.
Only Last, Profile, Status, GroupAdd support disallowed lists.
Returns the server’s updated dhash for use in subsequent updates.
Sourcepub async fn set_default_disappearing_mode(
&self,
duration: u32,
) -> Result<(), IqError>
pub async fn set_default_disappearing_mode( &self, duration: u32, ) -> Result<(), IqError>
Set the default disappearing messages duration (seconds). Pass 0 to disable.
Sourcepub async fn set_chat_disappearing_timer(
&self,
chat: Jid,
duration: u32,
) -> Result<SendResult, SendError>
pub async fn set_chat_disappearing_timer( &self, chat: Jid, duration: u32, ) -> Result<SendResult, SendError>
Turn disappearing messages on or off for a 1:1 chat (duration in
seconds; 0 disables).
Sends an EPHEMERAL_SETTING protocol message, mirroring WA Web’s
WAWebUpdateEphemeralSettingChatAction. For groups use
Groups::set_ephemeral; for the account
default use Client::set_default_disappearing_mode.
Sourcepub async fn get_business_profile(
&self,
jid: &Jid,
) -> Result<Option<BusinessProfile>, IqError>
pub async fn get_business_profile( &self, jid: &Jid, ) -> Result<Option<BusinessProfile>, IqError>
Get business profile for a WhatsApp Business account.
pub async fn send_digest_key_bundle(&self) -> Result<(), IqError>
Sourcepub async fn set_device_props(&self, override_: DevicePropsOverride)
pub async fn set_device_props(&self, override_: DevicePropsOverride)
Override DeviceProps fields before the initial pairing. Only fields
with Some are changed. In-memory only — WA Web regenerates
device_props at each registration, and it has no wire effect after
pairing. Call before connect() on every process start that still
needs to pair.
Sourcepub async fn set_client_profile(&self, profile: ClientProfile)
pub async fn set_client_profile(&self, profile: ClientProfile)
Set the noise-handshake ClientPayload profile. In-memory only;
call before each connect() on a fresh process.
Source§impl Client
impl Client
Sourcepub async fn add_lid_pn_mapping(
&self,
lid: &str,
phone_number: &str,
source: LearningSource,
) -> Result<()>
pub async fn add_lid_pn_mapping( &self, lid: &str, phone_number: &str, source: LearningSource, ) -> Result<()>
Awaits the persist + any device/session migrations. Hot paths should
prefer learn_lid_pn_mapping_fast.
Public so embedders can feed in pairs the library never observes
itself — e.g. app-state ContactAction mutations, which carry
lidJid/pnJid for the user’s address-book contacts — instead of
writing the backend mapping table behind the cache’s back.
lid and phone_number are bare user parts (no @lid /
@s.whatsapp.net server, no device suffix). Pick the
LearningSource that matches where the pair came from;
LearningSource::Other covers sources without a dedicated variant.
Sourcepub async fn add_lid_pn_mappings(
&self,
mappings: Vec<(String, String)>,
source: LearningSource,
) -> Result<usize>
pub async fn add_lid_pn_mappings( &self, mappings: Vec<(String, String)>, source: LearningSource, ) -> Result<usize>
Durably add a batch of linked-identifier mappings and run the same registry/session migrations as the single-entry path.
Sourcepub async fn is_lid_migrated(&self) -> bool
pub async fn is_lid_migrated(&self) -> bool
Mirrors WA Web Lid1X1MigrationUtils.isLidMigrated(): the pairing- or
migration-persisted account flag, with the lid_one_on_one_migration_enabled
ab prop covering sessions paired before the flag existed (the prop is
what lets WA Web start the 1:1 migration on an already-linked client).
Sourcepub async fn get_lid_pn_entry(&self, jid: &Jid) -> Result<Option<LidPnEntry>>
pub async fn get_lid_pn_entry(&self, jid: &Jid) -> Result<Option<LidPnEntry>>
Look up the LID↔phone mapping for a JID. Cache-aside: falls back to the backend on cache miss so mappings survive cache eviction and any backend implementation gets the fallback without warm-up.
Backend errors are propagated — callers can distinguish “no mapping”
(Ok(None)) from “lookup failed” (Err(_)).
Source§impl Client
impl Client
Sourcepub const RECONNECT_BACKOFF_STEP: u32 = 4
pub const RECONNECT_BACKOFF_STEP: u32 = 4
Backoff step used by reconnect() to create an offline window.
fibonacci_backoff(RECONNECT_BACKOFF_STEP) determines the delay before
the run loop re-connects. This must be longer than the mock server’s
chatstate TTL (CHATSTATE_TTL_SECS=3) so TTL-expiry tests pass.
Sequence: fib(0)=1s, fib(1)=1s, fib(2)=2s, fib(3)=3s, fib(4)=5s.
Sourcepub fn builder() -> ClientBuilder
pub fn builder() -> ClientBuilder
Create a runtime-validated low-level client builder.
pub fn shutdown_signal(&self) -> ShutdownSignal
Sourcepub fn signal_shutdown_sync(&self)
pub fn signal_shutdown_sync(&self)
Synchronous flag-only equivalent of the first lines of disconnect().
Spawned tasks watching is_shutting_down() / shutdown_notifier exit
on their next poll. Does NOT flush, close the transport, or touch
persistence — prefer disconnect() whenever you can await. Exists
for Drop impls on FFI wrappers (e.g. WasmWhatsAppClient) that
can’t run async cleanup synchronously.
Sourcepub async fn new(
runtime: Arc<dyn Runtime>,
persistence_manager: Arc<PersistenceManager>,
transport_factory: Arc<dyn TransportFactory>,
http_client: Arc<dyn HttpClient>,
override_version: Option<(u32, u32, u32)>,
) -> (Arc<Self>, Receiver<MajorSyncTask>)
pub async fn new( runtime: Arc<dyn Runtime>, persistence_manager: Arc<PersistenceManager>, transport_factory: Arc<dyn TransportFactory>, http_client: Arc<dyn HttpClient>, override_version: Option<(u32, u32, u32)>, ) -> (Arc<Self>, Receiver<MajorSyncTask>)
Create a new Client with default cache configuration.
This is the standard constructor. Use Client::new_with_cache_config
if you need to customise cache TTL / capacity.
Sourcepub async fn new_with_cache_config(
runtime: Arc<dyn Runtime>,
persistence_manager: Arc<PersistenceManager>,
transport_factory: Arc<dyn TransportFactory>,
http_client: Arc<dyn HttpClient>,
override_version: Option<(u32, u32, u32)>,
cache_config: CacheConfig,
) -> (Arc<Self>, Receiver<MajorSyncTask>)
pub async fn new_with_cache_config( runtime: Arc<dyn Runtime>, persistence_manager: Arc<PersistenceManager>, transport_factory: Arc<dyn TransportFactory>, http_client: Arc<dyn HttpClient>, override_version: Option<(u32, u32, u32)>, cache_config: CacheConfig, ) -> (Arc<Self>, Receiver<MajorSyncTask>)
Create a new Client with a custom CacheConfig.
pub async fn run(self: &Arc<Self>)
Sourcepub async fn connect(self: &Arc<Self>) -> Result<(), ConnectError>
pub async fn connect(self: &Arc<Self>) -> Result<(), ConnectError>
Boxed barrier: see crate::bot::Bot::run. Coroutines are LocalCopy
across crates, so consumers awaiting the connect graph directly would
re-codegen it; the box makes them poll through a vtable instead.
Sourcepub async fn logout(self: &Arc<Self>)
pub async fn logout(self: &Arc<Self>)
Deregister this companion device and disconnect. Does NOT wipe stored keys. Delete the storage backend to fully clear credentials.
Infallible on purpose: the deregistration IQ is best-effort (it cannot be sent at all while offline), and the local teardown runs either way, so a caller has nothing to branch on. A failed IQ is logged at warn.
pub async fn disconnect(self: &Arc<Self>)
Sourcepub async fn reconnect(self: &Arc<Self>)
pub async fn reconnect(self: &Arc<Self>)
Drop the current connection and trigger the auto-reconnect loop.
Unlike disconnect, this does not stop the run loop. The client
will reconnect automatically using the same persisted identity/store,
just as it would after a network interruption. Use
wait_for_connected to wait for the new connection to be ready.
This is useful for:
- Handling network changes (e.g., Wi-Fi → cellular)
- Forcing a fresh server session
- Testing offline message delivery
Sourcepub async fn reconnect_immediately(self: &Arc<Self>)
pub async fn reconnect_immediately(self: &Arc<Self>)
Drop the current connection and reconnect immediately with no delay.
Unlike reconnect, which introduces a deliberate offline window,
this method sets the expected_disconnect flag so the run loop
skips the backoff delay and reconnects as fast as possible.
Sourcepub async fn wait_for_socket(
&self,
timeout: Duration,
) -> Result<(), ConnectError>
pub async fn wait_for_socket( &self, timeout: Duration, ) -> Result<(), ConnectError>
Waits for the noise socket to be established.
Returns Ok(()) when the socket is ready, or Err on timeout.
This is useful for code that needs to send messages before login,
such as requesting a pair code during initial pairing.
If the socket is already connected, returns immediately.
Sourcepub async fn wait_for_connected(
&self,
timeout: Duration,
) -> Result<(), ConnectError>
pub async fn wait_for_connected( &self, timeout: Duration, ) -> Result<(), ConnectError>
Waits for the client to establish a connection and complete login.
Returns Ok(()) when connected, or Err on timeout.
This is useful for code that needs to run after connection is established
and authentication is complete.
If the client is already connected and logged in, returns immediately.
pub fn is_connected(&self) -> bool
pub fn is_logged_in(&self) -> bool
Source§impl Client
impl Client
Sourcepub async fn send_raw_bytes(
&self,
plaintext: Vec<u8>,
) -> Result<(), ClientError>
pub async fn send_raw_bytes( &self, plaintext: Vec<u8>, ) -> Result<(), ClientError>
Send pre-marshaled plaintext bytes through the noise socket.
The bytes must be a valid WABinary-marshaled stanza (as produced by
wacore_binary::marshal::marshal_to). Sending malformed data will
cause the server to close the connection.
This bypasses node logging and sent_node_waiter resolution — use
send_node for normal stanza sending.
pub async fn send_node(&self, node: Node) -> Result<(), ClientError>
pub async fn edit_message( &self, to: impl Into<Jid>, original_id: impl Into<String>, new_content: Message, ) -> Result<String, SendError>
Sourcepub async fn edit_message_with_options(
&self,
to: impl Into<Jid>,
original_id: impl Into<String>,
new_content: Message,
options: EditOptions,
) -> Result<String, SendError>
pub async fn edit_message_with_options( &self, to: impl Into<Jid>, original_id: impl Into<String>, new_content: Message, options: EditOptions, ) -> Result<String, SendError>
Edits a message you own (original_id) with caller-supplied
crate::send::EditOptions. The edit-path counterpart of
crate::send::SendOptions::message_id (which overrides the stanza id
for plain sends): stanza_id lets callers control the outer stanza id —
for example to collide it with an existing message so clients re-render
that slot.
When stanza_id is set, no id-keyed local state is bound to the borrowed
id (the edit skips outbound-secret and retry-cache persistence, leaving
the original message’s state intact), and whether the collision is
honored is server/client dependent — treat it as best-effort. See
crate::send::EditOptions::stanza_id.
Sourcepub async fn edit_message_encrypted(
&self,
to: impl Into<Jid>,
original_id: impl Into<String>,
message_secret: &[u8],
new_content: Message,
) -> Result<String, SendError>
pub async fn edit_message_encrypted( &self, to: impl Into<Jid>, original_id: impl Into<String>, message_secret: &[u8], new_content: Message, ) -> Result<String, SendError>
Edit a message via the message-secret encrypted path (secret_encrypted_message
with secret_enc_type = MESSAGE_EDIT), instead of the plaintext protocolMessage
edit. This is the form Community Announcement Group / channel edits require, and
what WA Web sends when message_edit_to_message_secret_sender_enabled is on.
message_secret is the original message’s 32-byte secret (you generated it when
you sent that message). You can only edit your own messages, so the original
sender and the editor are both you.
Sourcepub async fn register_chatstate_handler(
&self,
handler: Arc<dyn Fn(ChatStateEvent) + Send + Sync>,
)
pub async fn register_chatstate_handler( &self, handler: Arc<dyn Fn(ChatStateEvent) + Send + Sync>, )
Register a chatstate handler which will be invoked when a <chatstate> stanza is received.
The handler receives a ChatStateEvent with the parsed chat state information.
Sourcepub fn set_force_active_delivery_receipts(&self, active: bool)
pub fn set_force_active_delivery_receipts(&self, active: bool)
Force active delivery receipts even when offline (whatsmeow’s
SetForceActiveDeliveryReceipts); off restores the default.
Source§impl Client
impl Client
Sourcepub async fn acknowledge_stanza(
&self,
stanza: &NodeRef<'_>,
) -> Result<(), StanzaResponseError>
pub async fn acknowledge_stanza( &self, stanza: &NodeRef<'_>, ) -> Result<(), StanzaResponseError>
Confirm a received stanza using its original borrowed node.
Unlike the tolerant automatic receive path, malformed input is returned to the caller and no successful outcome is reported unless the response reaches the transport.
Source§impl Client
impl Client
Sourcepub async fn download(&self, downloadable: &dyn Downloadable) -> Result<Vec<u8>>
pub async fn download(&self, downloadable: &dyn Downloadable) -> Result<Vec<u8>>
Downloads and decrypts media from WhatsApp’s CDN into memory.
Only needed when you need the plaintext bytes (processing, transcoding, re-upload). To forward existing media unchanged, reuse the original message’s CDN fields directly, no round-trip required.
Sourcepub async fn fetch_sticker_pack(
&self,
pack_id: &str,
locale: &str,
) -> Result<StickerPack>
pub async fn fetch_sticker_pack( &self, pack_id: &str, locale: &str, ) -> Result<StickerPack>
Fetch a first-party sticker pack’s metadata and sticker list from the CDN.
Each returned wacore::sticker_pack::StickerPackItem is Downloadable,
so individual stickers can be fetched with Self::download. The locale
only affects localized pack names; "en" mirrors whatsmeow’s default.
Sourcepub async fn download_from_params(
&self,
params: &DownloadParams,
) -> Result<Vec<u8>>
pub async fn download_from_params( &self, params: &DownloadParams, ) -> Result<Vec<u8>>
Downloads and decrypts media from raw parameters without needing the original message.
Sourcepub async fn download_to_writer<W: DownloadWriter + Send + 'static>(
&self,
downloadable: &dyn Downloadable,
writer: W,
) -> Result<W>
pub async fn download_to_writer<W: DownloadWriter + Send + 'static>( &self, downloadable: &dyn Downloadable, writer: W, ) -> Result<W>
Downloads and decrypts media with streaming (constant memory usage).
The entire HTTP download, decryption, and file write happen in a single blocking thread. The writer is seeked back to position 0 before returning.
On success the writer holds exactly the decrypted media and nothing else.
Every attempt starts by emptying it, so neither content the caller left
behind nor a host that streamed out plaintext before failing its MAC can
survive into the result. Providing that is what DownloadWriter is for,
and why this does not take a plain Write + Seek.
On failure the writer is emptied too, on a best-effort basis: a sink that refuses to empty is logged rather than replacing the download’s own error, and a writer lost to a panicking executor cannot be reached to be cleaned at all. Both leave unverified bytes only in a sink the caller reaches through a handle it kept, since this otherwise consumes the writer.
Memory usage: ~40KB regardless of file size (8KB read buffer + decrypt state).
Sourcepub async fn download_from_params_to_writer<W: DownloadWriter + Send + 'static>(
&self,
params: &DownloadParams,
writer: W,
) -> Result<W>
pub async fn download_from_params_to_writer<W: DownloadWriter + Send + 'static>( &self, params: &DownloadParams, writer: W, ) -> Result<W>
Streaming variant of download_from_params that writes to a writer
instead of buffering in memory.
Source§impl Client
impl Client
Sourcepub async fn request_message_retry(
self: &Arc<Self>,
stanza: &NodeRef<'_>,
options: RetryRequestOptions,
) -> Result<RetryRequestOutcome, RetryRequestError>
pub async fn request_message_retry( self: &Arc<Self>, stanza: &NodeRef<'_>, options: RetryRequestOptions, ) -> Result<RetryRequestOutcome, RetryRequestError>
Request retransmission of an inbound message stanza.
The stanza is parsed once into the canonical message metadata model. This operation sends only the retry receipt; transport acknowledgement remains the caller’s responsibility.
Source§impl Client
impl Client
Sourcepub async fn pair_with_code(
self: &Arc<Self>,
options: PairCodeOptions,
) -> Result<String, PairError>
pub async fn pair_with_code( self: &Arc<Self>, options: PairCodeOptions, ) -> Result<String, PairError>
Initiates pair code authentication as an alternative to QR code pairing.
This method starts the phone number linking process. The returned code should be displayed to the user, who then enters it on their phone in: WhatsApp > Linked Devices > Link a Device > Link with phone number instead
This can run concurrently with QR code pairing - whichever completes first wins.
§One code at a time
Fails with PairCodeError::CodeAlreadyOutstanding while a previously
issued code is still within its validity window. A second code does not
replace the first for the phone: the server routes primary_hello by
number, so whoever enters the older code still reaches stage 2 and is
answered with a key bundle their code cannot open — the phone reports a
failed link and nothing surfaces here. Call
Client::cancel_pair_code first when the replacement is intentional.
In particular, do not drive this from QR-code rotation: the two have unrelated lifetimes, and a code being typed into a phone outlives several QR refs.
§Arguments
options- Configuration for pair code authentication
§Returns
Ok(String)- The 8-character pairing code to displayErr- If validation fails, a code is already outstanding, not connected, or server error. APairError::RequestFailedcarryingbad-requestmay be rate-limiting (throttled per phone number), not invalid input — back off and retry.
§Example
use whatsapp_rust::pair_code::PairCodeOptions;
let options = PairCodeOptions {
phone_number: "15551234567".to_string(),
show_push_notification: true,
custom_code: None, // Generate random code
..Default::default()
};
let code = client.pair_with_code(options).await?;
println!("Enter this code on your phone: {}", code);Sourcepub async fn cancel_pair_code(self: &Arc<Self>)
pub async fn cancel_pair_code(self: &Arc<Self>)
Abandons the outstanding pair-code flow, if any.
The explicit reset Client::pair_with_code requires before it will
mint a replacement — WA Web’s initializeAltDeviceLinking(). After this
the previous code can no longer complete: a primary_hello for it is
dropped rather than answered with a bundle its holder cannot open.
A flow cancelled after it reached stage 2 also gives up the adv secret
that stage derived: it is keyed to a primary that will never link. A
PairCodeState::Completed flow does not, because that secret belongs
to a device that paired, and re-minting it would invalidate the
account’s own ADV signatures.
Source§impl Client
impl Client
Sourcepub async fn set_passkey_authenticator(
&self,
authenticator: Arc<dyn PasskeyAuthenticator>,
)
pub async fn set_passkey_authenticator( &self, authenticator: Arc<dyn PasskeyAuthenticator>, )
Register a passkey authenticator. When set, the client auto-drives the
assertion step and auto-confirms a re-link (where the handoff proof skips the
verification-code UX). Leave it unset to drive the steps manually via the
Event::PairPasskey* events.
Sourcepub async fn send_passkey_response(
&self,
assertion: Assertion,
) -> Result<(), PasskeyError>
pub async fn send_passkey_response( &self, assertion: Assertion, ) -> Result<(), PasskeyError>
Send the WebAuthn assertion as <passkey_prologue> and open the handshake.
Call after an Event::PairPasskeyRequest.
Sourcepub async fn send_passkey_confirmation(&self) -> Result<(), PasskeyError>
pub async fn send_passkey_confirmation(&self) -> Result<(), PasskeyError>
Finish the link. For a fresh link, call this only after the user confirms the
Event::PairPasskeyConfirmation code.
Source§impl Client
impl Client
Sourcepub fn plugin<P: ClientPlugin>(&self) -> Option<Arc<P::Api>>
Available on crate feature plugins only.
pub fn plugin<P: ClientPlugin>(&self) -> Option<Arc<P::Api>>
plugins only.Return the API exposed by plugin marker P, if that plugin was installed.
Sourcepub fn plugin_manifests(&self) -> &[PluginManifest]
Available on crate feature plugins only.
pub fn plugin_manifests(&self) -> &[PluginManifest]
plugins only.Manifests in dependency-resolved installation order.
Sourcepub fn plugin_stats(&self) -> Option<PluginHostStats>
Available on crate feature plugins only.
pub fn plugin_stats(&self) -> Option<PluginHostStats>
plugins only.Snapshot lifecycle, task, subscription, and custom-event health for installed plugins.
Sourcepub fn plugin_event_router(&self) -> Option<PluginEventRouter>
Available on crate feature plugins only.
pub fn plugin_event_router(&self) -> Option<PluginEventRouter>
plugins only.Subscribe to custom events emitted by installed plugins.
Returns None when no manifest requested custom-event publication.
Source§impl Client
impl Client
Sourcepub fn generate_message_id(&self) -> String
pub fn generate_message_id(&self) -> String
Generates a unique message ID that conforms to the WhatsApp protocol format.
This is an advanced function that allows library users to generate message IDs that are compatible with the WhatsApp protocol. The generated ID includes timestamp, user JID, and random components to ensure uniqueness.
§Advanced Use Case
This function is intended for advanced users who need to build custom protocol
interactions or manage message IDs manually. Most users should use higher-level
methods like send_message which handle ID generation automatically.
§Returns
A string containing the generated message ID in the format expected by WhatsApp.
Sourcepub async fn send_iq(
&self,
query: InfoQuery<'_>,
) -> Result<Arc<OwnedNodeRef>, IqError>
pub async fn send_iq( &self, query: InfoQuery<'_>, ) -> Result<Arc<OwnedNodeRef>, IqError>
Sends a custom IQ (Info/Query) stanza to the WhatsApp server.
This is an advanced function that allows library users to send custom IQ stanzas for protocol interactions that are not covered by higher-level methods. Common use cases include live location updates, custom presence management, or other advanced WhatsApp features.
§Advanced Use Case
This function bypasses some of the higher-level abstractions and safety checks provided by other client methods. Users should be familiar with the WhatsApp protocol and IQ stanza format before using this function.
§Arguments
query- The IQ query to send, containing the stanza type, namespace, content, and optional timeout
§Returns
Ok(Arc<OwnedNodeRef>)- The response node from the server (zero-copy, borrowed from decode buffer)Err(IqError)- Various error conditions including timeout, connection issues, or server errors
§Example
use wacore::request::{InfoQuery, InfoQueryType};
use wacore_binary::builder::NodeBuilder;
use wacore_binary::NodeContent;
use wacore_binary::{Jid, Server};
// This is a simplified example - real usage requires proper setup
let query_node = NodeBuilder::new("presence")
.attr("type", "available")
.build();
let server_jid = Jid::new("", Server::Pn);
let query = InfoQuery {
query_type: InfoQueryType::Set,
namespace: "presence",
to: server_jid,
target: None,
content: Some(NodeContent::Nodes(vec![query_node])),
id: None,
timeout: None,
};
let response = client.send_iq(query).await?;
// Access the node via response.get()Sourcepub async fn send_iq_node(
&self,
node: Node,
timeout: Option<Duration>,
) -> Result<Arc<OwnedNodeRef>, IqError>
pub async fn send_iq_node( &self, node: Node, timeout: Option<Duration>, ) -> Result<Arc<OwnedNodeRef>, IqError>
Sends a fully constructed IQ stanza and waits for its matching response.
The stanza ID is preserved when supplied and generated otherwise. The same waiter, cancellation, timeout and response validation path used by typed IQ specifications handles the request.
Sourcepub async fn execute<S>(&self, spec: S) -> Result<S::Response, IqError>where
S: IqSpec,
pub async fn execute<S>(&self, spec: S) -> Result<S::Response, IqError>where
S: IqSpec,
Executes an IQ specification and returns the typed response.
This is a convenience method that combines building the IQ request, sending it, and parsing the response into a single operation.
§Example
use wacore::iq::groups::GroupQueryIq;
let group_info = client.execute(GroupQueryIq::new(&group_jid)).await?;
println!("Group subject: {}", group_info.subject);Source§impl Client
impl Client
Sourcepub async fn revoke_message(
&self,
to: impl Into<Jid>,
message_id: impl Into<String>,
revoke_type: RevokeType,
) -> Result<(), SendError>
pub async fn revoke_message( &self, to: impl Into<Jid>, message_id: impl Into<String>, revoke_type: RevokeType, ) -> Result<(), SendError>
Delete a message for everyone in the chat (revoke).
This sends a revoke protocol message that removes the message for all participants. The message will show as “This message was deleted” for recipients.
§Arguments
to- The chat JID (DM or group)message_id- The ID of the message to deleterevoke_type- UseRevokeType::Senderto delete your own message, orRevokeType::Admin { original_sender }to delete another user’s message as group admin
Sourcepub async fn keep_message(
&self,
chat: impl Into<Jid>,
key: MessageKey,
keep: bool,
) -> Result<SendResult, SendError>
pub async fn keep_message( &self, chat: impl Into<Jid>, key: MessageKey, keep: bool, ) -> Result<SendResult, SendError>
Keep (or un-keep) a message in a disappearing chat for everyone.
Sends a keepInChatMessage add-on (WA Web WAWebKeepInChatMsgAction):
keep = true requests KEEP_FOR_ALL, keep = false requests
UNDO_KEEP_FOR_ALL. key is the target (kept) message’s key; the keep
message itself is sent with a fresh id. The send path classifies this as a
text add-on and maps the undo case to a sender-revoke edit attribute.
Sourcepub async fn pin_message(
&self,
chat: impl Into<Jid>,
key: MessageKey,
duration: PinDuration,
) -> Result<(), SendError>
pub async fn pin_message( &self, chat: impl Into<Jid>, key: MessageKey, duration: PinDuration, ) -> Result<(), SendError>
Pin a message in a chat for all participants.
Sourcepub async fn unpin_message(
&self,
chat: impl Into<Jid>,
key: MessageKey,
) -> Result<(), SendError>
pub async fn unpin_message( &self, chat: impl Into<Jid>, key: MessageKey, ) -> Result<(), SendError>
Unpin a previously pinned message.
Source§impl Client
impl Client
Sourcepub fn send_message(
&self,
to: impl Into<Jid>,
message: Message,
) -> impl Future<Output = Result<SendResult, SendError>> + '_
pub fn send_message( &self, to: impl Into<Jid>, message: Message, ) -> impl Future<Output = Result<SendResult, SendError>> + '_
Send a message to a user, group, or newsletter.
Newsletter messages are sent as plaintext (no E2E encryption).
For status/story updates use Client::status() instead.
Sourcepub fn send_text(
&self,
to: impl Into<Jid>,
text: impl Into<String>,
) -> impl Future<Output = Result<SendResult, SendError>> + '_
pub fn send_text( &self, to: impl Into<Jid>, text: impl Into<String>, ) -> impl Future<Output = Result<SendResult, SendError>> + '_
Plain-text convenience over Client::send_message.
Sourcepub fn forward_message(
&self,
to: impl Into<Jid>,
message: &Message,
) -> impl Future<Output = Result<SendResult, SendError>> + '_
pub fn forward_message( &self, to: impl Into<Jid>, message: &Message, ) -> impl Future<Output = Result<SendResult, SendError>> + '_
Forward an existing message to a chat.
Builds a forward-ready copy of message (sets is_forwarded, bumps the
forwarding score, strips the reply/quote chain, and drops the source
message_secret) via
MessageExt::prepare_for_forward,
then sends it.
message may be a received body or a wrapper (ephemeral/view-once); the
inner content is unwrapped before forwarding. Existing media is relayed
from the same CDN blob rather than re-uploaded.
Sourcepub fn send_message_with_options(
&self,
to: impl Into<Jid>,
message: Message,
options: SendOptions,
) -> impl Future<Output = Result<SendResult, SendError>> + '_
pub fn send_message_with_options( &self, to: impl Into<Jid>, message: Message, options: SendOptions, ) -> impl Future<Output = Result<SendResult, SendError>> + '_
Send a message with additional options.
Source§impl Client
impl Client
Sourcepub async fn upload(
&self,
data: Vec<u8>,
media_type: MediaType,
options: UploadOptions,
) -> Result<UploadResponse>
pub async fn upload( &self, data: Vec<u8>, media_type: MediaType, options: UploadOptions, ) -> Result<UploadResponse>
Encrypts and uploads media to WhatsApp’s CDN.
Only needed for new or modified media. To forward existing media unchanged, reuse the original message’s CDN fields directly, no round-trip required.
Sourcepub async fn upload_stream<S>(
&self,
source: S,
info: EncryptedMediaInfo,
media_type: MediaType,
) -> Result<UploadResponse>where
S: UploadSource + 'static,
pub async fn upload_stream<S>(
&self,
source: S,
info: EncryptedMediaInfo,
media_type: MediaType,
) -> Result<UploadResponse>where
S: UploadSource + 'static,
Uploads already-encrypted media streamed from source, keeping memory
constant regardless of file size. Encrypt the plaintext first with
wacore::upload::encrypt_media_streaming (or ..._with_key) into the
storage of your choice, then pass that storage as source plus the
returned wacore::upload::EncryptedMediaInfo. The caller owns where the
ciphertext lives (temp file, memory, …); this method never touches disk.
Source§impl Client
impl Client
Sourcepub async fn send_pdo_placeholder_resend_request(
self: &Arc<Self>,
info: &Arc<MessageInfo>,
) -> Result<(), Error>
pub async fn send_pdo_placeholder_resend_request( self: &Arc<Self>, info: &Arc<MessageInfo>, ) -> Result<(), Error>
Sends a PDO (Peer Data Operation) request to our own primary phone to get the decrypted content of a message that we failed to decrypt.
This is called when decryption fails and we want to ask our phone for the message. The phone will respond with a PeerDataOperationRequestResponseMessage containing the full WebMessageInfo which we can then dispatch as a normal message event.
§Arguments
info- The MessageInfo for the message that failed to decrypt
§Returns
Ok(())if the request was sent successfullyErrif we couldn’t send the request (e.g., not logged in)
Sourcepub async fn fetch_message_history(
self: &Arc<Self>,
chat_jid: &Jid,
oldest_msg_id: &str,
oldest_msg_from_me: bool,
oldest_msg_timestamp_ms: i64,
count: i32,
) -> Result<String, Error>
pub async fn fetch_message_history( self: &Arc<Self>, chat_jid: &Jid, oldest_msg_id: &str, oldest_msg_from_me: bool, oldest_msg_timestamp_ms: i64, count: i32, ) -> Result<String, Error>
Request on-demand message history from the primary phone via PDO.
Sourcepub async fn handle_pdo_response(
self: &Arc<Self>,
response: &PeerDataOperationRequestResponseMessage,
pdo_msg_info: &MessageInfo,
)
pub async fn handle_pdo_response( self: &Arc<Self>, response: &PeerDataOperationRequestResponseMessage, pdo_msg_info: &MessageInfo, )
Handles a PDO response message from our primary phone. This is called when we receive a PeerDataOperationRequestResponseMessage.
§Arguments
response- The PDO response messageinfo- The MessageInfo for the PDO response message itself
Source§impl Client
impl Client
Sourcepub async fn refresh_pre_keys(&self) -> Result<(), Error>
pub async fn refresh_pre_keys(&self) -> Result<(), Error>
Force-refresh the server’s one-time pre-key pool with a fresh batch.
Intended for callers that just restored a device from an external source
into an InMemoryBackend. The server
may still hold pre-key IDs whose private key material the caller cannot
reconstruct; any pkmsg referencing those IDs will fail forever with
InvalidPreKeyId. Uploading a fresh batch gives the server new IDs the
caller does have locally, and old unmatched IDs drain as peers consume
them.
Acquires prekey_upload_lock for the duration so this force-upload
cannot race on start_id with the count-based and digest-repair paths.
Sourcepub async fn refresh_pre_keys_with_count(
&self,
count: usize,
) -> Result<(), Error>
pub async fn refresh_pre_keys_with_count( &self, count: usize, ) -> Result<(), Error>
Force-refresh the server pool using a caller-selected batch size without changing the client’s configured background replenishment size. The count is clamped to the same protocol-safe bounds as regular uploads.
Sourcepub async fn ensure_pre_keys(&self) -> Result<(), Error>
pub async fn ensure_pre_keys(&self) -> Result<(), Error>
Ensure the server pool is above the low-water mark.
Sourcepub async fn validate_digest_key(&self) -> Result<(), Error>
pub async fn validate_digest_key(&self) -> Result<(), Error>
Validate server key bundle digest, re-uploading only when the server has no record.
Matches WA Web’s WAWebDigestKeyJob.digestKey():
- Queries server for key bundle digest (identity + signed prekey + prekey IDs + SHA-1 hash)
- If server returns 404 (no record): triggers
upload_pre_keys_with_retry() - If server returns 406/503/other error: logs and does nothing
- On success: loads local keys and computes SHA-1 over the same material
- If validation fails (regId mismatch, missing prekey, hash mismatch): logs warning,
does NOT re-upload — WA Web catches all
validateLocalKeyBundleexceptions without re-uploading; the normalRotateKeyJobwill eventually refresh keys
Source§impl Client
impl Client
Sourcepub async fn reject_stanza(
&self,
stanza: &NodeRef<'_>,
rejection: StanzaRejection,
) -> Result<(), StanzaResponseError>
pub async fn reject_stanza( &self, stanza: &NodeRef<'_>, rejection: StanzaRejection, ) -> Result<(), StanzaResponseError>
Reject a received stanza using its original borrowed representation.
Sourcepub async fn mark_as_read(
&self,
chat: &Jid,
sender: Option<&Jid>,
message_ids: &[&str],
) -> Result<(), Error>
pub async fn mark_as_read( &self, chat: &Jid, sender: Option<&Jid>, message_ids: &[&str], ) -> Result<(), Error>
Sends read receipts for one or more messages.
For group messages, pass the message sender as sender.
Sourcepub async fn mark_as_played(
&self,
chat: &Jid,
sender: Option<&Jid>,
message_ids: &[&str],
) -> Result<(), Error>
pub async fn mark_as_played( &self, chat: &Jid, sender: Option<&Jid>, message_ids: &[&str], ) -> Result<(), Error>
Marks one or more voice/video notes as played (<receipt type="played">).
Mirrors WA Web WAWebSendPlayedReceiptJob. For group/broadcast chats pass
the message sender as sender so the receipt carries participant; in DMs
pass None. Newsletters emit played-self. When readreceipts privacy is
none, a DM emits played-self too (the sender is not notified), matching
mark_as_read.
Source§impl Client
impl Client
Sourcepub async fn retransmit_message(
&self,
request: MessageRetransmission,
) -> Result<(), SendError>
pub async fn retransmit_message( &self, request: MessageRetransmission, ) -> Result<(), SendError>
Retransmit a message to one requesting device.
The client derives the stanza from native protocol data and retains ownership of routing, encryption, sender-key tracking, persistence, and transport. The original message ID and retry count are preserved.
Source§impl Client
impl Client
Sourcepub async fn send_history_sync_server_error_receipt(
&self,
message_id: &str,
media_key: &[u8],
) -> Result<(), Error>
pub async fn send_history_sync_server_error_receipt( &self, message_id: &str, media_key: &[u8], ) -> Result<(), Error>
Ask the phone to re-upload a history-sync blob whose download failed,
by sending a <receipt type="server-error" category="peer"> with the
blob’s media_key.
WA Web (WAWebHandleHistorySyncNotification) sends this on a non-network
download failure; the encrypted payload is the same ServerErrorReceipt
used for media retries. Exposed for consumers that detect an undownloadable
or unwanted history-sync chunk and want the phone to re-send it.
Source§impl Client
impl Client
Sourcepub async fn query_usync(
&self,
query: UsyncQuery,
) -> Result<UsyncResponse, IqError>
pub async fn query_usync( &self, query: UsyncQuery, ) -> Result<UsyncResponse, IqError>
Executes a typed USync query.
The client generates the protocol sid independently from the IQ ID,
matching WhatsApp Web. This neutral operation only returns decoded wire
data; cache and persistence effects remain in specialized client APIs.
Source§impl Client
impl Client
pub fn chat_actions(&self) -> ChatActions<'_>
Sourcepub async fn send_app_state_action(
&self,
schema: &Schema,
index_args: &[&str],
value: &SyncActionValue,
) -> Result<(), AppStateError>
pub async fn send_app_state_action( &self, schema: &Schema, index_args: &[&str], value: &SyncActionValue, ) -> Result<(), AppStateError>
Send any app-state (syncd) Set action, driven by a generated
Schema from
wacore::appstate::schemas. The collection, action version, and index
shape come from the registry; the caller only fills the typed
SyncActionValue (its action sub-field, plus a
timestamp) and supplies the non-literal index args in index_parts
order. This is the generic escape hatch for actions without a dedicated
helper (e.g. clear_chat, favorites, quick_reply); the typed APIs
like ChatActions and Labels wrap it.
use whatsapp_rust::schemas;
use whatsapp_rust::waproto::whatsapp as wa;
let value = wa::SyncActionValue {
clear_chat_action: Some(Default::default()).into(),
timestamp: Some(1_700_000_000_000), // a real epoch-ms timestamp
..Default::default()
};
// Args are the non-literal index parts in `schema.index_parts` order;
// CLEAR_CHAT is [chatJid, deleteStarred, deleteMedia].
client
.send_app_state_action(
&schemas::CLEAR_CHAT,
&["123@s.whatsapp.net", "0", "0"],
&value,
)
.await?;Source§impl Client
impl Client
Sourcepub fn media_reupload(&self) -> MediaReupload<'_>
pub fn media_reupload(&self) -> MediaReupload<'_>
Access media reupload operations.
Source§impl Client
impl Client
Sourcepub async fn send_reaction(
&self,
chat: impl Into<Jid>,
target_key: MessageKey,
emoji: &str,
) -> Result<SendResult, SendError>
pub async fn send_reaction( &self, chat: impl Into<Jid>, target_key: MessageKey, emoji: &str, ) -> Result<SendResult, SendError>
React to a DM, group, or status@broadcast message.
target_key references the message being reacted to. For groups and
status it must carry participant (the original sender) so the receipt
can be attributed; crate::bot::MessageContext::react fills this in
from the incoming message. An empty emoji removes a previous reaction
(WA Web’s empty-text reaction == sender-revoke).
For a Community Announcement Group the reaction is encrypted with the
target’s messageSecret (captured when the message was received) and
sent as enc_reaction_message; reacting to a message whose secret was
never captured fails rather than emitting a plaintext reaction the
channel would reject.
status@broadcast reactions fan out to the status author’s devices; the
author is read from target_key.participant by the send path.
Source§impl Client
impl Client
Sourcepub async fn rotate_signed_pre_key(&self) -> Result<(), SignalMaintenanceError>
pub async fn rotate_signed_pre_key(&self) -> Result<(), SignalMaintenanceError>
Stage a fresh signed pre-key durably, upload it, and only on server
acceptance promote it locally: retain the outgoing key, advance the
current key + cadence, and prune to SIGNED_PRE_KEY_RETENTION.
Both the new candidate and the outgoing key are written to the backend
table before upload (the candidate reused verbatim on retry), so every
partial failure is safe: whatever the server ends up advertising, we hold
its private key, and the old id’s decrypt window survives regardless. An
ambiguous transport error (the server may have accepted new_id) leaves
the staged key decryptable via the load fallback; a definitive rejection
just leaves the current key in place to retry — never advancing the
cadence or pruning the key the server still hands out. Calls are
serialized with the automatic rotation path.
Source§impl Client
impl Client
Sourcepub fn status(&self) -> Status<'_>
pub fn status(&self) -> Status<'_>
Access the status/story API for posting, revoking, and managing status updates.
§Example
use waproto::whatsapp::message::extended_text_message::FontType;
let recipients = [whatsapp_rust::Jid::pn("15551234567")];
let id = client
.status()
.send_text("Hello!", 0xFF1E6E4F, FontType::SYSTEM, &recipients, Default::default())
.await?;Source§impl Client
impl Client
Sourcepub async fn send_spam_report(
&self,
request: SpamReportRequest,
) -> Result<SpamReportResult, IqError>
pub async fn send_spam_report( &self, request: SpamReportRequest, ) -> Result<SpamReportResult, IqError>
Send a spam report to WhatsApp.
This sends a spam_list IQ stanza to report one or more messages as spam.
§Arguments
request- The spam report request containing message details
§Returns
Ok(SpamReportResult)- If the report was successfully submittedErr- If there was an error sending or processing the report
§Example
let result = client.send_spam_report(SpamReportRequest {
message_id: "MSG_ID".to_string(),
message_timestamp: 1234567890,
from_jid: Some(sender_jid),
spam_flow: SpamFlow::MessageMenu,
..Default::default()
}).await?;Trait Implementations§
Source§impl SendContextResolver for Client
impl SendContextResolver for Client
fn resolve_devices<'life0, 'life1, 'async_trait>(
&'life0 self,
jids: &'life1 [Jid],
) -> Pin<Box<dyn Future<Output = Result<Vec<Jid>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn fetch_prekeys<'life0, 'life1, 'async_trait>(
&'life0 self,
jids: &'life1 [Jid],
) -> Pin<Box<dyn Future<Output = Result<HashMap<Jid, PreKeyBundle>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn fetch_prekeys_for_identity_check<'life0, 'life1, 'async_trait>(
&'life0 self,
jids: &'life1 [Jid],
) -> Pin<Box<dyn Future<Output = Result<PreKeyFetchOutcome, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn fetch_prekeys_for_identity_check<'life0, 'life1, 'async_trait>(
&'life0 self,
jids: &'life1 [Jid],
) -> Pin<Box<dyn Future<Output = Result<PreKeyFetchOutcome, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn resolve_group_info<'life0, 'life1, 'async_trait>(
&'life0 self,
jid: &'life1 Jid,
) -> Pin<Box<dyn Future<Output = Result<Arc<GroupInfo>, Error>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_lid_for_phone<'life0, 'life1, 'async_trait>(
&'life0 self,
phone_user: &'life1 str,
) -> Pin<Box<dyn Future<Output = Option<CompactString>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_lid_for_phone<'life0, 'life1, 'async_trait>(
&'life0 self,
phone_user: &'life1 str,
) -> Pin<Box<dyn Future<Output = Option<CompactString>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn on_local_identity_change(&self, jid: &Jid)
fn on_local_identity_change(&self, jid: &Jid)
jid replaced a previously-stored
identity key (local detection of a peer identity change on the send path). Read moreSource§fn lock_device_sessions<'life0, 'life1, 'async_trait>(
&'life0 self,
device_jids: &'life1 [Jid],
) -> Pin<Box<dyn Future<Output = SessionLockGuard> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn lock_device_sessions<'life0, 'life1, 'async_trait>(
&'life0 self,
device_jids: &'life1 [Jid],
) -> Pin<Box<dyn Future<Output = SessionLockGuard> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
sender_key_lock
only serializes the sender-key chain, not these pairwise sessions. Default:
no-op — tests and benches don’t race concurrent sends.Auto Trait Implementations§
impl !Freeze for Client
impl !RefUnwindSafe for Client
impl !UnwindSafe for Client
impl Send for Client
impl Sync for Client
impl Unpin for Client
impl UnsafeUnpin for Client
Blanket Implementations§
Source§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
Source§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read moreSource§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read moreSource§fn aggregate_filter<P>(self, f: P) -> Self::Output
fn aggregate_filter<P>(self, f: P) -> Self::Output
Source§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> IntoSql for T
impl<T> IntoSql for T
Source§fn into_sql<T>(self) -> Self::Expression
fn into_sql<T>(self) -> Self::Expression
self to an expression for Diesel’s query builder. Read moreSource§fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
&self to an expression for Diesel’s query builder. Read more