1use futures::FutureExt as _;
2use simploxide_api_types::{
3 AChatItem, CIDeleteMode, ChatDeleteMode, ChatItem, Contact, GroupInfo, GroupMember,
4 GroupMemberRole, GroupProfile, JsonObject, MsgContent, MsgReaction, NewUser, UpdatedMessage,
5 UserInfo,
6 client_api::{
7 AllowUndocumentedResponses as _, ClientApi, ClientApiError as _, UndocumentedResponse,
8 },
9 commands::{
10 ApiBlockMembersForAll, ApiChatItemReaction, ApiListGroups, ApiRemoveMembers,
11 ApiSetContactCustomData, ApiSetGroupCustomData, ApiUpdateChatItem, Connect, ReceiveFile,
12 },
13 responses::{
14 AcceptingContactRequestResponse, ActiveUserResponse, ApiAddGroupRelaysResponse,
15 ApiDeleteChatResponse, ApiUpdateChatItemResponse, CancelFileResponse,
16 ChatItemReactionResponse, ChatItemsDeletedResponse, CmdOkResponse, ConnectResponse,
17 ContactRequestRejectedResponse, GroupLinkCreatedResponse, GroupLinkDeletedResponse,
18 GroupLinkResponse, GroupRelaysResponse, GroupUpdatedResponse, LeftMemberUserResponse,
19 MemberAcceptedResponse, MembersBlockedForAllUserResponse, MembersRoleUserResponse,
20 ReceiveFileResponse, SentGroupInvitationResponse, UserAcceptedGroupSentResponse,
21 UserDeletedMembersResponse,
22 },
23};
24
25use std::{pin::Pin, sync::Arc};
26
27use crate::{
28 id::{
29 ChatId, ContactId, ContactRequestId, FileId, GroupId, MemberId, MessageId, RelayId, UserId,
30 },
31 messages::{MessageBuilder, MessageLike, MulticastBuilder},
32};
33
34pub type InitiateConnectionResponse<C> =
35 Result<UndocumentedResponse<ConnectResponse>, <C as ClientApi>::Error>;
36
37pub type AcceptContactResponse<C> =
38 Result<Arc<AcceptingContactRequestResponse>, <C as ClientApi>::Error>;
39pub type RejectContactResponse<C> =
40 Result<Arc<ContactRequestRejectedResponse>, <C as ClientApi>::Error>;
41
42pub type RejectFileResponse<C> = Result<CancelFileResponse, <C as ClientApi>::Error>;
43
44pub type ContactsResponse<C> = Result<Vec<Contact>, <C as ClientApi>::Error>;
45pub type GroupsResponse<C> = Result<Vec<GroupInfo>, <C as ClientApi>::Error>;
46
47pub type DeleteChatResponse<C> = Result<ApiDeleteChatResponse, <C as ClientApi>::Error>;
48pub type DeleteMessageResponse<C> = Result<Arc<ChatItemsDeletedResponse>, <C as ClientApi>::Error>;
49
50pub type UpdateMessageReactionsResponse<C> =
51 Vec<Result<Arc<ChatItemReactionResponse>, <C as ClientApi>::Error>>;
52pub type UpdateMessageResponse<C> = Result<ApiUpdateChatItemResponse, <C as ClientApi>::Error>;
53
54pub type NewUserResponse<C> = Result<Arc<ActiveUserResponse>, <C as ClientApi>::Error>;
55pub type UsersResponse<C> = Result<Vec<UserInfo>, <C as ClientApi>::Error>;
56
57pub type AddMemberResponse<C> = Result<Arc<SentGroupInvitationResponse>, <C as ClientApi>::Error>;
58pub type JoinGroupResponse<C> = Result<Arc<UserAcceptedGroupSentResponse>, <C as ClientApi>::Error>;
59pub type AcceptMemberResponse<C> = Result<Arc<MemberAcceptedResponse>, <C as ClientApi>::Error>;
60pub type SetMembersRoleResponse<C> = Result<Arc<MembersRoleUserResponse>, <C as ClientApi>::Error>;
61pub type BlockMembersResponse<C> =
62 Result<Arc<MembersBlockedForAllUserResponse>, <C as ClientApi>::Error>;
63pub type RemoveMembersResponse<C> =
64 Result<Arc<UserDeletedMembersResponse>, <C as ClientApi>::Error>;
65pub type LeaveGroupResponse<C> = Result<Arc<LeftMemberUserResponse>, <C as ClientApi>::Error>;
66pub type ListMembersResponse<C> = Result<Vec<GroupMember>, <C as ClientApi>::Error>;
67pub type UpdateGroupProfileResponse<C> = Result<Arc<GroupUpdatedResponse>, <C as ClientApi>::Error>;
68pub type SetContactCustomDataResponse<C> = Result<Arc<CmdOkResponse>, <C as ClientApi>::Error>;
69pub type SetGroupCustomDataResponse<C> = Result<Arc<CmdOkResponse>, <C as ClientApi>::Error>;
70pub type CreateGroupLinkResult<C> = Result<Arc<GroupLinkCreatedResponse>, <C as ClientApi>::Error>;
71pub type GroupLinkResult<C> = Result<Arc<GroupLinkResponse>, <C as ClientApi>::Error>;
72pub type DeleteGroupLinkResult<C> = Result<Arc<GroupLinkDeletedResponse>, <C as ClientApi>::Error>;
73pub type GetGroupRelaysResponse<C> = Result<Arc<GroupRelaysResponse>, <C as ClientApi>::Error>;
74pub type AddGroupRelaysResponse<C> = Result<ApiAddGroupRelaysResponse, <C as ClientApi>::Error>;
75
76pub trait ClientApiExt: ClientApi {
77 fn users(&self) -> impl Future<Output = UsersResponse<Self>>;
78
79 fn contacts<UID: Into<UserId>>(
80 &self,
81 user_id: UID,
82 ) -> impl Future<Output = ContactsResponse<Self>>;
83
84 fn groups<UID: Into<UserId>>(&self, user_id: UID)
85 -> impl Future<Output = GroupsResponse<Self>>;
86
87 fn accept_contact<CRID: Into<ContactRequestId>>(
88 &self,
89 contact_request_id: CRID,
90 ) -> impl Future<Output = AcceptContactResponse<Self>>;
91
92 fn reject_contact<CRID: Into<ContactRequestId>>(
93 &self,
94 contact_request_id: CRID,
95 ) -> impl Future<Output = RejectContactResponse<Self>>;
96
97 fn new_user(&self, user: NewUser) -> impl Future<Output = NewUserResponse<Self>>;
101
102 fn send_message<CID: Into<ChatId>, M: MessageLike>(
105 &self,
106 chat_id: CID,
107 msg: M,
108 ) -> MessageBuilder<'_, Self, M::Kind>;
109
110 fn multicast_message<I, M>(
112 &self,
113 chat_ids: I,
114 msg: M,
115 ) -> MulticastBuilder<'_, I, Self, M::Kind>
116 where
117 I: IntoIterator<Item = ChatId>,
118 M: MessageLike;
119
120 fn update_message<CID: Into<ChatId>, MID: Into<MessageId>>(
121 &self,
122 chat_id: CID,
123 message_id: MID,
124 new_content: MsgContent,
125 ) -> impl Future<Output = UpdateMessageResponse<Self>>;
126
127 fn batch_delete_messages<CID: Into<ChatId>, I: IntoIterator<Item = MessageId>>(
128 &self,
129 chat_id: CID,
130 message_ids: I,
131 mode: CIDeleteMode,
132 ) -> impl Future<Output = DeleteMessageResponse<Self>>;
133
134 fn delete_message<CID: Into<ChatId>, MID: Into<MessageId>>(
135 &self,
136 chat_id: CID,
137 message_id: MID,
138 mode: CIDeleteMode,
139 ) -> impl Future<Output = DeleteMessageResponse<Self>> {
140 self.batch_delete_messages(chat_id, std::iter::once(message_id.into()), mode)
141 }
142
143 fn batch_message_reactions<
144 CID: Into<ChatId>,
145 MID: Into<MessageId>,
146 I: IntoIterator<Item = Reaction>,
147 >(
148 &self,
149 chat_id: CID,
150 message_id: MID,
151 reactions: I,
152 ) -> impl Future<Output = UpdateMessageReactionsResponse<Self>>;
153
154 fn update_message_reaction<CID: Into<ChatId>, MID: Into<MessageId>>(
155 &self,
156 chat_id: CID,
157 message_id: MID,
158 reaction: Reaction,
159 ) -> impl Future<Output = UpdateMessageReactionsResponse<Self>> {
160 self.batch_message_reactions(chat_id, message_id, std::iter::once(reaction))
161 }
162
163 fn accept_file<FID: Into<FileId>>(&self, file_id: FID) -> AcceptFileBuilder<'_, Self>;
164
165 fn reject_file<FID: Into<FileId>>(
166 &self,
167 file_id: FID,
168 ) -> impl Future<Output = RejectFileResponse<Self>>;
169
170 fn initiate_connection(
171 &self,
172 link: impl Into<String>,
173 ) -> impl Future<Output = InitiateConnectionResponse<Self>>;
174
175 fn delete_chat<CID: Into<ChatId>>(
176 &self,
177 chat_id: CID,
178 mode: DeleteMode,
179 ) -> impl Future<Output = DeleteChatResponse<Self>>;
180
181 fn add_member<GID: Into<GroupId>, CID: Into<ContactId>>(
182 &self,
183 group_id: GID,
184 contact_id: CID,
185 role: GroupMemberRole,
186 ) -> impl Future<Output = AddMemberResponse<Self>>;
187
188 fn join_group<GID: Into<GroupId>>(
189 &self,
190 group_id: GID,
191 ) -> impl Future<Output = JoinGroupResponse<Self>>;
192
193 fn accept_member<GID: Into<GroupId>, MID: Into<MemberId>>(
194 &self,
195 group_id: GID,
196 member_id: MID,
197 role: GroupMemberRole,
198 ) -> impl Future<Output = AcceptMemberResponse<Self>>;
199
200 fn set_members_role<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
201 &self,
202 group_id: GID,
203 member_ids: I,
204 role: GroupMemberRole,
205 ) -> impl Future<Output = SetMembersRoleResponse<Self>>;
206
207 fn set_member_role<GID: Into<GroupId>, MID: Into<MemberId>>(
208 &self,
209 group_id: GID,
210 member_id: MID,
211 role: GroupMemberRole,
212 ) -> impl Future<Output = SetMembersRoleResponse<Self>> {
213 self.set_members_role(group_id, std::iter::once(member_id.into()), role)
214 }
215
216 fn block_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
217 &self,
218 group_id: GID,
219 member_ids: I,
220 ) -> impl Future<Output = BlockMembersResponse<Self>>;
221
222 fn unblock_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
223 &self,
224 group_id: GID,
225 member_ids: I,
226 ) -> impl Future<Output = BlockMembersResponse<Self>>;
227
228 fn block_member_for_all<GID: Into<GroupId>, MID: Into<MemberId>>(
229 &self,
230 group_id: GID,
231 member_id: MID,
232 ) -> impl Future<Output = BlockMembersResponse<Self>> {
233 self.block_members_for_all(group_id, std::iter::once(member_id.into()))
234 }
235
236 fn unblock_member_for_all<GID: Into<GroupId>, MID: Into<MemberId>>(
237 &self,
238 group_id: GID,
239 member_id: MID,
240 ) -> impl Future<Output = BlockMembersResponse<Self>> {
241 self.unblock_members_for_all(group_id, std::iter::once(member_id.into()))
242 }
243
244 fn remove_members<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
245 &self,
246 group_id: GID,
247 member_ids: I,
248 ) -> impl Future<Output = RemoveMembersResponse<Self>>;
249
250 fn remove_members_with_messages<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
251 &self,
252 group_id: GID,
253 member_ids: I,
254 ) -> impl Future<Output = RemoveMembersResponse<Self>>;
255
256 fn remove_member<GID: Into<GroupId>, MID: Into<MemberId>>(
257 &self,
258 group_id: GID,
259 member_id: MID,
260 ) -> impl Future<Output = RemoveMembersResponse<Self>> {
261 self.remove_members(group_id, std::iter::once(member_id.into()))
262 }
263
264 fn remove_member_with_messages<GID: Into<GroupId>, MID: Into<MemberId>>(
265 &self,
266 group_id: GID,
267 member_id: MID,
268 ) -> impl Future<Output = RemoveMembersResponse<Self>> {
269 self.remove_members_with_messages(group_id, std::iter::once(member_id.into()))
270 }
271
272 fn leave_group<GID: Into<GroupId>>(
273 &self,
274 group_id: GID,
275 ) -> impl Future<Output = LeaveGroupResponse<Self>>;
276
277 fn list_members<GID: Into<GroupId>>(
278 &self,
279 group_id: GID,
280 ) -> impl Future<Output = ListMembersResponse<Self>>;
281
282 fn moderate_messages<GID: Into<GroupId>, I: IntoIterator<Item = MessageId>>(
283 &self,
284 group_id: GID,
285 message_ids: I,
286 ) -> impl Future<Output = DeleteMessageResponse<Self>>;
287
288 fn moderate_message<GID: Into<GroupId>, MID: Into<MessageId>>(
289 &self,
290 group_id: GID,
291 message_id: MID,
292 ) -> impl Future<Output = DeleteMessageResponse<Self>> {
293 self.moderate_messages(group_id, std::iter::once(message_id.into()))
294 }
295
296 fn update_group_profile<GID: Into<GroupId>>(
297 &self,
298 group_id: GID,
299 profile: GroupProfile,
300 ) -> impl Future<Output = UpdateGroupProfileResponse<Self>>;
301
302 fn set_group_custom_data<GID: Into<GroupId>>(
303 &self,
304 group_id: GID,
305 data: Option<JsonObject>,
306 ) -> impl Future<Output = SetGroupCustomDataResponse<Self>>;
307
308 fn set_contact_custom_data<CID: Into<ContactId>>(
309 &self,
310 contact_id: CID,
311 data: Option<JsonObject>,
312 ) -> impl Future<Output = SetContactCustomDataResponse<Self>>;
313
314 fn create_group_link<GID: Into<GroupId>>(
315 &self,
316 group_id: GID,
317 role: GroupMemberRole,
318 ) -> impl Future<Output = CreateGroupLinkResult<Self>>;
319
320 fn set_group_link_role<GID: Into<GroupId>>(
321 &self,
322 group_id: GID,
323 role: GroupMemberRole,
324 ) -> impl Future<Output = GroupLinkResult<Self>>;
325
326 fn delete_group_link<GID: Into<GroupId>>(
327 &self,
328 group_id: GID,
329 ) -> impl Future<Output = DeleteGroupLinkResult<Self>>;
330
331 fn get_group_link<GID: Into<GroupId>>(
332 &self,
333 group_id: GID,
334 ) -> impl Future<Output = GroupLinkResult<Self>>;
335
336 fn get_group_relays<GID: Into<GroupId>>(
337 &self,
338 group_id: GID,
339 ) -> impl Future<Output = GetGroupRelaysResponse<Self>>;
340
341 fn add_group_relays<GID: Into<GroupId>, I: IntoIterator<Item = RelayId>>(
342 &self,
343 group_id: GID,
344 relay_ids: I,
345 ) -> impl Future<Output = AddGroupRelaysResponse<Self>>;
346
347 fn add_group_relay<GID: Into<GroupId>, RID: Into<RelayId>>(
348 &self,
349 group_id: GID,
350 relay_id: RID,
351 ) -> impl Future<Output = AddGroupRelaysResponse<Self>> {
352 self.add_group_relays(group_id, std::iter::once(relay_id.into()))
353 }
354}
355
356impl<C> ClientApiExt for C
357where
358 C: ClientApi,
359{
360 async fn users(&self) -> UsersResponse<Self> {
361 let mut response = self.list_users().await?;
362 let response = Arc::get_mut(&mut response).unwrap();
363
364 Ok(std::mem::take(&mut response.users))
365 }
366
367 async fn contacts<UID: Into<UserId>>(&self, user_id: UID) -> ContactsResponse<Self> {
368 let mut response = self.api_list_contacts(user_id.into().0).await?;
369 let response = Arc::get_mut(&mut response).unwrap();
370
371 Ok(std::mem::take(&mut response.contacts))
372 }
373
374 async fn groups<UID: Into<UserId>>(&self, user_id: UID) -> GroupsResponse<Self> {
375 let mut response = self
376 .api_list_groups(ApiListGroups::new(user_id.into().0))
377 .await?;
378 let response = Arc::get_mut(&mut response).unwrap();
379
380 Ok(std::mem::take(&mut response.groups))
381 }
382
383 async fn new_user(&self, mut user: NewUser) -> NewUserResponse<Self> {
384 match self.create_active_user(user.clone()).await {
385 Ok(response) => Ok(response),
386 Err(e) => match e.bad_response().and_then(|e| {
387 e.chat_error()
388 .and_then(|e| e.error().and_then(|e| e.invalid_display_name()))
389 }) {
390 Some(err) => {
391 user.profile.as_mut().unwrap().display_name = err.valid_name.clone();
392 self.create_active_user(user).await
393 }
394 None => Err(e),
395 },
396 }
397 }
398
399 fn accept_contact<CRID: Into<ContactRequestId>>(
400 &self,
401 contact_request_id: CRID,
402 ) -> impl Future<Output = AcceptContactResponse<Self>> {
403 self.api_accept_contact(contact_request_id.into().0)
404 }
405
406 fn reject_contact<CRID: Into<ContactRequestId>>(
407 &self,
408 contact_request_id: CRID,
409 ) -> impl Future<Output = RejectContactResponse<Self>> {
410 self.api_reject_contact(contact_request_id.into().0)
411 }
412
413 fn send_message<CID: Into<ChatId>, M: MessageLike>(
414 &self,
415 cid: CID,
416 msg: M,
417 ) -> MessageBuilder<'_, Self, M::Kind> {
418 let (composed, kind) = msg.into_builder_parts();
419 MessageBuilder {
420 client: self,
421 chat_id: cid.into(),
422 live: false,
423 ttl: None,
424 msg: composed,
425 kind,
426 }
427 }
428
429 fn multicast_message<I, M>(&self, chat_ids: I, msg: M) -> MulticastBuilder<'_, I, Self, M::Kind>
430 where
431 I: IntoIterator<Item = ChatId>,
432 M: MessageLike,
433 {
434 let (msg, kind) = msg.into_builder_parts();
435 MulticastBuilder {
436 client: self,
437 chat_ids,
438 ttl: None,
439 msg,
440 kind,
441 }
442 }
443
444 fn update_message<CID: Into<ChatId>, MID: Into<MessageId>>(
445 &self,
446 chat_id: CID,
447 message_id: MID,
448 new_content: MsgContent,
449 ) -> impl Future<Output = UpdateMessageResponse<Self>> {
450 self.api_update_chat_item(ApiUpdateChatItem {
451 chat_ref: chat_id.into().into_chat_ref(),
452 chat_item_id: message_id.into().0,
453 live_message: false,
454 updated_message: UpdatedMessage {
455 msg_content: new_content,
456 mentions: Default::default(),
457 undocumented: Default::default(),
458 },
459 })
460 }
461
462 fn batch_delete_messages<CID: Into<ChatId>, I: IntoIterator<Item = MessageId>>(
463 &self,
464 chat_id: CID,
465 message_ids: I,
466 mode: CIDeleteMode,
467 ) -> impl Future<Output = DeleteMessageResponse<Self>> {
468 self.api_delete_chat_item(
469 chat_id.into().into_chat_ref(),
470 message_ids.into_iter().map(|id| id.0).collect(),
471 mode,
472 )
473 }
474
475 fn batch_message_reactions<
476 CID: Into<ChatId>,
477 MID: Into<MessageId>,
478 I: IntoIterator<Item = Reaction>,
479 >(
480 &self,
481 chat_id: CID,
482 message_id: MID,
483 reactions: I,
484 ) -> impl Future<Output = UpdateMessageReactionsResponse<Self>> {
485 let chat_id = chat_id.into();
486 let message_id = message_id.into();
487
488 futures::future::join_all(reactions.into_iter().map(|r| {
489 let (add, emoji) = match r {
490 Reaction::Set(e) => (true, e),
491 Reaction::Unset(e) => (false, e),
492 };
493
494 self.api_chat_item_reaction(ApiChatItemReaction {
495 chat_ref: chat_id.into_chat_ref(),
496 chat_item_id: message_id.0,
497 add,
498 reaction: MsgReaction::Emoji {
499 emoji,
500 undocumented: Default::default(),
501 },
502 })
503 }))
504 }
505
506 fn accept_file<FID: Into<FileId>>(&self, file_id: FID) -> AcceptFileBuilder<'_, Self> {
507 AcceptFileBuilder {
508 client: self,
509 cmd: ReceiveFile::new(file_id.into().0),
510 }
511 }
512
513 fn reject_file<FID: Into<FileId>>(
514 &self,
515 file_id: FID,
516 ) -> impl Future<Output = RejectFileResponse<Self>> {
517 self.cancel_file(file_id.into().0)
518 }
519
520 fn initiate_connection(
521 &self,
522 link: impl Into<String>,
523 ) -> impl Future<Output = InitiateConnectionResponse<Self>> {
524 self.connect(Connect {
525 incognito: false,
526 conn_link: Some(link.into()),
527 })
528 .map(|res| res.allow_undocumented())
529 }
530
531 fn delete_chat<CID: Into<ChatId>>(
532 &self,
533 chat_id: CID,
534 mode: DeleteMode,
535 ) -> impl Future<Output = DeleteChatResponse<Self>> {
536 self.api_delete_chat(chat_id.into().into_chat_ref(), mode.into())
537 }
538
539 fn add_member<GID: Into<GroupId>, CID: Into<ContactId>>(
540 &self,
541 group_id: GID,
542 contact_id: CID,
543 role: GroupMemberRole,
544 ) -> impl Future<Output = AddMemberResponse<Self>> {
545 self.api_add_member(group_id.into().0, contact_id.into().0, role)
546 }
547
548 fn join_group<GID: Into<GroupId>>(
549 &self,
550 group_id: GID,
551 ) -> impl Future<Output = JoinGroupResponse<Self>> {
552 self.api_join_group(group_id.into().0)
553 }
554
555 fn accept_member<GID: Into<GroupId>, MID: Into<MemberId>>(
556 &self,
557 group_id: GID,
558 member_id: MID,
559 role: GroupMemberRole,
560 ) -> impl Future<Output = AcceptMemberResponse<Self>> {
561 self.api_accept_member(group_id.into().0, member_id.into().0, role)
562 }
563
564 fn set_members_role<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
565 &self,
566 group_id: GID,
567 member_ids: I,
568 role: GroupMemberRole,
569 ) -> impl Future<Output = SetMembersRoleResponse<Self>> {
570 self.api_members_role(
571 group_id.into().0,
572 member_ids.into_iter().map(|id| id.0).collect(),
573 role,
574 )
575 }
576
577 fn block_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
578 &self,
579 group_id: GID,
580 member_ids: I,
581 ) -> impl Future<Output = BlockMembersResponse<Self>> {
582 self.api_block_members_for_all(ApiBlockMembersForAll {
583 group_id: group_id.into().0,
584 group_member_ids: member_ids.into_iter().map(|id| id.0).collect(),
585 blocked: true,
586 })
587 }
588
589 fn unblock_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
590 &self,
591 group_id: GID,
592 member_ids: I,
593 ) -> impl Future<Output = BlockMembersResponse<Self>> {
594 self.api_block_members_for_all(ApiBlockMembersForAll {
595 group_id: group_id.into().0,
596 group_member_ids: member_ids.into_iter().map(|id| id.0).collect(),
597 blocked: false,
598 })
599 }
600
601 fn remove_members<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
602 &self,
603 group_id: GID,
604 member_ids: I,
605 ) -> impl Future<Output = RemoveMembersResponse<Self>> {
606 self.api_remove_members(ApiRemoveMembers {
607 group_id: group_id.into().0,
608 group_member_ids: member_ids.into_iter().map(|id| id.0).collect(),
609 with_messages: false,
610 })
611 }
612
613 fn remove_members_with_messages<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
614 &self,
615 group_id: GID,
616 member_ids: I,
617 ) -> impl Future<Output = RemoveMembersResponse<Self>> {
618 self.api_remove_members(ApiRemoveMembers {
619 group_id: group_id.into().0,
620 group_member_ids: member_ids.into_iter().map(|id| id.0).collect(),
621 with_messages: true,
622 })
623 }
624
625 fn leave_group<GID: Into<GroupId>>(
626 &self,
627 group_id: GID,
628 ) -> impl Future<Output = LeaveGroupResponse<Self>> {
629 self.api_leave_group(group_id.into().0)
630 }
631
632 async fn list_members<GID: Into<GroupId>>(&self, group_id: GID) -> ListMembersResponse<Self> {
633 let mut response = self.api_list_members(group_id.into().0).await?;
634 let response = Arc::get_mut(&mut response).unwrap();
635 Ok(std::mem::take(&mut response.group.members))
636 }
637
638 fn moderate_messages<GID: Into<GroupId>, I: IntoIterator<Item = MessageId>>(
639 &self,
640 group_id: GID,
641 message_ids: I,
642 ) -> impl Future<Output = DeleteMessageResponse<Self>> {
643 self.api_delete_member_chat_item(
644 group_id.into().0,
645 message_ids.into_iter().map(|id| id.0).collect(),
646 )
647 }
648
649 fn update_group_profile<GID: Into<GroupId>>(
650 &self,
651 group_id: GID,
652 profile: GroupProfile,
653 ) -> impl Future<Output = UpdateGroupProfileResponse<Self>> {
654 self.api_update_group_profile(group_id.into().0, profile)
655 }
656
657 fn set_group_custom_data<GID: Into<GroupId>>(
658 &self,
659 group_id: GID,
660 data: Option<JsonObject>,
661 ) -> impl Future<Output = SetGroupCustomDataResponse<Self>> {
662 self.api_set_group_custom_data(ApiSetGroupCustomData {
663 group_id: group_id.into().0,
664 custom_data: data,
665 })
666 }
667
668 fn set_contact_custom_data<CID: Into<ContactId>>(
669 &self,
670 contact_id: CID,
671 data: Option<JsonObject>,
672 ) -> impl Future<Output = SetContactCustomDataResponse<Self>> {
673 self.api_set_contact_custom_data(ApiSetContactCustomData {
674 contact_id: contact_id.into().0,
675 custom_data: data,
676 })
677 }
678
679 fn create_group_link<GID: Into<GroupId>>(
680 &self,
681 group_id: GID,
682 role: GroupMemberRole,
683 ) -> impl Future<Output = CreateGroupLinkResult<Self>> {
684 self.api_create_group_link(group_id.into().0, role)
685 }
686
687 fn set_group_link_role<GID: Into<GroupId>>(
688 &self,
689 group_id: GID,
690 role: GroupMemberRole,
691 ) -> impl Future<Output = GroupLinkResult<Self>> {
692 self.api_group_link_member_role(group_id.into().0, role)
693 }
694
695 fn delete_group_link<GID: Into<GroupId>>(
696 &self,
697 group_id: GID,
698 ) -> impl Future<Output = DeleteGroupLinkResult<Self>> {
699 self.api_delete_group_link(group_id.into().0)
700 }
701
702 fn get_group_link<GID: Into<GroupId>>(
703 &self,
704 group_id: GID,
705 ) -> impl Future<Output = GroupLinkResult<Self>> {
706 self.api_get_group_link(group_id.into().0)
707 }
708
709 fn get_group_relays<GID: Into<GroupId>>(
710 &self,
711 group_id: GID,
712 ) -> impl Future<Output = GetGroupRelaysResponse<Self>> {
713 self.api_get_group_relays(group_id.into().0)
714 }
715
716 fn add_group_relays<GID: Into<GroupId>, I: IntoIterator<Item = RelayId>>(
717 &self,
718 group_id: GID,
719 relay_ids: I,
720 ) -> impl Future<Output = AddGroupRelaysResponse<Self>> {
721 self.api_add_group_relays(
722 group_id.into().0,
723 relay_ids.into_iter().map(|id| id.0).collect(),
724 )
725 }
726}
727
728pub trait FilterChatItems {
729 fn filter_messages(&self) -> impl Iterator<Item = (ChatId, &ChatItem, &MsgContent)>;
730}
731
732impl FilterChatItems for Vec<AChatItem> {
733 fn filter_messages(&self) -> impl Iterator<Item = (ChatId, &ChatItem, &MsgContent)> {
734 self.iter().filter_map(|item| {
735 ChatId::from_chat_info(&item.chat_info).and_then(|cid| {
736 item.chat_item
737 .content
738 .rcv_msg_content()
739 .map(|msg| (cid, &item.chat_item, msg))
740 })
741 })
742 }
743}
744
745#[derive(Debug, Clone, Copy)]
746pub enum DeleteMode {
747 Full { notify: bool },
748 Entity { notify: bool },
749 Messages,
750}
751
752impl Default for DeleteMode {
753 fn default() -> Self {
754 Self::Full { notify: true }
755 }
756}
757
758impl From<DeleteMode> for ChatDeleteMode {
759 fn from(mode: DeleteMode) -> Self {
760 match mode {
761 DeleteMode::Full { notify } => ChatDeleteMode::Full {
762 notify,
763 undocumented: Default::default(),
764 },
765 DeleteMode::Entity { notify } => ChatDeleteMode::Entity {
766 notify,
767 undocumented: Default::default(),
768 },
769 DeleteMode::Messages => ChatDeleteMode::Messages,
770 }
771 }
772}
773
774impl TryFrom<ChatDeleteMode> for DeleteMode {
776 type Error = ChatDeleteMode;
777
778 fn try_from(mode: ChatDeleteMode) -> Result<Self, Self::Error> {
779 match mode {
780 ChatDeleteMode::Full {
781 notify,
782 undocumented: _,
783 } => Ok(Self::Full { notify }),
784 ChatDeleteMode::Entity {
785 notify,
786 undocumented: _,
787 } => Ok(Self::Entity { notify }),
788 ChatDeleteMode::Messages => Ok(Self::Messages),
789 ChatDeleteMode::Undocumented(_) => Err(mode),
790 _ => Err(mode),
791 }
792 }
793}
794
795pub struct AcceptFileBuilder<'a, C: 'a + ?Sized> {
796 client: &'a C,
797 cmd: ReceiveFile,
798}
799
800impl<'a, C: 'a + ?Sized> AcceptFileBuilder<'a, C> {
801 pub fn via_user_approved_relays(mut self) -> Self {
802 self.cmd.user_approved_relays = true;
803 self
804 }
805
806 pub fn store_encrypted(mut self) -> Self {
807 self.cmd.store_encrypted = Some(true);
808 self
809 }
810
811 pub fn inline(mut self) -> Self {
812 self.cmd.file_inline = Some(true);
813 self
814 }
815
816 pub fn file_path<P: AsRef<std::path::Path>>(mut self, path: P) -> Self {
817 self.cmd.file_path = Some(path.as_ref().display().to_string());
818 self
819 }
820}
821
822impl<'a, C: 'a + ?Sized + ClientApi> IntoFuture for AcceptFileBuilder<'a, C> {
823 type Output = Result<ReceiveFileResponse, C::Error>;
824 type IntoFuture = Pin<Box<dyn 'a + Send + Future<Output = Self::Output>>>;
825
826 fn into_future(self) -> Self::IntoFuture {
827 Box::pin(self.client.receive_file(self.cmd))
828 }
829}
830
831#[derive(Debug, Clone)]
832pub enum Reaction {
833 Set(String),
834 Unset(String),
835}