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