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