> ## 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.

# waproto

> Protocol Buffers definitions for WhatsApp messages

## Overview

`waproto` contains the Protocol Buffers definitions for all WhatsApp message types. It's auto-generated from `whatsapp.proto` using [buffa](https://github.com/anthropics/buffa) and provides strongly-typed Rust structs for working with WhatsApp's binary protocol.

```toml theme={null}
[dependencies]
waproto = "0.6"
```

## Structure

```
waproto/
├── src/
│   ├── lib.rs               # Module definition + tags re-export
│   ├── whatsapp.proto       # Source protobuf definitions
│   ├── whatsapp.desc        # Binary FileDescriptorSet (committed)
│   └── whatsapp.desc.sha256 # SHA-256 of descriptor and proto
└── build.rs                 # Always-on: generates whatsapp.rs + tags.rs → OUT_DIR
```

The build writes `whatsapp.rs` and `tags.rs` to `OUT_DIR`. Do not commit these files. Consumers of the crate never need `protoc` installed — only editors of `whatsapp.proto` do, to regenerate the committed descriptor.

## Usage

All protobuf types are under the `waproto::whatsapp` module:

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

let message = wa::Message {
    conversation: Some("Hello, World!".to_string()),
    ..Default::default()
};
```

Sub-message fields (nested protobuf messages) are `buffa::MessageField<T>`, not `Option<Box<T>>` or `Option<T>`. Construct one with `buffa::MessageField::some(..)`, and read it back with `.as_option()`, `.is_set()`, or `.is_unset()`.

<Note>
  `waproto` re-exports `buffa` (`waproto::buffa`), and `whatsapp-rust` in turn re-exports it as `whatsapp_rust::buffa` (with `MessageField` also available directly from `prelude`). Naming `buffa::MessageField` no longer requires a direct `buffa` dependency of your own — see [Installation](/installation#add-to-your-project). You can still add `buffa` directly if you want to pin your own version or use APIs beyond `MessageField`.
</Note>

```rust theme={null}
use waproto::buffa::MessageField;
use waproto::whatsapp as wa;

let img = wa::Message {
    image_message: MessageField::some(wa::message::ImageMessage {
        caption: Some("Check this out!".to_string()),
        ..Default::default()
    }),
    ..Default::default()
};

if let Some(image) = img.image_message.as_option() {
    println!("caption: {:?}", image.caption);
}
```

Plain scalar fields (`String`, `Vec<u8>`, `bool`, integers) are still `Option<T>`, same as before. Enum fields are a typed `Option<EnumType>` rather than a raw `Option<i32>`, and enum variants are `SCREAMING_SNAKE_CASE` (e.g. `wa::message::protocol_message::Type::MESSAGE_EDIT`).

## Key message types

### Core message types

#### Message

The main message container used for all WhatsApp messages.

```rust theme={null}
pub struct Message {
    // Text message
    pub conversation: Option<String>,

    // Media messages
    pub image_message: MessageField<message::ImageMessage>,
    pub video_message: MessageField<message::VideoMessage>,
    pub audio_message: MessageField<message::AudioMessage>,
    pub document_message: MessageField<message::DocumentMessage>,
    pub sticker_message: MessageField<message::StickerMessage>,

    // Rich messages
    pub extended_text_message: MessageField<message::ExtendedTextMessage>,
    pub interactive_message: MessageField<message::InteractiveMessage>,
    pub template_message: MessageField<message::TemplateMessage>,
    pub buttons_message: MessageField<message::ButtonsMessage>,
    pub list_message: MessageField<message::ListMessage>,

    // Group messages
    pub sender_key_distribution_message: MessageField<message::SenderKeyDistributionMessage>,

    // System messages
    pub protocol_message: MessageField<message::ProtocolMessage>,
    pub ephemeral_message: MessageField<message::FutureProofMessage>,
    pub view_once_message: MessageField<message::FutureProofMessage>,
    pub view_once_message_v2: MessageField<message::FutureProofMessage>,
    pub view_once_message_v2_extension: MessageField<message::FutureProofMessage>,

    // Reactions and interactions
    pub reaction_message: MessageField<message::ReactionMessage>,
    pub edited_message: MessageField<message::FutureProofMessage>,
    pub keep_in_chat_message: MessageField<message::KeepInChatMessage>,

    // Metadata
    pub message_context_info: MessageField<MessageContextInfo>,
    // ... and many more
}
```

**Usage in main library:**

```rust theme={null}
use waproto::buffa::MessageField;
use waproto::whatsapp as wa;

// Construct a text message
let msg = wa::Message {
    conversation: Some("Hello!".to_string()),
    ..Default::default()
};

// Construct an image message
let img = wa::Message {
    image_message: MessageField::some(wa::message::ImageMessage {
        url: Some(media_url),
        media_key: Some(media_key.to_vec()),
        file_sha256: Some(file_sha256.to_vec()),
        file_enc_sha256: Some(file_enc_sha256.to_vec()),
        caption: Some("Check this out!".to_string()),
        ..Default::default()
    }),
    ..Default::default()
};
```

#### MessageKey

Identifies a specific message in a conversation.

```rust theme={null}
pub struct MessageKey {
    pub remote_jid: Option<String>,  // Chat JID
    pub from_me: Option<bool>,       // Sent by me?
    pub id: Option<String>,          // Message ID
    pub participant: Option<String>, // Group participant JID
}
```

### Media Messages

#### ImageMessage

```rust theme={null}
pub struct ImageMessage {
    pub url: Option<String>,
    pub mimetype: Option<String>,
    pub caption: Option<String>,
    pub file_sha256: Option<Vec<u8>>,
    pub file_length: Option<u64>,
    pub height: Option<u32>,
    pub width: Option<u32>,
    pub media_key: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
    pub jpeg_thumbnail: Option<Vec<u8>>,
    pub context_info: MessageField<ContextInfo>,
    // ...
}
```

#### VideoMessage

```rust theme={null}
pub struct VideoMessage {
    pub url: Option<String>,
    pub mimetype: Option<String>,
    pub caption: Option<String>,
    pub file_sha256: Option<Vec<u8>>,
    pub file_length: Option<u64>,
    pub seconds: Option<u32>,
    pub media_key: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
    pub jpeg_thumbnail: Option<Vec<u8>>,
    pub gif_playback: Option<bool>,
    // ...
}
```

#### AudioMessage

```rust theme={null}
pub struct AudioMessage {
    pub url: Option<String>,
    pub mimetype: Option<String>,
    pub file_sha256: Option<Vec<u8>>,
    pub file_length: Option<u64>,
    pub seconds: Option<u32>,
    pub ptt: Option<bool>,  // Push-to-talk (voice note)
    pub media_key: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
    // ...
}
```

#### DocumentMessage

```rust theme={null}
pub struct DocumentMessage {
    pub url: Option<String>,
    pub mimetype: Option<String>,
    pub title: Option<String>,
    pub file_sha256: Option<Vec<u8>>,
    pub file_length: Option<u64>,
    pub page_count: Option<u32>,
    pub media_key: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
    pub file_name: Option<String>,
    pub jpeg_thumbnail: Option<Vec<u8>>,
    // ...
}
```

#### StickerMessage

```rust theme={null}
pub struct StickerMessage {
    pub url: Option<String>,
    pub file_sha256: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
    pub media_key: Option<Vec<u8>>,
    pub mimetype: Option<String>,
    pub height: Option<u32>,
    pub width: Option<u32>,
    pub is_animated: Option<bool>,
    // ...
}
```

### Rich content messages

#### ExtendedTextMessage

Text with formatting, links, and quoted messages.

```rust theme={null}
pub struct ExtendedTextMessage {
    pub text: Option<String>,
    pub matched_text: Option<String>,
    pub description: Option<String>,
    pub title: Option<String>,
    pub jpeg_thumbnail: Option<Vec<u8>>,
    pub context_info: MessageField<ContextInfo>,
    // ...
}
```

#### InteractiveMessage

Buttons, lists, and other interactive elements.

```rust theme={null}
pub struct InteractiveMessage {
    pub header: MessageField<interactive_message::Header>,
    pub body: MessageField<interactive_message::Body>,
    pub footer: MessageField<interactive_message::Footer>,
    pub context_info: MessageField<ContextInfo>,
    // One of:
    pub native_flow_message: Option<InteractiveMessage>,
    pub shop_storefront_message: Option<ShopMessage>,
    // ...
}
```

#### ButtonsMessage

```rust theme={null}
pub struct ButtonsMessage {
    pub content_text: Option<String>,
    pub footer_text: Option<String>,
    pub context_info: MessageField<ContextInfo>,
    pub buttons: Vec<buttons_message::Button>,
    pub header_type: Option<buttons_message::HeaderType>,
    // ...
}
```

#### ListMessage

```rust theme={null}
pub struct ListMessage {
    pub title: Option<String>,
    pub description: Option<String>,
    pub button_text: Option<String>,
    pub list_type: Option<list_message::ListType>,
    pub sections: Vec<list_message::Section>,
    pub context_info: MessageField<ContextInfo>,
    // ...
}
```

### Encryption Messages

#### SenderKeyDistributionMessage

Used for group message encryption.

```rust theme={null}
pub struct SenderKeyDistributionMessage {
    pub group_id: Option<String>,
    pub axolotl_sender_key_distribution_message: Option<Vec<u8>>,
}
```

#### PreKeySignalMessage

Used for establishing 1:1 encryption.

```rust theme={null}
pub struct PreKeySignalMessage {
    // Signal Protocol encrypted message
}
```

### System & protocol messages

#### ProtocolMessage

For protocol-level operations.

```rust theme={null}
pub struct ProtocolMessage {
    pub key: MessageField<MessageKey>,
    pub r#type: Option<protocol_message::Type>,  // MESSAGE_EDIT, REVOKE, etc.
    pub ephemeral_expiration: Option<u32>,
    pub ephemeral_setting_timestamp: Option<i64>,
    // ...
}
```

**Common types:**

* `REVOKE` - Revoke sent message
* `MESSAGE_EDIT` - Edit sent message
* `EPHEMERAL_SETTING` - Ephemeral message setting

#### ReactionMessage

```rust theme={null}
pub struct ReactionMessage {
    pub key: MessageField<MessageKey>,
    pub text: Option<String>,  // Emoji
    pub grouping_key: Option<String>,
    pub sender_timestamp_ms: Option<i64>,
}
```

#### EditMessage

```rust theme={null}
pub struct EditMessage {
    pub key: MessageField<MessageKey>,
    pub message: MessageField<MessageText>,  // New text content
    pub timestamp_ms: Option<i64>,
}
```

#### AlbumMessage

Parent message for grouped media albums. Declares expected image/video counts so WhatsApp clients know how many items to group together.

```rust theme={null}
pub struct AlbumMessage {
    pub expected_image_count: Option<u32>,
    pub expected_video_count: Option<u32>,
    pub context_info: MessageField<ContextInfo>,
}
```

Each child media message is wrapped in `associated_child_message` (a `FutureProofMessage`) and linked to the parent via a `MessageAssociation`:

```rust theme={null}
pub struct MessageAssociation {
    pub association_type: Option<message_association::AssociationType>, // MEDIA_ALBUM = 1
    pub parent_message_key: MessageField<MessageKey>,
    pub message_index: Option<i32>,
}
```

Use `whatsapp_rust::proto_helpers::wrap_as_album_child` to construct album children. See [Sending Messages - Album messages](/guides/sending-messages#album-messages) for usage examples.

### AI & bot messages

#### AIRichResponseMessage

```rust theme={null}
pub struct AIRichResponseMessage {
    pub message_type: Option<AIRichResponseMessageType>,
    pub submessages: Vec<AIRichResponseSubMessage>,
    pub context_info: MessageField<ContextInfo>,
    // ...
}
```

#### BotFeedbackMessage

```rust theme={null}
pub struct BotFeedbackMessage {
    pub message_key: MessageField<MessageKey>,
    pub kind: Option<bot_feedback_message::BotFeedbackKind>,  // Thumbs up/down
    pub text: Option<String>,
    // ...
}
```

### Metadata & Context

#### MessageContextInfo

```rust theme={null}
pub struct MessageContextInfo {
    pub device_list_metadata: MessageField<DeviceListMetadata>,
    pub device_list_metadata_version: Option<i32>,
    pub message_secret: Option<Vec<u8>>,
    pub padding_bytes: Option<Vec<u8>>,
    pub message_add_on_duration_in_secs: Option<u32>,
    pub message_association: MessageField<MessageAssociation>, // Album/child linking
    // ...
}
```

#### ContextInfo

Quoted messages, mentions, and forwarding info.

```rust theme={null}
pub struct ContextInfo {
    pub stanza_id: Option<String>,
    pub participant: Option<String>,
    pub quoted_message: MessageField<Message>,
    pub remote_jid: Option<String>,
    pub mentioned_jid: Vec<String>,
    pub conversion_source: Option<String>,
    pub forwarding_score: Option<u32>,
    pub is_forwarded: Option<bool>,
    // ...
}
```

## Device & identity types

### ADV Messages

Account Device Verification messages.

```rust theme={null}
pub struct ADVDeviceIdentity {
    pub raw_id: Option<u32>,
    pub timestamp: Option<u64>,
    pub key_index: Option<u32>,
    pub account_type: Option<ADVEncryptionType>,
    pub device_type: Option<ADVEncryptionType>,
}

pub struct ADVSignedDeviceIdentity {
    pub details: Option<Vec<u8>>,
    pub account_signature_key: Option<Vec<u8>>,
    pub account_signature: Option<Vec<u8>>,
    pub device_signature: Option<Vec<u8>>,
}

pub struct ADVKeyIndexList {
    pub raw_id: Option<u32>,
    pub timestamp: Option<u64>,
    pub current_index: Option<u32>,
    pub valid_indexes: Vec<u32>,
    pub account_type: Option<ADVEncryptionType>,
}
```

<Note>
  These ADV types are capitalized `ADV...` (not `Adv...`) to match buffa's acronym-preserving type naming.
</Note>

### Signal protocol structures

```rust theme={null}
pub struct PreKeyRecordStructure {
    pub id: Option<u32>,
    pub public_key: Option<Vec<u8>>,
    pub private_key: Option<Vec<u8>>,
}

pub struct SignedPreKeyRecordStructure {
    pub id: Option<u32>,
    pub public_key: Option<Vec<u8>>,
    pub private_key: Option<Vec<u8>>,
    pub signature: Option<Vec<u8>>,
    pub timestamp: Option<u64>,
}
```

These are all-scalar structs (no nested messages), so they're unaffected by the `MessageField` change above.

## Handshake & connection types

### HandshakeMessage

Used during initial connection handshake.

```rust theme={null}
pub struct HandshakeMessage {
    pub client_hello: MessageField<handshake_message::ClientHello>,
    pub server_hello: MessageField<handshake_message::ServerHello>,
    pub client_finish: MessageField<handshake_message::ClientFinish>,
}
```

### ClientPayload

Device and client information during pairing.

```rust theme={null}
pub struct ClientPayload {
    pub username: Option<u64>,
    pub passive: Option<bool>,
    pub user_agent: MessageField<client_payload::UserAgent>,
    pub web_info: MessageField<client_payload::WebInfo>,
    pub push_name: Option<String>,
    pub session_id: Option<i32>,
    pub short_connect: Option<bool>,
    pub connect_type: Option<client_payload::ConnectType>,
    pub connect_reason: Option<client_payload::ConnectReason>,
    // ...
}
```

## History sync types

### HistorySyncNotification

```rust theme={null}
pub struct HistorySyncNotification {
    pub file_sha256: Option<Vec<u8>>,
    pub file_length: Option<u64>,
    pub media_key: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
    pub direct_path: Option<String>,
    pub sync_type: Option<message::HistorySyncType>,
    pub chunk_order: Option<u32>,
    pub original_message_id: Option<String>,
}
```

### HistorySync

```rust theme={null}
pub struct HistorySync {
    pub sync_type: history_sync::HistorySyncType,
    pub conversations: Vec<Conversation>,
    pub status_v3_messages: Vec<WebMessageInfo>,
    pub chunk_order: Option<u32>,
    pub progress: Option<u32>,
    // ...
}
```

## Media reference types

### ExternalBlobReference

References to uploaded media files.

```rust theme={null}
pub struct ExternalBlobReference {
    pub media_key: Option<Vec<u8>>,
    pub direct_path: Option<String>,
    pub handle: Option<String>,
    pub file_size_bytes: Option<u64>,
    pub file_sha256: Option<Vec<u8>>,
    pub file_enc_sha256: Option<Vec<u8>>,
}
```

## App state types

### SyncActionValue

App state synchronization actions.

```rust theme={null}
pub struct SyncActionValue {
    pub timestamp: Option<i64>,
    // One of many action types:
    pub contact_action: MessageField<sync_action_value::ContactAction>,
    pub mute_action: MessageField<sync_action_value::MuteAction>,
    pub pin_action: MessageField<sync_action_value::PinAction>,
    pub push_name_setting: MessageField<sync_action_value::PushNameSetting>,
    pub archive_chat_action: MessageField<sync_action_value::ArchiveChatAction>,
    pub delete_chat_action: MessageField<sync_action_value::DeleteChatAction>,
    pub star_action: MessageField<sync_action_value::StarAction>,
    // ... and many more
}
```

## Enums

waproto generates real Rust enums (not raw `i32` constants). Variant names are `SCREAMING_SNAKE_CASE`:

```rust theme={null}
// Message types
pub mod protocol_message {
    pub enum Type {
        REVOKE = 0,
        EPHEMERAL_SETTING = 3,
        EPHEMERAL_SYNC_RESPONSE = 4,
        HISTORY_SYNC_NOTIFICATION = 5,
        APP_STATE_SYNC_KEY_SHARE = 6,
        APP_STATE_SYNC_KEY_REQUEST = 7,
        MESSAGE_EDIT = 14,
        // ...
    }
}

// Media types
pub mod video_message {
    pub enum Attribution {
        NONE = 0,
        GIPHY = 1,
        TENOR = 2,
    }
}
```

<Note>
  buffa generates a **closed** enum by default: an unrecognized wire value decodes to `None` rather than being preserved, unlike the old prost-generated `Option<i32>` fields which round-tripped any integer. In practice WhatsApp only ever sends in-schema values, so this only matters for forward compatibility with brand-new server-side enum variants.

  `SyncdMutation.operation` (`SyncdOperation`) is the one deliberate exception: `build.rs` opts it into buffa's **open** enum mode, so the field type is `Option<buffa::EnumValue<SyncdOperation>>` instead of `Option<SyncdOperation>`. An unrecognized wire value decodes to `EnumValue::Unknown(n)` rather than `None`. `wacore_appstate`'s `process_patch` rejects an unknown operation with a typed `AppStateError::UnsupportedSyncdOperation` before mutating any state, instead of silently treating it as `SET` and corrupting the app-state LTHash — which is what the previous closed-enum decode did.
</Note>

## Feature flags

| Feature             | Description                                                                                                                                                                                                 | Implies             |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `serde-deserialize` | Adds `#[derive(serde::Deserialize)]` and `#[serde(default)]` to all types. Use when you need to parse protobuf types from JSON.                                                                             | —                   |
| `serde-snake-case`  | Adds `#[serde(rename_all(deserialize = "snake_case"))]` to all types. Allows snake\_case enum variant names during deserialization (buffa generates SCREAMING\_SNAKE\_CASE). Serialize output is unchanged. | `serde-deserialize` |
| `serde-enum-repr`   | Enums (de)serialize as their numeric repr instead of the variant name — what the JS/WASM bridge and camelCase serializer expect.                                                                            | —                   |

<Note>
  The `generate` feature was removed in [#836](https://github.com/oxidezap/whatsapp-rust/pull/836). Code generation is now always-on — `buffa-build`, `buffa-descriptor`, `heck`, and `sha2` are unconditional build dependencies. Remove `--features generate` from any build scripts.
</Note>

## Serde support

All generated types derive `serde::Serialize` by default. Deserialization and snake\_case renaming are behind optional feature flags (`serde-deserialize` and `serde-snake-case` above).

Enable them in your `Cargo.toml`:

```toml theme={null}
[dependencies]
waproto = { version = "0.6", features = ["serde-snake-case"] }
```

### Default behavior (no feature flags)

All types derive `Serialize` only:

```rust theme={null}
#[derive(Clone, PartialEq, Default)]
#[derive(serde::Serialize)]
pub struct Message {
    // ...
}
```

This allows JSON serialization for debugging:

```rust theme={null}
let json = serde_json::to_string_pretty(&message)?;
println!("Message: {}", json);
```

### With `serde-deserialize`

All types also derive `Deserialize` with `#[serde(default)]`, matching protobuf semantics where missing fields use default values:

```rust theme={null}
#[derive(serde::Serialize)]
#[cfg_attr(feature = "serde-deserialize", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-deserialize", serde(default))]
pub struct Message {
    // ...
}
```

### With `serde-snake-case`

All types additionally accept snake\_case during deserialization. This primarily affects enum and oneof variants (buffa generates SCREAMING\_SNAKE\_CASE names), while struct fields are already snake\_case. Serialization output remains unchanged.

```rust theme={null}
// Without serde-snake-case: must use SCREAMING_SNAKE_CASE
let json = r#"{"accountType": "ENTERPRISE"}"#;

// With serde-snake-case: snake_case also accepted
let json = r#"{"account_type": "enterprise"}"#;
```

<Note>
  The `serde-snake-case` feature is primarily useful for WASM bridge scenarios where JavaScript sends snake\_case JSON to the Rust backend. For most Rust-only use cases, you only need the default `Serialize` support.
</Note>

## waproto::tags

The build generates the `waproto::tags` module from the compiled protobuf descriptor. You get one `pub mod` per proto message with one `pub const FIELD_NAME: u32 = N;` per field. Nested messages produce nested modules.

```rust theme={null}
use waproto::tags;

// Field numbers derived directly from whatsapp.proto
let _ = tags::web_message_info::KEY;           // 1
let _ = tags::web_message_info::MESSAGE;       // 2
let _ = tags::message::MESSAGE_CONTEXT_INFO;   // 35
let _ = tags::history_sync::CONVERSATIONS;     // 2
let _ = tags::history_sync::PUSHNAMES;         // 7
```

The history-sync wire walkers use these constants internally, and compile-time `assert!` blocks pin them in the hand-written mirror structs. If `whatsapp.proto` renumbers a field the consts update automatically on next build; if a field referenced by the walkers is renamed or removed, compilation fails rather than the decoder silently reading the wrong wire data.

## Code generation

The build generates protobuf code — `build.rs` always runs. It reads the committed binary descriptor (`src/whatsapp.desc`), verifies its SHA-256 against `src/whatsapp.desc.sha256`, and hands it to `buffa-build`, writing two files into `OUT_DIR`:

* **`whatsapp.rs`** — full buffa-generated structs and enums, including zero-copy view types
* **`tags.rs`** — `waproto::tags` field-number constants (see [waproto::tags](#waprototags) above)

`whatsapp.proto` stays in its upstream camelCase form; `buffa-build`'s `idiomatic_field_names` option converts field and oneof identifiers to snake\_case Rust idents at codegen time (word boundaries match `heck`/prost), so the generated API keeps the prost-style names build.rs used to produce by hand. `buffa-descriptor` and `heck` remain build dependencies — they back the `waproto::tags` generation described above, which reads the original (camelCase) descriptor directly.

Neither generated file is committed. `buffa-build`, `buffa-descriptor`, `heck`, and `sha2` are unconditional build dependencies. `protoc` is only needed to regenerate the descriptor after editing `whatsapp.proto` — normal builds of `waproto` (or anything depending on it) never invoke `protoc`.

**To update after modifying `whatsapp.proto`:**

```bash theme={null}
scripts/regenerate-proto-desc.sh   # writes waproto/src/whatsapp.desc + .sha256 (wraps protoc)
cargo build -p waproto             # regenerates whatsapp.rs and tags.rs
```

The build aborts with a clear message if the descriptor SHA-256 does not match.

<Note>
  The `generate` feature flag was removed. Code generation is now always-on and does not require any feature flags. Remove `--features generate` from any existing build scripts.
</Note>

## Usage Examples

### Constructing Messages

```rust theme={null}
use waproto::buffa::MessageField;
use waproto::whatsapp as wa;

// Text message
let text = wa::Message {
    conversation: Some("Hello!".to_string()),
    ..Default::default()
};

// Image with caption
let image = wa::Message {
    image_message: MessageField::some(wa::message::ImageMessage {
        url: Some(media_url),
        media_key: Some(media_key.to_vec()),
        file_sha256: Some(file_sha256.to_vec()),
        file_enc_sha256: Some(file_enc_sha256.to_vec()),
        caption: Some("Nice photo!".to_string()),
        jpeg_thumbnail: Some(thumbnail),
        ..Default::default()
    }),
    ..Default::default()
};

// Quoted reply
let reply = wa::Message {
    extended_text_message: MessageField::some(wa::message::ExtendedTextMessage {
        text: Some("Great question!".to_string()),
        context_info: MessageField::some(wa::message::ContextInfo {
            stanza_id: Some(original_message_id),
            participant: Some(original_sender),
            quoted_message: MessageField::some(original_message),
            ..Default::default()
        }),
        ..Default::default()
    }),
    ..Default::default()
};
```

### Pattern Matching

`MessageField` doesn't pattern-match like `Option` directly — match on `.as_option()` instead:

```rust theme={null}
match (&proto_msg.conversation, proto_msg.image_message.as_option(), proto_msg.video_message.as_option()) {
    (Some(text), _, _) => {
        println!("Text: {}", text);
    }
    (_, Some(img), _) => {
        println!("Image from: {}", img.url.as_deref().unwrap_or_default());
    }
    (_, _, Some(vid)) => {
        println!("Video: {} seconds", vid.seconds.unwrap_or(0));
    }
    _ => println!("Other message type"),
}
```

### Media Downloads

See [Media Handling](/guides/media-handling) for complete examples.

```rust theme={null}
use waproto::whatsapp as wa;
use wacore::download::{Downloadable, MediaType};

if let Some(img) = message.image_message.as_option() {
    // Use the Downloadable trait to download and decrypt
    let data = client.download(img).await?;
}
```

## WhatsApp Version

The protobuf definitions are based on:

```rust theme={null}
// This file is @generated by buffa-build.
// WhatsApp Version: 2.3000.1035617621
```

This version is automatically included in the generated file header.

## Relationship with wacore

wacore provides utilities for working with waproto messages:

* **`proto_helpers`** - Conversion between protobuf and internal types
* **`download`** - `Downloadable` trait for media messages
* **`upload`** - Media encryption for upload
* **`messages`** - Message encryption/decryption
* **`send`** - Message building and sending

Example:

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

// Convert internal JID to protobuf MessageKey
let key = proto_helpers::message_key(&jid, &message_id, from_me);

// Build revoke message
let revoke = wa::Message {
    protocol_message: MessageField::some(wa::message::ProtocolMessage {
        key: MessageField::some(key),
        r#type: Some(wa::message::protocol_message::Type::REVOKE),
        ..Default::default()
    }),
    ..Default::default()
};
```

## Next Steps

<CardGroup cols={2}>
  <Card title="wacore" icon="cube" href="/api/wacore">
    Platform-agnostic protocol implementation
  </Card>

  <Card title="Sending Messages" icon="message" href="/guides/sending-messages">
    Sending and receiving messages
  </Card>

  <Card title="Media Handling" icon="image" href="/guides/media-handling">
    Working with media uploads and downloads
  </Card>

  <Card title="Signal Protocol" icon="shield" href="/advanced/signal-protocol">
    End-to-end encryption details
  </Card>
</CardGroup>
