1use simploxide_api_types::{
18 AddressSettings, AutoAccept, CIDeleteMode, ChatListQuery, ChatPeerType, ConnectionPlan,
19 Contact, CreatedConnLink, GroupInfo, GroupMember, GroupMemberRole, GroupProfile, JsonObject,
20 LocalProfile, MsgContent, NewUser, PaginationByTime, Preferences, Profile, User,
21 client_api::{ClientApi, ClientApiError as _, UndocumentedResponse},
22 commands::{
23 ApiAddContact, ApiConnectPlan, ApiGetChats, ApiNewGroup, ApiNewPublicGroup,
24 ApiSetActiveUser, ApiSetProfileAddress, ApiSetUserAutoAcceptMemberContacts,
25 },
26 responses::{
27 AcceptingContactRequestResponse, ActiveUserResponse, ApiChatsResponse,
28 ApiDeleteChatResponse, ApiNewPublicGroupResponse, ApiUpdateChatItemResponse,
29 ApiUpdateProfileResponse, CancelFileResponse, ChatItemReactionResponse,
30 ChatItemsDeletedResponse, CmdOkResponse, ConnectResponse, ConnectionPlanResponse,
31 ContactPrefsUpdatedResponse, ContactRequestRejectedResponse, GroupCreatedResponse,
32 GroupLinkCreatedResponse, GroupLinkDeletedResponse, GroupUpdatedResponse,
33 InvitationResponse, LeftMemberUserResponse, MemberAcceptedResponse,
34 MembersBlockedForAllUserResponse, MembersRoleUserResponse, SentGroupInvitationResponse,
35 UserAcceptedGroupSentResponse, UserDeletedMembersResponse, UserProfileUpdatedResponse,
36 },
37};
38
39use std::sync::Arc;
40
41use crate::{
42 ext::{
43 AcceptFileBuilder, AddGroupRelaysResponse, ClientApiExt as _, DeleteMode,
44 GetGroupRelaysResponse, GroupLinkResult, Reaction,
45 },
46 id::{
47 ChatId, ContactId, ContactRequestId, FileId, GroupId, MemberId, MessageId, RelayId, UserId,
48 },
49 messages::{MessageBuilder, MessageLike, MulticastBuilder},
50 preferences,
51 preview::ImagePreview,
52};
53
54#[cfg(feature = "farm")]
55pub mod farm;
56
57#[cfg(feature = "farm")]
58pub use farm::BotFarm;
59
60#[derive(Clone)]
62pub struct Bot<C> {
63 client: C,
64 user_id: i64,
65}
66
67impl<C> Bot<C> {
68 pub fn client(&self) -> &C {
69 &self.client
70 }
71
72 pub fn user_id(&self) -> UserId {
73 UserId::from_raw(self.user_id)
74 }
75}
76
77impl<C: ClientApi> Bot<C> {
78 #[cfg(feature = "farm")]
79 fn new(client: C, user_id: UserId) -> Self {
80 Self {
81 client,
82 user_id: user_id.raw(),
83 }
84 }
85
86 pub async fn init(client: C, settings: BotSettings) -> Result<Self, C::Error> {
87 let mut users = client.users().await?;
88
89 match users.iter_mut().find_map(|info| {
90 (info.user.profile.display_name == settings.display_name).then_some(&mut info.user)
91 }) {
92 Some(user) => Self::init_existing(client, user, settings).await,
93 None => Self::init_new(client, settings).await,
94 }
95 }
96
97 async fn init_existing(
98 client: C,
99 user: &mut User,
100 settings: BotSettings,
101 ) -> Result<Self, C::Error> {
102 if !user.active_user {
103 client
104 .api_set_active_user(ApiSetActiveUser::new(user.user_id))
105 .await?;
106 }
107
108 let avatar = if let Some(preview) = settings.avatar {
109 Some(preview.resolve().await)
110 } else {
111 None
112 };
113
114 let bot = Bot {
115 client,
116 user_id: user.user_id,
117 };
118
119 bot.setup_auto_accept(settings.auto_accept, user.profile.contact_link.is_some())
120 .await?;
121
122 let mut profile = match settings.profile_settings {
123 Some(BotProfileSettings::Preferences(preferences)) => {
124 let mut current_profile = extract_profile(&mut user.profile);
125 current_profile.preferences = Some(preferences);
126 current_profile
127 }
128 Some(BotProfileSettings::FullProfile(profile)) => profile,
129 None => Self::default_profile(user.profile.display_name.clone()),
130 };
131 profile.image = avatar;
132 bot.client.api_update_profile(user.user_id, profile).await?;
133
134 Ok(bot)
135 }
136
137 async fn init_new(client: C, settings: BotSettings) -> Result<Self, C::Error> {
138 let avatar = if let Some(preview) = settings.avatar {
139 Some(preview.resolve().await)
140 } else {
141 None
142 };
143
144 let mut bot_profile = match settings.profile_settings {
145 Some(BotProfileSettings::Preferences(preferences)) => {
146 let mut profile = Self::default_profile(settings.display_name.clone());
147 profile.preferences = Some(preferences);
148 profile
149 }
150 Some(BotProfileSettings::FullProfile(profile)) => profile,
151 None => Self::default_profile(settings.display_name.clone()),
152 };
153 bot_profile.image = avatar;
154
155 let response = client
156 .new_user(NewUser {
157 profile: Some(bot_profile),
158 past_timestamp: false,
159 user_chat_relay: false,
160 undocumented: Default::default(),
161 })
162 .await?;
163
164 let bot = Bot {
165 client,
166 user_id: response.user.user_id,
167 };
168
169 bot.setup_auto_accept(settings.auto_accept, false).await?;
170 Ok(bot)
171 }
172
173 async fn setup_auto_accept(
174 &self,
175 auto_accept: Option<String>,
176 has_existing_address: bool,
177 ) -> Result<(), C::Error> {
178 if let Some(welcome_message) = auto_accept {
179 if !has_existing_address {
180 self.get_or_create_address().await?;
181 self.publish_address().await?;
182 }
183
184 self.configure_address(AddressSettings {
185 business_address: false,
186 auto_accept: Some(AutoAccept {
187 accept_incognito: false,
188 undocumented: Default::default(),
189 }),
190 auto_reply: (!welcome_message.is_empty())
191 .then(|| MsgContent::make_text(welcome_message)),
192 undocumented: Default::default(),
193 })
194 .await?;
195 } else if has_existing_address {
196 self.configure_address(AddressSettings {
197 business_address: false,
198 auto_accept: None,
199 auto_reply: None,
200 undocumented: Default::default(),
201 })
202 .await?;
203
204 self.hide_address().await?;
205 }
206
207 Ok(())
208 }
209
210 pub fn wrap_client<W, F>(self, wrap: F) -> Bot<W>
216 where
217 W: ClientApi,
218 F: FnOnce(C) -> W,
219 {
220 let new_client = wrap(self.client);
221
222 Bot {
223 client: new_client,
224 user_id: self.user_id,
225 }
226 }
227
228 pub fn default_profile(name: impl Into<String>) -> Profile {
230 Profile {
231 display_name: name.into(),
232 full_name: String::default(),
233 short_descr: None,
234 image: None,
235 contact_link: None,
236 preferences: Some(Preferences {
237 timed_messages: preferences::timed_messages::NO,
238 full_delete: preferences::YES,
239 reactions: preferences::NO,
240 voice: preferences::NO,
241 files: preferences::NO,
242 calls: preferences::NO,
243 sessions: preferences::NO,
244 commands: None,
245 undocumented: Default::default(),
246 }),
247 badge: None,
248 peer_type: Some(ChatPeerType::Bot),
249 undocumented: serde_json::Value::Null,
250 }
251 }
252
253 pub async fn info(&self) -> Result<Arc<ActiveUserResponse>, C::Error> {
255 self.client.show_active_user().await
256 }
257
258 pub async fn initiate_connection(
270 &self,
271 link: impl Into<String>,
272 ) -> Result<UndocumentedResponse<ConnectResponse>, C::Error> {
273 self.client.initiate_connection(link).await
274 }
275
276 pub async fn check_connection_plan(
279 &self,
280 link: impl Into<String>,
281 ) -> Result<Arc<ConnectionPlanResponse>, C::Error> {
282 self.client
283 .api_connect_plan(ApiConnectPlan {
284 user_id: self.user_id,
285 connection_link: Some(link.into()),
286 resolve_known: true,
287 link_owner_sig: None,
288 })
289 .await
290 }
291
292 pub async fn initiate_connection_if<F: FnOnce(&ConnectionPlan) -> bool>(
306 &self,
307 link: impl Into<String>,
308 predicate: F,
309 ) -> Result<Connection, C::Error> {
310 let link = link.into();
311 let plan_resp = self.check_connection_plan(link.clone()).await?;
312
313 if !predicate(&plan_resp.connection_plan) {
314 return Ok(Connection::Rejected(plan_resp));
315 }
316
317 self.initiate_connection(link)
318 .await
319 .map(Connection::Initiated)
320 }
321
322 pub async fn create_invitation_link(
327 &self,
328 ) -> Result<(String, Arc<InvitationResponse>), C::Error> {
329 let response = self
330 .client
331 .api_add_contact(ApiAddContact::new(self.user_id))
332 .await?;
333
334 let link = extract_address(&response.conn_link_invitation);
335 Ok((link, response))
336 }
337
338 pub async fn create_address(&self) -> Result<String, C::Error> {
339 let response = self.client.api_create_my_address(self.user_id).await?;
340 Ok(extract_address(&response.conn_link_contact))
341 }
342
343 pub async fn address(&self) -> Result<String, C::Error> {
346 let response = self.client.api_show_my_address(self.user_id).await?;
347 Ok(extract_address(&response.contact_link.conn_link_contact))
348 }
349
350 pub async fn get_or_create_address(&self) -> Result<String, C::Error> {
351 match self.address().await {
352 Ok(address) => Ok(address),
353 Err(e)
354 if e.bad_response()
355 .and_then(|e| {
356 e.chat_error().and_then(|e| {
357 e.error_store().map(|e| e.is_user_contact_link_not_found())
358 })
359 })
360 .unwrap_or(false) =>
361 {
362 self.create_address().await
363 }
364 Err(e) => Err(e),
365 }
366 }
367
368 pub async fn configure_address(&self, settings: AddressSettings) -> Result<(), C::Error> {
369 self.client
370 .api_set_address_settings(self.user_id, settings)
371 .await
372 .map(drop)
373 }
374
375 pub async fn publish_address(&self) -> Result<Arc<UserProfileUpdatedResponse>, C::Error> {
377 self.client
378 .api_set_profile_address(ApiSetProfileAddress {
379 user_id: self.user_id,
380 enable: true,
381 })
382 .await
383 }
384
385 pub async fn hide_address(&self) -> Result<Arc<UserProfileUpdatedResponse>, C::Error> {
387 self.client
388 .api_set_profile_address(ApiSetProfileAddress {
389 user_id: self.user_id,
390 enable: false,
391 })
392 .await
393 }
394
395 pub async fn delete_address(&self) -> Result<(), C::Error> {
396 self.client.api_delete_my_address(self.user_id).await?;
397 Ok(())
398 }
399
400 pub async fn profile(&self) -> Result<Profile, C::Error> {
401 let mut response = self.client.show_active_user().await?;
402 let response = Arc::get_mut(&mut response).unwrap();
403
404 Ok(extract_profile(&mut response.user.profile))
405 }
406
407 pub async fn update_profile<F>(&self, updater: F) -> Result<ApiUpdateProfileResponse, C::Error>
409 where
410 F: 'static + Send + FnOnce(&mut Profile),
411 {
412 let mut profile = self.profile().await?;
413 updater(&mut profile);
414 match self
415 .client
416 .api_update_profile(self.user_id, profile.clone())
417 .await
418 {
419 Ok(resp) => Ok(resp),
420 Err(e) => match e.bad_response().and_then(|e| {
421 e.chat_error()
422 .and_then(|e| e.error().and_then(|e| e.invalid_display_name()))
423 }) {
424 Some(err) => {
425 profile.display_name = err.valid_name.clone();
426 self.client.api_update_profile(self.user_id, profile).await
427 }
428 None => Err(e),
429 },
430 }
431 }
432
433 pub async fn set_display_name(
434 &self,
435 name: impl Into<String>,
436 ) -> Result<ApiUpdateProfileResponse, C::Error> {
437 let name = name.into();
438 self.update_profile(move |profile| profile.display_name = name)
439 .await
440 }
441
442 pub async fn set_full_name(
443 &self,
444 full_name: impl Into<String>,
445 ) -> Result<ApiUpdateProfileResponse, C::Error> {
446 let full_name = full_name.into();
447 self.update_profile(move |profile| profile.full_name = full_name)
448 .await
449 }
450
451 pub async fn set_bio(
452 &self,
453 bio: impl Into<String>,
454 ) -> Result<ApiUpdateProfileResponse, C::Error> {
455 let bio = bio.into();
456 self.update_profile(move |profile| profile.short_descr = Some(bio))
457 .await
458 }
459
460 pub async fn set_avatar(
462 &self,
463 avatar: ImagePreview,
464 ) -> Result<ApiUpdateProfileResponse, C::Error> {
465 let image = avatar.resolve().await;
466 self.update_profile(move |profile| profile.image = Some(image))
467 .await
468 }
469
470 pub async fn set_peer_type(
472 &self,
473 peer_type: ChatPeerType,
474 ) -> Result<ApiUpdateProfileResponse, C::Error> {
475 self.update_profile(move |profile| profile.peer_type = Some(peer_type))
476 .await
477 }
478
479 pub async fn set_preferences(
481 &self,
482 preferences: Preferences,
483 ) -> Result<ApiUpdateProfileResponse, C::Error> {
484 self.update_profile(move |profile| profile.preferences = Some(preferences))
485 .await
486 }
487
488 pub async fn update_preferences<F>(
490 &self,
491 updater: F,
492 ) -> Result<ApiUpdateProfileResponse, C::Error>
493 where
494 F: 'static + Send + FnOnce(&mut Preferences),
495 {
496 let mut response = self.client.show_active_user().await?;
497 let response = Arc::get_mut(&mut response).unwrap();
498
499 let mut profile = extract_profile(&mut response.user.profile);
500 let mut preferences = extract_preferences(&mut profile.preferences);
501 updater(&mut preferences);
502 profile.preferences = Some(preferences);
503
504 self.client.api_update_profile(self.user_id, profile).await
505 }
506
507 pub async fn set_contact_preferences<CID: Into<ContactId>>(
509 &self,
510 contact_id: CID,
511 preferences: Preferences,
512 ) -> Result<Arc<ContactPrefsUpdatedResponse>, C::Error> {
513 self.client
514 .api_set_contact_prefs(contact_id.into().raw(), preferences)
515 .await
516 }
517
518 pub async fn tweak_preferences_for_contact<CID: Into<ContactId>, F>(
521 &self,
522 contact_id: CID,
523 updater: F,
524 ) -> Result<Arc<ContactPrefsUpdatedResponse>, C::Error>
525 where
526 F: 'static + Send + FnOnce(&mut Preferences),
527 {
528 let mut response = self.client.show_active_user().await?;
529 let response = Arc::get_mut(&mut response).unwrap();
530
531 let mut preferences = extract_preferences(&mut response.user.profile.preferences);
532 updater(&mut preferences);
533
534 self.client
535 .api_set_contact_prefs(contact_id.into().raw(), preferences)
536 .await
537 }
538
539 pub async fn contacts(&self) -> Result<Vec<Contact>, C::Error> {
541 self.client.contacts(self.user_id()).await
542 }
543
544 pub async fn groups(&self) -> Result<Vec<GroupInfo>, C::Error> {
546 self.client.groups(self.user_id()).await
547 }
548
549 pub async fn accept_contact<CRID: Into<ContactRequestId>>(
551 &self,
552 contact_request_id: CRID,
553 ) -> Result<Arc<AcceptingContactRequestResponse>, <C as ClientApi>::Error> {
554 self.client.accept_contact(contact_request_id).await
555 }
556
557 pub async fn reject_contact<CRID: Into<ContactRequestId>>(
559 &self,
560 contact_request_id: CRID,
561 ) -> Result<Arc<ContactRequestRejectedResponse>, <C as ClientApi>::Error> {
562 self.client.reject_contact(contact_request_id).await
563 }
564
565 pub fn send_msg<CID: Into<ChatId>, M: MessageLike>(
567 &self,
568 chat_id: CID,
569 msg: M,
570 ) -> MessageBuilder<'_, C, M::Kind> {
571 self.client.send_message(chat_id.into(), msg)
572 }
573
574 pub fn multicast<I, M>(&self, chat_ids: I, msg: M) -> MulticastBuilder<'_, I, C, M::Kind>
576 where
577 I: IntoIterator<Item = ChatId>,
578 M: MessageLike,
579 {
580 self.client.multicast_message(chat_ids, msg)
581 }
582
583 pub async fn chat_ids(&self) -> Result<impl Iterator<Item = ChatId>, C::Error> {
585 self.chat_ids_with(|_| true).await
586 }
587
588 pub async fn chat_ids_with<F>(
590 &self,
591 f: F,
592 ) -> Result<impl 'static + Send + Iterator<Item = ChatId>, C::Error>
593 where
594 F: 'static + Send + FnMut(&ChatId) -> bool,
595 {
596 let (contacts, groups) = futures::future::try_join(self.contacts(), self.groups()).await?;
597
598 Ok(contacts
599 .into_iter()
600 .map(ChatId::from)
601 .chain(groups.into_iter().map(ChatId::from))
602 .filter(f))
603 }
604
605 pub async fn prepare_broadcast<M: MessageLike>(
614 &self,
615 msg: M,
616 ) -> Result<
617 MulticastBuilder<'_, impl 'static + Send + Iterator<Item = ChatId>, C, M::Kind>,
618 C::Error,
619 > {
620 self.prepare_broadcast_with(msg, |_| true).await
621 }
622
623 pub async fn prepare_broadcast_with<M, F>(
633 &self,
634 msg: M,
635 f: F,
636 ) -> Result<
637 MulticastBuilder<'_, impl 'static + Send + Iterator<Item = ChatId>, C, M::Kind>,
638 C::Error,
639 >
640 where
641 F: 'static + Send + FnMut(&ChatId) -> bool,
642 M: MessageLike,
643 {
644 let ids = self.chat_ids_with(f).await?;
645 let (msg, kind) = msg.into_builder_parts();
646
647 Ok(MulticastBuilder {
648 client: self.client(),
649 chat_ids: ids,
650 ttl: None,
651 msg,
652 kind,
653 })
654 }
655
656 pub async fn update_msg<CID: Into<ChatId>, MID: Into<MessageId>>(
657 &self,
658 chat_id: CID,
659 message_id: MID,
660 new_content: MsgContent,
661 ) -> Result<ApiUpdateChatItemResponse, C::Error> {
662 self.client
663 .update_message(chat_id, message_id, new_content)
664 .await
665 }
666
667 pub async fn delete_msg<CID: Into<ChatId>, MID: Into<MessageId>>(
668 &self,
669 chat_id: CID,
670 message_id: MID,
671 mode: CIDeleteMode,
672 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
673 self.client.delete_message(chat_id, message_id, mode).await
674 }
675
676 pub async fn batch_delete_msgs<CID: Into<ChatId>, I: IntoIterator<Item = MessageId>>(
677 &self,
678 chat_id: CID,
679 message_ids: I,
680 mode: CIDeleteMode,
681 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
682 self.client
683 .batch_delete_messages(chat_id, message_ids, mode)
684 .await
685 }
686
687 pub async fn batch_msg_reactions<
689 CID: Into<ChatId>,
690 MID: Into<MessageId>,
691 I: IntoIterator<Item = Reaction>,
692 >(
693 &self,
694 chat_id: CID,
695 message_id: MID,
696 reactions: I,
697 ) -> Vec<Result<Arc<ChatItemReactionResponse>, C::Error>> {
698 self.client
699 .batch_message_reactions(chat_id, message_id, reactions)
700 .await
701 }
702
703 pub async fn update_msg_reaction<CID: Into<ChatId>, MID: Into<MessageId>>(
704 &self,
705 chat_id: CID,
706 message_id: MID,
707 reaction: Reaction,
708 ) -> Vec<Result<Arc<ChatItemReactionResponse>, C::Error>> {
709 self.client
710 .update_message_reaction(chat_id, message_id, reaction)
711 .await
712 }
713
714 pub fn accept_file<FID: Into<FileId>>(&self, file_id: FID) -> AcceptFileBuilder<'_, C> {
716 self.client.accept_file(file_id)
717 }
718
719 pub async fn reject_file<FID: Into<FileId>>(
720 &self,
721 file_id: FID,
722 ) -> Result<CancelFileResponse, C::Error> {
723 self.client.reject_file(file_id).await
724 }
725
726 pub async fn delete_chat<CID: Into<ChatId>>(
727 &self,
728 chat_id: CID,
729 mode: DeleteMode,
730 ) -> Result<ApiDeleteChatResponse, C::Error> {
731 self.client.delete_chat(chat_id, mode).await
732 }
733
734 pub async fn create_group(
736 &self,
737 mut profile: GroupProfile,
738 ) -> Result<Arc<GroupCreatedResponse>, C::Error> {
739 match self
740 .client
741 .api_new_group(ApiNewGroup::new(self.user_id, profile.clone()))
742 .await
743 {
744 Ok(resp) => Ok(resp),
745 Err(e) => match e.bad_response().and_then(|e| {
746 e.chat_error()
747 .and_then(|e| e.error().and_then(|e| e.invalid_display_name()))
748 }) {
749 Some(err) => {
750 profile.display_name = err.valid_name.clone();
751 self.client
752 .api_new_group(ApiNewGroup::new(self.user_id, profile))
753 .await
754 }
755 None => Err(e),
756 },
757 }
758 }
759
760 pub async fn create_public_group<I: IntoIterator<Item = RelayId>>(
763 &self,
764 relay_ids: I,
765 mut profile: GroupProfile,
766 ) -> Result<ApiNewPublicGroupResponse, C::Error> {
767 let relays: Vec<_> = relay_ids.into_iter().map(|id| id.raw()).collect();
768
769 match self
770 .client
771 .api_new_public_group(ApiNewPublicGroup::new(
772 self.user_id,
773 relays.clone(),
774 profile.clone(),
775 ))
776 .await
777 {
778 Ok(resp) => Ok(resp),
779 Err(e) => match e.bad_response().and_then(|e| {
780 e.chat_error()
781 .and_then(|e| e.error().and_then(|e| e.invalid_display_name()))
782 }) {
783 Some(err) => {
784 profile.display_name = err.valid_name.clone();
785 self.client
786 .api_new_public_group(ApiNewPublicGroup::new(self.user_id, relays, profile))
787 .await
788 }
789 None => Err(e),
790 },
791 }
792 }
793
794 pub async fn set_auto_accept_member_contacts(
796 &self,
797 on: bool,
798 ) -> Result<Arc<CmdOkResponse>, C::Error> {
799 self.client
800 .api_set_user_auto_accept_member_contacts(ApiSetUserAutoAcceptMemberContacts {
801 user_id: self.user_id,
802 on_off: on,
803 })
804 .await
805 }
806
807 pub async fn add_member<GID: Into<GroupId>, CID: Into<ContactId>>(
809 &self,
810 group_id: GID,
811 contact_id: CID,
812 role: GroupMemberRole,
813 ) -> Result<Arc<SentGroupInvitationResponse>, C::Error> {
814 self.client.add_member(group_id, contact_id, role).await
815 }
816
817 pub async fn join_group<GID: Into<GroupId>>(
819 &self,
820 group_id: GID,
821 ) -> Result<Arc<UserAcceptedGroupSentResponse>, C::Error> {
822 self.client.join_group(group_id).await
823 }
824
825 pub async fn accept_member<GID: Into<GroupId>, MID: Into<MemberId>>(
827 &self,
828 group_id: GID,
829 member_id: MID,
830 role: GroupMemberRole,
831 ) -> Result<Arc<MemberAcceptedResponse>, C::Error> {
832 self.client.accept_member(group_id, member_id, role).await
833 }
834
835 pub async fn set_members_role<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
836 &self,
837 group_id: GID,
838 member_ids: I,
839 role: GroupMemberRole,
840 ) -> Result<Arc<MembersRoleUserResponse>, C::Error> {
841 self.client
842 .set_members_role(group_id, member_ids, role)
843 .await
844 }
845
846 pub async fn set_member_role<GID: Into<GroupId>, MID: Into<MemberId>>(
847 &self,
848 group_id: GID,
849 member_id: MID,
850 role: GroupMemberRole,
851 ) -> Result<Arc<MembersRoleUserResponse>, C::Error> {
852 self.client.set_member_role(group_id, member_id, role).await
853 }
854
855 pub async fn block_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
857 &self,
858 group_id: GID,
859 member_ids: I,
860 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
861 self.client
862 .block_members_for_all(group_id, member_ids)
863 .await
864 }
865
866 pub async fn unblock_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
868 &self,
869 group_id: GID,
870 member_ids: I,
871 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
872 self.client
873 .unblock_members_for_all(group_id, member_ids)
874 .await
875 }
876
877 pub async fn block_member_for_all<GID: Into<GroupId>, MID: Into<MemberId>>(
879 &self,
880 group_id: GID,
881 member_id: MID,
882 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
883 self.client.block_member_for_all(group_id, member_id).await
884 }
885
886 pub async fn unblock_member_for_all<GID: Into<GroupId>, MID: Into<MemberId>>(
888 &self,
889 group_id: GID,
890 member_id: MID,
891 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
892 self.client
893 .unblock_member_for_all(group_id, member_id)
894 .await
895 }
896
897 pub async fn remove_members<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
899 &self,
900 group_id: GID,
901 member_ids: I,
902 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
903 self.client.remove_members(group_id, member_ids).await
904 }
905
906 pub async fn remove_members_with_messages<
908 GID: Into<GroupId>,
909 I: IntoIterator<Item = MemberId>,
910 >(
911 &self,
912 group_id: GID,
913 member_ids: I,
914 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
915 self.client
916 .remove_members_with_messages(group_id, member_ids)
917 .await
918 }
919
920 pub async fn remove_member<GID: Into<GroupId>, MID: Into<MemberId>>(
922 &self,
923 group_id: GID,
924 member_id: MID,
925 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
926 self.client.remove_member(group_id, member_id).await
927 }
928
929 pub async fn remove_member_with_messages<GID: Into<GroupId>, MID: Into<MemberId>>(
931 &self,
932 group_id: GID,
933 member_id: MID,
934 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
935 self.client
936 .remove_member_with_messages(group_id, member_id)
937 .await
938 }
939
940 pub async fn leave_group<GID: Into<GroupId>>(
941 &self,
942 group_id: GID,
943 ) -> Result<Arc<LeftMemberUserResponse>, C::Error> {
944 self.client.leave_group(group_id).await
945 }
946
947 pub async fn list_members<GID: Into<GroupId>>(
948 &self,
949 group_id: GID,
950 ) -> Result<Vec<GroupMember>, C::Error> {
951 self.client.list_members(group_id).await
952 }
953
954 pub async fn moderate_messages<GID: Into<GroupId>, I: IntoIterator<Item = MessageId>>(
956 &self,
957 group_id: GID,
958 message_ids: I,
959 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
960 self.client.moderate_messages(group_id, message_ids).await
961 }
962
963 pub async fn moderate_message<GID: Into<GroupId>, MID: Into<MessageId>>(
965 &self,
966 group_id: GID,
967 message_id: MID,
968 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
969 self.client.moderate_message(group_id, message_id).await
970 }
971
972 pub async fn update_group_profile<GID: Into<GroupId>>(
973 &self,
974 group_id: GID,
975 profile: GroupProfile,
976 ) -> Result<Arc<GroupUpdatedResponse>, C::Error> {
977 self.client.update_group_profile(group_id, profile).await
978 }
979
980 pub async fn set_group_custom_data<GID: Into<GroupId>>(
982 &self,
983 group_id: GID,
984 data: Option<JsonObject>,
985 ) -> Result<Arc<CmdOkResponse>, C::Error> {
986 self.client.set_group_custom_data(group_id, data).await
987 }
988
989 pub async fn set_contact_custom_data<CID: Into<ContactId>>(
991 &self,
992 contact_id: CID,
993 data: Option<JsonObject>,
994 ) -> Result<Arc<CmdOkResponse>, C::Error> {
995 self.client.set_contact_custom_data(contact_id, data).await
996 }
997
998 pub async fn create_group_link<GID: Into<GroupId>>(
999 &self,
1000 group_id: GID,
1001 role: GroupMemberRole,
1002 ) -> Result<Arc<GroupLinkCreatedResponse>, C::Error> {
1003 self.client.create_group_link(group_id, role).await
1004 }
1005
1006 pub async fn set_group_link_role<GID: Into<GroupId>>(
1008 &self,
1009 group_id: GID,
1010 role: GroupMemberRole,
1011 ) -> GroupLinkResult<C> {
1012 self.client.set_group_link_role(group_id, role).await
1013 }
1014
1015 pub async fn delete_group_link<GID: Into<GroupId>>(
1016 &self,
1017 group_id: GID,
1018 ) -> Result<Arc<GroupLinkDeletedResponse>, C::Error> {
1019 self.client.delete_group_link(group_id).await
1020 }
1021
1022 pub async fn get_group_link<GID: Into<GroupId>>(&self, group_id: GID) -> GroupLinkResult<C> {
1023 self.client.get_group_link(group_id).await
1024 }
1025
1026 pub async fn get_group_relays<GID: Into<GroupId>>(
1027 &self,
1028 group_id: GID,
1029 ) -> GetGroupRelaysResponse<C> {
1030 self.client.get_group_relays(group_id).await
1031 }
1032
1033 pub async fn add_group_relays<GID: Into<GroupId>, I: IntoIterator<Item = RelayId>>(
1034 &self,
1035 group_id: GID,
1036 relay_ids: I,
1037 ) -> AddGroupRelaysResponse<C> {
1038 self.client.add_group_relays(group_id, relay_ids).await
1039 }
1040
1041 pub async fn add_group_relay<GID: Into<GroupId>, RID: Into<RelayId>>(
1042 &self,
1043 group_id: GID,
1044 relay_id: RID,
1045 ) -> AddGroupRelaysResponse<C> {
1046 self.client.add_group_relay(group_id, relay_id).await
1047 }
1048
1049 pub async fn get_chats(
1052 &self,
1053 pagination: PaginationByTime,
1054 query: ChatListQuery,
1055 ) -> Result<Arc<ApiChatsResponse>, C::Error> {
1056 self.client
1057 .api_get_chats(ApiGetChats::new(self.user_id, pagination, query))
1058 .await
1059 }
1060
1061 pub async fn default_relays(&self) -> Result<Vec<RelayId>, C::Error> {
1063 self.client.default_relays().await
1064 }
1065
1066 pub async fn accept_remote_ctrl(
1075 &self,
1076 handle: &crate::remote::CtrlHandle,
1077 link: &str,
1078 ) -> Result<(), crate::remote::CtrlError<C::Error>> {
1079 handle.accept_remote_ctrl(&self.client, link).await
1080 }
1081}
1082
1083#[cfg(feature = "xftp")]
1084impl<C: crate::xftp::XftpExt> Bot<C> {
1085 pub fn download_file<FID: Into<FileId>>(
1086 &self,
1087 file_id: FID,
1088 ) -> crate::xftp::DownloadFileBuilder<'_, C> {
1089 self.client.download_file(file_id)
1090 }
1091}
1092
1093#[cfg(feature = "websocket")]
1094impl crate::ws::Bot {
1095 pub fn shutdown(self) -> impl Future<Output = ()> {
1096 self.client.disconnect()
1097 }
1098}
1099
1100#[cfg(feature = "ffi")]
1101impl crate::ffi::Bot {
1102 pub fn shutdown(self) -> impl Future<Output = ()> {
1103 self.client.disconnect()
1104 }
1105}
1106
1107#[derive(Debug, Clone)]
1109pub struct BotSettings {
1110 pub display_name: String,
1111 pub auto_accept: Option<String>,
1114 pub profile_settings: Option<BotProfileSettings>,
1115 pub avatar: Option<ImagePreview>,
1116}
1117
1118impl BotSettings {
1119 pub fn new(name: impl Into<String>) -> Self {
1120 Self {
1121 display_name: name.into(),
1122 auto_accept: None,
1123 profile_settings: None,
1124 avatar: None,
1125 }
1126 }
1127
1128 pub fn with_avatar(mut self, avatar: ImagePreview) -> Self {
1129 self.avatar = Some(avatar);
1130 self
1131 }
1132
1133 pub fn auto_accept(mut self) -> Self {
1135 self.auto_accept = Some(String::default());
1136 self
1137 }
1138
1139 pub fn auto_accept_with(mut self, welcome_message: impl Into<String>) -> Self {
1141 self.auto_accept = Some(welcome_message.into());
1142 self
1143 }
1144
1145 pub fn with_profile_settings(mut self, settings: BotProfileSettings) -> Self {
1146 self.profile_settings = Some(settings);
1147 self
1148 }
1149}
1150
1151#[allow(clippy::large_enum_variant)]
1153#[derive(Debug, Clone)]
1154pub enum BotProfileSettings {
1155 Preferences(Preferences),
1157 FullProfile(Profile),
1159}
1160
1161pub enum Connection {
1162 Initiated(UndocumentedResponse<ConnectResponse>),
1163 Rejected(Arc<ConnectionPlanResponse>),
1164}
1165
1166impl Connection {
1167 pub fn rejected(&self) -> Option<&ConnectionPlan> {
1168 if let Self::Rejected(resp) = self {
1169 Some(&resp.connection_plan)
1170 } else {
1171 None
1172 }
1173 }
1174
1175 pub fn initiated(&self) -> Option<&UndocumentedResponse<ConnectResponse>> {
1176 if let Self::Initiated(resp) = self {
1177 Some(resp)
1178 } else {
1179 None
1180 }
1181 }
1182
1183 pub fn is_rejected(&self) -> bool {
1184 self.rejected().is_some()
1185 }
1186
1187 pub fn is_initiated(&self) -> bool {
1188 self.initiated().is_some()
1189 }
1190}
1191
1192fn extract_address(link: &CreatedConnLink) -> String {
1193 link.conn_short_link
1194 .clone()
1195 .unwrap_or_else(|| link.conn_full_link.clone())
1196}
1197
1198fn extract_profile(local: &mut LocalProfile) -> Profile {
1199 Profile {
1200 display_name: std::mem::take(&mut local.display_name),
1201 full_name: std::mem::take(&mut local.full_name),
1202 short_descr: local.short_descr.take(),
1203 image: local.image.take(),
1204 contact_link: local.contact_link.take(),
1205 preferences: local.preferences.take(),
1206 peer_type: local.peer_type.take(),
1207 badge: None,
1208 undocumented: std::mem::take(&mut local.undocumented),
1209 }
1210}
1211
1212fn extract_preferences(preferences: &mut Option<Preferences>) -> Preferences {
1213 match preferences.as_mut() {
1214 Some(prefs) => Preferences {
1215 timed_messages: prefs.timed_messages.take(),
1216 full_delete: prefs.full_delete.take(),
1217 reactions: prefs.reactions.take(),
1218 voice: prefs.voice.take(),
1219 files: prefs.files.take(),
1220 calls: prefs.calls.take(),
1221 sessions: prefs.sessions.take(),
1222 commands: prefs.commands.take(),
1223 undocumented: std::mem::take(&mut prefs.undocumented),
1224 },
1225 None => Preferences {
1226 timed_messages: None,
1227 full_delete: None,
1228 reactions: None,
1229 voice: None,
1230 files: None,
1231 calls: None,
1232 sessions: None,
1233 commands: None,
1234 undocumented: Default::default(),
1235 },
1236 }
1237}