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