Skip to main content

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}