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}