1use layer_tl_types as tl;
6use layer_tl_types::{Cursor, Deserializable};
7
8use crate::{Client, InvocationError};
9
10#[derive(Debug, Clone)]
14pub struct Participant {
15 pub user: tl::types::User,
17 pub status: ParticipantStatus,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum ParticipantStatus {
24 Member,
26 Creator,
28 Admin,
30 Restricted,
32 Left,
34 Banned,
36}
37
38impl Client {
41 pub async fn get_participants(
48 &self,
49 peer: tl::enums::Peer,
50 limit: i32,
51 ) -> Result<Vec<Participant>, InvocationError> {
52 match &peer {
53 tl::enums::Peer::Channel(c) => {
54 let cache = self.inner.peer_cache.lock().await;
55 let access_hash = cache.channels.get(&c.channel_id).copied().unwrap_or(0);
56 drop(cache);
57 self.get_channel_participants(c.channel_id, access_hash, limit).await
58 }
59 tl::enums::Peer::Chat(c) => {
60 self.get_chat_participants(c.chat_id).await
61 }
62 _ => Err(InvocationError::Deserialize("get_participants: peer must be a chat or channel".into())),
63 }
64 }
65
66 async fn get_channel_participants(
67 &self,
68 channel_id: i64,
69 access_hash: i64,
70 limit: i32,
71 ) -> Result<Vec<Participant>, InvocationError> {
72 let limit = if limit <= 0 { 200 } else { limit };
73 let req = tl::functions::channels::GetParticipants {
74 channel: tl::enums::InputChannel::InputChannel(tl::types::InputChannel {
75 channel_id, access_hash,
76 }),
77 filter: tl::enums::ChannelParticipantsFilter::ChannelParticipantsRecent,
78 offset: 0,
79 limit,
80 hash: 0,
81 };
82 let body = self.rpc_call_raw_pub(&req).await?;
83 let mut cur = Cursor::from_slice(&body);
84 let raw = match tl::enums::channels::ChannelParticipants::deserialize(&mut cur)? {
85 tl::enums::channels::ChannelParticipants::ChannelParticipants(p) => p,
86 tl::enums::channels::ChannelParticipants::NotModified => return Ok(vec![]),
87 };
88
89 let user_map: std::collections::HashMap<i64, tl::types::User> = raw.users.into_iter()
91 .filter_map(|u| match u { tl::enums::User::User(u) => Some((u.id, u)), _ => None })
92 .collect();
93
94 {
96 let mut cache = self.inner.peer_cache.lock().await;
97 for u in user_map.values() {
98 if let Some(h) = u.access_hash { cache.users.insert(u.id, h); }
99 }
100 }
101
102 let mut result = Vec::new();
103 for p in raw.participants {
104 let (user_id, status) = match &p {
105 tl::enums::ChannelParticipant::ChannelParticipant(x) => (x.user_id, ParticipantStatus::Member),
106 tl::enums::ChannelParticipant::ParticipantSelf(x) => (x.user_id, ParticipantStatus::Member),
107 tl::enums::ChannelParticipant::Creator(x) => (x.user_id, ParticipantStatus::Creator),
108 tl::enums::ChannelParticipant::Admin(x) => (x.user_id, ParticipantStatus::Admin),
109 tl::enums::ChannelParticipant::Banned(x) => (x.peer.user_id_or(0), ParticipantStatus::Banned),
110 tl::enums::ChannelParticipant::Left(x) => (x.peer.user_id_or(0), ParticipantStatus::Left),
111 };
112 if let Some(user) = user_map.get(&user_id).cloned() {
113 result.push(Participant { user, status });
114 }
115 }
116 Ok(result)
117 }
118
119 async fn get_chat_participants(&self, chat_id: i64) -> Result<Vec<Participant>, InvocationError> {
120 let req = tl::functions::messages::GetFullChat { chat_id };
121 let body = self.rpc_call_raw_pub(&req).await?;
122 let mut cur = Cursor::from_slice(&body);
123 let full: tl::types::messages::ChatFull = match tl::enums::messages::ChatFull::deserialize(&mut cur)? {
124 tl::enums::messages::ChatFull::ChatFull(c) => c,
125 };
126
127 let user_map: std::collections::HashMap<i64, tl::types::User> = full.users.into_iter()
128 .filter_map(|u| match u { tl::enums::User::User(u) => Some((u.id, u)), _ => None })
129 .collect();
130
131 {
132 let mut cache = self.inner.peer_cache.lock().await;
133 for u in user_map.values() {
134 if let Some(h) = u.access_hash { cache.users.insert(u.id, h); }
135 }
136 }
137
138 let participants = match &full.full_chat {
139 tl::enums::ChatFull::ChatFull(cf) => match &cf.participants {
140 tl::enums::ChatParticipants::ChatParticipants(p) => p.participants.clone(),
141 tl::enums::ChatParticipants::Forbidden(_) => vec![],
142 },
143 tl::enums::ChatFull::ChannelFull(_) => {
144 return Err(InvocationError::Deserialize(
145 "get_chat_participants: peer is a channel, use get_participants with a Channel peer instead".into()
146 ));
147 }
148 };
149
150 let mut result = Vec::new();
151 for p in participants {
152 let (user_id, status) = match p {
153 tl::enums::ChatParticipant::ChatParticipant(x) => (x.user_id, ParticipantStatus::Member),
154 tl::enums::ChatParticipant::Creator(x) => (x.user_id, ParticipantStatus::Creator),
155 tl::enums::ChatParticipant::Admin(x) => (x.user_id, ParticipantStatus::Admin),
156 };
157 if let Some(user) = user_map.get(&user_id).cloned() {
158 result.push(Participant { user, status });
159 }
160 }
161 Ok(result)
162 }
163
164 pub async fn kick_participant(
166 &self,
167 chat_id: i64,
168 user_id: i64,
169 ) -> Result<(), InvocationError> {
170 let cache = self.inner.peer_cache.lock().await;
171 let access_hash = cache.users.get(&user_id).copied().unwrap_or(0);
172 drop(cache);
173 let req = tl::functions::messages::DeleteChatUser {
174 revoke_history: false,
175 chat_id,
176 user_id: tl::enums::InputUser::InputUser(tl::types::InputUser { user_id, access_hash }),
177 };
178 self.rpc_call_raw_pub(&req).await?;
179 Ok(())
180 }
181
182 pub async fn ban_participant(
186 &self,
187 channel: tl::enums::Peer,
188 user_id: i64,
189 until_date: i32,
190 ) -> Result<(), InvocationError> {
191 let (channel_id, ch_hash) = match &channel {
192 tl::enums::Peer::Channel(c) => {
193 let h = self.inner.peer_cache.lock().await.channels.get(&c.channel_id).copied().unwrap_or(0);
194 (c.channel_id, h)
195 }
196 _ => return Err(InvocationError::Deserialize("ban_participant: peer must be a channel".into())),
197 };
198 let user_hash = self.inner.peer_cache.lock().await.users.get(&user_id).copied().unwrap_or(0);
199
200 let req = tl::functions::channels::EditBanned {
201 channel: tl::enums::InputChannel::InputChannel(tl::types::InputChannel {
202 channel_id: channel_id, access_hash: ch_hash,
203 }),
204 participant: tl::enums::InputPeer::User(tl::types::InputPeerUser {
205 user_id, access_hash: user_hash,
206 }),
207 banned_rights: tl::enums::ChatBannedRights::ChatBannedRights(tl::types::ChatBannedRights {
208 view_messages: true,
209 send_messages: true,
210 send_media: true,
211 send_stickers: true,
212 send_gifs: true,
213 send_games: true,
214 send_inline: true,
215 embed_links: true,
216 send_polls: true,
217 change_info: true,
218 invite_users: true,
219 pin_messages: true,
220 manage_topics: false,
221 send_photos: false,
222 send_videos: false,
223 send_roundvideos: false,
224 send_audios: false,
225 send_voices: false,
226 send_docs: false,
227 send_plain: false,
228 until_date,
229 }),
230 };
231 self.rpc_call_raw_pub(&req).await?;
232 Ok(())
233 }
234
235 pub async fn promote_participant(
239 &self,
240 channel: tl::enums::Peer,
241 user_id: i64,
242 promote: bool,
243 ) -> Result<(), InvocationError> {
244 let (channel_id, ch_hash) = match &channel {
245 tl::enums::Peer::Channel(c) => {
246 let h = self.inner.peer_cache.lock().await.channels.get(&c.channel_id).copied().unwrap_or(0);
247 (c.channel_id, h)
248 }
249 _ => return Err(InvocationError::Deserialize("promote_participant: peer must be a channel".into())),
250 };
251 let user_hash = self.inner.peer_cache.lock().await.users.get(&user_id).copied().unwrap_or(0);
252
253 let rights = if promote {
254 tl::types::ChatAdminRights {
255 change_info: true,
256 post_messages: true,
257 edit_messages: true,
258 delete_messages: true,
259 ban_users: true,
260 invite_users: true,
261 pin_messages: true,
262 add_admins: false,
263 anonymous: false,
264 manage_call: true,
265 other: false,
266 manage_topics: false,
267 post_stories: false,
268 edit_stories: false,
269 delete_stories: false,
270 manage_direct_messages: false,
271 }
272 } else {
273 tl::types::ChatAdminRights {
274 change_info: false,
275 post_messages: false,
276 edit_messages: false,
277 delete_messages: false,
278 ban_users: false,
279 invite_users: false,
280 pin_messages: false,
281 add_admins: false,
282 anonymous: false,
283 manage_call: false,
284 other: false,
285 manage_topics: false,
286 post_stories: false,
287 edit_stories: false,
288 delete_stories: false,
289 manage_direct_messages: false,
290 }
291 };
292
293 let req = tl::functions::channels::EditAdmin {
294 channel: tl::enums::InputChannel::InputChannel(tl::types::InputChannel {
295 channel_id, access_hash: ch_hash,
296 }),
297 user_id: tl::enums::InputUser::InputUser(tl::types::InputUser { user_id, access_hash: user_hash }),
298 admin_rights: tl::enums::ChatAdminRights::ChatAdminRights(rights),
299 rank: String::new(),
300 };
301 self.rpc_call_raw_pub(&req).await?;
302 Ok(())
303 }
304
305 pub async fn get_profile_photos(
309 &self,
310 peer: tl::enums::Peer,
311 limit: i32,
312 ) -> Result<Vec<tl::enums::Photo>, InvocationError> {
313 let input_peer = {
314 let cache = self.inner.peer_cache.lock().await;
315 cache.peer_to_input(&peer)
316 };
317
318 let req = tl::functions::photos::GetUserPhotos {
319 user_id: match &input_peer {
320 tl::enums::InputPeer::User(u) => tl::enums::InputUser::InputUser(
321 tl::types::InputUser { user_id: u.user_id, access_hash: u.access_hash }
322 ),
323 tl::enums::InputPeer::PeerSelf => tl::enums::InputUser::UserSelf,
324 _ => return Err(InvocationError::Deserialize("get_profile_photos: peer must be a user".into())),
325 },
326 offset: 0,
327 max_id: 0,
328 limit,
329 };
330 let body = self.rpc_call_raw_pub(&req).await?;
331 let mut cur = Cursor::from_slice(&body);
332 match tl::enums::photos::Photos::deserialize(&mut cur)? {
333 tl::enums::photos::Photos::Photos(p) => Ok(p.photos),
334 tl::enums::photos::Photos::Slice(p) => Ok(p.photos),
335 }
336 }
337
338 pub async fn search_peer(
342 &self,
343 query: &str,
344 ) -> Result<Vec<tl::enums::Peer>, InvocationError> {
345 let req = tl::functions::contacts::Search {
346 q: query.to_string(),
347 limit: 20,
348 };
349 let body = self.rpc_call_raw_pub(&req).await?;
350 let mut cur = Cursor::from_slice(&body);
351 let found = match tl::enums::contacts::Found::deserialize(&mut cur)? {
352 tl::enums::contacts::Found::Found(f) => f,
353 };
354
355 self.cache_users_slice_pub(&found.users).await;
356 self.cache_chats_slice_pub(&found.chats).await;
357
358 let mut peers = Vec::new();
359 for r in found.my_results.iter().chain(found.results.iter()) {
360 peers.push(r.clone());
361 }
362 Ok(peers)
363 }
364
365 pub async fn send_reaction(
369 &self,
370 peer: tl::enums::Peer,
371 message_id: i32,
372 reaction: &str,
373 ) -> Result<(), InvocationError> {
374 let input_peer = {
375 let cache = self.inner.peer_cache.lock().await;
376 cache.peer_to_input(&peer)
377 };
378
379 let reactions = if reaction.is_empty() {
380 vec![]
381 } else {
382 vec![tl::enums::Reaction::Emoji(tl::types::ReactionEmoji {
383 emoticon: reaction.to_string(),
384 })]
385 };
386
387 let req = tl::functions::messages::SendReaction {
388 big: false,
389 add_to_recent: false,
390 peer: input_peer,
391 msg_id: message_id,
392 reaction: Some(reactions),
393 };
394 self.rpc_call_raw_pub(&req).await?;
395 Ok(())
396 }
397}
398
399trait PeerUserIdExt {
402 fn user_id_or(&self, default: i64) -> i64;
403}
404
405impl PeerUserIdExt for tl::enums::Peer {
406 fn user_id_or(&self, default: i64) -> i64 {
407 match self {
408 tl::enums::Peer::User(u) => u.user_id,
409 _ => default,
410 }
411 }
412}