simploxide_api_types/client_api.rs
1use crate::{commands::*, responses::*, utils::CommandSyntax, *};
2use std::future::Future;
3use std::sync::Arc;
4
5/// A helper trait to handle different response wrappers
6pub trait ExtractResponse<'de, T>: Deserialize<'de> {
7 fn extract_response(self) -> Result<T, BadResponseError>;
8}
9
10pub trait ClientApiError: From<BadResponseError> + std::error::Error {
11 /// If current error is a bad response error return a reference to it
12 fn bad_response(&self) -> Option<&BadResponseError>;
13
14 /// If current error is a bad response error return a mut reference to it
15 fn bad_response_mut(&mut self) -> Option<&mut BadResponseError>;
16}
17
18pub trait ClientApi: Sync {
19 type ResponseShape<'de, T>: ExtractResponse<'de, T>
20 where
21 T: 'de + Deserialize<'de>;
22 type Error: ClientApiError;
23
24 fn send_raw(&self, command: String)
25 -> impl Future<Output = Result<String, Self::Error>> + Send;
26
27 fn send<C, R>(&self, cmd: C) -> impl Future<Output = Result<R, Self::Error>> + Send
28 where
29 C: Send + CommandSyntax,
30 R: for<'de> Deserialize<'de>,
31 {
32 async move {
33 let raw = self.send_raw(cmd.to_command_string()).await?;
34 let response_shape: Self::ResponseShape<'_, R> =
35 serde_json::from_str(&raw).map_err(BadResponseError::InvalidJson)?;
36 let response = response_shape.extract_response()?;
37 Ok(response)
38 }
39 }
40
41 /// ### Address commands
42 ///
43 /// Bots can use these commands to automatically check and create address when initialized
44 ///
45 /// ----
46 ///
47 /// Create bot address.
48 ///
49 /// *Network usage*: interactive.
50 ///
51 /// *Syntax:*
52 ///
53 /// ```
54 /// /_address <userId>
55 /// ```
56 fn api_create_my_address(
57 &self,
58 user_id: i64,
59 ) -> impl Future<Output = Result<Arc<UserContactLinkCreatedResponse>, Self::Error>> + Send {
60 async move {
61 let command = ApiCreateMyAddress { user_id };
62 let response: ApiCreateMyAddressResponse = self.send(command).await?;
63 Ok(response.into_inner())
64 }
65 }
66
67 /// ### Address commands
68 ///
69 /// Bots can use these commands to automatically check and create address when initialized
70 ///
71 /// ----
72 ///
73 /// Delete bot address.
74 ///
75 /// *Network usage*: background.
76 ///
77 /// *Syntax:*
78 ///
79 /// ```
80 /// /_delete_address <userId>
81 /// ```
82 fn api_delete_my_address(
83 &self,
84 user_id: i64,
85 ) -> impl Future<Output = Result<Arc<UserContactLinkDeletedResponse>, Self::Error>> + Send {
86 async move {
87 let command = ApiDeleteMyAddress { user_id };
88 let response: ApiDeleteMyAddressResponse = self.send(command).await?;
89 Ok(response.into_inner())
90 }
91 }
92
93 /// ### Address commands
94 ///
95 /// Bots can use these commands to automatically check and create address when initialized
96 ///
97 /// ----
98 ///
99 /// Get bot address and settings.
100 ///
101 /// *Network usage*: no.
102 ///
103 /// *Syntax:*
104 ///
105 /// ```
106 /// /_show_address <userId>
107 /// ```
108 fn api_show_my_address(
109 &self,
110 user_id: i64,
111 ) -> impl Future<Output = Result<Arc<UserContactLinkResponse>, Self::Error>> + Send {
112 async move {
113 let command = ApiShowMyAddress { user_id };
114 let response: ApiShowMyAddressResponse = self.send(command).await?;
115 Ok(response.into_inner())
116 }
117 }
118
119 /// ### Address commands
120 ///
121 /// Bots can use these commands to automatically check and create address when initialized
122 ///
123 /// ----
124 ///
125 /// Add address to bot profile.
126 ///
127 /// *Network usage*: interactive.
128 ///
129 /// *Syntax:*
130 ///
131 /// ```
132 /// /_profile_address <userId> on|off
133 /// ```
134 fn api_set_profile_address(
135 &self,
136 command: ApiSetProfileAddress,
137 ) -> impl Future<Output = Result<Arc<UserProfileUpdatedResponse>, Self::Error>> + Send {
138 async move {
139 let response: ApiSetProfileAddressResponse = self.send(command).await?;
140 Ok(response.into_inner())
141 }
142 }
143
144 /// ### Address commands
145 ///
146 /// Bots can use these commands to automatically check and create address when initialized
147 ///
148 /// ----
149 ///
150 /// Set bot address settings.
151 ///
152 /// *Network usage*: interactive.
153 ///
154 /// *Syntax:*
155 ///
156 /// ```
157 /// /_address_settings <userId> <json(settings)>
158 /// ```
159 fn api_set_address_settings(
160 &self,
161 user_id: i64,
162 settings: AddressSettings,
163 ) -> impl Future<Output = Result<Arc<UserContactLinkUpdatedResponse>, Self::Error>> + Send {
164 async move {
165 let command = ApiSetAddressSettings { user_id, settings };
166 let response: ApiSetAddressSettingsResponse = self.send(command).await?;
167 Ok(response.into_inner())
168 }
169 }
170
171 /// ### Message commands
172 ///
173 /// Commands to send, update, delete, moderate messages and set message reactions
174 ///
175 /// ----
176 ///
177 /// Send messages.
178 ///
179 /// *Network usage*: background.
180 ///
181 /// *Syntax:*
182 ///
183 /// ```
184 /// /_send <str(sendRef)>[ live=on][ ttl=<ttl>] json <json(composedMessages)>
185 /// ```
186 fn api_send_messages(
187 &self,
188 command: ApiSendMessages,
189 ) -> impl Future<Output = Result<Arc<NewChatItemsResponse>, Self::Error>> + Send {
190 async move {
191 let response: ApiSendMessagesResponse = self.send(command).await?;
192 Ok(response.into_inner())
193 }
194 }
195
196 /// ### Message commands
197 ///
198 /// Commands to send, update, delete, moderate messages and set message reactions
199 ///
200 /// ----
201 ///
202 /// Update message.
203 ///
204 /// *Network usage*: background.
205 ///
206 /// *Syntax:*
207 ///
208 /// ```
209 /// /_update item <str(chatRef)> <chatItemId>[ live=on] json <json(updatedMessage)>
210 /// ```
211 fn api_update_chat_item(
212 &self,
213 command: ApiUpdateChatItem,
214 ) -> impl Future<Output = Result<ApiUpdateChatItemResponse, Self::Error>> + Send {
215 async move {
216 let response: ApiUpdateChatItemResponse = self.send(command).await?;
217 Ok(response)
218 }
219 }
220
221 /// ### Message commands
222 ///
223 /// Commands to send, update, delete, moderate messages and set message reactions
224 ///
225 /// ----
226 ///
227 /// Delete message.
228 ///
229 /// *Network usage*: background.
230 ///
231 /// *Syntax:*
232 ///
233 /// ```
234 /// /_delete item <str(chatRef)> <chatItemIds[0]>[,<chatItemIds[1]>...] broadcast|internal|internalMark|history
235 /// ```
236 fn api_delete_chat_item(
237 &self,
238 chat_ref: ChatRef,
239 chat_item_ids: Vec<i64>,
240 delete_mode: CIDeleteMode,
241 ) -> impl Future<Output = Result<Arc<ChatItemsDeletedResponse>, Self::Error>> + Send {
242 async move {
243 let command = ApiDeleteChatItem {
244 chat_ref,
245 chat_item_ids,
246 delete_mode,
247 };
248 let response: ApiDeleteChatItemResponse = self.send(command).await?;
249 Ok(response.into_inner())
250 }
251 }
252
253 /// ### Message commands
254 ///
255 /// Commands to send, update, delete, moderate messages and set message reactions
256 ///
257 /// ----
258 ///
259 /// Moderate message. Requires Moderator role (and higher than message author's).
260 ///
261 /// *Network usage*: background.
262 ///
263 /// *Syntax:*
264 ///
265 /// ```
266 /// /_delete member item #<groupId> <chatItemIds[0]>[,<chatItemIds[1]>...]
267 /// ```
268 fn api_delete_member_chat_item(
269 &self,
270 group_id: i64,
271 chat_item_ids: Vec<i64>,
272 ) -> impl Future<Output = Result<Arc<ChatItemsDeletedResponse>, Self::Error>> + Send {
273 async move {
274 let command = ApiDeleteMemberChatItem {
275 group_id,
276 chat_item_ids,
277 };
278 let response: ApiDeleteMemberChatItemResponse = self.send(command).await?;
279 Ok(response.into_inner())
280 }
281 }
282
283 /// ### Message commands
284 ///
285 /// Commands to send, update, delete, moderate messages and set message reactions
286 ///
287 /// ----
288 ///
289 /// Add/remove message reaction.
290 ///
291 /// *Network usage*: background.
292 ///
293 /// *Syntax:*
294 ///
295 /// ```
296 /// /_reaction <str(chatRef)> <chatItemId> on|off <json(reaction)>
297 /// ```
298 fn api_chat_item_reaction(
299 &self,
300 command: ApiChatItemReaction,
301 ) -> impl Future<Output = Result<Arc<ChatItemReactionResponse>, Self::Error>> + Send {
302 async move {
303 let response: ApiChatItemReactionResponse = self.send(command).await?;
304 Ok(response.into_inner())
305 }
306 }
307
308 /// ### File commands
309 ///
310 /// Commands to receive and to cancel files. Files are sent as part of the message, there are no separate commands to send files.
311 ///
312 /// ----
313 ///
314 /// Receive file.
315 ///
316 /// *Network usage*: no.
317 ///
318 /// *Syntax:*
319 ///
320 /// ```
321 /// /freceive <fileId>[ approved_relays=on][ encrypt=on|off][ inline=on|off][ <filePath>]
322 /// ```
323 fn receive_file(
324 &self,
325 command: ReceiveFile,
326 ) -> impl Future<Output = Result<ReceiveFileResponse, Self::Error>> + Send {
327 async move {
328 let response: ReceiveFileResponse = self.send(command).await?;
329 Ok(response)
330 }
331 }
332
333 /// ### File commands
334 ///
335 /// Commands to receive and to cancel files. Files are sent as part of the message, there are no separate commands to send files.
336 ///
337 /// ----
338 ///
339 /// Cancel file.
340 ///
341 /// *Network usage*: background.
342 ///
343 /// *Syntax:*
344 ///
345 /// ```
346 /// /fcancel <fileId>
347 /// ```
348 fn cancel_file(
349 &self,
350 file_id: i64,
351 ) -> impl Future<Output = Result<CancelFileResponse, Self::Error>> + Send {
352 async move {
353 let command = CancelFile { file_id };
354 let response: CancelFileResponse = self.send(command).await?;
355 Ok(response)
356 }
357 }
358
359 /// ### Group commands
360 ///
361 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
362 ///
363 /// ----
364 ///
365 /// Add contact to group. Requires bot to have Admin role.
366 ///
367 /// *Network usage*: interactive.
368 ///
369 /// *Syntax:*
370 ///
371 /// ```
372 /// /_add #<groupId> <contactId> relay|observer|author|member|moderator|admin|owner
373 /// ```
374 fn api_add_member(
375 &self,
376 group_id: i64,
377 contact_id: i64,
378 member_role: GroupMemberRole,
379 ) -> impl Future<Output = Result<Arc<SentGroupInvitationResponse>, Self::Error>> + Send {
380 async move {
381 let command = ApiAddMember {
382 group_id,
383 contact_id,
384 member_role,
385 };
386 let response: ApiAddMemberResponse = self.send(command).await?;
387 Ok(response.into_inner())
388 }
389 }
390
391 /// ### Group commands
392 ///
393 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
394 ///
395 /// ----
396 ///
397 /// Join group.
398 ///
399 /// *Network usage*: interactive.
400 ///
401 /// *Syntax:*
402 ///
403 /// ```
404 /// /_join #<groupId>
405 /// ```
406 fn api_join_group(
407 &self,
408 group_id: i64,
409 ) -> impl Future<Output = Result<Arc<UserAcceptedGroupSentResponse>, Self::Error>> + Send {
410 async move {
411 let command = ApiJoinGroup { group_id };
412 let response: ApiJoinGroupResponse = self.send(command).await?;
413 Ok(response.into_inner())
414 }
415 }
416
417 /// ### Group commands
418 ///
419 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
420 ///
421 /// ----
422 ///
423 /// Accept group member. Requires Admin role.
424 ///
425 /// *Network usage*: background.
426 ///
427 /// *Syntax:*
428 ///
429 /// ```
430 /// /_accept member #<groupId> <groupMemberId> relay|observer|author|member|moderator|admin|owner
431 /// ```
432 fn api_accept_member(
433 &self,
434 group_id: i64,
435 group_member_id: i64,
436 member_role: GroupMemberRole,
437 ) -> impl Future<Output = Result<Arc<MemberAcceptedResponse>, Self::Error>> + Send {
438 async move {
439 let command = ApiAcceptMember {
440 group_id,
441 group_member_id,
442 member_role,
443 };
444 let response: ApiAcceptMemberResponse = self.send(command).await?;
445 Ok(response.into_inner())
446 }
447 }
448
449 /// ### Group commands
450 ///
451 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
452 ///
453 /// ----
454 ///
455 /// Set members role. Requires Admin role.
456 ///
457 /// *Network usage*: background.
458 ///
459 /// *Syntax:*
460 ///
461 /// ```
462 /// /_member role #<groupId> <groupMemberIds[0]>[,<groupMemberIds[1]>...] relay|observer|author|member|moderator|admin|owner
463 /// ```
464 fn api_members_role(
465 &self,
466 group_id: i64,
467 group_member_ids: Vec<i64>,
468 member_role: GroupMemberRole,
469 ) -> impl Future<Output = Result<Arc<MembersRoleUserResponse>, Self::Error>> + Send {
470 async move {
471 let command = ApiMembersRole {
472 group_id,
473 group_member_ids,
474 member_role,
475 };
476 let response: ApiMembersRoleResponse = self.send(command).await?;
477 Ok(response.into_inner())
478 }
479 }
480
481 /// ### Group commands
482 ///
483 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
484 ///
485 /// ----
486 ///
487 /// Block members. Requires Moderator role.
488 ///
489 /// *Network usage*: background.
490 ///
491 /// *Syntax:*
492 ///
493 /// ```
494 /// /_block #<groupId> <groupMemberIds[0]>[,<groupMemberIds[1]>...] blocked=on|off
495 /// ```
496 fn api_block_members_for_all(
497 &self,
498 command: ApiBlockMembersForAll,
499 ) -> impl Future<Output = Result<Arc<MembersBlockedForAllUserResponse>, Self::Error>> + Send
500 {
501 async move {
502 let response: ApiBlockMembersForAllResponse = self.send(command).await?;
503 Ok(response.into_inner())
504 }
505 }
506
507 /// ### Group commands
508 ///
509 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
510 ///
511 /// ----
512 ///
513 /// Remove members. Requires Admin role.
514 ///
515 /// *Network usage*: background.
516 ///
517 /// *Syntax:*
518 ///
519 /// ```
520 /// /_remove #<groupId> <groupMemberIds[0]>[,<groupMemberIds[1]>...][ messages=on]
521 /// ```
522 fn api_remove_members(
523 &self,
524 command: ApiRemoveMembers,
525 ) -> impl Future<Output = Result<Arc<UserDeletedMembersResponse>, Self::Error>> + Send {
526 async move {
527 let response: ApiRemoveMembersResponse = self.send(command).await?;
528 Ok(response.into_inner())
529 }
530 }
531
532 /// ### Group commands
533 ///
534 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
535 ///
536 /// ----
537 ///
538 /// Leave group.
539 ///
540 /// *Network usage*: background.
541 ///
542 /// *Syntax:*
543 ///
544 /// ```
545 /// /_leave #<groupId>
546 /// ```
547 fn api_leave_group(
548 &self,
549 group_id: i64,
550 ) -> impl Future<Output = Result<Arc<LeftMemberUserResponse>, Self::Error>> + Send {
551 async move {
552 let command = ApiLeaveGroup { group_id };
553 let response: ApiLeaveGroupResponse = self.send(command).await?;
554 Ok(response.into_inner())
555 }
556 }
557
558 /// ### Group commands
559 ///
560 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
561 ///
562 /// ----
563 ///
564 /// Get group members.
565 ///
566 /// *Network usage*: no.
567 ///
568 /// *Syntax:*
569 ///
570 /// ```
571 /// /_members #<groupId>
572 /// ```
573 fn api_list_members(
574 &self,
575 group_id: i64,
576 ) -> impl Future<Output = Result<Arc<GroupMembersResponse>, Self::Error>> + Send {
577 async move {
578 let command = ApiListMembers { group_id };
579 let response: ApiListMembersResponse = self.send(command).await?;
580 Ok(response.into_inner())
581 }
582 }
583
584 /// ### Group commands
585 ///
586 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
587 ///
588 /// ----
589 ///
590 /// Create group.
591 ///
592 /// *Network usage*: no.
593 ///
594 /// *Syntax:*
595 ///
596 /// ```
597 /// /_group <userId>[ incognito=on] <json(groupProfile)>
598 /// ```
599 fn api_new_group(
600 &self,
601 command: ApiNewGroup,
602 ) -> impl Future<Output = Result<Arc<GroupCreatedResponse>, Self::Error>> + Send {
603 async move {
604 let response: ApiNewGroupResponse = self.send(command).await?;
605 Ok(response.into_inner())
606 }
607 }
608
609 /// ### Group commands
610 ///
611 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
612 ///
613 /// ----
614 ///
615 /// Create public group.
616 ///
617 /// *Network usage*: interactive.
618 ///
619 /// *Syntax:*
620 ///
621 /// ```
622 /// /_public group <userId>[ incognito=on] <relayIds[0]>[,<relayIds[1]>...] <json(groupProfile)>
623 /// ```
624 fn api_new_public_group(
625 &self,
626 command: ApiNewPublicGroup,
627 ) -> impl Future<Output = Result<ApiNewPublicGroupResponse, Self::Error>> + Send {
628 async move {
629 let response: ApiNewPublicGroupResponse = self.send(command).await?;
630 Ok(response)
631 }
632 }
633
634 /// ### Group commands
635 ///
636 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
637 ///
638 /// ----
639 ///
640 /// Get group relays.
641 ///
642 /// *Network usage*: no.
643 ///
644 /// *Syntax:*
645 ///
646 /// ```
647 /// /_get relays #<groupId>
648 /// ```
649 fn api_get_group_relays(
650 &self,
651 group_id: i64,
652 ) -> impl Future<Output = Result<Arc<GroupRelaysResponse>, Self::Error>> + Send {
653 async move {
654 let command = ApiGetGroupRelays { group_id };
655 let response: ApiGetGroupRelaysResponse = self.send(command).await?;
656 Ok(response.into_inner())
657 }
658 }
659
660 /// ### Group commands
661 ///
662 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
663 ///
664 /// ----
665 ///
666 /// Add relays to group.
667 ///
668 /// *Network usage*: interactive.
669 ///
670 /// *Syntax:*
671 ///
672 /// ```
673 /// /_add relays #<groupId> <relayIds[0]>[,<relayIds[1]>...]
674 /// ```
675 fn api_add_group_relays(
676 &self,
677 group_id: i64,
678 relay_ids: Vec<i64>,
679 ) -> impl Future<Output = Result<ApiAddGroupRelaysResponse, Self::Error>> + Send {
680 async move {
681 let command = ApiAddGroupRelays {
682 group_id,
683 relay_ids,
684 };
685 let response: ApiAddGroupRelaysResponse = self.send(command).await?;
686 Ok(response)
687 }
688 }
689
690 /// ### Group commands
691 ///
692 /// Commands to manage and moderate groups. These commands can be used with business chats as well - they are groups. E.g., a common scenario would be to add human agents to business chat with the customer who connected via business address.
693 ///
694 /// ----
695 ///
696 /// Update group profile.
697 ///
698 /// *Network usage*: background.
699 ///
700 /// *Syntax:*
701 ///
702 /// ```
703 /// /_group_profile #<groupId> <json(groupProfile)>
704 /// ```
705 fn api_update_group_profile(
706 &self,
707 group_id: i64,
708 group_profile: GroupProfile,
709 ) -> impl Future<Output = Result<Arc<GroupUpdatedResponse>, Self::Error>> + Send {
710 async move {
711 let command = ApiUpdateGroupProfile {
712 group_id,
713 group_profile,
714 };
715 let response: ApiUpdateGroupProfileResponse = self.send(command).await?;
716 Ok(response.into_inner())
717 }
718 }
719
720 /// ### Group link commands
721 ///
722 /// These commands can be used by bots that manage multiple public groups
723 ///
724 /// ----
725 ///
726 /// Create group link.
727 ///
728 /// *Network usage*: interactive.
729 ///
730 /// *Syntax:*
731 ///
732 /// ```
733 /// /_create link #<groupId> relay|observer|author|member|moderator|admin|owner
734 /// ```
735 fn api_create_group_link(
736 &self,
737 group_id: i64,
738 member_role: GroupMemberRole,
739 ) -> impl Future<Output = Result<Arc<GroupLinkCreatedResponse>, Self::Error>> + Send {
740 async move {
741 let command = ApiCreateGroupLink {
742 group_id,
743 member_role,
744 };
745 let response: ApiCreateGroupLinkResponse = self.send(command).await?;
746 Ok(response.into_inner())
747 }
748 }
749
750 /// ### Group link commands
751 ///
752 /// These commands can be used by bots that manage multiple public groups
753 ///
754 /// ----
755 ///
756 /// Set member role for group link.
757 ///
758 /// *Network usage*: no.
759 ///
760 /// *Syntax:*
761 ///
762 /// ```
763 /// /_set link role #<groupId> relay|observer|author|member|moderator|admin|owner
764 /// ```
765 fn api_group_link_member_role(
766 &self,
767 group_id: i64,
768 member_role: GroupMemberRole,
769 ) -> impl Future<Output = Result<Arc<GroupLinkResponse>, Self::Error>> + Send {
770 async move {
771 let command = ApiGroupLinkMemberRole {
772 group_id,
773 member_role,
774 };
775 let response: ApiGroupLinkMemberRoleResponse = self.send(command).await?;
776 Ok(response.into_inner())
777 }
778 }
779
780 /// ### Group link commands
781 ///
782 /// These commands can be used by bots that manage multiple public groups
783 ///
784 /// ----
785 ///
786 /// Delete group link.
787 ///
788 /// *Network usage*: background.
789 ///
790 /// *Syntax:*
791 ///
792 /// ```
793 /// /_delete link #<groupId>
794 /// ```
795 fn api_delete_group_link(
796 &self,
797 group_id: i64,
798 ) -> impl Future<Output = Result<Arc<GroupLinkDeletedResponse>, Self::Error>> + Send {
799 async move {
800 let command = ApiDeleteGroupLink { group_id };
801 let response: ApiDeleteGroupLinkResponse = self.send(command).await?;
802 Ok(response.into_inner())
803 }
804 }
805
806 /// ### Group link commands
807 ///
808 /// These commands can be used by bots that manage multiple public groups
809 ///
810 /// ----
811 ///
812 /// Get group link.
813 ///
814 /// *Network usage*: no.
815 ///
816 /// *Syntax:*
817 ///
818 /// ```
819 /// /_get link #<groupId>
820 /// ```
821 fn api_get_group_link(
822 &self,
823 group_id: i64,
824 ) -> impl Future<Output = Result<Arc<GroupLinkResponse>, Self::Error>> + Send {
825 async move {
826 let command = ApiGetGroupLink { group_id };
827 let response: ApiGetGroupLinkResponse = self.send(command).await?;
828 Ok(response.into_inner())
829 }
830 }
831
832 /// ### Connection commands
833 ///
834 /// These commands may be used to create connections. Most bots do not need to use them - bot users will connect via bot address with auto-accept enabled.
835 ///
836 /// ----
837 ///
838 /// Create 1-time invitation link.
839 ///
840 /// *Network usage*: interactive.
841 ///
842 /// *Syntax:*
843 ///
844 /// ```
845 /// /_connect <userId>[ incognito=on]
846 /// ```
847 fn api_add_contact(
848 &self,
849 command: ApiAddContact,
850 ) -> impl Future<Output = Result<Arc<InvitationResponse>, Self::Error>> + Send {
851 async move {
852 let response: ApiAddContactResponse = self.send(command).await?;
853 Ok(response.into_inner())
854 }
855 }
856
857 /// ### Connection commands
858 ///
859 /// These commands may be used to create connections. Most bots do not need to use them - bot users will connect via bot address with auto-accept enabled.
860 ///
861 /// ----
862 ///
863 /// Determine SimpleX link type and if the bot is already connected via this link.
864 ///
865 /// *Network usage*: interactive.
866 ///
867 /// *Syntax:*
868 ///
869 /// ```
870 /// /_connect plan <userId> <connectionLink>
871 /// ```
872 fn api_connect_plan(
873 &self,
874 command: ApiConnectPlan,
875 ) -> impl Future<Output = Result<Arc<ConnectionPlanResponse>, Self::Error>> + Send {
876 async move {
877 let response: ApiConnectPlanResponse = self.send(command).await?;
878 Ok(response.into_inner())
879 }
880 }
881
882 /// ### Connection commands
883 ///
884 /// These commands may be used to create connections. Most bots do not need to use them - bot users will connect via bot address with auto-accept enabled.
885 ///
886 /// ----
887 ///
888 /// Connect via prepared SimpleX link. The link can be 1-time invitation link, contact address or group link.
889 ///
890 /// *Network usage*: interactive.
891 ///
892 /// *Syntax:*
893 ///
894 /// ```
895 /// /_connect <userId>[ <str(preparedLink_)>]
896 /// ```
897 fn api_connect(
898 &self,
899 command: ApiConnect,
900 ) -> impl Future<Output = Result<ApiConnectResponse, Self::Error>> + Send {
901 async move {
902 let response: ApiConnectResponse = self.send(command).await?;
903 Ok(response)
904 }
905 }
906
907 /// ### Connection commands
908 ///
909 /// These commands may be used to create connections. Most bots do not need to use them - bot users will connect via bot address with auto-accept enabled.
910 ///
911 /// ----
912 ///
913 /// Connect via SimpleX link as string in the active user profile.
914 ///
915 /// *Network usage*: interactive.
916 ///
917 /// *Syntax:*
918 ///
919 /// ```
920 /// /connect[ <connLink_>]
921 /// ```
922 fn connect(
923 &self,
924 command: Connect,
925 ) -> impl Future<Output = Result<ConnectResponse, Self::Error>> + Send {
926 async move {
927 let response: ConnectResponse = self.send(command).await?;
928 Ok(response)
929 }
930 }
931
932 /// ### Connection commands
933 ///
934 /// These commands may be used to create connections. Most bots do not need to use them - bot users will connect via bot address with auto-accept enabled.
935 ///
936 /// ----
937 ///
938 /// Accept contact request.
939 ///
940 /// *Network usage*: interactive.
941 ///
942 /// *Syntax:*
943 ///
944 /// ```
945 /// /_accept <contactReqId>
946 /// ```
947 fn api_accept_contact(
948 &self,
949 contact_req_id: i64,
950 ) -> impl Future<Output = Result<Arc<AcceptingContactRequestResponse>, Self::Error>> + Send
951 {
952 async move {
953 let command = ApiAcceptContact { contact_req_id };
954 let response: ApiAcceptContactResponse = self.send(command).await?;
955 Ok(response.into_inner())
956 }
957 }
958
959 /// ### Connection commands
960 ///
961 /// These commands may be used to create connections. Most bots do not need to use them - bot users will connect via bot address with auto-accept enabled.
962 ///
963 /// ----
964 ///
965 /// Reject contact request. The user who sent the request is **not notified**.
966 ///
967 /// *Network usage*: no.
968 ///
969 /// *Syntax:*
970 ///
971 /// ```
972 /// /_reject <contactReqId>
973 /// ```
974 fn api_reject_contact(
975 &self,
976 contact_req_id: i64,
977 ) -> impl Future<Output = Result<Arc<ContactRequestRejectedResponse>, Self::Error>> + Send {
978 async move {
979 let command = ApiRejectContact { contact_req_id };
980 let response: ApiRejectContactResponse = self.send(command).await?;
981 Ok(response.into_inner())
982 }
983 }
984
985 /// ### Chat commands
986 ///
987 /// Commands to list and delete conversations.
988 ///
989 /// ----
990 ///
991 /// Get contacts.
992 ///
993 /// *Network usage*: no.
994 ///
995 /// *Syntax:*
996 ///
997 /// ```
998 /// /_contacts <userId>
999 /// ```
1000 fn api_list_contacts(
1001 &self,
1002 user_id: i64,
1003 ) -> impl Future<Output = Result<Arc<ContactsListResponse>, Self::Error>> + Send {
1004 async move {
1005 let command = ApiListContacts { user_id };
1006 let response: ApiListContactsResponse = self.send(command).await?;
1007 Ok(response.into_inner())
1008 }
1009 }
1010
1011 /// ### Chat commands
1012 ///
1013 /// Commands to list and delete conversations.
1014 ///
1015 /// ----
1016 ///
1017 /// Get groups.
1018 ///
1019 /// *Network usage*: no.
1020 ///
1021 /// *Syntax:*
1022 ///
1023 /// ```
1024 /// /_groups <userId>[ @<contactId_>][ <search>]
1025 /// ```
1026 fn api_list_groups(
1027 &self,
1028 command: ApiListGroups,
1029 ) -> impl Future<Output = Result<Arc<GroupsListResponse>, Self::Error>> + Send {
1030 async move {
1031 let response: ApiListGroupsResponse = self.send(command).await?;
1032 Ok(response.into_inner())
1033 }
1034 }
1035
1036 /// ### Chat commands
1037 ///
1038 /// Commands to list and delete conversations.
1039 ///
1040 /// ----
1041 ///
1042 /// Get chat previews. Supports time-based pagination — use this instead of APIListContacts / APIListGroups when scanning at scale (those load every record into memory and fail on large databases).
1043 ///
1044 /// *Network usage*: no.
1045 ///
1046 /// *Syntax:*
1047 ///
1048 /// ```
1049 /// /_get chats <userId>[ pcc=on] <str(pagination)> <json(query)>
1050 /// ```
1051 fn api_get_chats(
1052 &self,
1053 command: ApiGetChats,
1054 ) -> impl Future<Output = Result<Arc<ApiChatsResponse>, Self::Error>> + Send {
1055 async move {
1056 let response: ApiGetChatsResponse = self.send(command).await?;
1057 Ok(response.into_inner())
1058 }
1059 }
1060
1061 /// ### Chat commands
1062 ///
1063 /// Commands to list and delete conversations.
1064 ///
1065 /// ----
1066 ///
1067 /// Delete chat.
1068 ///
1069 /// *Network usage*: background.
1070 ///
1071 /// *Syntax:*
1072 ///
1073 /// ```
1074 /// /_delete <str(chatRef)> <str(chatDeleteMode)>
1075 /// ```
1076 fn api_delete_chat(
1077 &self,
1078 chat_ref: ChatRef,
1079 chat_delete_mode: ChatDeleteMode,
1080 ) -> impl Future<Output = Result<ApiDeleteChatResponse, Self::Error>> + Send {
1081 async move {
1082 let command = ApiDeleteChat {
1083 chat_ref,
1084 chat_delete_mode,
1085 };
1086 let response: ApiDeleteChatResponse = self.send(command).await?;
1087 Ok(response)
1088 }
1089 }
1090
1091 /// ### Chat commands
1092 ///
1093 /// Commands to list and delete conversations.
1094 ///
1095 /// ----
1096 ///
1097 /// Set group custom data.
1098 ///
1099 /// *Network usage*: no.
1100 ///
1101 /// *Syntax:*
1102 ///
1103 /// ```
1104 /// /_set custom #<groupId>[ <json(customData)>]
1105 /// ```
1106 fn api_set_group_custom_data(
1107 &self,
1108 command: ApiSetGroupCustomData,
1109 ) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
1110 async move {
1111 let response: ApiSetGroupCustomDataResponse = self.send(command).await?;
1112 Ok(response.into_inner())
1113 }
1114 }
1115
1116 /// ### Chat commands
1117 ///
1118 /// Commands to list and delete conversations.
1119 ///
1120 /// ----
1121 ///
1122 /// Set contact custom data.
1123 ///
1124 /// *Network usage*: no.
1125 ///
1126 /// *Syntax:*
1127 ///
1128 /// ```
1129 /// /_set custom @<contactId>[ <json(customData)>]
1130 /// ```
1131 fn api_set_contact_custom_data(
1132 &self,
1133 command: ApiSetContactCustomData,
1134 ) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
1135 async move {
1136 let response: ApiSetContactCustomDataResponse = self.send(command).await?;
1137 Ok(response.into_inner())
1138 }
1139 }
1140
1141 /// ### Chat commands
1142 ///
1143 /// Commands to list and delete conversations.
1144 ///
1145 /// ----
1146 ///
1147 /// Set auto-accept member contacts.
1148 ///
1149 /// *Network usage*: no.
1150 ///
1151 /// *Syntax:*
1152 ///
1153 /// ```
1154 /// /_set accept member contacts <userId> on|off
1155 /// ```
1156 fn api_set_user_auto_accept_member_contacts(
1157 &self,
1158 command: ApiSetUserAutoAcceptMemberContacts,
1159 ) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
1160 async move {
1161 let response: ApiSetUserAutoAcceptMemberContactsResponse = self.send(command).await?;
1162 Ok(response.into_inner())
1163 }
1164 }
1165
1166 /// ### User profile commands
1167 ///
1168 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1169 ///
1170 /// ----
1171 ///
1172 /// Get active user profile.
1173 ///
1174 /// *Network usage*: no.
1175 ///
1176 /// *Syntax:*
1177 ///
1178 /// ```
1179 /// /user
1180 /// ```
1181 fn show_active_user(
1182 &self,
1183 ) -> impl Future<Output = Result<Arc<ActiveUserResponse>, Self::Error>> + Send {
1184 async move {
1185 let command = ShowActiveUser {};
1186 let response: ShowActiveUserResponse = self.send(command).await?;
1187 Ok(response.into_inner())
1188 }
1189 }
1190
1191 /// ### User profile commands
1192 ///
1193 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1194 ///
1195 /// ----
1196 ///
1197 /// Create new user profile.
1198 ///
1199 /// *Network usage*: no.
1200 ///
1201 /// *Syntax:*
1202 ///
1203 /// ```
1204 /// /_create user <json(newUser)>
1205 /// ```
1206 fn create_active_user(
1207 &self,
1208 new_user: NewUser,
1209 ) -> impl Future<Output = Result<Arc<ActiveUserResponse>, Self::Error>> + Send {
1210 async move {
1211 let command = CreateActiveUser { new_user };
1212 let response: CreateActiveUserResponse = self.send(command).await?;
1213 Ok(response.into_inner())
1214 }
1215 }
1216
1217 /// ### User profile commands
1218 ///
1219 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1220 ///
1221 /// ----
1222 ///
1223 /// Get all user profiles.
1224 ///
1225 /// *Network usage*: no.
1226 ///
1227 /// *Syntax:*
1228 ///
1229 /// ```
1230 /// /users
1231 /// ```
1232 fn list_users(
1233 &self,
1234 ) -> impl Future<Output = Result<Arc<UsersListResponse>, Self::Error>> + Send {
1235 async move {
1236 let command = ListUsers {};
1237 let response: ListUsersResponse = self.send(command).await?;
1238 Ok(response.into_inner())
1239 }
1240 }
1241
1242 /// ### User profile commands
1243 ///
1244 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1245 ///
1246 /// ----
1247 ///
1248 /// Set active user profile.
1249 ///
1250 /// *Network usage*: no.
1251 ///
1252 /// *Syntax:*
1253 ///
1254 /// ```
1255 /// /_user <userId>[ <json(viewPwd)>]
1256 /// ```
1257 fn api_set_active_user(
1258 &self,
1259 command: ApiSetActiveUser,
1260 ) -> impl Future<Output = Result<Arc<ActiveUserResponse>, Self::Error>> + Send {
1261 async move {
1262 let response: ApiSetActiveUserResponse = self.send(command).await?;
1263 Ok(response.into_inner())
1264 }
1265 }
1266
1267 /// ### User profile commands
1268 ///
1269 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1270 ///
1271 /// ----
1272 ///
1273 /// Delete user profile.
1274 ///
1275 /// *Network usage*: background.
1276 ///
1277 /// *Syntax:*
1278 ///
1279 /// ```
1280 /// /_delete user <userId> del_smp=on|off[ <json(viewPwd)>]
1281 /// ```
1282 fn api_delete_user(
1283 &self,
1284 command: ApiDeleteUser,
1285 ) -> impl Future<Output = Result<Arc<CmdOkResponse>, Self::Error>> + Send {
1286 async move {
1287 let response: ApiDeleteUserResponse = self.send(command).await?;
1288 Ok(response.into_inner())
1289 }
1290 }
1291
1292 /// ### User profile commands
1293 ///
1294 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1295 ///
1296 /// ----
1297 ///
1298 /// Update user profile.
1299 ///
1300 /// *Network usage*: background.
1301 ///
1302 /// *Syntax:*
1303 ///
1304 /// ```
1305 /// /_profile <userId> <json(profile)>
1306 /// ```
1307 fn api_update_profile(
1308 &self,
1309 user_id: i64,
1310 profile: Profile,
1311 ) -> impl Future<Output = Result<ApiUpdateProfileResponse, Self::Error>> + Send {
1312 async move {
1313 let command = ApiUpdateProfile { user_id, profile };
1314 let response: ApiUpdateProfileResponse = self.send(command).await?;
1315 Ok(response)
1316 }
1317 }
1318
1319 /// ### User profile commands
1320 ///
1321 /// Most bots don't need to use these commands, as bot profile can be configured manually via CLI or desktop client. These commands can be used by bots that need to manage multiple user profiles (e.g., the profiles of support agents).
1322 ///
1323 /// ----
1324 ///
1325 /// Configure chat preference overrides for the contact.
1326 ///
1327 /// *Network usage*: background.
1328 ///
1329 /// *Syntax:*
1330 ///
1331 /// ```
1332 /// /_set prefs @<contactId> <json(preferences)>
1333 /// ```
1334 fn api_set_contact_prefs(
1335 &self,
1336 contact_id: i64,
1337 preferences: Preferences,
1338 ) -> impl Future<Output = Result<Arc<ContactPrefsUpdatedResponse>, Self::Error>> + Send {
1339 async move {
1340 let command = ApiSetContactPrefs {
1341 contact_id,
1342 preferences,
1343 };
1344 let response: ApiSetContactPrefsResponse = self.send(command).await?;
1345 Ok(response.into_inner())
1346 }
1347 }
1348
1349 /// ### Chat management
1350 ///
1351 /// These commands should not be used with CLI-based bots
1352 ///
1353 /// ----
1354 ///
1355 /// Start chat controller.
1356 ///
1357 /// *Network usage*: no.
1358 ///
1359 /// *Syntax:*
1360 ///
1361 /// ```
1362 /// /_start
1363 /// ```
1364 fn start_chat(
1365 &self,
1366 command: StartChat,
1367 ) -> impl Future<Output = Result<StartChatResponse, Self::Error>> + Send {
1368 async move {
1369 let response: StartChatResponse = self.send(command).await?;
1370 Ok(response)
1371 }
1372 }
1373
1374 /// ### Chat management
1375 ///
1376 /// These commands should not be used with CLI-based bots
1377 ///
1378 /// ----
1379 ///
1380 /// Stop chat controller.
1381 ///
1382 /// *Network usage*: no.
1383 ///
1384 /// *Syntax:*
1385 ///
1386 /// ```
1387 /// /_stop
1388 /// ```
1389 fn api_stop_chat(
1390 &self,
1391 ) -> impl Future<Output = Result<Arc<ChatStoppedResponse>, Self::Error>> + Send {
1392 async move {
1393 let command = ApiStopChat {};
1394 let response: ApiStopChatResponse = self.send(command).await?;
1395 Ok(response.into_inner())
1396 }
1397 }
1398}
1399
1400/// Use this as [`ClientApi::ResponseShape`] to extract web socket responses
1401#[derive(Serialize, Deserialize)]
1402pub struct WebSocketResponseShape<T> {
1403 pub resp: WebSocketResponseShapeInner<T>,
1404}
1405
1406#[derive(Serialize, Deserialize)]
1407#[serde(untagged)]
1408pub enum WebSocketResponseShapeInner<T> {
1409 Response(T),
1410 Error(ChatCmdError),
1411 Undocumented(JsonObject),
1412}
1413
1414impl<'de, T: 'de + Deserialize<'de>> ExtractResponse<'de, T> for WebSocketResponseShape<T> {
1415 fn extract_response(self) -> Result<T, BadResponseError> {
1416 self.resp.extract_response()
1417 }
1418}
1419
1420impl<'de, T: 'de + Deserialize<'de>> ExtractResponse<'de, T> for WebSocketResponseShapeInner<T> {
1421 fn extract_response(self) -> Result<T, BadResponseError> {
1422 match self {
1423 Self::Response(resp) => Ok(resp),
1424 Self::Error(err) => Err(BadResponseError::ChatError(
1425 err.into_inner().chat_error.clone(),
1426 )),
1427 Self::Undocumented(json) => Err(BadResponseError::Undocumented(json)),
1428 }
1429 }
1430}
1431
1432/// Use this as [`ClientApi::ResponseShape`] to extract FFI responses
1433#[derive(Serialize, Deserialize)]
1434pub enum FfiResponseShape<T> {
1435 #[serde(rename = "result")]
1436 Result(T),
1437
1438 #[serde(rename = "error")]
1439 Error(Arc<ChatError>),
1440
1441 #[serde(untagged)]
1442 Undocumented(JsonObject),
1443}
1444
1445impl<'de, T: 'de + Deserialize<'de>> ExtractResponse<'de, T> for FfiResponseShape<T> {
1446 fn extract_response(self) -> Result<T, BadResponseError> {
1447 match self {
1448 Self::Result(resp) => Ok(resp),
1449 Self::Error(err) => Err(BadResponseError::ChatError(err)),
1450 Self::Undocumented(json) => Err(BadResponseError::Undocumented(json)),
1451 }
1452 }
1453}
1454
1455#[derive(Debug)]
1456pub enum BadResponseError {
1457 ChatError(Arc<ChatError>),
1458 InvalidJson(serde_json::Error),
1459 Undocumented(JsonObject),
1460}
1461
1462impl BadResponseError {
1463 pub fn chat_error(&self) -> Option<&ChatError> {
1464 if let Self::ChatError(error) = self {
1465 Some(error.as_ref())
1466 } else {
1467 None
1468 }
1469 }
1470
1471 pub fn invalid_json(&self) -> Option<&serde_json::Error> {
1472 if let Self::InvalidJson(error) = self {
1473 Some(error)
1474 } else {
1475 None
1476 }
1477 }
1478
1479 pub fn undocumented(&self) -> Option<&JsonObject> {
1480 if let Self::Undocumented(error) = self {
1481 Some(error)
1482 } else {
1483 None
1484 }
1485 }
1486}
1487
1488impl std::error::Error for BadResponseError {
1489 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1490 match self {
1491 Self::ChatError(error) => Some(error.as_ref()),
1492 Self::InvalidJson(error) => Some(error),
1493 Self::Undocumented(_) => None,
1494 }
1495 }
1496}
1497
1498impl std::fmt::Display for BadResponseError {
1499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1500 match self {
1501 Self::ChatError(resp) => writeln!(f, "Bad response:\n{resp:#}"),
1502 Self::Undocumented(resp) => writeln!(f, "Unexpected response:\n{resp:#}"),
1503 Self::InvalidJson(err) => writeln!(f, "Invalid JSON:\n{err:#}"),
1504 }
1505 }
1506}
1507
1508pub enum UndocumentedResponse<T> {
1509 Documented(T),
1510 Undocumented(JsonObject),
1511}
1512
1513/// If you want to ~~suffer~~ handle undocumented responses you can use this extension trait
1514/// on client API return values which moves Undocumented from `Err` to `Ok` variant.
1515///
1516/// Example:
1517///
1518/// ```ignore
1519/// match client
1520/// .api_create_my_address(1)
1521/// .await
1522/// .allow_undocumented()?
1523/// {
1524/// UndocumentedResponse::Documented(resp) => {
1525/// // Process expected response...
1526/// }
1527/// UndocumentedResponse::Undocumented(resp) => {
1528/// // Do something with the unexpected response...
1529/// }
1530/// }
1531/// }
1532/// ```
1533pub trait AllowUndocumentedResponses<T, E> {
1534 fn allow_undocumented(self) -> Result<UndocumentedResponse<T>, E>;
1535}
1536
1537impl<T, E> AllowUndocumentedResponses<T, E> for Result<T, E>
1538where
1539 E: ClientApiError,
1540{
1541 fn allow_undocumented(self) -> Result<UndocumentedResponse<T>, E> {
1542 match self {
1543 Ok(resp) => Ok(UndocumentedResponse::Documented(resp)),
1544 Err(mut e) => match e.bad_response_mut() {
1545 Some(BadResponseError::Undocumented(btree_map)) => Ok(
1546 UndocumentedResponse::Undocumented(std::mem::take(btree_map)),
1547 ),
1548 _ => Err(e),
1549 },
1550 }
1551 }
1552}