Skip to main content

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}