Skip to main content

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