grammers_session/message_box/
adaptor.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.
8use super::defs::{Entry, Gap, PtsInfo, NO_PTS, NO_SEQ};
9use super::ChatHashCache;
10use grammers_tl_types as tl;
11use log::info;
12
13// > The `updateShortMessage`, `updateShortSentMessage` and `updateShortChatMessage` constructors
14// > [...] should be transformed to `updateShort` upon receiving.
15//
16// We don't use `updateShort` because it's fairly limitting. `updatesCombined` is big enough to
17// contain all updates inside, and sticking to a single type (and not enum) simplifies things.
18//
19// This module's job is just converting the various updates types into `updatesCombined`.
20// It also converts updates into their corresponding `PtsInfo`.
21
22pub(super) fn updates(updates: tl::types::Updates) -> tl::types::UpdatesCombined {
23    tl::types::UpdatesCombined {
24        updates: updates.updates,
25        users: updates.users,
26        chats: updates.chats,
27        date: updates.date,
28        seq_start: updates.seq,
29        seq: updates.seq,
30    }
31}
32
33pub(super) fn update_short(short: tl::types::UpdateShort) -> tl::types::UpdatesCombined {
34    tl::types::UpdatesCombined {
35        updates: vec![short.update],
36        users: Vec::new(),
37        chats: Vec::new(),
38        date: short.date,
39        seq_start: NO_SEQ,
40        seq: NO_SEQ,
41    }
42}
43
44pub(super) fn update_short_message(
45    short: tl::types::UpdateShortMessage,
46    self_id: i64,
47) -> tl::types::UpdatesCombined {
48    update_short(tl::types::UpdateShort {
49        update: tl::types::UpdateNewMessage {
50            message: tl::types::Message {
51                out: short.out,
52                mentioned: short.mentioned,
53                media_unread: short.media_unread,
54                silent: short.silent,
55                post: false,
56                from_scheduled: false,
57                legacy: false,
58                edit_hide: false,
59                pinned: false,
60                noforwards: false,
61                invert_media: false,
62                reactions: None,
63                id: short.id,
64                from_id: Some(
65                    tl::types::PeerUser {
66                        user_id: if short.out { self_id } else { short.user_id },
67                    }
68                    .into(),
69                ),
70                from_boosts_applied: None,
71                peer_id: tl::types::PeerChat {
72                    chat_id: short.user_id,
73                }
74                .into(),
75                saved_peer_id: None,
76                fwd_from: short.fwd_from,
77                via_bot_id: short.via_bot_id,
78                reply_to: short.reply_to,
79                date: short.date,
80                message: short.message,
81                media: None,
82                reply_markup: None,
83                entities: short.entities,
84                views: None,
85                forwards: None,
86                replies: None,
87                edit_date: None,
88                post_author: None,
89                grouped_id: None,
90                restriction_reason: None,
91                ttl_period: short.ttl_period,
92                quick_reply_shortcut_id: None,
93                offline: false,
94                via_business_bot_id: None,
95                effect: None,
96                factcheck: None,
97            }
98            .into(),
99            pts: short.pts,
100            pts_count: short.pts_count,
101        }
102        .into(),
103        date: short.date,
104    })
105}
106
107pub(super) fn update_short_chat_message(
108    short: tl::types::UpdateShortChatMessage,
109) -> tl::types::UpdatesCombined {
110    update_short(tl::types::UpdateShort {
111        update: tl::types::UpdateNewMessage {
112            message: tl::types::Message {
113                out: short.out,
114                mentioned: short.mentioned,
115                media_unread: short.media_unread,
116                silent: short.silent,
117                post: false,
118                from_scheduled: false,
119                legacy: false,
120                edit_hide: false,
121                pinned: false,
122                noforwards: false,
123                invert_media: false,
124                reactions: None,
125                id: short.id,
126                from_id: Some(
127                    tl::types::PeerUser {
128                        user_id: short.from_id,
129                    }
130                    .into(),
131                ),
132                from_boosts_applied: None,
133                peer_id: tl::types::PeerChat {
134                    chat_id: short.chat_id,
135                }
136                .into(),
137                saved_peer_id: None,
138                fwd_from: short.fwd_from,
139                via_bot_id: short.via_bot_id,
140                reply_to: short.reply_to,
141                date: short.date,
142                message: short.message,
143                media: None,
144                reply_markup: None,
145                entities: short.entities,
146                views: None,
147                forwards: None,
148                replies: None,
149                edit_date: None,
150                post_author: None,
151                grouped_id: None,
152                restriction_reason: None,
153                ttl_period: short.ttl_period,
154                quick_reply_shortcut_id: None,
155                offline: false,
156                via_business_bot_id: None,
157                effect: None,
158                factcheck: None,
159            }
160            .into(),
161            pts: short.pts,
162            pts_count: short.pts_count,
163        }
164        .into(),
165        date: short.date,
166    })
167}
168
169pub(super) fn update_short_sent_message(
170    short: tl::types::UpdateShortSentMessage,
171) -> tl::types::UpdatesCombined {
172    update_short(tl::types::UpdateShort {
173        update: tl::types::UpdateNewMessage {
174            message: tl::types::MessageEmpty {
175                id: short.id,
176                peer_id: None,
177            }
178            .into(),
179            pts: short.pts,
180            pts_count: short.pts_count,
181        }
182        .into(),
183        date: short.date,
184    })
185}
186
187pub(super) fn adapt(
188    updates: tl::enums::Updates,
189    chat_hashes: &ChatHashCache,
190) -> Result<tl::types::UpdatesCombined, Gap> {
191    Ok(match updates {
192        // > `updatesTooLong` indicates that there are too many events pending to be pushed
193        // > to the client, so one needs to fetch them manually.
194        tl::enums::Updates::TooLong => {
195            info!("received updatesTooLong, treating as gap");
196            return Err(Gap);
197        }
198        // > `updateShortMessage`, `updateShortSentMessage` and `updateShortChatMessage` [...]
199        // > should be transformed to `updateShort` upon receiving.
200        tl::enums::Updates::UpdateShortMessage(short) => {
201            // > Incomplete update: the client is missing data about a chat/user from one of
202            // > the shortened constructors, such as `updateShortChatMessage`, etc.
203            //
204            // The check for missing hashes is done elsewhere to avoid doing the same work twice.
205            // This "only" needs to be done for "short messages", to get the private chat (user)
206            // where the message occured. Anywhere else, Telegram should send information
207            // about the chat so that [min constructors][0] can be used.
208            //
209            // [0]: https://core.telegram.org/api/min
210            update_short_message(short, chat_hashes.self_id())
211        }
212        tl::enums::Updates::UpdateShortChatMessage(short) => {
213            // No need to check for chats here. Small group chats do not require an access
214            // hash, and min constructors can be used to access the user.
215            update_short_chat_message(short)
216        }
217        // > `updateShort` […] have lower priority and are broadcast to a large number of users.
218        //
219        // There *shouldn't* be updates mentioning peers we're unaware of here.
220        //
221        // If later it turns out these can happen, the code will need to be updated to
222        // consider chats missing here a gap as well.
223        tl::enums::Updates::UpdateShort(short) => update_short(short),
224        // > [the] `seq` attribute, which indicates the remote `Updates` state after the
225        // > generation of the `Updates`, and `seq_start` indicates the remote `Updates` state
226        // > after the first of the `Updates` in the packet is generated
227        tl::enums::Updates::Combined(combined) => combined,
228        // > [the] `seq_start` attribute is omitted, because it is assumed that it is always
229        // > equal to `seq`.
230        tl::enums::Updates::Updates(updates) => self::updates(updates),
231        // Even though we lack fields like the message text, it still needs to be handled, so
232        // that the `pts` can be kept consistent.
233        tl::enums::Updates::UpdateShortSentMessage(short) => update_short_sent_message(short),
234    })
235}
236
237fn message_peer(message: &tl::enums::Message) -> Option<tl::enums::Peer> {
238    match message {
239        tl::enums::Message::Empty(_) => None,
240        tl::enums::Message::Message(m) => Some(m.peer_id.clone()),
241        tl::enums::Message::Service(m) => Some(m.peer_id.clone()),
242    }
243}
244
245fn message_channel_id(message: &tl::enums::Message) -> Option<i64> {
246    match message {
247        tl::enums::Message::Empty(_) => None,
248        tl::enums::Message::Message(m) => match &m.peer_id {
249            tl::enums::Peer::Channel(c) => Some(c.channel_id),
250            _ => None,
251        },
252        tl::enums::Message::Service(m) => match &m.peer_id {
253            tl::enums::Peer::Channel(c) => Some(c.channel_id),
254            _ => None,
255        },
256    }
257}
258
259impl PtsInfo {
260    pub(super) fn from_update(update: &tl::enums::Update) -> Option<Self> {
261        use tl::enums::Update::*;
262        match update {
263            NewMessage(u) => {
264                assert!(!matches!(
265                    message_peer(&u.message),
266                    Some(tl::enums::Peer::Channel(_))
267                ));
268                Some(Self {
269                    pts: u.pts,
270                    pts_count: u.pts_count,
271                    entry: Entry::AccountWide,
272                })
273            }
274            MessageId(_) => None,
275            DeleteMessages(u) => Some(Self {
276                pts: u.pts,
277                pts_count: u.pts_count,
278                entry: Entry::AccountWide,
279            }),
280            UserTyping(_) => None,
281            ChatUserTyping(_) => None,
282            ChatParticipants(_) => None,
283            UserStatus(_) => None,
284            UserName(_) => None,
285            NewAuthorization(_) => None,
286            NewEncryptedMessage(u) => Some(Self {
287                pts: u.qts,
288                pts_count: 1,
289                entry: Entry::SecretChats,
290            }),
291            EncryptedChatTyping(_) => None,
292            Encryption(_) => None,
293            EncryptedMessagesRead(_) => None,
294            ChatParticipantAdd(_) => None,
295            ChatParticipantDelete(_) => None,
296            DcOptions(_) => None,
297            NotifySettings(_) => None,
298            ServiceNotification(_) => None,
299            Privacy(_) => None,
300            UserPhone(_) => None,
301            ReadHistoryInbox(u) => {
302                assert!(!matches!(u.peer, tl::enums::Peer::Channel(_)));
303                Some(Self {
304                    pts: u.pts,
305                    pts_count: u.pts_count,
306                    entry: Entry::AccountWide,
307                })
308            }
309            ReadHistoryOutbox(u) => {
310                assert!(!matches!(u.peer, tl::enums::Peer::Channel(_)));
311                Some(Self {
312                    pts: u.pts,
313                    pts_count: u.pts_count,
314                    entry: Entry::AccountWide,
315                })
316            }
317            WebPage(u) => Some(Self {
318                pts: u.pts,
319                pts_count: u.pts_count,
320                entry: Entry::AccountWide,
321            }),
322            ReadMessagesContents(u) => Some(Self {
323                pts: u.pts,
324                pts_count: u.pts_count,
325                entry: Entry::AccountWide,
326            }),
327            ChannelTooLong(u) => u.pts.map(|pts| Self {
328                pts,
329                pts_count: 0,
330                entry: Entry::Channel(u.channel_id),
331            }),
332            Channel(_) => None,
333            // Telegram actually sends `updateNewChannelMessage(messageEmpty(…))`, and because
334            // there's no way to tell which channel ID this `pts` belongs to, the best we can
335            // do is ignore it.
336            //
337            // Future messages should trigger a gap that we need to recover from.
338            NewChannelMessage(u) => message_channel_id(&u.message).map(|channel_id| Self {
339                pts: u.pts,
340                pts_count: u.pts_count,
341                entry: Entry::Channel(channel_id),
342            }),
343            ReadChannelInbox(u) => Some(Self {
344                pts: u.pts,
345                pts_count: 0,
346                entry: Entry::Channel(u.channel_id),
347            }),
348            DeleteChannelMessages(u) => Some(Self {
349                pts: u.pts,
350                pts_count: u.pts_count,
351                entry: Entry::Channel(u.channel_id),
352            }),
353            ChannelMessageViews(_) => None,
354            ChatParticipantAdmin(_) => None,
355            NewStickerSet(_) => None,
356            StickerSetsOrder(_) => None,
357            StickerSets(_) => None,
358            SavedGifs => None,
359            BotInlineQuery(_) => None,
360            BotInlineSend(_) => None,
361            EditChannelMessage(u) => message_channel_id(&u.message).map(|channel_id| Self {
362                pts: u.pts,
363                pts_count: u.pts_count,
364                entry: Entry::Channel(channel_id),
365            }),
366            BotCallbackQuery(_) => None,
367            EditMessage(u) => {
368                assert!(!matches!(
369                    message_peer(&u.message),
370                    Some(tl::enums::Peer::Channel(_))
371                ));
372                Some(Self {
373                    pts: u.pts,
374                    pts_count: u.pts_count,
375                    entry: Entry::AccountWide,
376                })
377            }
378            InlineBotCallbackQuery(_) => None,
379            ReadChannelOutbox(_) => None,
380            DraftMessage(_) => None,
381            ReadFeaturedStickers => None,
382            RecentStickers => None,
383            Config => None,
384            PtsChanged => None,
385            ChannelWebPage(u) => Some(Self {
386                pts: u.pts,
387                pts_count: u.pts_count,
388                entry: Entry::Channel(u.channel_id),
389            }),
390            DialogPinned(_) => None,
391            PinnedDialogs(_) => None,
392            BotWebhookJson(_) => None,
393            BotWebhookJsonquery(_) => None,
394            BotShippingQuery(_) => None,
395            BotPrecheckoutQuery(_) => None,
396            PhoneCall(_) => None,
397            LangPackTooLong(_) => None,
398            LangPack(_) => None,
399            FavedStickers => None,
400            ChannelReadMessagesContents(_) => None,
401            ContactsReset => None,
402            ChannelAvailableMessages(_) => None,
403            DialogUnreadMark(_) => None,
404            MessagePoll(_) => None,
405            ChatDefaultBannedRights(_) => None,
406            FolderPeers(u) => Some(Self {
407                pts: u.pts,
408                pts_count: u.pts_count,
409                entry: Entry::AccountWide,
410            }),
411            PeerSettings(_) => None,
412            PeerLocated(_) => None,
413            NewScheduledMessage(_) => None,
414            DeleteScheduledMessages(_) => None,
415            Theme(_) => None,
416            GeoLiveViewed(_) => None,
417            LoginToken => None,
418            MessagePollVote(_) => None,
419            DialogFilter(_) => None,
420            DialogFilterOrder(_) => None,
421            DialogFilters => None,
422            PhoneCallSignalingData(_) => None,
423            ChannelMessageForwards(_) => None,
424            ReadChannelDiscussionInbox(_) => None,
425            ReadChannelDiscussionOutbox(_) => None,
426            PeerBlocked(_) => None,
427            ChannelUserTyping(_) => None,
428            PinnedMessages(u) => {
429                assert!(!matches!(u.peer, tl::enums::Peer::Channel(_)));
430                Some(Self {
431                    pts: u.pts,
432                    pts_count: u.pts_count,
433                    entry: Entry::AccountWide,
434                })
435            }
436            PinnedChannelMessages(u) => Some(Self {
437                pts: u.pts,
438                pts_count: u.pts_count,
439                entry: Entry::Channel(u.channel_id),
440            }),
441            Chat(_) => None,
442            GroupCallParticipants(_) => None,
443            GroupCall(_) => None,
444            PeerHistoryTtl(_) => None,
445            ChatParticipant(u) => Some(Self {
446                pts: u.qts,
447                pts_count: 1,
448                entry: Entry::SecretChats,
449            }),
450            ChannelParticipant(u) => Some(Self {
451                pts: u.qts,
452                pts_count: 1,
453                entry: Entry::SecretChats,
454            }),
455            BotStopped(u) => Some(Self {
456                pts: u.qts,
457                pts_count: 1,
458                entry: Entry::SecretChats,
459            }),
460            GroupCallConnection(_) => None,
461            BotCommands(_) => None,
462            PendingJoinRequests(_) => None,
463            BotChatInviteRequester(u) => Some(Self {
464                pts: u.qts,
465                pts_count: 1,
466                entry: Entry::SecretChats,
467            }),
468            MessageReactions(_) => None,
469            AttachMenuBots => None,
470            WebViewResultSent(_) => None,
471            BotMenuButton(_) => None,
472            SavedRingtones => None,
473            TranscribedAudio(_) => None,
474            ReadFeaturedEmojiStickers => None,
475            UserEmojiStatus(_) => None,
476            RecentEmojiStatuses => None,
477            RecentReactions => None,
478            MoveStickerSetToTop(_) => None,
479            MessageExtendedMedia(_) => None,
480            ChannelPinnedTopic(_) => None,
481            ChannelPinnedTopics(_) => None,
482            User(_) => None,
483            AutoSaveSettings => None,
484            Story(_) => None,
485            NewStoryReaction(_) => None,
486            ReadStories(_) => None,
487            StoryId(_) => None,
488            StoriesStealthMode(_) => None,
489            SentStoryReaction(_) => None,
490            BotChatBoost(u) => Some(Self {
491                pts: u.qts,
492                pts_count: 1,
493                entry: Entry::SecretChats,
494            }),
495            ChannelViewForumAsMessages(_) => None,
496            PeerWallpaper(_) => None,
497            BotMessageReaction(u) => Some(Self {
498                pts: u.qts,
499                pts_count: 1,
500                entry: Entry::SecretChats,
501            }),
502            BotMessageReactions(u) => Some(Self {
503                pts: u.qts,
504                pts_count: 1,
505                entry: Entry::SecretChats,
506            }),
507            SavedDialogPinned(_) => None,
508            PinnedSavedDialogs(_) => None,
509            SavedReactionTags => None,
510            SmsJob(_) => None,
511            QuickReplies(_) => None,
512            NewQuickReply(_) => None,
513            DeleteQuickReply(_) => None,
514            QuickReplyMessage(_) => None,
515            DeleteQuickReplyMessages(_) => None,
516            BotBusinessConnect(_) => None,
517            BotNewBusinessMessage(u) => Some(Self {
518                pts: u.qts,
519                pts_count: 1,
520                entry: Entry::SecretChats,
521            }),
522            BotEditBusinessMessage(u) => Some(Self {
523                pts: u.qts,
524                pts_count: 1,
525                entry: Entry::SecretChats,
526            }),
527            BotDeleteBusinessMessage(u) => Some(Self {
528                pts: u.qts,
529                pts_count: u.messages.len() as i32,
530                entry: Entry::SecretChats,
531            }),
532            BroadcastRevenueTransactions(_) => None,
533            StarsBalance(_) => None,
534            BusinessBotCallbackQuery(_) => None,
535            StarsRevenueStatus(_) => None,
536        }
537        .filter(|info| info.pts != NO_PTS)
538    }
539}