Skip to main content

grammers_client/client/
messages.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
9//! Methods related to sending messages.
10
11use std::collections::HashMap;
12
13use chrono::{DateTime, FixedOffset};
14use grammers_mtsender::InvocationError;
15use grammers_session::types::{PeerAuth, PeerId, PeerKind, PeerRef};
16use grammers_tl_types::{self as tl, enums::InputPeer};
17use log::{Level, log_enabled, warn};
18
19use super::{Client, IterBuffer};
20use crate::media::{InputMedia, Media};
21use crate::message::{InputMessage, InputReactions, Message};
22use crate::utils::{generate_random_id, generate_random_ids};
23
24async fn map_random_ids_to_messages(
25    client: &Client,
26    fetched_in: PeerRef,
27    random_ids: &[i64],
28    updates: tl::enums::Updates,
29) -> Vec<Option<Message>> {
30    match updates {
31        tl::enums::Updates::Updates(tl::types::Updates {
32            updates,
33            users,
34            chats,
35            date: _,
36            seq: _,
37        }) => {
38            let peers = client.build_peer_map(users, chats).await;
39
40            let rnd_to_id = updates
41                .iter()
42                .filter_map(|update| match update {
43                    tl::enums::Update::MessageId(u) => Some((u.random_id, u.id)),
44                    _ => None,
45                })
46                .collect::<HashMap<_, _>>();
47
48            // TODO ideally this would use the same UpdateIter mechanism to make sure we don't
49            //      accidentally miss variants
50            let mut id_to_msg = updates
51                .into_iter()
52                .filter_map(|update| match update {
53                    tl::enums::Update::NewMessage(tl::types::UpdateNewMessage {
54                        message, ..
55                    }) => Some(message),
56                    tl::enums::Update::NewChannelMessage(tl::types::UpdateNewChannelMessage {
57                        message,
58                        ..
59                    }) => Some(message),
60                    tl::enums::Update::NewScheduledMessage(
61                        tl::types::UpdateNewScheduledMessage { message, .. },
62                    ) => Some(message),
63                    _ => None,
64                })
65                .map(|message| {
66                    Message::from_raw(client, message, Some(fetched_in.clone()), peers.handle())
67                })
68                .map(|message| (message.id(), message))
69                .collect::<HashMap<_, _>>();
70
71            random_ids
72                .iter()
73                .map(|rnd| {
74                    rnd_to_id
75                        .get(rnd)
76                        .and_then(|id| id_to_msg.remove(id))
77                        .or_else(|| {
78                            if id_to_msg.len() == 1 {
79                                // If there's no random_id to map from, in the common case a single message
80                                // should've been produced regardless, so try to recover by returning that.
81                                id_to_msg.drain().next().map(|(_, m)| m)
82                            } else {
83                                None
84                            }
85                        })
86                })
87                .collect()
88        }
89        _ => panic!("API returned something other than Updates so messages can't be mapped"),
90    }
91}
92
93pub(crate) async fn parse_mention_entities(
94    client: &Client,
95    mut entities: Vec<tl::enums::MessageEntity>,
96) -> Option<Vec<tl::enums::MessageEntity>> {
97    if entities.is_empty() {
98        return None;
99    }
100
101    if entities
102        .iter()
103        .any(|e| matches!(e, tl::enums::MessageEntity::MentionName(_)))
104    {
105        for entity in entities.iter_mut() {
106            if let tl::enums::MessageEntity::MentionName(mention_name) = entity {
107                *entity = tl::types::InputMessageEntityMentionName {
108                    offset: mention_name.offset,
109                    length: mention_name.length,
110                    user_id: tl::enums::InputUser::User(tl::types::InputUser {
111                        user_id: mention_name.user_id,
112                        access_hash: client
113                            .0
114                            .session
115                            .peer(PeerId::user_unchecked(mention_name.user_id))
116                            .await
117                            .and_then(|peer| peer.auth())
118                            .unwrap_or_default()
119                            .hash(),
120                    }),
121                }
122                .into()
123            }
124        }
125    }
126
127    Some(entities)
128}
129
130const MAX_LIMIT: usize = 100;
131
132impl<R: tl::RemoteCall<Return = tl::enums::messages::Messages>> IterBuffer<R, Message> {
133    /// Fetches the total unless cached.
134    ///
135    /// The `request.limit` should be set to the right value before calling this method.
136    async fn get_total(&mut self) -> Result<usize, InvocationError> {
137        if let Some(total) = self.total {
138            return Ok(total);
139        }
140
141        use tl::enums::messages::Messages;
142
143        let total = match self.client.invoke(&self.request).await? {
144            Messages::Messages(messages) => messages.messages.len(),
145            Messages::Slice(messages) => messages.count as usize,
146            Messages::ChannelMessages(messages) => messages.count as usize,
147            Messages::NotModified(messages) => messages.count as usize,
148        };
149        self.total = Some(total);
150        Ok(total)
151    }
152
153    /// Performs the network call, fills the buffer, and returns the `offset_rate` if any.
154    ///
155    /// The `request.limit` should be set to the right value before calling this method.
156    async fn fill_buffer(
157        &mut self,
158        limit: i32,
159        peer: Option<PeerRef>,
160    ) -> Result<Option<i32>, InvocationError> {
161        use tl::enums::messages::Messages;
162
163        let (messages, users, chats, rate) = match self.client.invoke(&self.request).await? {
164            Messages::Messages(m) => {
165                self.last_chunk = true;
166                self.total = Some(m.messages.len());
167                (m.messages, m.users, m.chats, None)
168            }
169            Messages::Slice(m) => {
170                // Can't rely on `count(messages) < limit` as the stop condition.
171                // See https://t.me/tdlibchat/57257 for discussion. As an example:
172                // offset_id 132002, limit 4 => we get msg 131999 & 131998
173                // offset_id 132002, limit 3 => we get msg 131999
174                // offset_id 132002, limit 2 => we get msg 132000
175                // offset_id 132002, limit 1 => we get msg 132001
176                //
177                // If the highest fetched message ID is lower than or equal to the limit,
178                // there can't be more messages after (highest ID - limit), because the
179                // absolute lowest message ID is 1.
180                self.last_chunk = m.messages.is_empty() || m.messages[0].id() <= limit;
181                self.total = Some(m.count as usize);
182                (m.messages, m.users, m.chats, m.next_rate)
183            }
184            Messages::ChannelMessages(m) => {
185                self.last_chunk = m.messages.is_empty() || m.messages[0].id() <= limit;
186                self.total = Some(m.count as usize);
187                (m.messages, m.users, m.chats, None)
188            }
189            Messages::NotModified(_) => {
190                panic!("API returned Messages::NotModified even though hash = 0")
191            }
192        };
193
194        let peers = self.client.build_peer_map(users, chats).await;
195
196        let client = self.client.clone();
197        self.buffer.extend(
198            messages
199                .into_iter()
200                .map(|message| Message::from_raw(&client, message, peer, peers.handle())),
201        );
202
203        Ok(rate)
204    }
205}
206
207/// Iterator returned by [`Client::iter_messages`].
208pub type MessageIter = IterBuffer<tl::functions::messages::GetHistory, Message>;
209
210impl MessageIter {
211    fn new(client: &Client, peer: PeerRef) -> Self {
212        Self::from_request(
213            client,
214            MAX_LIMIT,
215            tl::functions::messages::GetHistory {
216                peer: peer.into(),
217                offset_id: 0,
218                offset_date: 0,
219                add_offset: 0,
220                limit: 0,
221                max_id: 0,
222                min_id: 0,
223                hash: 0,
224            },
225        )
226    }
227
228    /// Changes the message identifier upper bound.
229    pub fn offset_id(mut self, offset: i32) -> Self {
230        self.request.offset_id = offset;
231        self
232    }
233
234    /// Changes the message send date upper bound.
235    pub fn max_date(mut self, offset: i32) -> Self {
236        self.request.offset_date = offset;
237        self
238    }
239
240    /// Determines how many messages there are in total.
241    ///
242    /// This only performs a network call if `next` has not been called before.
243    pub async fn total(&mut self) -> Result<usize, InvocationError> {
244        self.request.limit = 1;
245        self.get_total().await
246    }
247
248    /// Returns the next `Message` from the internal buffer, filling the buffer previously if it's
249    /// empty.
250    ///
251    /// Returns `None` if the `limit` is reached or there are no messages left.
252    pub async fn next(&mut self) -> Result<Option<Message>, InvocationError> {
253        if let Some(result) = self.next_raw() {
254            return result;
255        }
256
257        self.request.limit = self.determine_limit(MAX_LIMIT);
258        self.fill_buffer(self.request.limit, Some(self.request.peer.clone().into()))
259            .await?;
260
261        // Don't bother updating offsets if this is the last time stuff has to be fetched.
262        if !self.last_chunk && !self.buffer.is_empty() {
263            let last = &self.buffer[self.buffer.len() - 1];
264            self.request.offset_id = last.id();
265            self.request.offset_date = last.date_timestamp();
266        }
267
268        Ok(self.pop_item())
269    }
270}
271
272/// Iterator returned by [`Client::search_messages`].
273pub type SearchIter = IterBuffer<tl::functions::messages::Search, Message>;
274
275impl SearchIter {
276    fn new(client: &Client, peer: PeerRef) -> Self {
277        // TODO let users tweak all the options from the request
278        Self::from_request(
279            client,
280            MAX_LIMIT,
281            tl::functions::messages::Search {
282                peer: peer.into(),
283                q: String::new(),
284                from_id: None,
285                saved_peer_id: None,
286                saved_reaction: None,
287                top_msg_id: None,
288                filter: tl::enums::MessagesFilter::InputMessagesFilterEmpty,
289                min_date: 0,
290                max_date: 0,
291                offset_id: 0,
292                add_offset: 0,
293                limit: 0,
294                max_id: 0,
295                min_id: 0,
296                hash: 0,
297            },
298        )
299    }
300
301    /// Changes the message identifier upper bound.
302    pub fn offset_id(mut self, offset: i32) -> Self {
303        self.request.offset_id = offset;
304        self
305    }
306
307    /// Changes the query of the search. Telegram servers perform a somewhat fuzzy search over
308    /// this query (so a word in singular may also return messages with the word in plural, for
309    /// example).
310    pub fn query(mut self, query: &str) -> Self {
311        self.request.q = query.to_string();
312        self
313    }
314
315    /// Restricts results to messages sent by the logged-in user
316    pub fn sent_by_self(mut self) -> Self {
317        self.request.from_id = Some(InputPeer::PeerSelf);
318        self
319    }
320
321    /// Returns only messages with date bigger than date_time.
322    ///
323    /// ```
324    /// use chrono::DateTime;
325    ///
326    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
327    /// // Search messages sent after Jan 1st, 2021
328    /// let min_date = DateTime::parse_from_rfc3339("2021-01-01T00:00:00-00:00").unwrap();
329    ///
330    /// let mut messages = client.search_messages(peer).min_date(&min_date);
331    ///
332    /// # Ok(())
333    /// # }
334    /// ```
335    pub fn min_date(mut self, date_time: &DateTime<FixedOffset>) -> Self {
336        self.request.min_date = date_time.timestamp() as i32;
337        self
338    }
339
340    /// Returns only messages with date smaller than date_time
341    ///
342    /// ```
343    /// use chrono::DateTime;
344    ///
345    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
346    /// // Search messages sent before Dec, 25th 2022
347    /// let max_date = DateTime::parse_from_rfc3339("2022-12-25T00:00:00-00:00").unwrap();
348    ///
349    /// let mut messages = client.search_messages(peer).max_date(&max_date);
350    ///
351    /// # Ok(())
352    /// # }
353    /// ```
354    pub fn max_date(mut self, date_time: &DateTime<FixedOffset>) -> Self {
355        self.request.max_date = date_time.timestamp() as i32;
356        self
357    }
358
359    /// Changes the media filter. Only messages with this type of media will be fetched.
360    pub fn filter(mut self, filter: tl::enums::MessagesFilter) -> Self {
361        self.request.filter = filter;
362        self
363    }
364
365    /// Determines how many messages there are in total.
366    ///
367    /// This only performs a network call if `next` has not been called before.
368    pub async fn total(&mut self) -> Result<usize, InvocationError> {
369        // Unlike most requests, a limit of 0 actually returns 0 and not a default amount
370        // (as of layer 120).
371        self.request.limit = 0;
372        self.get_total().await
373    }
374
375    /// Return the next `Message` from the internal buffer, filling the buffer previously if it's
376    /// empty.
377    ///
378    /// Returns `None` if the `limit` is reached or there are no messages left.
379    pub async fn next(&mut self) -> Result<Option<Message>, InvocationError> {
380        if let Some(result) = self.next_raw() {
381            return result;
382        }
383
384        self.request.limit = self.determine_limit(MAX_LIMIT);
385        self.fill_buffer(self.request.limit, Some(self.request.peer.clone().into()))
386            .await?;
387
388        // Don't bother updating offsets if this is the last time stuff has to be fetched.
389        if !self.last_chunk && !self.buffer.is_empty() {
390            let last = &self.buffer[self.buffer.len() - 1];
391            self.request.offset_id = last.id();
392            self.request.max_date = last.date_timestamp();
393        }
394
395        Ok(self.pop_item())
396    }
397}
398
399/// Iterator returned by [`Client::search_all_messages`].
400pub type GlobalSearchIter = IterBuffer<tl::functions::messages::SearchGlobal, Message>;
401
402impl GlobalSearchIter {
403    fn new(client: &Client) -> Self {
404        // TODO let users tweak all the options from the request
405        Self::from_request(
406            client,
407            MAX_LIMIT,
408            tl::functions::messages::SearchGlobal {
409                folder_id: None,
410                q: String::new(),
411                filter: tl::enums::MessagesFilter::InputMessagesFilterEmpty,
412                min_date: 0,
413                max_date: 0,
414                offset_rate: 0,
415                offset_peer: tl::enums::InputPeer::Empty,
416                offset_id: 0,
417                limit: 0,
418                broadcasts_only: false,
419                groups_only: false,
420                users_only: false,
421            },
422        )
423    }
424
425    /// Changes the message identifier upper bound.
426    pub fn offset_id(mut self, offset: i32) -> Self {
427        self.request.offset_id = offset;
428        self
429    }
430
431    /// Changes the query of the search. Telegram servers perform a somewhat fuzzy search over
432    /// this query (so a word in singular may also return messages with the word in plural, for
433    /// example).
434    pub fn query(mut self, query: &str) -> Self {
435        self.request.q = query.to_string();
436        self
437    }
438
439    /// Changes the media filter. Only messages with this type of media will be fetched.
440    pub fn filter(mut self, filter: tl::enums::MessagesFilter) -> Self {
441        self.request.filter = filter;
442        self
443    }
444
445    /// Determines how many messages there are in total.
446    ///
447    /// This only performs a network call if `next` has not been called before.
448    pub async fn total(&mut self) -> Result<usize, InvocationError> {
449        self.request.limit = 1;
450        self.get_total().await
451    }
452
453    /// Return the next `Message` from the internal buffer, filling the buffer previously if it's
454    /// empty.
455    ///
456    /// Returns `None` if the `limit` is reached or there are no messages left.
457    pub async fn next(&mut self) -> Result<Option<Message>, InvocationError> {
458        if let Some(result) = self.next_raw() {
459            return result;
460        }
461
462        self.request.limit = self.determine_limit(MAX_LIMIT);
463        let offset_rate = self.fill_buffer(self.request.limit, None).await?;
464
465        // Don't bother updating offsets if this is the last time stuff has to be fetched.
466        if !self.last_chunk && !self.buffer.is_empty() {
467            let last = &self.buffer[self.buffer.len() - 1];
468            self.request.offset_rate = offset_rate.unwrap_or(0);
469            self.request.offset_peer = last
470                .peer_ref()
471                .await
472                .map(|peer| peer.into())
473                .unwrap_or(tl::enums::InputPeer::Empty);
474            self.request.offset_id = last.id();
475        }
476
477        Ok(self.pop_item())
478    }
479}
480
481/// Method implementations related to sending, modifying or getting messages.
482impl Client {
483    /// Sends a message to the desired peer.
484    ///
485    /// This method can also be used to send media such as photos, videos, documents, polls, etc.
486    ///
487    /// If you want to send a local file as media, you will need to use
488    /// [`Client::upload_file`] first.
489    ///
490    /// Refer to [`InputMessage`] to learn more formatting options, such as using markdown or
491    /// adding buttons under your message (if you're logged in as a bot).
492    ///
493    /// See also: [`Message::respond`], [`Message::reply`].
494    ///
495    /// # Examples
496    ///
497    /// ```
498    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
499    /// client.send_message(peer, "Boring text message :-(").await?;
500    ///
501    /// use grammers_client::message::InputMessage;
502    ///
503    /// client.send_message(peer, InputMessage::new().text("Sneaky message").silent(true)).await?;
504    /// # Ok(())
505    /// # }
506    /// ```
507    pub async fn send_message<C: Into<PeerRef>, M: Into<InputMessage>>(
508        &self,
509        peer: C,
510        message: M,
511    ) -> Result<Message, InvocationError> {
512        let peer = peer.into();
513        let message = message.into();
514        let random_id = generate_random_id();
515        let entities = parse_mention_entities(self, message.entities.clone()).await;
516        let updates = if let Some(media) = message.media.clone() {
517            self.invoke(&tl::functions::messages::SendMedia {
518                silent: message.silent,
519                background: message.background,
520                clear_draft: message.clear_draft,
521                peer: peer.into(),
522                reply_to: message.reply_to.map(|reply_to_msg_id| {
523                    tl::types::InputReplyToMessage {
524                        reply_to_msg_id,
525                        top_msg_id: None,
526                        reply_to_peer_id: None,
527                        quote_text: None,
528                        quote_entities: None,
529                        quote_offset: None,
530                        monoforum_peer_id: None,
531                        todo_item_id: None,
532                    }
533                    .into()
534                }),
535                media,
536                message: message.text.clone(),
537                random_id,
538                reply_markup: message.reply_markup.clone(),
539                entities,
540                schedule_date: message.schedule_date,
541                schedule_repeat_period: None,
542                send_as: None,
543                noforwards: false,
544                update_stickersets_order: false,
545                invert_media: message.invert_media,
546                quick_reply_shortcut: None,
547                effect: None,
548                allow_paid_floodskip: false,
549                allow_paid_stars: None,
550                suggested_post: None,
551            })
552            .await
553        } else {
554            self.invoke(&tl::functions::messages::SendMessage {
555                no_webpage: !message.link_preview,
556                silent: message.silent,
557                background: message.background,
558                clear_draft: message.clear_draft,
559                peer: peer.into(),
560                reply_to: message.reply_to.map(|reply_to_msg_id| {
561                    tl::types::InputReplyToMessage {
562                        reply_to_msg_id,
563                        top_msg_id: None,
564                        reply_to_peer_id: None,
565                        quote_text: None,
566                        quote_entities: None,
567                        quote_offset: None,
568                        monoforum_peer_id: None,
569                        todo_item_id: None,
570                    }
571                    .into()
572                }),
573                message: message.text.clone(),
574                random_id,
575                reply_markup: message.reply_markup.clone(),
576                entities,
577                schedule_date: message.schedule_date,
578                schedule_repeat_period: None,
579                send_as: None,
580                noforwards: false,
581                update_stickersets_order: false,
582                invert_media: message.invert_media,
583                quick_reply_shortcut: None,
584                effect: None,
585                allow_paid_floodskip: false,
586                allow_paid_stars: None,
587                suggested_post: None,
588            })
589            .await
590        }?;
591
592        Ok(match updates {
593            tl::enums::Updates::UpdateShortSentMessage(updates) => {
594                let peer = if peer.id.kind() == PeerKind::UserSelf {
595                    // from_raw_short_updates needs the peer ID
596                    self.0.session.peer_ref(peer.id).await.unwrap()
597                } else {
598                    peer
599                };
600
601                Message::from_raw_short_updates(self, updates, message, peer)
602            }
603            updates => {
604                let updates_debug = if log_enabled!(Level::Warn) {
605                    Some(updates.clone())
606                } else {
607                    None
608                };
609
610                match map_random_ids_to_messages(self, peer, &[random_id], updates)
611                    .await
612                    .pop()
613                    .flatten()
614                {
615                    Some(message) => message,
616                    None => {
617                        if let Some(updates) = updates_debug {
618                            warn!(
619                                "failed to find just-sent message in response updates; please report this:"
620                            );
621                            warn!("{:#?}", updates);
622                        }
623                        Message::from_raw(
624                            self,
625                            tl::enums::Message::Empty(tl::types::MessageEmpty {
626                                id: 0,
627                                peer_id: Some(peer.id.into()),
628                            }),
629                            Some(peer),
630                            self.empty_peer_map(),
631                        )
632                    }
633                }
634            }
635        })
636    }
637
638    /// Sends a album to the desired peer.
639    ///
640    /// This method can also be used to send a bunch of media such as photos, videos, documents, polls, etc.
641    ///
642    /// If you want to send a local file as media, you will need to use
643    /// [`Client::upload_file`] first.
644    ///
645    /// Refer to [`InputMedia`] to learn more formatting options, such as using markdown.
646    ///
647    /// See also: [`Message::respond_album`], [`Message::reply_album`].
648    ///
649    /// # Examples
650    ///
651    /// ```
652    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
653    /// use grammers_client::media::InputMedia;
654    ///
655    /// client.send_album(peer, vec![InputMedia::new().caption("A album").photo_url("https://example.com/cat.jpg")]).await?;
656    /// # Ok(())
657    /// # }
658    /// ```
659    pub async fn send_album<C: Into<PeerRef>>(
660        &self,
661        peer: C,
662        mut medias: Vec<InputMedia>,
663    ) -> Result<Vec<Option<Message>>, InvocationError> {
664        let peer = peer.into();
665        let random_ids = generate_random_ids(medias.len());
666
667        // Upload external files
668        for media in medias.iter_mut() {
669            let raw_media = media.media.clone().unwrap();
670
671            if matches!(
672                raw_media,
673                tl::enums::InputMedia::UploadedPhoto(_)
674                    | tl::enums::InputMedia::PhotoExternal(_)
675                    | tl::enums::InputMedia::UploadedDocument(_)
676                    | tl::enums::InputMedia::DocumentExternal(_)
677            ) {
678                let uploaded = self
679                    .invoke(&tl::functions::messages::UploadMedia {
680                        business_connection_id: None,
681                        peer: peer.into(),
682                        media: raw_media,
683                    })
684                    .await?;
685                media.media = Some(
686                    Media::from_raw(uploaded)
687                        .unwrap()
688                        .to_raw_input_media()
689                        .unwrap(),
690                );
691            }
692        }
693
694        let first_media_reply = medias.first().unwrap().reply_to;
695
696        let mut multi_media = Vec::with_capacity(medias.len());
697        for (input_media, random_id) in medias.into_iter().zip(random_ids.iter()) {
698            let entities = parse_mention_entities(self, input_media.entities).await;
699            let raw_media = input_media.media.unwrap();
700            multi_media.push(tl::enums::InputSingleMedia::Media(
701                tl::types::InputSingleMedia {
702                    media: raw_media,
703                    random_id: *random_id,
704                    message: input_media.caption,
705                    entities,
706                },
707            ))
708        }
709
710        let updates = self
711            .invoke(&tl::functions::messages::SendMultiMedia {
712                silent: false,
713                background: false,
714                clear_draft: false,
715                peer: peer.into(),
716                reply_to: first_media_reply.map(|reply_to_msg_id| {
717                    tl::types::InputReplyToMessage {
718                        reply_to_msg_id,
719                        top_msg_id: None,
720                        reply_to_peer_id: None,
721                        quote_text: None,
722                        quote_entities: None,
723                        quote_offset: None,
724                        monoforum_peer_id: None,
725                        todo_item_id: None,
726                    }
727                    .into()
728                }),
729                schedule_date: None,
730                multi_media,
731                send_as: None,
732                noforwards: false,
733                update_stickersets_order: false,
734                invert_media: false,
735                quick_reply_shortcut: None,
736                effect: None,
737                allow_paid_floodskip: false,
738                allow_paid_stars: None,
739            })
740            .await?;
741
742        Ok(map_random_ids_to_messages(self, peer, &random_ids, updates).await)
743    }
744
745    /// Edits an existing message.
746    ///
747    /// Similar to [`Client::send_message`], advanced formatting can be achieved with the
748    /// options offered by [`InputMessage`].
749    ///
750    /// See also: [`Message::edit`].
751    ///
752    /// # Examples
753    ///
754    /// ```
755    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
756    /// let old_message_id = 123;
757    /// client.edit_message(peer, old_message_id, "New text message").await?;
758    /// # Ok(())
759    /// # }
760    /// ```
761    pub async fn edit_message<C: Into<PeerRef>, M: Into<InputMessage>>(
762        &self,
763        peer: C,
764        message_id: i32,
765        new_message: M,
766    ) -> Result<(), InvocationError> {
767        let new_message = new_message.into();
768        let entities = parse_mention_entities(self, new_message.entities).await;
769        self.invoke(&tl::functions::messages::EditMessage {
770            no_webpage: !new_message.link_preview,
771            invert_media: new_message.invert_media,
772            peer: peer.into().into(),
773            id: message_id,
774            message: Some(new_message.text),
775            media: new_message.media,
776            reply_markup: new_message.reply_markup,
777            entities,
778            schedule_date: new_message.schedule_date,
779            schedule_repeat_period: None,
780            quick_reply_shortcut_id: None,
781        })
782        .await?;
783
784        Ok(())
785    }
786
787    /// Deletes up to 100 messages in a peer.
788    ///
789    /// <div class="warning">
790    ///
791    /// When deleting messages from small group peers or private conversations, this
792    /// method cannot validate that the provided message IDs actually belong to the input peer due
793    /// to the way Telegram's API works. Make sure to pass correct [`Message::id`]'s.
794    ///
795    /// </div>
796    ///
797    /// The messages are deleted for both ends.
798    ///
799    /// The amount of deleted messages is returned (it might be less than the amount of input
800    /// message IDs if some of them were already missing). It is not possible to find out which
801    /// messages were actually deleted, but if the request succeeds, none of the specified message
802    /// IDs will appear in the message history from that point on.
803    ///
804    /// See also: [`Message::delete`].
805    ///
806    /// # Examples
807    ///
808    /// ```
809    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
810    /// let message_ids = [123, 456, 789];
811    ///
812    /// // Careful, these messages will be gone after the method succeeds!
813    /// client.delete_messages(peer, &message_ids).await?;
814    /// # Ok(())
815    /// # }
816    /// ```
817    pub async fn delete_messages<C: Into<PeerRef>>(
818        &self,
819        peer: C,
820        message_ids: &[i32],
821    ) -> Result<usize, InvocationError> {
822        let peer = peer.into();
823        let tl::enums::messages::AffectedMessages::Messages(affected) =
824            if peer.id.kind() == PeerKind::Channel {
825                self.invoke(&tl::functions::channels::DeleteMessages {
826                    channel: peer.into(),
827                    id: message_ids.to_vec(),
828                })
829                .await
830            } else {
831                self.invoke(&tl::functions::messages::DeleteMessages {
832                    revoke: true,
833                    id: message_ids.to_vec(),
834                })
835                .await
836            }?;
837
838        Ok(affected.pts_count as usize)
839    }
840
841    /// Forwards up to 100 messages from `source` into `destination`.
842    ///
843    /// For consistency with other methods, the peer upon which this request acts comes first
844    /// (destination), and then the source peer.
845    ///
846    /// Returns the new forwarded messages in a list. Those messages that could not be forwarded
847    /// will be `None`. The length of the resulting list is the same as the length of the input
848    /// message IDs, and the indices from the list of IDs map to the indices in the result so
849    /// you can find which messages were forwarded and which message they became.
850    ///
851    /// See also: [`Message::forward_to`].
852    ///
853    /// # Examples
854    ///
855    /// ```
856    /// # async fn f(destination: grammers_session::types::PeerRef, source: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
857    /// let message_ids = [123, 456, 789];
858    ///
859    /// let messages = client.forward_messages(destination, &message_ids, source).await?;
860    /// let fwd_count = messages.into_iter().filter(Option::is_some).count();
861    /// println!("Forwarded {} out of {} messages!", fwd_count, message_ids.len());
862    /// # Ok(())
863    /// # }
864    /// ```
865    pub async fn forward_messages<C: Into<PeerRef>, S: Into<PeerRef>>(
866        &self,
867        destination: C,
868        message_ids: &[i32],
869        source: S,
870    ) -> Result<Vec<Option<Message>>, InvocationError> {
871        // TODO let user customize more options
872        let peer = destination.into();
873        let request = tl::functions::messages::ForwardMessages {
874            silent: false,
875            background: false,
876            with_my_score: false,
877            drop_author: false,
878            drop_media_captions: false,
879            from_peer: source.into().into(),
880            id: message_ids.to_vec(),
881            random_id: generate_random_ids(message_ids.len()),
882            to_peer: peer.into(),
883            top_msg_id: None,
884            reply_to: None,
885            schedule_date: None,
886            schedule_repeat_period: None,
887            send_as: None,
888            noforwards: false,
889            quick_reply_shortcut: None,
890            allow_paid_floodskip: false,
891            effect: None,
892            video_timestamp: None,
893            allow_paid_stars: None,
894            suggested_post: None,
895        };
896        let result = self.invoke(&request).await?;
897        Ok(map_random_ids_to_messages(self, peer.into(), &request.random_id, result).await)
898    }
899
900    /// Gets the [`Message`] to which the input message is replying to.
901    ///
902    /// See also: [`Message::get_reply`].
903    ///
904    /// # Examples
905    ///
906    /// ```
907    /// # async fn f(message: grammers_client::message::Message, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
908    /// if let Some(reply) = client.get_reply_to_message(&message).await? {
909    ///     println!("The reply said: {}", reply.text());
910    /// }
911    /// # Ok(())
912    /// # }
913    /// ```
914    pub async fn get_reply_to_message(
915        &self,
916        message: &Message,
917    ) -> Result<Option<Message>, InvocationError> {
918        /// Helper method to fetch a single message by its input message.
919        async fn get_message(
920            client: &Client,
921            peer: PeerRef,
922            id: tl::enums::InputMessage,
923        ) -> Result<(tl::enums::messages::Messages, bool), InvocationError> {
924            if peer.id.kind() == PeerKind::Channel {
925                client
926                    .invoke(&tl::functions::channels::GetMessages {
927                        id: vec![id],
928                        channel: peer.into(),
929                    })
930                    .await
931                    .map(|res| (res, false))
932            } else {
933                client
934                    .invoke(&tl::functions::messages::GetMessages { id: vec![id] })
935                    .await
936                    .map(|res| (res, true))
937            }
938        }
939
940        // TODO shouldn't this method take in a message id anyway?
941        let peer_id = message.peer_id();
942        let peer = match peer_id.kind() {
943            PeerKind::User | PeerKind::UserSelf | PeerKind::Chat => PeerRef {
944                id: peer_id,
945                auth: PeerAuth::default(), // unused, so no need to bother fetching it
946            },
947            PeerKind::Channel => message.peer_ref().await.ok_or(InvocationError::Dropped)?,
948        };
949        let reply_to_message_id = match message.reply_to_message_id() {
950            Some(id) => id,
951            None => return Ok(None),
952        };
953
954        let input_id =
955            tl::enums::InputMessage::ReplyTo(tl::types::InputMessageReplyTo { id: message.id() });
956
957        let (res, filter_req) = match get_message(self, peer, input_id).await {
958            Ok(tup) => tup,
959            Err(_) => {
960                let input_id = tl::enums::InputMessage::Id(tl::types::InputMessageId {
961                    id: reply_to_message_id,
962                });
963                get_message(self, peer, input_id).await?
964            }
965        };
966
967        use tl::enums::messages::Messages;
968
969        let (messages, users, chats) = match res {
970            Messages::Messages(m) => (m.messages, m.users, m.chats),
971            Messages::Slice(m) => (m.messages, m.users, m.chats),
972            Messages::ChannelMessages(m) => (m.messages, m.users, m.chats),
973            Messages::NotModified(_) => {
974                panic!("API returned Messages::NotModified even though GetMessages was used")
975            }
976        };
977
978        let peers = self.build_peer_map(users, chats).await;
979        Ok(messages
980            .into_iter()
981            .map(|m| Message::from_raw(self, m, Some(peer.into()), peers.handle()))
982            .next()
983            .filter(|m| !filter_req || m.peer_id() == message.peer_id()))
984    }
985
986    /// Iterate over the message history of a peer, from most recent to oldest.
987    ///
988    /// # Examples
989    ///
990    /// ```
991    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
992    /// // Note we're setting a reasonable limit, or we'd print out ALL the messages in peer!
993    /// let mut messages = client.iter_messages(peer).limit(100);
994    ///
995    /// while let Some(message) = messages.next().await? {
996    ///     println!("{}", message.text());
997    /// }
998    /// # Ok(())
999    /// # }
1000    /// ```
1001    pub fn iter_messages<C: Into<PeerRef>>(&self, peer: C) -> MessageIter {
1002        MessageIter::new(self, peer.into())
1003    }
1004
1005    /// Iterate over the messages that match certain search criteria.
1006    ///
1007    /// This allows you to search by text within a peer or filter by media among other things.
1008    ///
1009    /// # Examples
1010    ///
1011    /// ```
1012    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1013    /// // Let's print all the people who think grammers is cool.
1014    /// let mut messages = client.search_messages(peer).query("grammers is cool");
1015    ///
1016    /// while let Some(message) = messages.next().await? {
1017    ///     let sender = message.sender().unwrap();
1018    ///     println!("{}", sender.name().unwrap_or(&sender.id().to_string()));
1019    /// }
1020    /// # Ok(())
1021    /// # }
1022    /// ```
1023    pub fn search_messages<C: Into<PeerRef>>(&self, peer: C) -> SearchIter {
1024        SearchIter::new(self, peer.into())
1025    }
1026
1027    /// Iterate over the messages that match certain search criteria, without being restricted to
1028    /// searching in a specific peer. The downside is that this global search supports less filters.
1029    ///
1030    /// This allows you to search by text within a peer or filter by media among other things.
1031    ///
1032    /// # Examples
1033    ///
1034    /// ```
1035    /// # async fn f(client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1036    /// // Let's print all the peers were people think grammers is cool.
1037    /// let mut messages = client.search_all_messages().query("grammers is cool");
1038    ///
1039    /// while let Some(message) = messages.next().await? {
1040    ///     println!("{}", message.peer().unwrap().name().unwrap_or(&message.peer().unwrap().id().to_string()));
1041    /// }
1042    /// # Ok(())
1043    /// # }
1044    /// ```
1045    pub fn search_all_messages(&self) -> GlobalSearchIter {
1046        GlobalSearchIter::new(self)
1047    }
1048
1049    /// Get up to 100 messages using their ID.
1050    ///
1051    /// Returns the new retrieved messages in a list. Those messages that could not be retrieved
1052    /// or do not belong to the input peer will be `None`. The length of the resulting list is the
1053    /// same as the length of the input message IDs, and the indices from the list of IDs map to
1054    /// the indices in the result so you can map them into the new list.
1055    ///
1056    /// # Examples
1057    ///
1058    /// ```
1059    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1060    /// let message_ids = [123, 456, 789];
1061    ///
1062    /// let messages = client.get_messages_by_id(peer, &message_ids).await?;
1063    /// let count = messages.into_iter().filter(Option::is_some).count();
1064    /// println!("{} out of {} messages were deleted!", message_ids.len() - count, message_ids.len());
1065    /// # Ok(())
1066    /// # }
1067    /// ```
1068    pub async fn get_messages_by_id<C: Into<PeerRef>>(
1069        &self,
1070        peer: C,
1071        message_ids: &[i32],
1072    ) -> Result<Vec<Option<Message>>, InvocationError> {
1073        let peer = peer.into();
1074        let id = message_ids
1075            .iter()
1076            .map(|&id| tl::enums::InputMessage::Id(tl::types::InputMessageId { id }))
1077            .collect();
1078
1079        let result = if peer.id.kind() == PeerKind::Channel {
1080            self.invoke(&tl::functions::channels::GetMessages {
1081                channel: peer.into(),
1082                id,
1083            })
1084            .await
1085        } else {
1086            self.invoke(&tl::functions::messages::GetMessages { id })
1087                .await
1088        }?;
1089
1090        let (messages, users, chats) = match result {
1091            tl::enums::messages::Messages::Messages(m) => (m.messages, m.users, m.chats),
1092            tl::enums::messages::Messages::Slice(m) => (m.messages, m.users, m.chats),
1093            tl::enums::messages::Messages::ChannelMessages(m) => (m.messages, m.users, m.chats),
1094            tl::enums::messages::Messages::NotModified(_) => {
1095                panic!("API returned Messages::NotModified even though GetMessages was used")
1096            }
1097        };
1098
1099        let peers = self.build_peer_map(users, chats).await;
1100        let mut map = messages
1101            .into_iter()
1102            .map(|m| Message::from_raw(self, m, Some(peer.into()), peers.handle()))
1103            .filter(|m| m.peer_id() == peer.id)
1104            .map(|m| (m.id(), m))
1105            .collect::<HashMap<_, _>>();
1106
1107        Ok(message_ids.iter().map(|id| map.remove(id)).collect())
1108    }
1109
1110    /// Get the latest pin from a peer.
1111    ///
1112    /// # Examples
1113    ///
1114    /// ```
1115    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1116    /// if let Some(message) = client.get_pinned_message(peer).await? {
1117    ///     println!("There is a message pinned: {}", message.text());
1118    /// } else {
1119    ///     println!("There are no messages pinned");
1120    /// }
1121    /// # Ok(())
1122    /// # }
1123    /// ```
1124    pub async fn get_pinned_message<C: Into<PeerRef>>(
1125        &self,
1126        peer: C,
1127    ) -> Result<Option<Message>, InvocationError> {
1128        let peer = peer.into();
1129        // TODO return types::Message and print its text in the example
1130        let id = vec![tl::enums::InputMessage::Pinned];
1131
1132        let result = if peer.id.kind() == PeerKind::Channel {
1133            self.invoke(&tl::functions::channels::GetMessages {
1134                channel: peer.into(),
1135                id,
1136            })
1137            .await
1138        } else {
1139            self.invoke(&tl::functions::messages::GetMessages { id })
1140                .await
1141        }?;
1142
1143        let (messages, users, chats) = match result {
1144            tl::enums::messages::Messages::Messages(m) => (m.messages, m.users, m.chats),
1145            tl::enums::messages::Messages::Slice(m) => (m.messages, m.users, m.chats),
1146            tl::enums::messages::Messages::ChannelMessages(m) => (m.messages, m.users, m.chats),
1147            tl::enums::messages::Messages::NotModified(_) => {
1148                panic!("API returned Messages::NotModified even though GetMessages was used")
1149            }
1150        };
1151
1152        let peers = self.build_peer_map(users, chats).await;
1153        Ok(messages
1154            .into_iter()
1155            .map(|m| Message::from_raw(self, m, Some(peer.into()), peers.handle()))
1156            .find(|m| m.peer_id() == peer.id))
1157    }
1158
1159    /// Pin a message in the peer. This will not notify any users.
1160    ///
1161    /// # Examples
1162    ///
1163    /// ```
1164    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1165    /// let message_id = 123;
1166    /// client.pin_message(peer, message_id).await?;
1167    /// # Ok(())
1168    /// # }
1169    /// ```
1170    // TODO return produced Option<service message>
1171    pub async fn pin_message<C: Into<PeerRef>>(
1172        &self,
1173        peer: C,
1174        message_id: i32,
1175    ) -> Result<(), InvocationError> {
1176        self.update_pinned(peer.into(), message_id, true).await
1177    }
1178
1179    /// Unpin a message from the peer.
1180    ///
1181    /// # Examples
1182    ///
1183    /// ```
1184    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1185    /// let message_id = 123;
1186    /// client.unpin_message(peer, message_id).await?;
1187    /// # Ok(())
1188    /// # }
1189    /// ```
1190    pub async fn unpin_message<C: Into<PeerRef>>(
1191        &self,
1192        peer: C,
1193        message_id: i32,
1194    ) -> Result<(), InvocationError> {
1195        self.update_pinned(peer.into(), message_id, false).await
1196    }
1197
1198    async fn update_pinned(
1199        &self,
1200        peer: PeerRef,
1201        id: i32,
1202        pin: bool,
1203    ) -> Result<(), InvocationError> {
1204        self.invoke(&tl::functions::messages::UpdatePinnedMessage {
1205            silent: true,
1206            unpin: !pin,
1207            pm_oneside: false,
1208            peer: peer.into(),
1209            id,
1210        })
1211        .await
1212        .map(drop)
1213    }
1214
1215    /// Unpin all currently-pinned messages from the peer.
1216    ///
1217    /// # Examples
1218    ///
1219    /// ```
1220    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1221    /// client.unpin_all_messages(peer).await?;
1222    /// # Ok(())
1223    /// # }
1224    /// ```
1225    pub async fn unpin_all_messages<C: Into<PeerRef>>(
1226        &self,
1227        peer: C,
1228    ) -> Result<(), InvocationError> {
1229        self.invoke(&tl::functions::messages::UnpinAllMessages {
1230            peer: peer.into().into(),
1231            top_msg_id: None,
1232            saved_peer_id: None,
1233        })
1234        .await?;
1235        Ok(())
1236    }
1237
1238    /// Send reaction.
1239    ///
1240    /// # Examples
1241    ///
1242    /// Via emoticon
1243    ///
1244    /// ```
1245    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1246    /// let message_id = 123;
1247    ///
1248    /// client.send_reactions(peer, message_id, "👍").await?;
1249    /// # Ok(())
1250    /// # }
1251    /// ```
1252    ///
1253    /// Make animation big & Add to recent
1254    ///
1255    /// ```
1256    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1257    /// use grammers_client::message::InputReactions;
1258    ///
1259    /// let message_id = 123;
1260    /// let reactions = InputReactions::emoticon("🤯").big().add_to_recent();
1261    ///
1262    /// client.send_reactions(peer, message_id, reactions).await?;
1263    /// # Ok(())
1264    /// # }
1265    /// ```
1266    ///
1267    /// Remove reactions
1268    ///
1269    /// ```
1270    /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
1271    /// use grammers_client::message::InputReactions;
1272    ///
1273    /// let message_id = 123;
1274    ///
1275    /// client.send_reactions(peer, message_id, InputReactions::remove()).await?;
1276    /// # Ok(())
1277    /// # }
1278    /// ```
1279    pub async fn send_reactions<C: Into<PeerRef>, R: Into<InputReactions>>(
1280        &self,
1281        peer: C,
1282        message_id: i32,
1283        reactions: R,
1284    ) -> Result<(), InvocationError> {
1285        let reactions = reactions.into();
1286
1287        self.invoke(&tl::functions::messages::SendReaction {
1288            big: reactions.big,
1289            add_to_recent: reactions.add_to_recent,
1290            peer: peer.into().into(),
1291            msg_id: message_id,
1292            reaction: Some(reactions.reactions),
1293        })
1294        .await?;
1295
1296        Ok(())
1297    }
1298}