# ferogram-msgbox
Update-gap tracking state machine for ferogram: pts/qts/seq bookkeeping,
per-channel state, and gap detection for Telegram's `Updates` stream.
[](https://crates.io/crates/ferogram-msgbox)
[](https://t.me/Ferogram) [](https://t.me/FerogramChat)
[](https://docs.rs/ferogram-msgbox)
[](#license)
`ferogram` re-exports this as `ferogram::message_box`, so most people never
depend on it directly. If you're just building a bot or a client, start with
[`ferogram`](https://crates.io/crates/ferogram) instead.
## What it does
Telegram's `Updates` stream is lossy over the wire (pushed updates can drop
or arrive out of order) but the protocol carries a `pts`/`qts`/`seq` counter
in every update, so a client can detect exactly when it missed something and
ask for a diff to fill the hole. `MessageBoxes` is the state machine that
does that bookkeeping:
- Tracks the global `pts`/`qts`/`seq`/`date` counters, plus a separate `pts`
per channel
- Detects gaps (an update arrives with a `pts_count` that doesn't chain onto
the last known `pts`) and buffers out-of-order updates for a short window
before giving up and requesting a diff
- Decides when a periodic catch-up `getDifference` is due, even with no
known gap (`NO_UPDATES_TIMEOUT`, 15 minutes)
- Classifies your own outgoing RPC responses (`classify_own_response`) so
updates piggybacked on them - e.g. `sendMessage` returning the new
message - get folded into the same pts sequence
- Applies `updates.getDifference` / `updates.getChannelDifference` results
back into state once the caller has fetched them
It's a **pure state machine**: no async, no networking, no RPC calls. It
takes an `UpdatesLike` in via `process_updates()` and hands back either the
update batch (plus any referenced users/chats) or a `Gap`, which tells the
caller which RPC to run and how to feed the result back.
## Usage
```rust
use ferogram_msgbox::{MessageBoxes, UpdatesLike};
let mut mbox = MessageBoxes::new();
// Feed a pushed update frame (or an own-RPC response) in.
match mbox.process_updates(UpdatesLike::Updates(Box::new(incoming))) {
Ok((updates, users, chats)) => {
// dispatch each Vec's items as usual.
}
Err(_gap) => {
// A gap was detected (or a diff was already due). Fetch it and
// feed the result back:
if let Some(req) = mbox.get_difference() {
let diff = client.invoke(&req).await?; // tl::enums::updates::Difference
let (updates, users, chats) = mbox.apply_difference(diff);
// dispatch updates/users/chats
}
}
}
// Called periodically (e.g. every tick of your update loop) even with no
// known gap - handles the 15-minute no-updates safety net and any pending
// per-channel diffs.
let deadline = mbox.check_deadlines();
```
On reconnect, feed `UpdatesLike::ConnectionClosed` in - anything sent while
the socket was down (or before you were listening) is exactly the kind of
gap this is designed to catch.
## Stack position
```
ferogram
└ ferogram-msgbox <-- here (used directly, not layered under mtsender/connect)
└ ferogram-tl-types
```
## License
This project is licensed under either the MIT License or Apache License 2.0, at your option. See [`LICENSE-MIT`](https://github.com/ankit-chaubey/ferogram/blob/main/LICENSE-MIT) and [`LICENSE-APACHE`](https://github.com/ankit-chaubey/ferogram/blob/main/LICENSE-APACHE) for details.
**Author:** Ankit Chaubey ([@ankit-chaubey](https://github.com/ankit-chaubey))