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