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