Skip to main content

Overview

This guide covers sending messages, including text, reactions, quotes, and message editing operations using the whatsapp-rust library.

Sending Text Messages

Simple Text Message

Use the conversation field for plain text messages:
use waproto::whatsapp as wa;
use wacore_binary::jid::Jid;

let to: Jid = "1234567890@s.whatsapp.net".parse()?;

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

let message_id = client.send_message(to, message).await?;
println!("Message sent with ID: {}", message_id);

Extended Text Message

For messages with formatting, links, or context (replies/quotes):
use waproto::whatsapp::message::ExtendedTextMessage;

let message = wa::Message {
    extended_text_message: Some(Box::new(ExtendedTextMessage {
        text: Some("Check out this link: https://example.com".to_string()),
        matched_text: Some("https://example.com".to_string()),
        title: Some("Example Site".to_string()),
        description: Some("A sample website".to_string()),
        ..Default::default()
    })),
    ..Default::default()
};

let message_id = client.send_message(to, message).await?;

Quoted Replies

Replying to a Message

Use build_quote_context to create a reply:
use wacore::proto_helpers::{build_quote_context, MessageExt};

// Assume you received the original message
let original_message: wa::Message = /* ... */;
let original_message_id = "3EB0ABC123";
let original_sender = "1234567890@s.whatsapp.net";

// Build quote context
let context = build_quote_context(
    original_message_id,
    original_sender,
    &original_message,
);

// Create reply message
let reply = wa::Message {
    extended_text_message: Some(Box::new(ExtendedTextMessage {
        text: Some("This is my reply".to_string()),
        context_info: Some(Box::new(context)),
        ..Default::default()
    })),
    ..Default::default()
};

let message_id = client.send_message(to, reply).await?;

Setting Context on Media Messages

You can add quote context to any message type using set_context_info:
use waproto::whatsapp::message::ImageMessage;

let mut reply = wa::Message {
    image_message: Some(Box::new(ImageMessage {
        url: Some("https://mmg.whatsapp.net/...".to_string()),
        mimetype: Some("image/jpeg".to_string()),
        caption: Some("Here's an image reply".to_string()),
        // ... other image fields
        ..Default::default()
    })),
    ..Default::default()
};

// Build and set context
let context = build_quote_context(
    original_message_id,
    original_sender,
    &original_message,
);

reply.set_context_info(context);

let message_id = client.send_message(to, reply).await?;

Reactions

Sending a Reaction

Reactions are sent using ReactionMessage:
use waproto::whatsapp::message::ReactionMessage;

let reaction = wa::Message {
    reaction_message: Some(Box::new(ReactionMessage {
        key: Some(wa::MessageKey {
            remote_jid: Some(chat_jid.to_string()),
            from_me: Some(false),
            id: Some(message_id_to_react_to.to_string()),
            participant: Some(sender_jid.to_string()),
        }),
        text: Some("👍".to_string()),
        sender_timestamp_ms: Some(
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_millis() as i64
        ),
        ..Default::default()
    })),
    ..Default::default()
};

let message_id = client.send_message(chat_jid, reaction).await?;

Removing a Reaction

Send an empty reaction text to remove:
let remove_reaction = wa::Message {
    reaction_message: Some(Box::new(ReactionMessage {
        key: Some(wa::MessageKey {
            remote_jid: Some(chat_jid.to_string()),
            from_me: Some(false),
            id: Some(message_id_to_react_to.to_string()),
            participant: Some(sender_jid.to_string()),
        }),
        text: Some("".to_string()),  // Empty to remove
        sender_timestamp_ms: Some(
            std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_millis() as i64
        ),
        ..Default::default()
    })),
    ..Default::default()
};

Editing Messages

You can edit text messages using EditedMessage:
use waproto::whatsapp::message::FutureProofMessage;

let edited = wa::Message {
    edited_message: Some(Box::new(FutureProofMessage {
        message: Some(Box::new(wa::Message {
            protocol_message: Some(Box::new(wa::message::ProtocolMessage {
                key: Some(wa::MessageKey {
                    remote_jid: Some(chat_jid.to_string()),
                    from_me: Some(true),
                    id: Some(original_message_id.to_string()),
                    participant: None,
                }),
                r#type: Some(wa::message::protocol_message::Type::MessageEdit as i32),
                edited_message: Some(Box::new(wa::Message {
                    conversation: Some("This is the edited text".to_string()),
                    ..Default::default()
                })),
                timestamp_ms: Some(
                    std::time::SystemTime::now()
                        .duration_since(std::time::UNIX_EPOCH)
                        .unwrap()
                        .as_millis() as i64
                ),
                ..Default::default()
            })),
            ..Default::default()
        })),
    })),
    ..Default::default()
};

let message_id = client.send_message(chat_jid, edited).await?;
Message editing only works for text messages (conversation or extended_text_message) sent by you within the last 15 minutes.

Deleting Messages (Revoke)

Delete Your Own Message

use whatsapp_rust::send::RevokeType;

client.revoke_message(
    chat_jid,
    message_id,
    RevokeType::Sender,
).await?;
See \1

Admin Delete (Group Only)

Group admins can delete messages from other participants:
let original_sender: Jid = "1234567890@s.whatsapp.net".parse()?;

client.revoke_message(
    group_jid,
    message_id,
    RevokeType::Admin { original_sender },
).await?;
Admin revoke only works in group chats. The original_sender must match the JID format (LID or phone number) of the message being deleted.

Send Options

Adding Extra Stanza Nodes

For advanced use cases, you can include custom XML nodes:
use whatsapp_rust::send::SendOptions;
use wacore_binary::builder::NodeBuilder;

let options = SendOptions {
    extra_stanza_nodes: vec![
        NodeBuilder::new("custom")
            .attr("key", "value")
            .build(),
    ],
};

let message_id = client.send_message_with_options(
    to,
    message,
    options,
).await?;
See \1

Message Preparation Helpers

Preparing Messages for Quoting

The prepare_for_quote method strips nested context info:
use wacore::proto_helpers::MessageExt;

let quoted_message = original_message.prepare_for_quote();

let context = wa::ContextInfo {
    stanza_id: Some(message_id.clone()),
    participant: Some(sender_jid.to_string()),
    quoted_message: Some(quoted_message),
    ..Default::default()
};
This ensures:
  • Nested mentions are stripped
  • Quote chains are broken (except for bot messages)
  • Content fields (text, caption, media) are preserved
See \1

Error Handling

use anyhow::Result;

async fn send_safe_message(
    client: &Client,
    to: Jid,
    text: &str,
) -> Result<String> {
    let message = wa::Message {
        conversation: Some(text.to_string()),
        ..Default::default()
    };

    match client.send_message(to.clone(), message).await {
        Ok(id) => {
            println!("✅ Message sent: {}", id);
            Ok(id)
        }
        Err(e) => {
            eprintln!("❌ Failed to send: {:?}", e);
            Err(e)
        }
    }
}

Best Practices

1
Use the Right Message Type
2
  • Simple text: Use conversation
  • Links/formatting: Use extended_text_message
  • Replies: Use extended_text_message with context_info
  • Media: Use specific media message types with optional captions
  • 3
    Handle Message IDs
    4
    // Store message ID for later operations
    let message_id = client.send_message(to, message).await?;
    
    // Use it for reactions, edits, or deletes
    client.revoke_message(to, &message_id, RevokeType::Sender).await?;
    
    5
    Quote Context Best Practices
    6
  • Always use prepare_for_quote() to avoid nested quote chains
  • Use build_quote_context_with_info for special message types (newsletters, status)
  • Preserve original media fields when quoting media messages
  • Next Steps