Skip to content
12 changes: 3 additions & 9 deletions examples/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,15 @@ fn main() {

let mut bot = builder
.on_event(move |event, client| async move {
match event {
match &*event {
Event::Message(msg, info) => {
let ctx = MessageContext {
message: msg,
info,
client,
};

if let Some(text) = ctx.message.text_content()
if let Some(text) = msg.text_content()
&& text == "ping"
{
let ctx = MessageContext::from_parts(msg, info, client);
info!("Received text ping, sending pong...");
Comment thread
coderabbitai[bot] marked this conversation as resolved.

let pong_text = format!("pong {}", ctx.info.id);

let reply_message = wa::Message {
conversation: Some(pong_text),
..Default::default()
Expand Down
32 changes: 17 additions & 15 deletions src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ pub struct MessageContext {
}

impl MessageContext {
pub fn from_parts(message: &wa::Message, info: &MessageInfo, client: Arc<Client>) -> Self {
Self {
message: Box::new(message.clone()),
info: info.clone(),
client,
}
}

pub fn from_event(event: &Event, client: Arc<Client>) -> Option<Self> {
let (msg, info) = event.as_message()?;
Some(Self::from_parts(msg, info, client))
}

pub async fn send_message(
&self,
message: wa::Message,
Expand All @@ -45,17 +58,7 @@ impl MessageContext {
.await
}

/// Build a quote context for this message.
///
/// Handles:
/// - Correct stanza_id/participant (newsletters + group status)
/// - Stripping nested mentions to avoid accidental tags
/// - Preserving bot quote chains (matches WhatsApp Web)
///
/// Use this when you need manual control but want correct quoting behavior.
pub fn build_quote_context(&self) -> wa::ContextInfo {
// Use the standalone function from wacore with full message info
// This handles newsletter/group status participant resolution
wacore::proto_helpers::build_quote_context_with_info(
&self.info.id,
&self.info.source.sender,
Expand Down Expand Up @@ -91,24 +94,23 @@ impl MessageContext {
}

type EventHandlerCallback =
Arc<dyn Fn(Event, Arc<Client>) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
Arc<dyn Fn(Arc<Event>, Arc<Client>) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;

struct BotEventHandler {
client: Arc<Client>,
event_handler: Option<EventHandlerCallback>,
}

impl EventHandler for BotEventHandler {
fn handle_event(&self, event: &Event) {
fn handle_event(&self, event: Arc<Event>) {
if let Some(handler) = &self.event_handler {
let handler_clone = handler.clone();
let event_clone = event.clone();
let client_clone = self.client.clone();

self.client
.runtime
.spawn(Box::pin(async move {
handler_clone(event_clone, client_clone).await;
handler_clone(event, client_clone).await;
}))
.detach();
}
Expand Down Expand Up @@ -425,7 +427,7 @@ impl<B, T, H> BotBuilder<B, T, H, Missing> {
impl<B, T, H, R> BotBuilder<B, T, H, R> {
pub fn on_event<F, Fut>(mut self, handler: F) -> Self
where
F: Fn(Event, Arc<Client>) -> Fut + Send + Sync + 'static,
F: Fn(Arc<Event>, Arc<Client>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send + 'static,
{
self.event_handler = Some(Arc::new(move |event, client| {
Expand Down
38 changes: 19 additions & 19 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ impl Client {
self.is_ready.store(true, Ordering::Relaxed);
self.core
.event_bus
.dispatch(&Event::Connected(crate::types::events::Connected));
.dispatch(Event::Connected(crate::types::events::Connected));
self.connected_notifier.notify(usize::MAX);
}

Expand Down Expand Up @@ -939,7 +939,7 @@ impl Client {

self.core
.event_bus
.dispatch(&Event::ChatPresence(ChatPresenceUpdate {
.dispatch(Event::ChatPresence(ChatPresenceUpdate {
source: MessageSource {
chat,
sender,
Expand Down Expand Up @@ -1014,7 +1014,7 @@ impl Client {
if unexpected_disconnect {
self.core
.event_bus
.dispatch(&Event::Disconnected(crate::types::events::Disconnected));
.dispatch(Event::Disconnected(crate::types::events::Disconnected));
}
}

Expand Down Expand Up @@ -1147,7 +1147,7 @@ impl Client {

self.core
.event_bus
.dispatch(&Event::LoggedOut(crate::types::events::LoggedOut {
.dispatch(Event::LoggedOut(crate::types::events::LoggedOut {
on_connect: false,
reason: ConnectFailureReason::LoggedOut,
}));
Expand Down Expand Up @@ -1640,7 +1640,7 @@ impl Client {
if self.raw_node_forwarding.load(Ordering::Relaxed) {
self.core
.event_bus
.dispatch(&Event::RawNode(Arc::clone(&node)));
.dispatch(Event::RawNode(Arc::clone(&node)));
}

if nr.tag.as_ref() == "xmlstreamend" {
Expand Down Expand Up @@ -2980,7 +2980,7 @@ impl Client {
self.persistence_manager
.process_command(DeviceCommand::SetPushName(new_name.clone()))
.await;
bus.dispatch(&Event::SelfPushNameUpdated(
bus.dispatch(Event::SelfPushNameUpdated(
crate::types::events::SelfPushNameUpdated {
from_server: true,
old_name: old.clone(),
Expand Down Expand Up @@ -3037,7 +3037,7 @@ impl Client {
reason: ConnectFailureReason::LoggedOut,
})
};
self.core.event_bus.dispatch(&event);
self.core.event_bus.dispatch(event);
should_disconnect = true;
} else {
match code {
Expand All @@ -3052,7 +3052,7 @@ impl Client {
info!("Got 516 stream error (device removed). Logging out.");
self.expected_disconnect.store(true, Ordering::Relaxed);
self.enable_auto_reconnect.store(false, Ordering::Relaxed);
self.core.event_bus.dispatch(&Event::LoggedOut(
self.core.event_bus.dispatch(Event::LoggedOut(
crate::types::events::LoggedOut {
on_connect: false,
reason: ConnectFailureReason::LoggedOut,
Expand All @@ -3064,7 +3064,7 @@ impl Client {
info!("Got 401 stream error (unauthorized). Logging out.");
self.expected_disconnect.store(true, Ordering::Relaxed);
self.enable_auto_reconnect.store(false, Ordering::Relaxed);
self.core.event_bus.dispatch(&Event::LoggedOut(
self.core.event_bus.dispatch(Event::LoggedOut(
crate::types::events::LoggedOut {
on_connect: false,
reason: ConnectFailureReason::LoggedOut,
Expand All @@ -3078,7 +3078,7 @@ impl Client {
self.enable_auto_reconnect.store(false, Ordering::Relaxed);
self.core
.event_bus
.dispatch(&Event::StreamReplaced(crate::types::events::StreamReplaced));
.dispatch(Event::StreamReplaced(crate::types::events::StreamReplaced));
should_disconnect = true;
}
"429" => {
Expand All @@ -3093,7 +3093,7 @@ impl Client {
_ => {
error!("Unknown stream error: {}", DisplayableNodeRef(node));
self.expected_disconnect.store(true, Ordering::Relaxed);
self.core.event_bus.dispatch(&Event::StreamError(
self.core.event_bus.dispatch(Event::StreamError(
crate::types::events::StreamError {
code: code.to_string(),
raw: Some(node.to_owned()),
Expand Down Expand Up @@ -3137,7 +3137,7 @@ impl Client {
info!("Got {reason:?} connect failure, logging out.");
self.core
.event_bus
.dispatch(&wacore::types::events::Event::LoggedOut(
.dispatch(wacore::types::events::Event::LoggedOut(
crate::types::events::LoggedOut {
on_connect: true,
reason,
Expand All @@ -3152,20 +3152,20 @@ impl Client {
"Temporary ban connect failure: {}",
DisplayableNodeRef(node)
);
self.core.event_bus.dispatch(&Event::TemporaryBan(
crate::types::events::TemporaryBan {
self.core
.event_bus
.dispatch(Event::TemporaryBan(crate::types::events::TemporaryBan {
code: crate::types::events::TempBanReason::from(ban_code),
expire: expire_duration,
},
));
}));
} else if let ConnectFailureReason::ClientOutdated = reason {
error!("Client is outdated and was rejected by server.");
self.core
.event_bus
.dispatch(&Event::ClientOutdated(crate::types::events::ClientOutdated));
.dispatch(Event::ClientOutdated(crate::types::events::ClientOutdated));
} else {
warn!("Unknown connect failure: {}", DisplayableNodeRef(node));
self.core.event_bus.dispatch(&Event::ConnectFailure(
self.core.event_bus.dispatch(Event::ConnectFailure(
crate::types::events::ConnectFailure {
reason,
message: attrs
Expand Down Expand Up @@ -3486,7 +3486,7 @@ impl Client {
.process_command(DeviceCommand::SetPushName(new_name.clone()))
.await;

self.core.event_bus.dispatch(&Event::SelfPushNameUpdated(
self.core.event_bus.dispatch(Event::SelfPushNameUpdated(
crate::types::events::SelfPushNameUpdated {
from_server: true,
old_name,
Expand Down
2 changes: 1 addition & 1 deletion src/client/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl Client {

self.core
.event_bus
.dispatch(&Event::OfflineSyncCompleted(OfflineSyncCompleted { count }));
.dispatch(Event::OfflineSyncCompleted(OfflineSyncCompleted { count }));
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/features/chat_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub(crate) fn dispatch_chat_mutation(
if let Some(val) = &m.action_value
&& let Some(act) = &val.mute_action
{
event_bus.dispatch(&Event::MuteUpdate(MuteUpdate {
event_bus.dispatch(Event::MuteUpdate(MuteUpdate {
jid,
timestamp: time,
action: Box::new(*act),
Expand All @@ -125,7 +125,7 @@ pub(crate) fn dispatch_chat_mutation(
if let Some(val) = &m.action_value
&& let Some(act) = &val.pin_action
{
event_bus.dispatch(&Event::PinUpdate(PinUpdate {
event_bus.dispatch(Event::PinUpdate(PinUpdate {
jid,
timestamp: time,
action: Box::new(*act),
Expand All @@ -138,7 +138,7 @@ pub(crate) fn dispatch_chat_mutation(
if let Some(val) = &m.action_value
&& let Some(act) = &val.archive_chat_action
{
event_bus.dispatch(&Event::ArchiveUpdate(ArchiveUpdate {
event_bus.dispatch(Event::ArchiveUpdate(ArchiveUpdate {
jid,
timestamp: time,
action: Box::new(act.clone()),
Expand All @@ -153,7 +153,7 @@ pub(crate) fn dispatch_chat_mutation(
&& let Some((message_id, from_me, participant_jid)) =
parse_message_key_fields(kind, &m.index)
{
event_bus.dispatch(&Event::StarUpdate(StarUpdate {
event_bus.dispatch(Event::StarUpdate(StarUpdate {
chat_jid: jid,
participant_jid,
message_id,
Expand All @@ -169,7 +169,7 @@ pub(crate) fn dispatch_chat_mutation(
if let Some(val) = &m.action_value
&& let Some(act) = &val.contact_action
{
event_bus.dispatch(&Event::ContactUpdate(ContactUpdate {
event_bus.dispatch(Event::ContactUpdate(ContactUpdate {
jid,
timestamp: time,
action: Box::new(act.clone()),
Expand All @@ -182,7 +182,7 @@ pub(crate) fn dispatch_chat_mutation(
if let Some(val) = &m.action_value
&& let Some(act) = &val.mark_chat_as_read_action
{
event_bus.dispatch(&Event::MarkChatAsReadUpdate(MarkChatAsReadUpdate {
event_bus.dispatch(Event::MarkChatAsReadUpdate(MarkChatAsReadUpdate {
jid,
timestamp: time,
action: Box::new(act.clone()),
Expand All @@ -197,7 +197,7 @@ pub(crate) fn dispatch_chat_mutation(
{
// delete_media is in index[2], not in the proto (which only has messageRange)
let delete_media = m.index.get(2).is_none_or(|v| v != "0");
event_bus.dispatch(&Event::DeleteChatUpdate(DeleteChatUpdate {
event_bus.dispatch(Event::DeleteChatUpdate(DeleteChatUpdate {
jid,
delete_media,
timestamp: time,
Expand All @@ -213,7 +213,7 @@ pub(crate) fn dispatch_chat_mutation(
&& let Some((message_id, from_me, participant_jid)) =
parse_message_key_fields(kind, &m.index)
{
event_bus.dispatch(&Event::DeleteMessageForMeUpdate(DeleteMessageForMeUpdate {
event_bus.dispatch(Event::DeleteMessageForMeUpdate(DeleteMessageForMeUpdate {
chat_jid: jid,
participant_jid,
message_id,
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/ib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ async fn handle_ib_impl(client: Arc<Client>, node: &wacore_binary::NodeRef<'_>)
client
.core
.event_bus
.dispatch(&Event::OfflineSyncPreview(OfflineSyncPreview {
.dispatch(Event::OfflineSyncPreview(OfflineSyncPreview {
total,
app_data_changes,
messages,
Expand Down
Loading
Loading