grammers_client/client/chats.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 users, groups and channels.
10
11use std::collections::HashMap;
12use std::collections::VecDeque;
13use std::future::Future;
14use std::sync::Arc;
15use std::time::Duration;
16
17use grammers_mtsender::InvocationError;
18use grammers_mtsender::RpcError;
19use grammers_session::types::{PeerId, PeerKind, PeerRef};
20use grammers_tl_types as tl;
21
22use super::{Client, IterBuffer};
23use crate::media::Photo;
24use crate::message::Message;
25use crate::peer::ActionSender;
26use crate::peer::AdminRightsBuilder;
27use crate::peer::BannedRightsBuilder;
28use crate::peer::Participant;
29use crate::peer::Peer;
30use crate::peer::PeerMap;
31use crate::peer::User;
32use crate::peer::chats::AdminRightsBuilderInner;
33use crate::peer::chats::BannedRightsBuilderInner;
34
35const MAX_PARTICIPANT_LIMIT: usize = 200;
36const MAX_PHOTO_LIMIT: usize = 100;
37const KICK_BAN_DURATION: i32 = 60; // in seconds, in case the second request fails
38
39/// A categorized search result item from [`Client::search_peer`].
40#[derive(Debug, Clone)]
41pub enum PeerSearchItem {
42 /// A user from the account's contact list.
43 Contact(Peer),
44 /// A peer from an existing dialog (non-contact user, group, or channel).
45 Dialog(Peer),
46 /// A peer found via global search.
47 Global(Peer),
48}
49
50impl PeerSearchItem {
51 /// Get a reference to the underlying peer.
52 pub fn peer(&self) -> &Peer {
53 match self {
54 Self::Contact(p) | Self::Dialog(p) | Self::Global(p) => p,
55 }
56 }
57
58 /// Unwrap into the underlying peer, discarding the category.
59 pub fn into_peer(self) -> Peer {
60 match self {
61 Self::Contact(p) | Self::Dialog(p) | Self::Global(p) => p,
62 }
63 }
64}
65
66enum ParticipantIterInner {
67 Empty,
68 Chat {
69 client: Client,
70 chat_id: i64,
71 buffer: VecDeque<Participant>,
72 total: Option<usize>,
73 },
74 Channel(IterBuffer<tl::functions::channels::GetParticipants, Participant>),
75}
76
77/// Iterator returned by [`Client::iter_participants`].
78pub struct ParticipantIter(ParticipantIterInner);
79
80impl ParticipantIter {
81 fn new(client: &Client, peer: PeerRef) -> Self {
82 Self(if peer.id.kind() == PeerKind::Channel {
83 ParticipantIterInner::Channel(IterBuffer::from_request(
84 client,
85 MAX_PARTICIPANT_LIMIT,
86 tl::functions::channels::GetParticipants {
87 channel: peer.into(),
88 filter: tl::enums::ChannelParticipantsFilter::ChannelParticipantsRecent,
89 offset: 0,
90 limit: 0,
91 hash: 0,
92 },
93 ))
94 } else if peer.id.kind() == PeerKind::Chat {
95 ParticipantIterInner::Chat {
96 client: client.clone(),
97 chat_id: peer.into(),
98 buffer: VecDeque::new(),
99 total: None,
100 }
101 } else {
102 ParticipantIterInner::Empty
103 })
104 }
105
106 /// Determines how many participants there are in total.
107 ///
108 /// This only performs a network call if `next` has not been called before.
109 pub async fn total(&mut self) -> Result<usize, InvocationError> {
110 match &self.0 {
111 ParticipantIterInner::Empty => Ok(0),
112 ParticipantIterInner::Chat { total, .. } => {
113 if let Some(total) = total {
114 Ok(*total)
115 } else {
116 self.fill_buffer().await
117 }
118 }
119 ParticipantIterInner::Channel(iter) => {
120 if let Some(total) = iter.total {
121 Ok(total)
122 } else {
123 self.fill_buffer().await
124 }
125 }
126 }
127 }
128
129 /// Fills the buffer, and returns the total count.
130 async fn fill_buffer(&mut self) -> Result<usize, InvocationError> {
131 match &mut self.0 {
132 ParticipantIterInner::Empty => Ok(0),
133 ParticipantIterInner::Chat {
134 client,
135 chat_id,
136 buffer,
137 total,
138 } => {
139 assert!(buffer.is_empty());
140 let tl::enums::messages::ChatFull::Full(full) = client
141 .invoke(&tl::functions::messages::GetFullChat { chat_id: *chat_id })
142 .await?;
143
144 let chat = match full.full_chat {
145 tl::enums::ChatFull::Full(chat) => chat,
146 tl::enums::ChatFull::ChannelFull(_) => panic!(
147 "API returned ChannelFull even though messages::GetFullChat was used"
148 ),
149 };
150
151 // Don't actually care for the chats, just the users.
152 let mut peers = client.build_peer_map(full.users, Vec::new()).await;
153
154 let participants = match chat.participants {
155 tl::enums::ChatParticipants::Forbidden(c) => {
156 if let Some(p) = c.self_participant {
157 buffer.push_back(Participant::from_raw_chat(&mut peers, p));
158 }
159 return Ok(buffer.len());
160 }
161 tl::enums::ChatParticipants::Participants(c) => c.participants,
162 };
163
164 buffer.extend(
165 participants
166 .into_iter()
167 .map(|p| Participant::from_raw_chat(&mut peers, p)),
168 );
169
170 *total = Some(buffer.len());
171 Ok(buffer.len())
172 }
173 ParticipantIterInner::Channel(iter) => {
174 assert!(iter.buffer.is_empty());
175 use tl::enums::channels::ChannelParticipants::*;
176
177 iter.request.limit = iter.determine_limit(MAX_PARTICIPANT_LIMIT);
178 let (count, participants, _, users) =
179 match iter.client.invoke(&iter.request).await? {
180 Participants(p) => (p.count, p.participants, p.chats, p.users),
181 NotModified => {
182 panic!("API returned Dialogs::NotModified even though hash = 0")
183 }
184 };
185
186 // Telegram can return less participants than asked for but the count being higher
187 // (for example, count=4825, participants=199, users=200). The missing participant
188 // was an admin bot account, not sure why it's not included.
189 //
190 // In any case we pick whichever size is highest to avoid weird cases like this.
191 iter.last_chunk =
192 usize::max(participants.len(), users.len()) < iter.request.limit as usize;
193 iter.request.offset += participants.len() as i32;
194
195 // Don't actually care for the chats, just the users.
196 let mut peers = iter.client.build_peer_map(users, Vec::new()).await;
197
198 iter.buffer.extend(
199 participants
200 .into_iter()
201 .map(|p| Participant::from_raw_channel(&mut peers, p)),
202 );
203
204 iter.total = Some(count as usize);
205 Ok(count as usize)
206 }
207 }
208 }
209
210 /// Return the next `Participant` from the internal buffer, filling the buffer previously if
211 /// it's empty.
212 ///
213 /// Returns `None` if the `limit` is reached or there are no participants left.
214 pub async fn next(&mut self) -> Result<Option<Participant>, InvocationError> {
215 // Need to split the `match` because `fill_buffer()` borrows mutably.
216 match &mut self.0 {
217 ParticipantIterInner::Empty => {}
218 ParticipantIterInner::Chat { buffer, .. } => {
219 if buffer.is_empty() {
220 self.fill_buffer().await?;
221 }
222 }
223 ParticipantIterInner::Channel(iter) => {
224 if let Some(result) = iter.next_raw() {
225 return result;
226 }
227 self.fill_buffer().await?;
228 }
229 }
230
231 match &mut self.0 {
232 ParticipantIterInner::Empty => Ok(None),
233 ParticipantIterInner::Chat { buffer, .. } => {
234 let result = buffer.pop_front();
235 if buffer.is_empty() {
236 self.0 = ParticipantIterInner::Empty;
237 }
238 Ok(result)
239 }
240 ParticipantIterInner::Channel(iter) => Ok(iter.pop_item()),
241 }
242 }
243
244 /// apply a filter on fetched participants, note that this filter will apply only on large `Channel` and not small groups
245 pub fn filter(mut self, filter: tl::enums::ChannelParticipantsFilter) -> Self {
246 match self.0 {
247 ParticipantIterInner::Channel(ref mut c) => {
248 c.request.filter = filter;
249 self
250 }
251 _ => self,
252 }
253 }
254}
255
256enum ProfilePhotoIterInner {
257 User(IterBuffer<tl::functions::photos::GetUserPhotos, Photo>),
258 Chat(IterBuffer<tl::functions::messages::Search, Message>),
259}
260
261/// Iterator returned by [`Client::iter_profile_photos`].
262pub struct ProfilePhotoIter(ProfilePhotoIterInner);
263
264impl ProfilePhotoIter {
265 fn new(client: &Client, peer: PeerRef) -> Self {
266 Self(
267 if matches!(peer.id.kind(), PeerKind::User | PeerKind::UserSelf) {
268 ProfilePhotoIterInner::User(IterBuffer::from_request(
269 client,
270 MAX_PHOTO_LIMIT,
271 tl::functions::photos::GetUserPhotos {
272 user_id: peer.into(),
273 offset: 0,
274 max_id: 0,
275 limit: 0,
276 },
277 ))
278 } else {
279 ProfilePhotoIterInner::Chat(
280 client
281 .search_messages(peer)
282 .filter(tl::enums::MessagesFilter::InputMessagesFilterChatPhotos),
283 )
284 },
285 )
286 }
287
288 /// Determines how many profile photos there are in total.
289 ///
290 /// This only performs a network call if `next` has not been called before.
291 pub async fn total(&mut self) -> Result<usize, InvocationError> {
292 match &mut self.0 {
293 ProfilePhotoIterInner::User(iter) => {
294 if let Some(total) = iter.total {
295 Ok(total)
296 } else {
297 self.fill_buffer().await
298 }
299 }
300 ProfilePhotoIterInner::Chat(iter) => iter.total().await,
301 }
302 }
303
304 /// Fills the buffer, and returns the total count.
305 async fn fill_buffer(&mut self) -> Result<usize, InvocationError> {
306 match &mut self.0 {
307 ProfilePhotoIterInner::User(iter) => {
308 use tl::enums::photos::Photos;
309
310 iter.request.limit = iter.determine_limit(MAX_PHOTO_LIMIT);
311 let (total, photos) = match iter.client.invoke(&iter.request).await? {
312 Photos::Photos(p) => {
313 iter.last_chunk = true;
314 iter.total = Some(p.photos.len());
315 (p.photos.len(), p.photos)
316 }
317 Photos::Slice(p) => {
318 iter.last_chunk = p.photos.len() < iter.request.limit as usize;
319 iter.total = Some(p.count as usize);
320 (p.count as usize, p.photos)
321 }
322 };
323
324 // Don't bother updating offsets if this is the last time stuff has to be fetched.
325 if !iter.last_chunk && !iter.buffer.is_empty() {
326 iter.request.offset += photos.len() as i32;
327 }
328
329 iter.buffer.extend(photos.into_iter().map(Photo::from_raw));
330
331 Ok(total)
332 }
333 ProfilePhotoIterInner::Chat(_) => {
334 panic!("fill_buffer should not be called for Chat variant")
335 }
336 }
337 }
338
339 /// Return the next photo from the internal buffer, filling the buffer previously if it's
340 /// empty.
341 ///
342 /// Returns `None` if the `limit` is reached or there are no photos left.
343 pub async fn next(&mut self) -> Result<Option<Photo>, InvocationError> {
344 // Need to split the `match` because `fill_buffer()` borrows mutably.
345 match &mut self.0 {
346 ProfilePhotoIterInner::User(iter) => {
347 if let Some(result) = iter.next_raw() {
348 return result;
349 }
350 self.fill_buffer().await?;
351 }
352 ProfilePhotoIterInner::Chat(iter) => {
353 while let Some(message) = iter.next().await? {
354 if let Some(tl::enums::MessageAction::ChatEditPhoto(
355 tl::types::MessageActionChatEditPhoto { photo },
356 )) = message.action()
357 {
358 return Ok(Some(Photo::from_raw(photo.clone())));
359 } else {
360 continue;
361 }
362 }
363 }
364 }
365
366 match &mut self.0 {
367 ProfilePhotoIterInner::User(iter) => Ok(iter.pop_item()),
368 ProfilePhotoIterInner::Chat(_) => Ok(None),
369 }
370 }
371}
372
373fn updates_to_chat(client: &Client, id: Option<i64>, updates: tl::enums::Updates) -> Option<Peer> {
374 use tl::enums::Updates;
375
376 let chats = match updates {
377 Updates::Combined(updates) => Some(updates.chats),
378 Updates::Updates(updates) => Some(updates.chats),
379 _ => None,
380 };
381
382 match chats {
383 Some(chats) => match id {
384 Some(id) => chats.into_iter().find(|chat| chat.id() == id),
385 None => chats.into_iter().next(),
386 },
387 None => None,
388 }
389 .map(|chat| Peer::from_raw(client, chat))
390}
391
392/// Method implementations related to dealing with peers.
393impl Client {
394 /// Resolves a username into the peer that owns it, if any.
395 ///
396 /// Note that this method is expensive to call, and can quickly cause long flood waits.
397 ///
398 /// # Examples
399 ///
400 /// ```
401 /// # async fn f(client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
402 /// if let Some(peer) = client.resolve_username("username").await? {
403 /// println!("Found peer!: {:?}", peer.name());
404 /// }
405 /// # Ok(())
406 /// # }
407 /// ```
408 pub async fn resolve_username(&self, username: &str) -> Result<Option<Peer>, InvocationError> {
409 let tl::types::contacts::ResolvedPeer { peer, users, chats } = match self
410 .invoke(&tl::functions::contacts::ResolveUsername {
411 username: username.into(),
412 referer: None,
413 })
414 .await
415 {
416 Ok(tl::enums::contacts::ResolvedPeer::Peer(p)) => p,
417 Err(err) if err.is("USERNAME_NOT_OCCUPIED") => return Ok(None),
418 Err(err) => return Err(err),
419 };
420
421 let mut peers = self.build_peer_map(users, chats).await;
422 let peer_id = PeerId::from(peer);
423
424 Ok(peers.take(peer_id))
425 }
426
427 /// Search for peers (users, groups, channels, bots) by name or username.
428 ///
429 /// Returns a `Vec<PeerSearchItem>` where each item is categorized as:
430 /// - [`PeerSearchItem::Contact`]: a user from your contact list
431 /// - [`PeerSearchItem::Dialog`]: a peer from an existing dialog (non-contact)
432 /// - [`PeerSearchItem::Global`]: a peer found via global search
433 ///
434 /// Found peers are automatically cached to the session for later use.
435 ///
436 /// This method is generally less rate-limited than [`Client::resolve_username`] and can be
437 /// used as a cross-check when `resolve_username` returns `None` to distinguish "username
438 /// does not exist" from "account is silently rate-limited".
439 ///
440 /// # Examples
441 ///
442 /// ```
443 /// # async fn f(client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
444 /// use grammers_client::PeerSearchItem;
445 ///
446 /// let results = client.search_peer("username", 5).await?;
447 /// for item in &results {
448 /// match item {
449 /// PeerSearchItem::Contact(peer) => println!("Contact: {:?}", peer.name()),
450 /// PeerSearchItem::Dialog(peer) => println!("Dialog: {:?}", peer.name()),
451 /// PeerSearchItem::Global(peer) => println!("Global: {:?}", peer.name()),
452 /// }
453 /// }
454 /// # Ok(())
455 /// # }
456 /// ```
457 pub async fn search_peer(
458 &self,
459 query: &str,
460 limit: usize,
461 ) -> Result<Vec<PeerSearchItem>, InvocationError> {
462 let tl::types::contacts::Found {
463 my_results,
464 results,
465 chats,
466 users,
467 } = match self
468 .invoke(&tl::functions::contacts::Search {
469 q: query.into(),
470 limit: limit as i32,
471 })
472 .await?
473 {
474 tl::enums::contacts::Found::Found(f) => f,
475 };
476
477 let peer_map = self.build_peer_map(users, chats).await;
478
479 let classify_my_result = |peer: Peer| -> PeerSearchItem {
480 match &peer {
481 Peer::User(user) if user.contact() => PeerSearchItem::Contact(peer),
482 _ => PeerSearchItem::Dialog(peer),
483 }
484 };
485
486 let mut items: Vec<PeerSearchItem> = my_results
487 .into_iter()
488 .filter_map(|p| peer_map.get(PeerId::from(p)).cloned())
489 .map(classify_my_result)
490 .collect();
491
492 items.extend(
493 results
494 .into_iter()
495 .filter_map(|p| peer_map.get(PeerId::from(p)).cloned())
496 .map(PeerSearchItem::Global),
497 );
498
499 Ok(items)
500 }
501
502 /// Fetch full information about the currently logged-in user.
503 ///
504 /// Although this method is cheap to call, you might want to cache the results somewhere.
505 ///
506 /// # Examples
507 ///
508 /// ```
509 /// # async fn f(client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
510 /// println!("Displaying full user information of the logged-in user:");
511 /// dbg!(client.get_me().await?);
512 /// # Ok(())
513 /// # }
514 /// ```
515 pub async fn get_me(&self) -> Result<User, InvocationError> {
516 let mut res = self
517 .invoke(&tl::functions::users::GetUsers {
518 id: vec![tl::enums::InputUser::UserSelf],
519 })
520 .await?;
521
522 if res.len() != 1 {
523 panic!("fetching only one user should exactly return one user");
524 }
525
526 Ok(User::from_raw(self, res.pop().unwrap()))
527 }
528
529 /// Iterate over the participants of a chat.
530 ///
531 /// The participants are returned in no particular order.
532 ///
533 /// When used to iterate the participants of "user", the iterator won't produce values.
534 ///
535 /// # Examples
536 ///
537 /// ```
538 /// # async fn f(chat: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
539 /// let mut participants = client.iter_participants(chat);
540 ///
541 /// while let Some(participant) = participants.next().await? {
542 /// println!(
543 /// "{} has role {:?}",
544 /// participant.user.first_name().unwrap_or(&participant.user.id().to_string()),
545 /// participant.role
546 /// );
547 /// }
548 /// # Ok(())
549 /// # }
550 /// ```
551 pub fn iter_participants<C: Into<PeerRef>>(&self, chat: C) -> ParticipantIter {
552 ParticipantIter::new(self, chat.into())
553 }
554
555 /// Kicks the participant from the chat.
556 ///
557 /// This will fail if you do not have sufficient permissions to perform said operation,
558 /// or the target user is the logged-in account. Use [`Self::delete_dialog`] for the latter instead.
559 ///
560 /// The kicked user will be able to join after being kicked (they are not permanently banned).
561 ///
562 /// Kicking someone who was not in the chat prior to running this method will be able to join
563 /// after as well (effectively unbanning them).
564 ///
565 /// When used to kick users from "user" chat, nothing will be done.
566 ///
567 /// # Examples
568 ///
569 /// ```
570 /// # async fn f(chat: grammers_session::types::PeerRef, user: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
571 /// match client.kick_participant(chat, user).await {
572 /// Ok(_) => println!("user is no more >:D"),
573 /// Err(_) => println!("Kick failed! Are you sure you're admin?"),
574 /// };
575 /// # Ok(())
576 /// # }
577 /// ```
578 pub async fn kick_participant<C: Into<PeerRef>, U: Into<PeerRef>>(
579 &self,
580 chat: C,
581 user: U,
582 ) -> Result<(), InvocationError> {
583 let chat = chat.into();
584 let user = user.into();
585 if chat.id.kind() == PeerKind::Channel {
586 self.set_banned_rights(chat, user)
587 .view_messages(false)
588 .duration(Duration::from_secs(KICK_BAN_DURATION as u64))
589 .await?;
590
591 self.set_banned_rights(chat, user).await
592 } else if chat.id.kind() == PeerKind::Chat {
593 self.invoke(&tl::functions::messages::DeleteChatUser {
594 chat_id: chat.into(),
595 user_id: user.into(),
596 revoke_history: false,
597 })
598 .await
599 .map(drop)
600 } else {
601 Ok(())
602 }
603 }
604
605 /// Returns a builder to set the banned rights for a specific user.
606 ///
607 /// Nothing is done until it is awaited, at which point it might result in
608 /// error if you do not have sufficient permissions to ban the user in the input chat.
609 ///
610 /// By default, the user has all rights, and you need to revoke those you want to take away
611 /// from the user by setting the permissions to `false`. This means that not taking away any
612 /// permissions will effectively unban someone, granting them all default user permissions.
613 ///
614 /// By default, the ban is applied forever, but this can be changed to a shorter duration.
615 ///
616 /// The default group rights are respected, despite individual restrictions.
617 ///
618 /// # Example
619 ///
620 /// ```
621 /// # async fn f(chat: grammers_session::types::PeerRef, user: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
622 /// // This user keeps spamming pepe stickers, take the sticker permission away from them
623 /// let res = client
624 /// .set_banned_rights(chat, user)
625 /// .send_stickers(false)
626 /// .await;
627 ///
628 /// match res {
629 /// Ok(_) => println!("No more sticker spam!"),
630 /// Err(_) => println!("Ban failed! Are you sure you're admin?"),
631 /// };
632 /// # Ok(())
633 /// # }
634 /// ```
635 pub fn set_banned_rights<C: Into<PeerRef>, U: Into<PeerRef>>(
636 &self,
637 channel: C,
638 user: U,
639 ) -> BannedRightsBuilder<impl Future<Output = Result<(), InvocationError>>> {
640 BannedRightsBuilder::new(
641 self.clone(),
642 channel.into(),
643 user.into(),
644 BannedRightsBuilderInner::invoke,
645 )
646 }
647
648 /// Returns a builder to set the administrator rights for a specific user.
649 ///
650 /// Nothing is done until it is awaited, at which point
651 /// it might result in error if you do not have sufficient permissions to grant those rights
652 /// to the other user.
653 ///
654 /// By default, no permissions are granted, and you need to specify those you want to grant by
655 /// setting the permissions to `true`. This means that not granting any permission will turn
656 /// the user into a normal user again, and they will no longer be an administrator.
657 ///
658 /// The change is applied forever and there is no way to set a specific duration. If the user
659 /// should only be an administrator for a set period of time, the administrator permissions
660 /// must be manually revoked at a later point in time.
661 ///
662 /// # Example
663 ///
664 /// ```
665 /// # async fn f(chat: grammers_session::types::PeerRef, user: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
666 /// // Let the user pin messages and ban other people
667 /// let res = client.set_admin_rights(chat, user)
668 /// .load_current()
669 /// .await?
670 /// .pin_messages(true)
671 /// .ban_users(true)
672 /// .await?;
673 /// # Ok(())
674 /// # }
675 /// ```
676 pub fn set_admin_rights<C: Into<PeerRef>, U: Into<PeerRef>>(
677 &self,
678 channel: C,
679 user: U,
680 ) -> AdminRightsBuilder<impl Future<Output = Result<(), InvocationError>>> {
681 AdminRightsBuilder::new(
682 self.clone(),
683 channel.into(),
684 user.into(),
685 AdminRightsBuilderInner::invoke,
686 )
687 }
688
689 /// Iterate over the history of profile photos for the given peer.
690 ///
691 /// Note that the current photo might not be present in the history, and to avoid doing more
692 /// work when it's generally not needed (the photo history tends to be complete but in some
693 /// cases it might not be), it's up to you to fetch this photo from the full channel.
694 ///
695 /// Note that you cannot use these photos to send them as messages directly. They must be
696 /// downloaded first, then uploaded, and finally sent.
697 ///
698 /// # Examples
699 ///
700 /// ```
701 /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
702 /// let mut photos = client.iter_profile_photos(peer);
703 ///
704 /// while let Some(photo) = photos.next().await? {
705 /// println!("Did you know peer has a photo with ID {}?", photo.id());
706 /// }
707 /// # Ok(())
708 /// # }
709 /// ```
710 pub fn iter_profile_photos<C: Into<PeerRef>>(&self, peer: C) -> ProfilePhotoIter {
711 ProfilePhotoIter::new(self, peer.into())
712 }
713
714 /// Convert a [`PeerRef`] back into a [`Peer`].
715 ///
716 /// # Example
717 ///
718 /// ```
719 /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
720 /// let peer = client.resolve_peer(peer).await?;
721 ///
722 /// println!("Found peer: {}", peer.name().unwrap_or(&peer.id().to_string()));
723 /// # Ok(())
724 /// # }
725 /// ```
726 pub async fn resolve_peer<C: Into<PeerRef>>(&self, peer: C) -> Result<Peer, InvocationError> {
727 let peer = peer.into();
728 Ok(match peer.id.kind() {
729 PeerKind::User | PeerKind::UserSelf => {
730 let mut res = self
731 .invoke(&tl::functions::users::GetUsers {
732 id: vec![peer.into()],
733 })
734 .await?;
735 if res.len() != 1 {
736 panic!("fetching only one user should exactly return one user");
737 }
738 Peer::from_user(self, res.pop().unwrap())
739 }
740 PeerKind::Chat => {
741 let mut res = match self
742 .invoke(&tl::functions::messages::GetChats {
743 id: vec![peer.into()],
744 })
745 .await?
746 {
747 tl::enums::messages::Chats::Chats(chats) => chats.chats,
748 tl::enums::messages::Chats::Slice(chat_slice) => chat_slice.chats,
749 };
750 if res.len() != 1 {
751 panic!("fetching only one chat should exactly return one chat");
752 }
753 Peer::from_raw(self, res.pop().unwrap())
754 }
755 PeerKind::Channel => {
756 let mut res = match self
757 .invoke(&tl::functions::channels::GetChannels {
758 id: vec![peer.into()],
759 })
760 .await?
761 {
762 tl::enums::messages::Chats::Chats(chats) => chats.chats,
763 tl::enums::messages::Chats::Slice(chat_slice) => chat_slice.chats,
764 };
765 if res.len() != 1 {
766 panic!("fetching only one chat should exactly return one chat");
767 }
768 Peer::from_raw(self, res.pop().unwrap())
769 }
770 })
771 }
772
773 /// Get permissions of participant `user` from chat `chat`.
774 ///
775 /// # Example
776 ///
777 /// ```
778 /// # async fn f(chat: grammers_session::types::PeerRef, user: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
779 /// let permissions = client.get_permissions(chat, user).await?;
780 /// println!("The user {} an admin", if permissions.is_admin() { "is" } else { "is not" });
781 /// # Ok(())
782 /// # }
783 /// ```
784 pub async fn get_permissions<C: Into<PeerRef>, U: Into<PeerRef>>(
785 &self,
786 chat: C,
787 user: U,
788 ) -> Result<ParticipantPermissions, InvocationError> {
789 let chat = chat.into();
790 let user = user.into();
791
792 // Get by chat
793 if chat.id.kind() == PeerKind::Chat {
794 // Get user id
795 let user = user.into();
796 let user_id = match user {
797 tl::enums::InputUser::User(user) => user.user_id,
798 tl::enums::InputUser::FromMessage(user) => user.user_id,
799 tl::enums::InputUser::UserSelf => {
800 let me = self.get_me().await?;
801 me.id().bare_id()
802 }
803 tl::enums::InputUser::Empty => return Err(InvocationError::Dropped),
804 };
805
806 // Get chat and find user
807 let chat = self
808 .invoke(&tl::functions::messages::GetFullChat {
809 chat_id: chat.into(),
810 })
811 .await?;
812 let tl::enums::messages::ChatFull::Full(chat) = chat;
813 if let tl::enums::ChatFull::Full(chat) = chat.full_chat {
814 if let tl::enums::ChatParticipants::Participants(participants) = chat.participants {
815 for participant in participants.participants {
816 if participant.user_id() == user_id {
817 return Ok(ParticipantPermissions(ParticipantPermissionsInner::Chat(
818 participant,
819 )));
820 }
821 }
822 }
823 }
824 return Err(InvocationError::Rpc(RpcError {
825 code: 400,
826 name: "USER_NOT_PARTICIPANT".to_string(),
827 value: None,
828 caused_by: None,
829 }));
830 }
831
832 // Get by channel
833 let participant = self
834 .invoke(&tl::functions::channels::GetParticipant {
835 channel: chat.into(),
836 participant: user.into(),
837 })
838 .await?;
839 let tl::enums::channels::ChannelParticipant::Participant(participant) = participant;
840 Ok(ParticipantPermissions(
841 ParticipantPermissionsInner::Channel(participant.participant),
842 ))
843 }
844
845 #[cfg(feature = "parse_invite_link")]
846 pub fn parse_invite_link(invite_link: &str) -> Option<String> {
847 let url_parse_result = url::Url::parse(invite_link);
848 if url_parse_result.is_err() {
849 return None;
850 }
851
852 let url_parse = url_parse_result.unwrap();
853 let scheme = url_parse.scheme();
854 let path = url_parse.path();
855 if url_parse.host_str().is_none() || !vec!["https", "http"].contains(&scheme) {
856 return None;
857 }
858 let host = url_parse.host_str().unwrap();
859 let hosts = [
860 "t.me",
861 "telegram.me",
862 "telegram.dog",
863 "tg.dev",
864 "telegram.me",
865 "telesco.pe",
866 ];
867
868 if !hosts.contains(&host) {
869 return None;
870 }
871 let paths = path.split("/").skip(1).collect::<Vec<&str>>();
872
873 if paths.len() == 1 {
874 if paths[0].starts_with("+") {
875 return Some(paths[0].replace("+", ""));
876 }
877 return None;
878 }
879
880 if paths.len() > 1 {
881 if paths[0].starts_with("joinchat") {
882 return Some(paths[1].to_string());
883 }
884 if paths[0].starts_with("+") {
885 return Some(paths[0].replace("+", ""));
886 }
887 return None;
888 }
889
890 None
891 }
892
893 /// Accept an invite link to join the corresponding private chat.
894 ///
895 /// If the chat is public (has a public username), [`Client::join_chat`](Client::join_chat) should be used instead.
896 #[cfg(feature = "parse_invite_link")]
897 pub async fn accept_invite_link(
898 &self,
899 invite_link: &str,
900 ) -> Result<Option<Peer>, InvocationError> {
901 match Self::parse_invite_link(invite_link) {
902 Some(hash) => Ok(updates_to_chat(
903 self,
904 None,
905 self.invoke(&tl::functions::messages::ImportChatInvite { hash })
906 .await?,
907 )),
908 None => Err(InvocationError::Rpc(RpcError {
909 code: 400,
910 name: "INVITE_HASH_INVALID".to_string(),
911 value: None,
912 caused_by: None,
913 })),
914 }
915 }
916
917 #[cfg_attr(
918 not(feature = "parse_invite_link"),
919 allow(rustdoc::broken_intra_doc_links)
920 )]
921 /// Join a public group or channel.
922 ///
923 /// A channel is public if it has a username.
924 /// To join private chats, [`Client::accept_invite_link`](Client::accept_invite_link) should be used instead.
925 pub async fn join_chat<C: Into<PeerRef>>(
926 &self,
927 chat: C,
928 ) -> Result<Option<Peer>, InvocationError> {
929 let chat: PeerRef = chat.into();
930 let channel = chat.into();
931 Ok(updates_to_chat(
932 self,
933 Some(chat.id.bare_id()),
934 self.invoke(&tl::functions::channels::JoinChannel { channel })
935 .await?,
936 ))
937 }
938
939 /// Send a message action (such as typing, uploading photo, or viewing an emoji interaction)
940 ///
941 /// # Examples
942 ///
943 /// **Do a one-shot pulse and let it fade away**
944 /// ```
945 /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
946 /// use grammers_tl_types::enums::SendMessageAction;
947 ///
948 /// client
949 /// .action(peer)
950 /// .oneshot(SendMessageAction::SendMessageTypingAction)
951 /// .await?;
952 /// # Ok(())
953 /// # }
954 /// ```
955 ///
956 /// **Repeat request until the future is done**
957 /// ```
958 /// # use std::time::Duration;
959 ///
960 /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
961 /// use grammers_tl_types as tl;
962 ///
963 /// let heavy_task = async {
964 /// tokio::time::sleep(Duration::from_secs(10)).await;
965 ///
966 /// 42
967 /// };
968 ///
969 /// tokio::pin!(heavy_task);
970 ///
971 /// let (task_result, _) = client
972 /// .action(peer)
973 /// .repeat(
974 /// // most clients doesn't actually show progress of an action
975 /// || tl::types::SendMessageUploadDocumentAction { progress: 0 },
976 /// heavy_task
977 /// )
978 /// .await;
979 ///
980 /// // Note: repeat function does not cancel actions automatically, they will just fade away
981 ///
982 /// assert_eq!(task_result, 42);
983 /// # Ok(())
984 /// # }
985 /// ```
986 ///
987 /// **Cancel any actions**
988 /// ```
989 /// # async fn f(peer: grammers_session::types::PeerRef, client: grammers_client::Client) -> Result<(), Box<dyn std::error::Error>> {
990 /// client.action(peer).cancel().await?;
991 /// # Ok(())
992 /// # }
993 /// ```
994 pub fn action<C: Into<PeerRef>>(&self, peer: C) -> ActionSender {
995 ActionSender::new(self, peer)
996 }
997
998 pub(crate) async fn build_peer_map(
999 &self,
1000 users: Vec<tl::enums::User>,
1001 chats: Vec<tl::enums::Chat>,
1002 ) -> PeerMap {
1003 let map = users
1004 .into_iter()
1005 .map(|user| Peer::from_user(self, user))
1006 .chain(chats.into_iter().map(|chat| Peer::from_raw(self, chat)))
1007 .map(|peer| (peer.id(), peer))
1008 .collect::<HashMap<_, _>>();
1009
1010 if self.0.configuration.auto_cache_peers {
1011 for peer in map.values() {
1012 if peer.auth().is_some() {
1013 self.0.session.cache_peer(&peer.into()).await;
1014 }
1015 }
1016 }
1017
1018 PeerMap {
1019 map: Arc::new(map),
1020 session: Arc::clone(&self.0.session),
1021 }
1022 }
1023
1024 pub(crate) fn empty_peer_map(&self) -> PeerMap {
1025 PeerMap {
1026 map: Arc::new(HashMap::new()),
1027 session: Arc::clone(&self.0.session),
1028 }
1029 }
1030}
1031
1032#[derive(Debug, Clone)]
1033enum ParticipantPermissionsInner {
1034 Channel(tl::enums::ChannelParticipant),
1035 Chat(tl::enums::ChatParticipant),
1036}
1037
1038/// Permissions returned by [`Client::get_permissions`].
1039#[derive(Debug, Clone)]
1040pub struct ParticipantPermissions(ParticipantPermissionsInner);
1041
1042impl ParticipantPermissions {
1043 /// Whether the user is the creator of the chat or not.
1044 pub fn is_creator(&self) -> bool {
1045 matches!(
1046 self.0,
1047 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Creator(_))
1048 | ParticipantPermissionsInner::Chat(tl::enums::ChatParticipant::Creator(_))
1049 )
1050 }
1051
1052 /// Whether the user is an administrator of the chat or not. The creator also counts as begin an administrator, since they have all permissions.
1053 pub fn is_admin(&self) -> bool {
1054 self.is_creator()
1055 || matches!(
1056 self.0,
1057 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Admin(_))
1058 | ParticipantPermissionsInner::Chat(tl::enums::ChatParticipant::Admin(_))
1059 )
1060 }
1061
1062 /// Whether the user is banned in the chat.
1063 pub fn is_banned(&self) -> bool {
1064 matches!(
1065 self.0,
1066 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Banned(_))
1067 )
1068 }
1069
1070 /// Whether the user left the chat.
1071 pub fn has_left(&self) -> bool {
1072 matches!(
1073 self.0,
1074 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Left(_))
1075 )
1076 }
1077
1078 /// Whether the user is a normal user of the chat (not administrator, but not banned either, and has no restrictions applied).
1079 pub fn has_default_permissions(&self) -> bool {
1080 matches!(
1081 self.0,
1082 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Participant(_))
1083 | ParticipantPermissionsInner::Channel(
1084 tl::enums::ChannelParticipant::ParticipantSelf(_)
1085 )
1086 | ParticipantPermissionsInner::Chat(tl::enums::ChatParticipant::Participant(_))
1087 )
1088 }
1089
1090 /// Whether the administrator can add new administrators with the same or less permissions than them.
1091 pub fn can_add_admins(&self) -> bool {
1092 if !self.is_admin() {
1093 return false;
1094 }
1095 match &self.0 {
1096 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Admin(
1097 participant,
1098 )) => {
1099 let tl::enums::ChatAdminRights::Rights(rights) = &participant.admin_rights;
1100 rights.add_admins
1101 }
1102 ParticipantPermissionsInner::Channel(tl::enums::ChannelParticipant::Creator(_)) => true,
1103 ParticipantPermissionsInner::Chat(_) => self.is_creator(),
1104 _ => false,
1105 }
1106 }
1107}