grammers_session/message_box/
defs.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8#[cfg(test)]
9use super::tests::Instant;
10use grammers_tl_types as tl;
11use std::time::Duration;
12#[cfg(not(test))]
13use std::time::Instant;
14
15/// Telegram sends `seq` equal to `0` when "it doesn't matter", so we use that value too.
16pub(super) const NO_SEQ: i32 = 0;
17
18/// It has been observed that Telegram may send updates with `qts` equal to `0` (for
19/// example with `ChannelParticipant`), interleaved with non-zero `qts` values. This
20/// presumably means that the ordering should be "ignored" in that case.
21///
22/// One can speculate this is done because the field is not optional in the TL definition.
23///
24/// Not ignoring the `pts` information in those updates can lead to failures resolving gaps.
25pub(super) const NO_PTS: i32 = 0;
26
27/// Non-update types like `messages.affectedMessages` can contain `pts` that should still be
28/// processed. Because there's no `date`, a value of `0` is used as the sentinel value for
29/// the `date` when constructing the dummy `Updates` (in order to handle them uniformly).
30pub(super) const NO_DATE: i32 = 0;
31
32// > It may be useful to wait up to 0.5 seconds
33pub(super) const POSSIBLE_GAP_TIMEOUT: Duration = Duration::from_millis(500);
34
35/// After how long without updates the client will "timeout".
36///
37/// When this timeout occurs, the client will attempt to fetch updates by itself, ignoring all the
38/// updates that arrive in the meantime. After all updates are fetched when this happens, the
39/// client will resume normal operation, and the timeout will reset.
40///
41/// Documentation recommends 15 minutes without updates (https://core.telegram.org/api/updates).
42pub(super) const NO_UPDATES_TIMEOUT: Duration = Duration::from_secs(15 * 60);
43
44/// A sortable [`MessageBox`] entry key.
45#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
46pub(crate) enum Key {
47    Common,
48    Secondary,
49    Channel(i64),
50}
51
52/// A live [`MessageBox`] entry.
53#[derive(Debug)]
54pub(super) struct LiveEntry {
55    /// The variant of the [`MessageBox`] that this entry represents.
56    pub(super) key: Key,
57
58    /// The local persistent timestamp value that this [`MessageBox`] has.
59    pub(super) pts: i32,
60
61    /// Next instant when we would get the update difference if no updates arrived before then.
62    pub(super) deadline: Instant,
63
64    /// If the entry has a gap and may soon trigger the need to get difference.
65    pub(super) possible_gap: Option<PossibleGap>,
66}
67
68/// Contains all live message boxes and is able to process incoming updates for each of them.
69///
70/// See <https://core.telegram.org/api/updates#message-related-event-sequences>.
71#[derive(Debug)]
72pub struct MessageBoxes {
73    /// Live entries, sorted by key.
74    pub(super) entries: Vec<LiveEntry>,
75
76    /// Common [`State`] fields.
77    pub(super) date: i32,
78    pub(super) seq: i32,
79
80    /// Optimization field to quickly query all entries that are currently being fetched.
81    pub(super) getting_diff_for: Vec<Key>,
82
83    /// Optimization field to quickly query all entries that have a possible gap.
84    pub(super) possible_gaps: Vec<Key>,
85
86    /// Optimization field holding the closest deadline instant.
87    pub(super) next_deadline: Instant,
88}
89
90/// Represents the information needed to correctly handle a specific `tl::enums::Update`.
91#[derive(Debug)]
92pub(super) struct PtsInfo {
93    pub(super) key: Key,
94    pub(super) pts: i32,
95    pub(super) count: i32,
96}
97
98// > ### Recovering gaps
99// > […] Manually obtaining updates is also required in the following situations:
100// > • Loss of sync: a gap was found in `seq` / `pts` / `qts` (as described above).
101// >   It may be useful to wait up to 0.5 seconds in this situation and abort the sync in case a new update
102// >   arrives, that fills the gap.
103//
104// This is really easy to trigger by spamming messages in a channel (with as little as 3 members works), because
105// the updates produced by the RPC request take a while to arrive (whereas the read update comes faster alone).
106#[derive(Debug)]
107pub(super) struct PossibleGap {
108    pub(super) deadline: Instant,
109    /// Pending updates (those with a larger PTS, producing the gap which may later be filled).
110    pub(super) updates: Vec<tl::enums::Update>,
111}
112
113/// Error when a gap has been detected during update processing.
114#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
115pub struct Gap;
116
117/// Alias for the commonly-referenced three-tuple of update and related peers.
118pub(super) type UpdateAndPeers = (
119    Vec<(tl::enums::Update, State)>,
120    Vec<tl::enums::User>,
121    Vec<tl::enums::Chat>,
122);
123
124/// Anything that should be treated like an update.
125///
126/// Telegram unfortunately also includes update state in types
127/// that are not part of the usual [`tl::enums::Updates`].
128#[derive(Debug)]
129pub enum UpdatesLike {
130    /// The usual variant, received passively from Telegram.
131    Updates(tl::enums::Updates),
132    /// Special-case for short-sent messages,
133    /// where the request is also needed to construct a complete update.
134    ShortSentMessage {
135        /// The request that triggered the short update.
136        request: tl::functions::messages::SendMessage,
137        /// The incomplete update caused by the request.
138        update: tl::types::UpdateShortSentMessage,
139    },
140    /// Special-case for requests that affect some messages.
141    AffectedMessages(tl::types::messages::AffectedMessages),
142    /// Special-case for requests that lead to users being invited.
143    InvitedUsers(tl::types::messages::InvitedUsers),
144}
145
146// Public interface around the more tightly-packed internal state.
147
148/// Update state, up to and including the update it is a part of.
149///
150/// When using `catch_up` with a client, all updates with a
151/// state containing a [`MessageBox`] higher than this one will be fetched.
152#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
153pub struct State {
154    /// Last date state assigned by Telegram, which changes somewhat arbitrarily.
155    pub date: i32,
156    /// Last sequence state assigned by Telegram, which changes somewhat arbitrarily.
157    pub seq: i32,
158    /// The particular message box change if the update pertains to a message-related event sequence.
159    pub message_box: Option<MessageBox>,
160}
161
162/// The message box and pts value that uniquely identifies the message-related update.
163#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
164pub enum MessageBox {
165    /// Account-wide persistent timestamp.
166    ///
167    /// This includes private conversations (one-to-one) and small group chats.
168    Common { pts: i32 },
169    /// Account-wide secondary persistent timestamp.
170    ///
171    /// This includes only certain bot updates and secret one-to-one chats.
172    Secondary { qts: i32 },
173    /// Channel-specific persistent timestamp.
174    ///
175    /// This includes "megagroup", "broadcast" and "supergroup" channels.
176    Channel { channel_id: i64, pts: i32 },
177}