Skip to main content
The Bot provides a simplified, ergonomic API for building WhatsApp bots. It handles client setup, event routing, and background sync tasks automatically.

Overview

Use the Bot builder pattern to:
  • Configure storage backend
  • Set up transport and HTTP client
  • Register event handlers
  • Configure device properties and versions
  • Enable pair code authentication
  • Skip history sync for bot use cases
The Bot is the recommended way to use whatsapp-rust. It provides sensible defaults and handles boilerplate setup.

Basic Usage

use whatsapp_rust::Bot;
use whatsapp_rust::types::events::Event;

let bot = Bot::builder()
    .with_backend(backend)
    .with_transport_factory(transport)
    .with_http_client(http_client)
    .on_event(|event, client| async move {
        match event {
            Event::Message(msg) => {
                println!("Message from {}: {:?}", msg.info.source.sender, msg.message);
            }
            Event::Connected(_) => {
                println!("Connected to WhatsApp!");
            }
            _ => {}
        }
    })
    .build()
    .await?;

let mut bot_handle = bot.run().await?;
bot_handle.await?;

Builder Methods

builder

pub fn builder() -> BotBuilder
Creates a new bot builder.

with_backend

pub fn with_backend(self, backend: Arc<dyn Backend>) -> Self
Sets the storage backend (required).
backend
Arc<dyn Backend>
required
Backend implementation providing storage operations
Example:
use whatsapp_rust::store::SqliteStore;

let backend = Arc::new(SqliteStore::new("whatsapp.db").await?);
let bot = Bot::builder()
    .with_backend(backend)
    // ...
For multi-account scenarios, use SqliteStore::new_for_device(path, device_id) to create isolated storage per account.

with_transport_factory

pub fn with_transport_factory<F>(self, factory: F) -> Self
where
    F: TransportFactory + 'static
Sets the transport factory for creating WebSocket connections (required).
factory
F: TransportFactory
required
Transport factory implementation
Example:
use whatsapp_rust_tokio_transport::TokioWebSocketTransportFactory;

let bot = Bot::builder()
    .with_transport_factory(TokioWebSocketTransportFactory::new())
    // ...

with_http_client

pub fn with_http_client<C>(self, client: C) -> Self
where
    C: HttpClient + 'static
Sets the HTTP client for media operations and version fetching (required).
client
C: HttpClient
required
HTTP client implementation
Example:
use whatsapp_rust_ureq_http_client::UreqHttpClient;

let bot = Bot::builder()
    .with_http_client(UreqHttpClient::new())
    // ...

Event Handling

on_event

pub fn on_event<F, Fut>(self, handler: F) -> Self
where
    F: Fn(Event, Arc<Client>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + 'static
Registers an async event handler.
handler
F
required
Async function that receives events and client Arc
Example:
use waproto::whatsapp as wa;

Bot::builder()
    .on_event(|event, client| async move {
        match event {
            Event::Message(msg) => {
                // Reply to messages
                let reply = wa::Message {
                    conversation: Some("Hello back!".to_string()),
                    ..Default::default()
                };
                let _ = client.send_message(msg.info.source.chat, reply).await;
            }
            Event::Connected(_) => {
                println!("Bot online!");
            }
            _ => {}
        }
    })
    // ...
See Events Reference for all event types.

with_enc_handler

pub fn with_enc_handler<H>(self, enc_type: impl Into<String>, handler: H) -> Self
where
    H: EncHandler + 'static
Registers a custom handler for specific encrypted message types.
enc_type
String
required
Encrypted message type (e.g., “frskmsg”, “skmsg”)
handler
H: EncHandler
required
Handler implementation

Configuration

with_version

pub fn with_version(self, version: (u32, u32, u32)) -> Self
Overrides the WhatsApp version used by the client.
version
(u32, u32, u32)
required
Tuple of (primary, secondary, tertiary) version numbers
Example:
Bot::builder()
    .with_version((2, 3000, 1027868167))
    // ...
By default, the client automatically fetches the latest version. Only use this if you need to pin a specific version.

with_device_props

pub fn with_device_props(
    self,
    os_name: Option<String>,
    version: Option<wa::device_props::AppVersion>,
    platform_type: Option<wa::device_props::PlatformType>
) -> Self
Overrides device properties sent to WhatsApp servers.
os_name
Option<String>
Operating system name (e.g., “macOS”, “Windows”, “Linux”)
version
Option<AppVersion>
App version struct
platform_type
Option<PlatformType>
Platform type (determines device name shown on phone)
Example:
use waproto::whatsapp::device_props::{AppVersion, PlatformType};

Bot::builder()
    .with_device_props(
        Some("macOS".to_string()),
        Some(AppVersion {
            primary: Some(2),
            secondary: Some(0),
            tertiary: Some(0),
            ..Default::default()
        }),
        Some(PlatformType::Chrome),
    )
    // ...
The platform_type determines what device name is shown on the phone’s “Linked Devices” list. Common values: Chrome, Firefox, Safari, Desktop.

Authentication

with_pair_code

pub fn with_pair_code(self, options: PairCodeOptions) -> Self
Configures pair code authentication to run automatically after connecting.
options
PairCodeOptions
required
Configuration for pair code authentication
Example:
use whatsapp_rust::pair_code::{PairCodeOptions, PlatformId};
use whatsapp_rust::types::events::Event;

Bot::builder()
    .with_pair_code(PairCodeOptions {
        phone_number: "15551234567".to_string(),
        show_push_notification: true,
        custom_code: None,
        platform_id: PlatformId::Chrome,
        platform_display: "Chrome (Linux)".to_string(),
    })
    .on_event(|event, _client| async move {
        match event {
            Event::PairingCode { code, timeout } => {
                println!("Enter this code on your phone: {}", code);
                println!("Expires in: {} seconds", timeout);
            }
            _ => {}
        }
    })
    // ...
Pair code runs concurrently with QR code pairing - whichever completes first wins.

History Sync

skip_history_sync

pub fn skip_history_sync(self) -> Self
Skips processing of history sync notifications from the phone. When enabled:
  • Acknowledges history sync notifications (so phone considers them delivered)
  • Does not download or process historical data
  • Emits debug log for each skipped notification
  • Useful for bot use cases where message history is not needed
Example:
Bot::builder()
    .skip_history_sync()
    // ...
For bots that only need to respond to new messages, enabling this can significantly reduce startup time and bandwidth usage.

Building and Running

build

pub async fn build(self) -> Result<Bot>
Builds the bot with the configured options. Errors:
  • Missing required fields (backend, transport_factory, http_client)
  • Backend initialization failures

client

pub fn client(&self) -> Arc<Client>
Returns the underlying Client Arc. Example:
let bot = Bot::builder()
    .with_backend(backend)
    .with_transport_factory(transport)
    .with_http_client(http_client)
    .build()
    .await?;

let client = bot.client();
let jid = client.get_pn().await;

run

pub async fn run(&mut self) -> Result<task::JoinHandle<()>>
Starts the bot’s connection loop and background workers. Returns a JoinHandle for the main client task. Example:
let mut bot = Bot::builder()
    // ... configuration
    .build()
    .await?;

let handle = bot.run().await?;

// Wait for bot to finish (runs until disconnect)
handle.await?;
You must call .await? on the returned handle to keep the bot running. If you drop the handle, the bot will continue running in the background.

Message Context

When handling message events, you get a MessageContext helper:
pub struct MessageContext {
    pub message: Box<wa::Message>,
    pub info: MessageInfo,
    pub client: Arc<Client>,
}

send_message

pub async fn send_message(&self, message: wa::Message) -> Result<String, anyhow::Error>
Sends a message to the same chat.

build_quote_context

pub fn build_quote_context(&self) -> wa::ContextInfo
Builds a quote context for replying to this message. Handles:
  • Correct stanza_id/participant for groups and newsletters
  • Stripping nested mentions
  • Preserving bot quote chains
Example:
use waproto::whatsapp as wa;

.on_event(|event, _client| async move {
    if let Event::Message(ctx) = event {
        let reply = wa::Message {
            conversation: Some("Quoted reply!".to_string()),
            context_info: Some(ctx.build_quote_context()),
            ..Default::default()
        };
        let _ = ctx.send_message(reply).await;
    }
})

edit_message

pub async fn edit_message(
    &self,
    original_message_id: impl Into<String>,
    new_message: wa::Message
) -> Result<String, anyhow::Error>
Edits a message in the same chat.

revoke_message

pub async fn revoke_message(
    &self,
    message_id: String,
    revoke_type: RevokeType
) -> Result<(), anyhow::Error>
Deletes a message in the same chat.

Complete Example

use whatsapp_rust::Bot;
use whatsapp_rust::store::SqliteStore;
use whatsapp_rust::types::events::Event;
use whatsapp_rust_tokio_transport::TokioWebSocketTransportFactory;
use whatsapp_rust_ureq_http_client::UreqHttpClient;
use waproto::whatsapp as wa;
use std::sync::Arc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Set up storage
    let backend = Arc::new(SqliteStore::new("bot.db").await?);

    // Build bot
    let mut bot = Bot::builder()
        .with_backend(backend)
        .with_transport_factory(TokioWebSocketTransportFactory::new())
        .with_http_client(UreqHttpClient::new())
        .skip_history_sync() // Bot only needs new messages
        .on_event(|event, client| async move {
            match event {
                Event::Message(ctx) => {
                    // Echo messages back
                    if let Some(text) = &ctx.message.conversation {
                        let reply = wa::Message {
                            conversation: Some(format!("You said: {}", text)),
                            ..Default::default()
                        };
                        let _ = ctx.send_message(reply).await;
                    }
                }
                Event::Connected(_) => {
                    println!("Bot is now online!");
                    // Set status
                    let _ = client.presence().set_available().await;
                }
                Event::QRCode(qr) => {
                    println!("Scan this QR code:");
                    for line in qr.lines() {
                        println!("{}", line);
                    }
                }
                _ => {}
            }
        })
        .build()
        .await?;

    // Run bot
    let handle = bot.run().await?;
    handle.await?;

    Ok(())
}

See Also