Expand description
Update-gap tracking state machine for ferogram: pts/qts/seq bookkeeping,
per-channel state, and gap detection for Telegram’s Updates stream.
This crate is part of ferogram, an async Rust MTProto client built by Ankit Chaubey.
- Channel: t.me/Ferogram
- Chat: t.me/FerogramChat
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 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/datecounters, plus a separateptsper channel. - Detects gaps (an update arrives with a
pts_countthat doesn’t chain onto the last knownpts) and buffers out-of-order updates for a short window before giving up and requesting a diff. - Decides when a periodic catch-up
getDifferenceis 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.sendMessagereturning the new message, get folded into the same pts sequence. - Applies
updates.getDifference/updates.getChannelDifferenceresults 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 MessageBoxes::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.
§Example
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.
Re-exports§
pub use defs::ChannelState;pub use defs::Gap;pub use defs::MessageBoxes;pub use defs::UpdatesLike;pub use defs::UpdatesStateSnap;
Modules§
Enums§
- Premature
EndReason - Reason for calling
MessageBoxes::end_channel_difference.
Functions§
- classify_
own_ response - Parse the raw body of one of our own RPC responses into an
UpdatesLike, so self-sent actions (sending/editing/deleting a message, etc.) can be fed back throughMessageBoxes::process_updatesthe same way a pushed update would be. Otherwise the next real update looks like a gap and triggers a spurious getDifference.