> ## Documentation Index
> Fetch the complete documentation index at: https://whatsapp-rust.jlucaso.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Início rápido

> Crie seu primeiro bot do WhatsApp em minutos

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.

## Exemplo básico

Aqui está um bot mínimo que responde a mensagens "ping":

```rust src/main.rs theme={null}
use std::sync::Arc;
use whatsapp_rust::bot::Bot;
use whatsapp_rust::TokioRuntime;
use whatsapp_rust::store::SqliteStore;
use whatsapp_rust_tokio_transport::TokioWebSocketTransportFactory;
use whatsapp_rust_ureq_http_client::UreqHttpClient;
use wacore::types::events::{Event, InboundMessage, PairingQrCode};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Inicializa o backend de armazenamento
    let backend = Arc::new(SqliteStore::new("whatsapp.db").await?);

    // Constrói o bot
    let mut bot = Bot::builder()
        .with_backend(backend)
        .with_transport_factory(TokioWebSocketTransportFactory::new())
        .with_http_client(UreqHttpClient::new())
        .with_runtime(TokioRuntime)
        .on_event(|event, client| async move {
            match &*event {
                Event::PairingQrCode(PairingQrCode { code, .. }) => {
                    println!("Scan this QR code with WhatsApp:\n{}", code);
                }
                Event::Messages(batch) => {
                    for InboundMessage { message: msg, info, .. } in batch.iter() {
                        println!("Message from {}: {:?}", info.source.sender, msg);
                    }
                }
                _ => {}
            }
        })
        .build()
        .await?;

    // Inicia o bot
    bot.run().await?.await?;
    Ok(())
}
```

## Passo a passo detalhado

<Steps>
  <Step title="Configure o backend de armazenamento">
    O bot precisa de armazenamento persistente para dados de sessão, chaves e estado:

    ```rust theme={null}
    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.
  </Step>

  <Step title="Configure o builder do bot">
    O padrão `Bot::builder()` permite que você configure todos os componentes obrigatórios:

    ```rust theme={null}
    let mut bot = Bot::builder()
        .with_backend(backend)
        .with_transport_factory(TokioWebSocketTransportFactory::new())
        .with_http_client(UreqHttpClient::new())
        .with_runtime(TokioRuntime)
    ```

    <Note>
      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.
    </Note>
  </Step>

  <Step title="Trate eventos">
    Use `.on_event()` para tratar eventos que chegam do WhatsApp:

    ```rust theme={null}
    .on_event(|event, client| async move {
        match &*event {
            Event::PairingQrCode(PairingQrCode { code, .. }) => {
                println!("QR Code:\n{}", code);
            }
            Event::Messages(batch) => {
                for InboundMessage { message: msg, info, .. } in batch.iter() {
                    // Trate a mensagem recebida
                }
            }
            Event::Connected(_) => {
                println!("Connected successfully!");
            }
            _ => {}
        }
    })
    ```

    O manipulador de eventos recebe dois parâmetros:

    * `event`: Um `Arc<Event>` — use `&*event` ou `event.as_ref()` para fazer pattern-matching no tipo interno do evento
    * `client`: Um `Arc<Client>` que você pode usar para enviar mensagens ou chamar métodos da API
  </Step>

  <Step title="Construa e execute o bot">
    Construa o bot e inicie o loop de eventos:

    ```rust theme={null}
    .build()
    .await?;

    bot.run().await?.await?;
    ```

    O duplo `.await?` é intencional:

    * O primeiro `.await?` inicia o bot e retorna um `BotHandle`
    * O segundo `.await?` aguarda o bot terminar a execução
  </Step>
</Steps>

## Respondendo a mensagens

Vamos estender o bot para responder "pong" a mensagens "ping":

```rust theme={null}
use wacore::proto_helpers::MessageExt;
use waproto::whatsapp as wa;

.on_event(|event, client| async move {
    match &*event {
        Event::PairingQrCode(PairingQrCode { code, .. }) => {
            println!("QR Code:\n{}", code);
        }
        Event::Messages(batch) => {
            for InboundMessage { message: msg, info, .. } in batch.iter() {
                // 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);
                        }
                    }
                }
            }
        }
        _ => {}
    }
})
```

### Métodos principais

* `msg.text_content()` - Extrai o texto de qualquer tipo de mensagem (conversation, extended text, etc.)
* `client.send_message()` - Envia uma mensagem para um chat
* `info.source.chat` - O JID (identificador) do chat de onde a mensagem veio
* `info.source.sender` - O JID do usuário que enviou a mensagem

## Métodos de autenticação

### Pareamento por QR code (padrão)

O bot gera automaticamente códigos QR quando não está autenticado. Escaneie com seu celular para vincular:

```rust theme={null}
Event::PairingQrCode(PairingQrCode { code, .. }) => {
    println!("Scan this QR code:\n{}", code);
}
```

### Pair code (número de telefone)

Alternativamente, vincule usando um número de telefone e um código de 8 dígitos:

```rust theme={null}
use whatsapp_rust::pair_code::PairCodeOptions;
use wacore::types::events::{Event, PairingCode};

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(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:

```rust theme={null}
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),
}
```

<Note>
  `platform_id` aceita o enum no wire [`CompanionWebClientType`](/concepts/authentication#companionwebclienttype) (ids ASCII de um único byte). A string de exibição é sempre derivada — não há um campo separado `platform_display`.
</Note>

<Note>
  A autenticação por pair code e por QR code rodam simultaneamente. O método que for concluído primeiro será o usado.
</Note>

## Executando o bot

<Steps>
  <Step title="Primeira execução - Autenticação">
    Na primeira execução, o bot irá gerar um QR code:

    ```bash theme={null}
    cargo run
    ```

    Escaneie o QR code com o WhatsApp no seu celular:

    1. Abra o WhatsApp no seu celular
    2. Vá em Configurações → Aparelhos conectados
    3. Toque em "Conectar um aparelho"
    4. Escaneie o QR code exibido no seu terminal
  </Step>

  <Step title="Execuções seguintes - Login automático">
    Após o pareamento, a sessão é salva. O bot irá reconectar automaticamente:

    ```bash theme={null}
    cargo run
    ```

    Você deverá ver:

    ```
    Connected successfully!
    ```
  </Step>

  <Step title="Teste o bot">
    Envie "ping" para o seu bot de qualquer chat do WhatsApp. Ele deve responder com "pong"!
  </Step>
</Steps>

### Flags de CLI do exemplo de demonstração

O repositório inclui um exemplo de bot de demonstração (`examples/demo.rs`) que suporta argumentos de CLI para autenticação:

```bash theme={null}
cargo run --example demo                                      # Pareamento somente por QR code
cargo run --example demo -- --phone 15551234567               # Pair code + QR code (concorrentes)
cargo run --example demo -- -p 15551234567                    # Forma curta
cargo run --example demo -- -p 15551234567 --code MYCODE12    # Pair code personalizado de 8 caracteres
cargo run --example demo -- -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.

## Usando MessageContext

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`:

```rust theme={null}
use whatsapp_rust::bot::{Bot, MessageContext};

.on_event(|event, client| async move {
    for inbound in event.messages() {
        let ctx = MessageContext::from_inbound(inbound, client.clone());
        handle_message(&ctx).await;
    }
})
```

Em seguida, defina funções de tratamento focadas:

```rust theme={null}
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);
            }
        }
    }
}
```

### Encaminhamento de mídia com reuso de CDN

Você também pode encaminhar mídia instantaneamente reusando os campos originais do CDN — sem necessidade de download ou re-upload:

<Note>
  Construir uma mensagem com um campo de sub-mensagem (como `image_message` abaixo) requer `MessageField`. O `whatsapp-rust` reexporta `buffa` como `whatsapp_rust::buffa` (e `MessageField` diretamente do `prelude`), então não é mais necessário adicionar `buffa` ao seu `Cargo.toml`.
</Note>

```rust theme={null}
/// 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.as_option() {
        return Some(wa::Message {
            image_message: whatsapp_rust::buffa::MessageField::some(wa::message::ImageMessage {
                caption: Some("Received your image!".to_string()),
                ..img.clone()
            }),
            ..Default::default()
        });
    }
    None
}
```

Veja o [guia de encaminhamento de mídia](/guides/media-handling#forwarding-media-via-cdn-reuse) para mais detalhes.

## Exemplo completo com logging

Aqui está um exemplo pronto para produção com logging adequado, reações, edição de mensagens e reuso de CDN para mídia:

```rust src/main.rs theme={null}
use chrono::{Local, Utc};
use log::{error, info};
use std::sync::Arc;
use wacore::proto_helpers::MessageExt;
use wacore::types::events::{Event, InboundMessage, PairingQrCode};
use waproto::whatsapp as wa;
use whatsapp_rust::bot::{Bot, MessageContext};
use whatsapp_rust::TokioRuntime;
use whatsapp_rust::store::SqliteStore;
use whatsapp_rust_tokio_transport::TokioWebSocketTransportFactory;
use whatsapp_rust_ureq_http_client::UreqHttpClient;

const PING_TRIGGER: &str = "🦀ping";
const PONG_TEXT: &str = "🏓 Pong!";

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
        .format(|buf, record| {
            use std::io::Write;
            writeln!(
                buf,
                "{} [{:<5}] [{}] - {}",
                Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                record.args()
            )
        })
        .init();

    let backend = Arc::new(SqliteStore::new("whatsapp.db").await?);
    info!("SQLite backend initialized");

    let mut bot = Bot::builder()
        .with_backend(backend)
        .with_transport_factory(TokioWebSocketTransportFactory::new())
        .with_http_client(UreqHttpClient::new())
        .with_runtime(TokioRuntime)
        .on_event(|event, client| async move {
            match &*event {
                Event::PairingQrCode(PairingQrCode { code, .. }) => {
                    println!("\n{}", code);
                }
                Event::Messages(batch) => {
                    for InboundMessage { message: msg, info, .. } in batch.iter() {
                        let ctx = MessageContext::from_parts(msg, info, client.clone());
                        handle_message(&ctx).await;
                    }
                }
                Event::Connected(_) => info!("Bot connected!"),
                Event::LoggedOut(_) => error!("Bot was logged out!"),
                _ => {}
            }
        })
        .build()
        .await?;

    info!("Starting bot...");
    bot.run().await?.await?;
    Ok(())
}

async fn handle_message(ctx: &MessageContext) {
    // Tenta a resposta de mídia via reuso de CDN primeiro (instantânea, sem download)
    if let Some(reply) = build_media_pong(&ctx.message) {
        if let Err(e) = ctx.send_message(reply).await {
            error!("Failed to send media pong: {}", e);
        }
        return;
    }

    // Trata o ping de texto
    if ctx.message.text_content() == Some(PING_TRIGGER) {
        let context_info = ctx.build_quote_context();
        let reply = wa::Message {
            extended_text_message: whatsapp_rust::buffa::MessageField::some(wa::message::ExtendedTextMessage {
                text: Some(PONG_TEXT.to_string()),
                context_info: whatsapp_rust::buffa::MessageField::some(context_info),
                ..Default::default()
            }),
            ..Default::default()
        };

        if let Err(e) = ctx.send_message(reply).await {
            error!("Failed to send pong: {}", e);
        }
    }
}

/// Reutiliza o blob original do CDN, apenas troca a legenda.
/// Instantâneo independentemente do tamanho do arquivo — sem download ou re-upload.
fn build_media_pong(message: &wa::Message) -> Option<wa::Message> {
    let base = message.get_base_message();
    if let Some(img) = base.image_message.as_option()
        && img.caption.as_deref() == Some(PING_TRIGGER)
    {
        return Some(wa::Message {
            image_message: whatsapp_rust::buffa::MessageField::some(wa::message::ImageMessage {
                caption: Some(PONG_TEXT.to_string()),
                ..img.clone()
            }),
            ..Default::default()
        });
    }
    if let Some(vid) = base.video_message.as_option()
        && vid.caption.as_deref() == Some(PING_TRIGGER)
    {
        return Some(wa::Message {
            video_message: whatsapp_rust::buffa::MessageField::some(wa::message::VideoMessage {
                caption: Some(PONG_TEXT.to_string()),
                ..vid.clone()
            }),
            ..Default::default()
        });
    }
    None
}
```

## Configurando alvos de log

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.

### Alvos de log disponíveis

| Alvo                    | Descrição                                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `Client/Keepalive`      | Pings/pongs de keepalive e detecção de socket morto                                                                     |
| `Client/Recv`           | Processamento de frames recebidos (unmarshal, descompressão)                                                            |
| `Client/Send`           | Criptografia e despacho de mensagens enviadas                                                                           |
| `Client/OfflineSync`    | Progresso e tempo da sincronização de mensagens offline                                                                 |
| `Client/AppState`       | Sincronização do estado do app (contatos, configurações, etc.)                                                          |
| `Client/AccountSync`    | Operações de sincronização em nível de conta                                                                            |
| `Client/PairCode`       | Fluxo de autenticação por pair code                                                                                     |
| `Client/Pair`           | Fluxo de pareamento por QR code                                                                                         |
| `Client/Receipt`        | Processamento de recibos (lido, entregue, reproduzido)                                                                  |
| `Client/TcToken`        | Operações de token de contato confiável                                                                                 |
| `Client/Group`          | Operações de metadados e participantes de grupos                                                                        |
| `Client/Contacts`       | Sincronização e consulta de contatos                                                                                    |
| `Client/Business`       | Atualizações de perfil business                                                                                         |
| `Client/PDO`            | Peer Data Operations — recuperação de mensagens do telefone principal via mensagens peer com JID nu e fallback de retry |
| `Client/IQ`             | Envio/recebimento de stanzas IQ                                                                                         |
| `Client/Ack`            | Confirmação de stanzas                                                                                                  |
| `Client/Status`         | Operações de status/stories                                                                                             |
| `Client/Picture`        | Atualizações de foto de perfil                                                                                          |
| `Client/UnifiedSession` | Estabelecimento de sessão                                                                                               |
| `Blocking`              | Operações de bloqueio/desbloqueio                                                                                       |
| `Chatstate`             | Eventos de indicador de digitação                                                                                       |
| `PresenceHandler`       | Processamento de atualizações de presença                                                                               |
| `AppState`              | Codificação/decodificação de patches do estado do app                                                                   |
| `Bot/PairCode`          | Tratamento de pair code em nível de bot                                                                                 |

### Exemplos de filtragem

```bash theme={null}
# Mostra apenas logs de conexão e mensagens
RUST_LOG="Client/Keepalive=debug,Client/Send=debug,Client/Recv=trace" cargo run

# Depura problemas de sync offline
RUST_LOG="Client/OfflineSync=debug" cargo run

# Modo silencioso: apenas erros e avisos
RUST_LOG="warn" cargo run

# Verboso: todos os internos do client em nível debug
RUST_LOG="debug" cargo run
```

<Note>
  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.
</Note>

## Executando com Docker

Você também pode executar o bot usando Docker em vez de compilar localmente:

```bash theme={null}
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](/pt/installation#implantação-com-docker) para mais detalhes.

## Benchmarking

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`:

```bash theme={null}
cargo run --example benchmark --features danger-skip-tls-verify
```

<Note>
  O exemplo de benchmark requer a feature flag `danger-skip-tls-verify` porque foi projetado para uso com servidores de teste locais.
</Note>

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:

```bash theme={null}
# 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](https://github.com/iai-callgrind/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:

```bash theme={null}
# Executa todos os benchmarks do wacore (requer valgrind e iai-callgrind-runner)
cargo bench --workspace
```

Veja a [documentação dos benchmarks do wacore](/api/wacore#benchmarks) para detalhes sobre cada suíte, otimizações de alocação e integração com CI.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Enviando mensagens" icon="message" href="/guides/sending-messages">
    Aprenda sobre os diferentes tipos de mensagem e como enviá-los
  </Card>

  <Card title="Manipulação de mídia" icon="image" href="/guides/media-handling">
    Faça upload e download de imagens, vídeos e documentos
  </Card>

  <Card title="Gerenciamento de grupos" icon="users" href="/guides/group-management">
    Crie e gerencie grupos do WhatsApp
  </Card>

  <Card title="Referência da API Client" icon="code" href="/api/client">
    Explore todos os métodos disponíveis do cliente
  </Card>
</CardGroup>
