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