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