Este guia ajudará você a criar um bot simples do WhatsApp que responde a mensagens. Você aprenderá os conceitos principais e terá um bot funcionando ao final.
O bot precisa de armazenamento persistente para dados de sessão, chaves e estado:
let backend = Arc::new(SqliteStore::new("whatsapp.db").await?);
Isso cria um arquivo de banco SQLite chamado whatsapp.db no diretório atual. A sessão será persistida entre reinicializações.
2
Configure o builder do bot
O padrão Bot::builder() permite que você configure todos os componentes obrigatórios:
let mut bot = Bot::builder() .with_backend(backend) .with_transport_factory(TokioWebSocketTransportFactory::new()) .with_http_client(UreqHttpClient::new()) .with_runtime(TokioRuntime)
Todos os quatro componentes (backend, transporte, cliente HTTP, runtime) são obrigatórios. O builder usa um padrão typestate — seu código não compilará se algum estiver faltando.
3
Trate eventos
Use .on_event() para tratar eventos que chegam do WhatsApp:
Vamos estender o bot para responder “pong” a mensagens “ping”:
use wacore::proto_helpers::MessageExt;use waproto::whatsapp as wa;.on_event(|event, client| async move { match &*event { Event::PairingQrCode { code, .. } => { println!("QR Code:\n{}", code); } Event::Message(msg, info) => { // Verifica se a mensagem é um texto dizendo "ping" if let Some(text) = msg.text_content() { if text == "ping" { // Cria a mensagem de resposta let reply = wa::Message { conversation: Some("pong".to_string()), ..Default::default() }; // Envia a resposta if let Err(e) = client.send_message(info.source.chat.clone(), reply).await { eprintln!("Failed to send reply: {}", e); } } } } _ => {} }})
Alternativamente, vincule usando um número de telefone e um código de 8 dígitos:
use whatsapp_rust::pair_code::PairCodeOptions;let mut bot = Bot::builder() .with_backend(backend) .with_transport_factory(TokioWebSocketTransportFactory::new()) .with_http_client(UreqHttpClient::new()) .with_runtime(TokioRuntime) .with_pair_code(PairCodeOptions { phone_number: "15551234567".to_string(), ..Default::default() }) .on_event(|event, client| async move { match &*event { Event::PairingCode { code, .. } => { println!("Enter this code on your phone: {}", code); } _ => {} } }) .build() .await?;
PairCodeOptions deriva companion_platform_id e companion_platform_display do PlatformType do dispositivo por padrão (Chrome com Chrome (Linux) para o perfil web padrão). Você pode sobrescrever o id no wire quando necessário:
use whatsapp_rust::pair_code::PairCodeOptions;use wacore::companion_reg::CompanionWebClientType;PairCodeOptions { phone_number: "15551234567".to_string(), show_push_notification: true, custom_code: Some("ABCD1234".to_string()), // ou None para aleatório // `None` deriva automaticamente de `Device.device_props.platform_type`. platform_id: Some(CompanionWebClientType::Chrome),}
platform_id aceita o enum no wire CompanionWebClientType (ids ASCII de um único byte). A string de exibição é sempre derivada — não há um campo separado platform_display.
A autenticação por pair code e por QR code rodam simultaneamente. O método que for concluído primeiro será o usado.
O repositório inclui um binário de bot de demonstração (src/main.rs) que suporta argumentos de CLI para autenticação:
cargo run # Pareamento somente por QR codecargo run -- --phone 15551234567 # Pair code + QR code (concorrentes)cargo run -- -p 15551234567 # Forma curtacargo run -- -p 15551234567 --code MYCODE12 # Pair code personalizado de 8 caracterescargo run -- -p 15551234567 -c MYCODE12 # Forma curta
O bot de demonstração responde a 🦀ping com uma resposta citada 🏓 Pong!, edita a resposta para anexar a latência de envio e suporta ping/pong de mídia via reuso de CDN.
Para um tratamento de mensagens mais limpo, use MessageContext para encapsular a mensagem, os metadados e o cliente juntos. Isso fornece métodos convenientes como send_message (que mira automaticamente o chat de origem), build_quote_context, edit_message e revoke_message:
use whatsapp_rust::bot::{Bot, MessageContext};.on_event(|event, client| async move { if let Some(ctx) = MessageContext::from_event(&event, client) { handle_message(&ctx).await; }})
Em seguida, defina funções de tratamento focadas:
async fn handle_message(ctx: &MessageContext) { if let Some(text) = ctx.message.text_content() { if text == "ping" { let reply = wa::Message { conversation: Some("pong".to_string()), ..Default::default() }; if let Err(e) = ctx.send_message(reply).await { eprintln!("Failed to send: {}", e); } } }}
Você também pode encaminhar mídia instantaneamente reusando os campos originais do CDN — sem necessidade de download ou re-upload:
/// Reutiliza o blob original do CDN, apenas troca a legenda./// Instantâneo independentemente do tamanho do arquivo.fn build_media_reply(message: &wa::Message) -> Option<wa::Message> { let base = message.get_base_message(); if let Some(img) = &base.image_message { return Some(wa::Message { image_message: Some(Box::new(wa::message::ImageMessage { caption: Some("Received your image!".to_string()), ..*img.clone() })), ..Default::default() }); } None}
whatsapp-rust usa o crate log com alvos específicos por módulo para filtragem detalhada. Você pode usar RUST_LOG para controlar quais componentes emitem saída de log.
# Mostra apenas logs de conexão e mensagensRUST_LOG="Client/Keepalive=debug,Client/Send=debug,Client/Recv=trace" cargo run# Depura problemas de sync offlineRUST_LOG="Client/OfflineSync=debug" cargo run# Modo silencioso: apenas erros e avisosRUST_LOG="warn" cargo run# Verboso: todos os internos do client em nível debugRUST_LOG="debug" cargo run
Durante o desligamento ou desconexão, o cliente automaticamente rebaixa erros de sincronização do nível error para debug para reduzir ruído. Isso significa que você não verá logs de erro espúrios quando o cliente estiver se desconectando intencionalmente.
Você também pode executar o bot usando Docker em vez de compilar localmente:
docker build -t whatsapp-rust .docker run -v ./data:/data whatsapp-rust
Os dados da sessão são armazenados no diretório /data dentro do contêiner. Monte um volume para persisti-los entre reinicializações. O contêiner é desligado graciosamente em docker stop — o bot se desconecta limpamente do WhatsApp antes de sair. Veja o guia de instalação para mais detalhes.
O repositório inclui um exemplo de benchmark em examples/benchmark.rs que você pode usar para testes rápidos de desempenho em nível de integração. Ele usa um backend em memória e suporta uma URL de WebSocket personalizada via a variável de ambiente WHATSAPP_WS_URL:
cargo run --example benchmark --features danger-skip-tls-verify
O exemplo de benchmark requer a feature flag danger-skip-tls-verify porque foi projetado para uso com servidores de teste locais.
Para benchmarks de integração mais abrangentes com rastreamento de alocações, a suíte bench-integration mede cenários do mundo real (conectar, enviar, receber, reconectar) e reporta o tempo de relógio e contagens de alocação no heap por operação:
# Requer um servidor mock (por exemplo, Bartender)MOCK_SERVER_URL="wss://127.0.0.1:8080/ws/chat" \ cargo run -p bench-integration --release
Para benchmarks de protocolo de baixo nível, o crate wacore inclui uma suíte de benchmarks com iai-callgrind que mede contagens de instruções para o pipeline completo de envio/recebimento (mensagens DM e em grupo com várias contagens de participantes), codificação do protocolo binário, operações do Signal Protocol e geração de tokens de reporte:
# Executa todos os benchmarks do wacore (requer valgrind e iai-callgrind-runner)cargo bench --workspace