Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions concepts/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pub enum Event {
Receipt(Receipt),
UndecryptableMessage(UndecryptableMessage),
Notification(Arc<OwnedNodeRef>),
ServerAck(ServerAck),

// Presence
ChatPresence(ChatPresenceUpdate),
Expand Down Expand Up @@ -210,6 +211,10 @@ pub enum Event {
The `Event` enum is `#[non_exhaustive]`, so your `match` statements must include a wildcard arm (`_ => {}`). New variants may be added in minor releases without a breaking change.
</Note>

<Note>
**Payload stability (pre-1.0):** the payload structs themselves are *not* sealed — while the crate is `0.x`, an existing payload may gain new fields in a minor release, so read the fields you need (e.g. `ack.class`) or keep a `..` rest when destructuring, rather than binding every field. A maybe-absent field is always modeled as `Option<T>`, never an empty-string or zero sentinel. Sealing payloads behind `#[non_exhaustive]` + constructors is deferred to the 1.0 API freeze.
</Note>

## Connection Events

### Connected
Expand Down Expand Up @@ -932,6 +937,46 @@ Event::UndecryptableMessage(undec) => {
}
```

### ServerAck

**Emitted:** Observe-only, for every server `<ack>` stanza that carries an id — dispatched independently of the internal send-waiter resolution, so registering a handler never interacts with the send/phash flow.

```rust
#[derive(Debug, Clone, Serialize)]
pub struct ServerAck {
pub id: String,
pub class: Option<String>,
pub from: Option<Jid>,
pub timestamp: Option<DateTime<Utc>>,
pub error: Option<String>,
}
```

| Field | Description |
|-------|-------------|
| `id` | Id of the acked stanza (for a sent message, its message id). |
| `class` | Stanza class the ack refers to (`"message"`, `"receipt"`, `"notification"`, `"call"`, …). `None` when the server omits it. |
| `from` | Chat/entity the ack refers to, when present and parseable. |
| `timestamp` | Server timestamp from the ack's `t` attribute, when present. For a message ack this is the authoritative send timestamp — the same attribute the [whatsmeow](https://github.com/tulir/whatsmeow) Go library reads into its own `SendResponse.Timestamp`. |
| `error` | Nack code (e.g. `"479"`), when the ack is actually a nack; `None` for a plain ack. |

<Note>
Server acks cover every outgoing stanza class, not just messages — filter on `class` rather than correlating ids blind. Dispatch is gated on a registered handler existing for this event kind, so the hot ack path allocates nothing when no consumer subscribes.
</Note>

**Example:**
```rust
Event::ServerAck(ack) => {
if ack.class.as_deref() == Some("message") {
if let Some(err) = &ack.error {
eprintln!("Message {} nacked: {err}", ack.id);
} else {
println!("Message {} accepted by the server", ack.id);
}
}
}
```

## Presence Events

### ChatPresence
Expand Down