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 edit_rank: false,
229 until_date,
230 }),
231 };
232 self.rpc_call_raw_pub(&req).await?;
233 Ok(())
234 }
235
236 pub async fn promote_participant(
240 &self,
241 channel: tl::enums::Peer,
242 user_id: i64,
243 promote: bool,
244 ) -> Result<(), InvocationError> {
245 let (channel_id, ch_hash) = match &channel {
246 tl::enums::Peer::Channel(c) => {
247 let h = self.inner.peer_cache.lock().await.channels.get(&c.channel_id).copied().unwrap_or(0);
248 (c.channel_id, h)
249 }
250 _ => return Err(InvocationError::Deserialize("promote_participant: peer must be a channel".into())),
251 };
252 let user_hash = self.inner.peer_cache.lock().await.users.get(&user_id).copied().unwrap_or(0);
253
254 let rights = if promote {
255 tl::types::ChatAdminRights {
256 change_info: true,
257 post_messages: true,
258 edit_messages: true,
259 delete_messages: true,
260 ban_users: true,
261 invite_users: true,
262 pin_messages: true,
263 add_admins: false,
264 anonymous: false,
265 manage_call: true,
266 other: false,
267 manage_topics: false,
268 post_stories: false,
269 edit_stories: false,
270 delete_stories: false,
271 manage_direct_messages: false,
272 manage_ranks: false,
273 }
274 } else {
275 tl::types::ChatAdminRights {
276 change_info: false,
277 post_messages: false,
278 edit_messages: false,
279 delete_messages: false,
280 ban_users: false,
281 invite_users: false,
282 pin_messages: false,
283 add_admins: false,
284 anonymous: false,
285 manage_call: false,
286 other: false,
287 manage_topics: false,
288 post_stories: false,
289 edit_stories: false,
290 delete_stories: false,
291 manage_direct_messages: false,
292 manage_ranks: false,
293 }
294 };
295
296 let req = tl::functions::channels::EditAdmin {
297 channel: tl::enums::InputChannel::InputChannel(tl::types::InputChannel {
298 channel_id, access_hash: ch_hash,
299 }),
300 user_id: tl::enums::InputUser::InputUser(tl::types::InputUser { user_id, access_hash: user_hash }),
301 admin_rights: tl::enums::ChatAdminRights::ChatAdminRights(rights),
302 rank: None,
303 };
304 self.rpc_call_raw_pub(&req).await?;
305 Ok(())
306 }
307
308 pub async fn get_profile_photos(
312 &self,
313 peer: tl::enums::Peer,
314 limit: i32,
315 ) -> Result<Vec<tl::enums::Photo>, InvocationError> {
316 let input_peer = {
317 let cache = self.inner.peer_cache.lock().await;
318 cache.peer_to_input(&peer)
319 };
320
321 let req = tl::functions::photos::GetUserPhotos {
322 user_id: match &input_peer {
323 tl::enums::InputPeer::User(u) => tl::enums::InputUser::InputUser(
324 tl::types::InputUser { user_id: u.user_id, access_hash: u.access_hash }
325 ),
326 tl::enums::InputPeer::PeerSelf => tl::enums::InputUser::UserSelf,
327 _ => return Err(InvocationError::Deserialize("get_profile_photos: peer must be a user".into())),
328 },
329 offset: 0,
330 max_id: 0,
331 limit,
332 };
333 let body = self.rpc_call_raw_pub(&req).await?;
334 let mut cur = Cursor::from_slice(&body);
335 match tl::enums::photos::Photos::deserialize(&mut cur)? {
336 tl::enums::photos::Photos::Photos(p) => Ok(p.photos),
337 tl::enums::photos::Photos::Slice(p) => Ok(p.photos),
338 }
339 }
340
341 pub async fn search_peer(
345 &self,
346 query: &str,
347 ) -> Result<Vec<tl::enums::Peer>, InvocationError> {
348 let req = tl::functions::contacts::Search {
349 q: query.to_string(),
350 limit: 20,
351 };
352 let body = self.rpc_call_raw_pub(&req).await?;
353 let mut cur = Cursor::from_slice(&body);
354 let found = match tl::enums::contacts::Found::deserialize(&mut cur)? {
355 tl::enums::contacts::Found::Found(f) => f,
356 };
357
358 self.cache_users_slice_pub(&found.users).await;
359 self.cache_chats_slice_pub(&found.chats).await;
360
361 let mut peers = Vec::new();
362 for r in found.my_results.iter().chain(found.results.iter()) {
363 peers.push(r.clone());
364 }
365 Ok(peers)
366 }
367
368 pub async fn send_reaction(
372 &self,
373 peer: tl::enums::Peer,
374 message_id: i32,
375 reaction: &str,
376 ) -> Result<(), InvocationError> {
377 let input_peer = {
378 let cache = self.inner.peer_cache.lock().await;
379 cache.peer_to_input(&peer)
380 };
381
382 let reactions = if reaction.is_empty() {
383 vec![]
384 } else {
385 vec![tl::enums::Reaction::Emoji(tl::types::ReactionEmoji {
386 emoticon: reaction.to_string(),
387 })]
388 };
389
390 let req = tl::functions::messages::SendReaction {
391 big: false,
392 add_to_recent: false,
393 peer: input_peer,
394 msg_id: message_id,
395 reaction: Some(reactions),
396 };
397 self.rpc_call_raw_pub(&req).await?;
398 Ok(())
399 }
400}
401
402trait PeerUserIdExt {
405 fn user_id_or(&self, default: i64) -> i64;
406}
407
408impl PeerUserIdExt for tl::enums::Peer {
409 fn user_id_or(&self, default: i64) -> i64 {
410 match self {
411 tl::enums::Peer::User(u) => u.user_id,
412 _ => default,
413 }
414 }
415}