Skip to main content

BotBuilder

Struct BotBuilder 

Source
pub struct BotBuilder<B = MissingBackend, T = MissingTransport, H = MissingHttpClient, R = MissingRuntime> { /* private fields */ }
Expand description

Builder for Bot using the typestate pattern.

The four type parameters track whether the required fields (backend, transport factory, HTTP client, runtime) have been provided: build() is only available once all four are Provided, turning missing-field errors into compile-time errors. With the default cargo features, transport, HTTP client and runtime start Provided (Tokio WebSocket, ureq, Tokio), so only the backend is required.

Implementations§

Source§

impl<B, T, H, R> BotBuilder<B, T, H, R>

Source

pub fn with_backend( self, backend: impl Backend + 'static, ) -> BotBuilder<Provided, T, H, R>

Use a backend implementation for storage. This is the only required field when the default transport/HTTP/runtime features are enabled.

The backend is wrapped in an Arc internally; use BotBuilder::with_backend_arc to pass an already-shared backend.

§Example
let bot = Bot::builder()
    .with_backend(SqliteStore::new("whatsapp.db").await?)
    .build()
    .await?;
Source

pub fn with_backend_arc( self, backend: Arc<dyn Backend>, ) -> BotBuilder<Provided, T, H, R>

BotBuilder::with_backend for an already-shared Arc<dyn Backend>.

Source

pub fn with_transport_factory<F>( self, factory: F, ) -> BotBuilder<B, Provided, H, R>
where F: TransportFactory + 'static,

Set the transport factory for creating network connections, replacing the tokio-transport default when that feature is enabled.

Source

pub fn with_http_client<C>(self, client: C) -> BotBuilder<B, T, Provided, R>
where C: HttpClient + 'static,

Set the HTTP client used for media operations and version fetching, replacing the ureq-client default when that feature is enabled.

Source

pub fn with_runtime<Rt: Runtime>( self, runtime: Rt, ) -> BotBuilder<B, T, H, Provided>

Set the async runtime implementation, replacing the tokio-runtime default when that feature is enabled.

Source

pub fn with_task_instrument(self, instrument: Arc<dyn TaskInstrument>) -> Self

Instrument the client’s internal tasks with a TaskInstrument hook, called around each poll (and around blocking work).

Runtime-agnostic: the hook wraps whatever runtime the client uses, so every task spawned through the Runtime trait is covered, and Bot::run meters the main run loop itself — the read loop reports on either launch path (voip media tasks spawn directly on Tokio and are not covered). Pass a CpuMeter for per-session CPU accounting (keep a clone to read snapshots), or a custom hook to scope allocator-attribution or platform samplers to this client’s work. Default: no hook — the runtime is used untouched.

Occupies the same single-instrument slot as with_alloc_meter (last setter wins): calling this after with_alloc_meter drops the typed alloc-meter handle, so Client::resource_report’s alloc field reverts to None.

§Example
use std::sync::Arc;
use wacore::stats::CpuMeter;

let cpu = Arc::new(CpuMeter::new());
let bot = Bot::builder()
    .with_backend(backend)
    .with_task_instrument(cpu.clone())
    .build()
    .await?;
// later: cpu.snapshot().busy
Source

pub fn with_alloc_meter(self, meter: Arc<AllocMeter>) -> Self

Install an AllocMeter as this client’s task instrument and keep a typed handle so Client::resource_report folds in its allocation-churn snapshot.

Sugar over with_task_instrument: it occupies the single instrument slot (so it’s mutually exclusive with a CpuMeter or another hook — last setter wins). The host still installs a #[global_allocator] that calls AllocMeter::on_alloc / AllocMeter::on_dealloc; see examples/alloc_tracking.rs.

Source

pub fn with_plugin<P: ClientPlugin>(self, plugin: P) -> Self

Available on crate feature plugins only.

Register a native plugin without changing the builder’s typestate.

Source

pub fn with_plugin_arc<P: ClientPlugin>(self, plugin: Arc<P>) -> Self

Available on crate feature plugins only.

Register an already-shared native plugin without changing its marker type.

Source

pub fn with_untyped_plugin<P: UntypedClientPlugin>(self, plugin: P) -> Self

Available on crate feature plugins only.

Register a manifest-ID-keyed plugin that exposes no Rust typed API.

Source

pub fn with_untyped_plugin_arc<P: UntypedClientPlugin + ?Sized>( self, plugin: Arc<P>, ) -> Self

Available on crate feature plugins only.

Register an already-shared manifest-ID-keyed plugin.

Source

pub fn with_plugin_host_config(self, config: PluginHostConfig) -> Self

Available on crate feature plugins only.

Configure plugin lifecycle and tracked-task deadlines.

Source

pub fn on_event<F, Fut>(self, handler: F) -> Self
where F: Fn(Arc<Event>, Arc<Client>) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Register a handler that receives every event kind.

Source

pub fn on_event_for<F, Fut>(self, kinds: &[EventKind], handler: F) -> Self
where F: Fn(Arc<Event>, Arc<Client>) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Register a handler that receives only the given event kinds. The bus skips materializing (and boxing the handler future for) every other kind, so a narrowly-scoped bot does not pay for events it ignores.

Source

pub fn on_message<F, Fut>(self, handler: F) -> Self
where F: Fn(MessageContext) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler for every incoming message, with a ready MessageContext (reply/react/edit helpers included).

Event::Messages batches (one per commit during an offline drain, single-message on live traffic) are fanned out here in arrival order, awaiting each handler before the next — per-message bots keep their ergonomics and gain in-batch ordering.

The handler is CALLED for every message in the batch up front and the returned futures then run in order (an async closure runs no body code at call time, so for the typical handler this is unobservable). Interleaving call+await instead would hold a MessageContext across an await, which is not Send on wasm32.

Source

pub fn on_qr_code<F, Fut>(self, handler: F) -> Self
where F: Fn(String, Duration) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler with the QR payload (and validity window) each time a pairing QR code is issued. Render code as a QR image for scanning.

Source

pub fn on_pair_code<F, Fut>(self, handler: F) -> Self
where F: Fn(String, Duration) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler with the 8-character pairing code (and validity window) generated by BotBuilder::with_pair_code linking.

Source

pub fn on_pair_code_error<F, Fut>(self, handler: F) -> Self
where F: Fn(PairingCodeError, Arc<Client>) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler when a pair-code request fails, so no code will be issued (Event::PairingCodeError).

The counterpart to BotBuilder::on_pair_code, and the only way to observe the failure of a BotBuilder::with_pair_code request: that one runs in a detached task, so its Err reaches no caller.

Branch on err.rejection rather than the message. PairCodeRejection::is_throttled is the case to slow down for — re-requesting on the original schedule spends more of the budget the server just refused — and err.backoff carries the server’s own delay when it named one.

Source

pub fn on_pair_code_refresh<F, Fut>(self, handler: F) -> Self
where F: Fn(bool, Arc<Client>) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler when the server asks the companion to refresh an in-progress pairing code (Event::PairingCodeRefresh). The bool is force_manual. The typical reaction is to request a fresh code via Client::pair_with_code with the same phone number.

Source

pub fn on_connected<F, Fut>(self, handler: F) -> Self
where F: Fn(Arc<Client>) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler once the client is connected and authenticated.

Source

pub fn on_logged_out<F, Fut>(self, handler: F) -> Self
where F: Fn(LoggedOut) -> Fut + Send + Sync + 'static, Fut: Future<Output = ()> + Send + 'static,

Run handler when the device is logged out (unlinked from the phone).

Source

pub fn with_event_handler(self, handler: impl EventHandler + 'static) -> Self

Register a struct-based EventHandler directly on the event bus.

Unlike the closure registrars, the handler keeps its state in &self (no per-field clone dance) and handle_event runs inline on the dispatch path: spawn your own task for slow work.

Source

pub fn with_event_delivery(self, delivery: EventDelivery) -> Self

Choose how registered callbacks receive events. Defaults to EventDelivery::Concurrent; use EventDelivery::Ordered for in-arrival-order, bounded delivery. Only affects the closure-based callbacks, not raw with_event_handler handlers, which always run inline on the dispatch path.

Source

pub fn with_enc_handler<Eh>( self, enc_type: impl Into<String>, handler: Eh, ) -> Self
where Eh: EncHandler + 'static,

Register a custom handler for a specific encrypted message type

§Arguments
  • enc_type - The encrypted message type (e.g., “frskmsg”)
  • handler - The handler implementation for this type
Source

pub fn with_inbound_durability_hook<Dh>(self, hook: Dh) -> Self
where Dh: InboundDurabilityHook + 'static,

Register an inbound durability hook for at-least-once delivery.

By default the client acks a message as soon as it is decrypted (at-most-once): a crash or failed commit before the consumer persists it loses the message. With a hook registered, the ack is deferred until the hook commits the message; on failure the message is redelivered on the next connect. The hook must be idempotent (dedupe by (chat, sender, id), since stanza ids are only unique within a chat/sender). See InboundDurabilityHook for the full contract and caveats.

Source

pub fn with_version(self, version: (u32, u32, u32)) -> Self

Override the WhatsApp version used by the client.

By default, the client will automatically fetch the latest version from WhatsApp’s servers. Use this method to force a specific version instead.

§Arguments
  • version - A tuple of (primary, secondary, tertiary) version numbers
Source

pub fn with_device_props(self, override_: DevicePropsOverride) -> Self

Override the device properties sent to WhatsApp servers. This allows customizing how your device appears on the linked devices list.

platform_type controls the display name in Linked Devices; defaults to Unknown (“Unknown device”). Only applied on the initial pairing.

§Example
use waproto::whatsapp::device_props::PlatformType;
use wacore::store::DevicePropsOverride;

Bot::builder()
    .with_backend(backend)
    .with_device_props(
        DevicePropsOverride::new()
            .with_os("macOS")
            .with_platform_type(PlatformType::CHROME),
    );
Source

pub fn with_pair_code(self, options: PairCodeOptions) -> Self

Configure pair code authentication to run automatically after connecting.

When set, the pair code request will be sent automatically after establishing a connection, and the pairing code will be dispatched via Event::PairingCode (see BotBuilder::on_pair_code). This runs concurrently with QR code pairing - whichever completes first wins.

The request runs in a detached task, so a failure cannot be returned to the caller: it arrives as Event::PairingCodeError instead (see BotBuilder::on_pair_code_error). Subscribe to it if the consumer must distinguish “still waiting for the user” from “no code is coming” — a rate-limited request is otherwise indistinguishable from the former.

§Example
use whatsapp_rust::pair_code::PairCodeOptions;

// Platform identity is derived from `DeviceProps` configured via
// `Bot::builder().with_device_props(...)`. Explicit overrides below
// are optional — omit them to let derivation do the right thing.
let bot = Bot::builder()
    .with_backend(backend)
    .with_pair_code(PairCodeOptions {
        phone_number: "15551234567".to_string(),
        custom_code: Some("ABCD1234".to_string()),
        ..Default::default()
    })
    .on_pair_code(|code, _timeout| async move {
        println!("Enter this code on your phone: {code}");
    })
    .build()
    .await?;
Source

pub fn skip_history_sync(self) -> Self

Skip processing of history sync notifications from the phone.

When enabled, the client will acknowledge all incoming history sync notifications (so the phone considers them delivered) but will not download or process any historical data (INITIAL_BOOTSTRAP, RECENT, FULL, PUSH_NAME, etc.). A debug log entry is emitted for each skipped notification. This is useful for bot use cases where message history is not needed.

Default: false (history sync is processed normally).

Source

pub fn with_wanted_pre_key_count(self, count: usize) -> Self

Set how many one-time pre-keys are generated and uploaded per batch.

Defaults to WA Web’s UPLOAD_KEYS_COUNT (812). The value is clamped to the protocol-safe range at upload time. Useful for memory-constrained or embedded consumers that want a smaller batch.

Source

pub fn with_resend_rate_limit(self, burst: u32, refill_per_min: u32) -> Self

Tune the per-chat outbound resend rate limiter.

Outbound retry resends to a chat are bounded by a token bucket: burst is the instantaneous allowance, refill_per_min the sustained ceiling per chat. This caps the aggregate resend rate 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 it.

Defaults are conservative (burst 20, refill 10/min) and apply without calling this. Can also be retuned live via Client::set_resend_rate_limit.

Source

pub fn with_push_name(self, name: impl Into<String>) -> Self

Set an initial push name on the device before connecting.

This is included in the ClientPayload during registration, allowing the mock server to deterministically assign phone numbers based on push name (same push name = same phone, enabling multi-device testing).

Source

pub fn with_cache_config(self, config: CacheConfig) -> Self

Configure cache TTL and capacity settings.

By default, all caches match WhatsApp Web behavior. Use this method to customize cache durations for your use case.

§Example
use whatsapp_rust::{CacheConfig, CacheEntryConfig};

// Disable TTL for group and device caches (good for bots with few groups)
let bot = Bot::builder()
    .with_backend(backend)
    .with_cache_config(CacheConfig {
        group_cache: CacheEntryConfig::new(None, 1_000),
        device_registry_cache: CacheEntryConfig::new(None, 5_000),
        ..Default::default()
    })
    .build()
    .await?;
Source§

impl BotBuilder<Provided, Provided, Provided, Provided>

Source

pub async fn build(self) -> Result<Bot, BotBuilderError>

Boxed barrier: see Bot::run. Building the client wires every cache and background loop, so an unboxed await here would duplicate that whole construction graph into the consumer crate.

Auto Trait Implementations§

§

impl<B = MissingBackend, T = MissingTransport, H = MissingHttpClient, R = MissingRuntime> !RefUnwindSafe for BotBuilder<B, T, H, R>

§

impl<B = MissingBackend, T = MissingTransport, H = MissingHttpClient, R = MissingRuntime> !UnwindSafe for BotBuilder<B, T, H, R>

§

impl<B, T, H, R> Freeze for BotBuilder<B, T, H, R>

§

impl<B, T, H, R> Send for BotBuilder<B, T, H, R>
where B: Send, T: Send, H: Send, R: Send,

§

impl<B, T, H, R> Sync for BotBuilder<B, T, H, R>
where B: Sync, T: Sync, H: Sync, R: Sync,

§

impl<B, T, H, R> Unpin for BotBuilder<B, T, H, R>
where B: Unpin, T: Unpin, H: Unpin, R: Unpin,

§

impl<B, T, H, R> UnsafeUnpin for BotBuilder<B, T, H, R>

Blanket Implementations§

Source§

impl<T> AggregateExpressionMethods for T

Source§

fn aggregate_distinct(self) -> Self::Output
where Self: DistinctDsl,

DISTINCT modifier for aggregate functions Read more
Source§

fn aggregate_all(self) -> Self::Output
where Self: AllDsl,

ALL modifier for aggregate functions Read more
Source§

fn aggregate_filter<P>(self, f: P) -> Self::Output
where P: AsExpression<Bool>, Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,

Add an aggregate function filter Read more
Source§

fn aggregate_order<O>(self, o: O) -> Self::Output
where Self: OrderAggregateDsl<O>,

Add an aggregate function order Read more
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Send + Sync>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoSql for T

Source§

fn into_sql<T>(self) -> Self::Expression

Convert self to an expression for Diesel’s query builder. Read more
Source§

fn as_sql<'a, T>(&'a self) -> <&'a Self as AsExpression<T>>::Expression
where &'a Self: AsExpression<T>, T: SqlType + TypedExpressionType,

Convert &self to an expression for Diesel’s query builder. Read more
Source§

impl<T> MaybeSend for T
where T: Send + ?Sized,

Source§

impl<T> MaybeSendSync for T
where T: Send + Sync + ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Spawnable for T
where T: Send + 'static,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WindowExpressionMethods for T

Source§

fn over(self) -> Self::Output
where Self: OverDsl,

Turn a function call into a window function call Read more
Source§

fn window_filter<P>(self, f: P) -> Self::Output
where P: AsExpression<Bool>, Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,

Add a filter to the current window function Read more
Source§

fn partition_by<E>(self, expr: E) -> Self::Output
where Self: PartitionByDsl<E>,

Add a partition clause to the current window function Read more
Source§

fn window_order<E>(self, expr: E) -> Self::Output
where Self: OrderWindowDsl<E>,

Add a order clause to the current window function Read more
Source§

fn frame_by<E>(self, expr: E) -> Self::Output
where Self: FrameDsl<E>,

Add a frame clause to the current window function Read more