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}