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(settings.display_name),
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 peer_type: Some(ChatPeerType::Bot),
248 undocumented: serde_json::Value::Null,
249 }
250 }
251
252 pub async fn info(&self) -> Result<Arc<ActiveUserResponse>, C::Error> {
254 self.client.show_active_user().await
255 }
256
257 pub async fn initiate_connection(
269 &self,
270 link: impl Into<String>,
271 ) -> Result<UndocumentedResponse<ConnectResponse>, C::Error> {
272 self.client.initiate_connection(link).await
273 }
274
275 pub async fn check_connection_plan(
278 &self,
279 link: impl Into<String>,
280 ) -> Result<Arc<ConnectionPlanResponse>, C::Error> {
281 self.client
282 .api_connect_plan(ApiConnectPlan {
283 user_id: self.user_id,
284 connection_link: Some(link.into()),
285 resolve_known: true,
286 link_owner_sig: None,
287 })
288 .await
289 }
290
291 pub async fn initiate_connection_if<F: FnOnce(&ConnectionPlan) -> bool>(
305 &self,
306 link: impl Into<String>,
307 predicate: F,
308 ) -> Result<Connection, C::Error> {
309 let link = link.into();
310 let plan_resp = self.check_connection_plan(link.clone()).await?;
311
312 if !predicate(&plan_resp.connection_plan) {
313 return Ok(Connection::Rejected(plan_resp));
314 }
315
316 self.initiate_connection(link)
317 .await
318 .map(Connection::Initiated)
319 }
320
321 pub async fn create_invitation_link(
326 &self,
327 ) -> Result<(String, Arc<InvitationResponse>), C::Error> {
328 let response = self
329 .client
330 .api_add_contact(ApiAddContact::new(self.user_id))
331 .await?;
332
333 let link = extract_address(&response.conn_link_invitation);
334 Ok((link, response))
335 }
336
337 pub async fn create_address(&self) -> Result<String, C::Error> {
338 let response = self.client.api_create_my_address(self.user_id).await?;
339 Ok(extract_address(&response.conn_link_contact))
340 }
341
342 pub async fn address(&self) -> Result<String, C::Error> {
345 let response = self.client.api_show_my_address(self.user_id).await?;
346 Ok(extract_address(&response.contact_link.conn_link_contact))
347 }
348
349 pub async fn get_or_create_address(&self) -> Result<String, C::Error> {
350 match self.address().await {
351 Ok(address) => Ok(address),
352 Err(e)
353 if e.bad_response()
354 .and_then(|e| {
355 e.chat_error().and_then(|e| {
356 e.error_store().map(|e| e.is_user_contact_link_not_found())
357 })
358 })
359 .unwrap_or(false) =>
360 {
361 self.create_address().await
362 }
363 Err(e) => Err(e),
364 }
365 }
366
367 pub async fn configure_address(&self, settings: AddressSettings) -> Result<(), C::Error> {
368 self.client
369 .api_set_address_settings(self.user_id, settings)
370 .await
371 .map(drop)
372 }
373
374 pub async fn publish_address(&self) -> Result<Arc<UserProfileUpdatedResponse>, C::Error> {
376 self.client
377 .api_set_profile_address(ApiSetProfileAddress {
378 user_id: self.user_id,
379 enable: true,
380 })
381 .await
382 }
383
384 pub async fn hide_address(&self) -> Result<Arc<UserProfileUpdatedResponse>, C::Error> {
386 self.client
387 .api_set_profile_address(ApiSetProfileAddress {
388 user_id: self.user_id,
389 enable: false,
390 })
391 .await
392 }
393
394 pub async fn delete_address(&self) -> Result<(), C::Error> {
395 self.client.api_delete_my_address(self.user_id).await?;
396 Ok(())
397 }
398
399 pub async fn profile(&self) -> Result<Profile, C::Error> {
400 let mut response = self.client.show_active_user().await?;
401 let response = Arc::get_mut(&mut response).unwrap();
402
403 Ok(extract_profile(&mut response.user.profile))
404 }
405
406 pub async fn update_profile<F>(&self, updater: F) -> Result<ApiUpdateProfileResponse, C::Error>
408 where
409 F: 'static + Send + FnOnce(&mut Profile),
410 {
411 let mut profile = self.profile().await?;
412 updater(&mut profile);
413 self.client.api_update_profile(self.user_id, profile).await
414 }
415
416 pub async fn set_display_name(
417 &self,
418 name: impl Into<String>,
419 ) -> Result<ApiUpdateProfileResponse, C::Error> {
420 let name = name.into();
421 self.update_profile(move |profile| profile.display_name = name)
422 .await
423 }
424
425 pub async fn set_full_name(
426 &self,
427 full_name: impl Into<String>,
428 ) -> Result<ApiUpdateProfileResponse, C::Error> {
429 let full_name = full_name.into();
430 self.update_profile(move |profile| profile.full_name = full_name)
431 .await
432 }
433
434 pub async fn set_bio(
435 &self,
436 bio: impl Into<String>,
437 ) -> Result<ApiUpdateProfileResponse, C::Error> {
438 let bio = bio.into();
439 self.update_profile(move |profile| profile.short_descr = Some(bio))
440 .await
441 }
442
443 pub async fn set_avatar(
445 &self,
446 avatar: ImagePreview,
447 ) -> Result<ApiUpdateProfileResponse, C::Error> {
448 let image = avatar.resolve().await;
449 self.update_profile(move |profile| profile.image = Some(image))
450 .await
451 }
452
453 pub async fn set_peer_type(
455 &self,
456 peer_type: ChatPeerType,
457 ) -> Result<ApiUpdateProfileResponse, C::Error> {
458 self.update_profile(move |profile| profile.peer_type = Some(peer_type))
459 .await
460 }
461
462 pub async fn set_preferences(
464 &self,
465 preferences: Preferences,
466 ) -> Result<ApiUpdateProfileResponse, C::Error> {
467 self.update_profile(move |profile| profile.preferences = Some(preferences))
468 .await
469 }
470
471 pub async fn update_preferences<F>(
473 &self,
474 updater: F,
475 ) -> Result<ApiUpdateProfileResponse, C::Error>
476 where
477 F: 'static + Send + FnOnce(&mut Preferences),
478 {
479 let mut response = self.client.show_active_user().await?;
480 let response = Arc::get_mut(&mut response).unwrap();
481
482 let mut profile = extract_profile(&mut response.user.profile);
483 let mut preferences = extract_preferences(&mut profile.preferences);
484 updater(&mut preferences);
485 profile.preferences = Some(preferences);
486
487 self.client.api_update_profile(self.user_id, profile).await
488 }
489
490 pub async fn set_contact_preferences<CID: Into<ContactId>>(
492 &self,
493 contact_id: CID,
494 preferences: Preferences,
495 ) -> Result<Arc<ContactPrefsUpdatedResponse>, C::Error> {
496 self.client
497 .api_set_contact_prefs(contact_id.into().raw(), preferences)
498 .await
499 }
500
501 pub async fn tweak_preferences_for_contact<CID: Into<ContactId>, F>(
504 &self,
505 contact_id: CID,
506 updater: F,
507 ) -> Result<Arc<ContactPrefsUpdatedResponse>, C::Error>
508 where
509 F: 'static + Send + FnOnce(&mut Preferences),
510 {
511 let mut response = self.client.show_active_user().await?;
512 let response = Arc::get_mut(&mut response).unwrap();
513
514 let mut preferences = extract_preferences(&mut response.user.profile.preferences);
515 updater(&mut preferences);
516
517 self.client
518 .api_set_contact_prefs(contact_id.into().raw(), preferences)
519 .await
520 }
521
522 pub async fn contacts(&self) -> Result<Vec<Contact>, C::Error> {
524 self.client.contacts(self.user_id()).await
525 }
526
527 pub async fn groups(&self) -> Result<Vec<GroupInfo>, C::Error> {
529 self.client.groups(self.user_id()).await
530 }
531
532 pub async fn accept_contact<CRID: Into<ContactRequestId>>(
534 &self,
535 contact_request_id: CRID,
536 ) -> Result<Arc<AcceptingContactRequestResponse>, <C as ClientApi>::Error> {
537 self.client.accept_contact(contact_request_id).await
538 }
539
540 pub async fn reject_contact<CRID: Into<ContactRequestId>>(
542 &self,
543 contact_request_id: CRID,
544 ) -> Result<Arc<ContactRequestRejectedResponse>, <C as ClientApi>::Error> {
545 self.client.reject_contact(contact_request_id).await
546 }
547
548 pub fn send_msg<CID: Into<ChatId>, M: MessageLike>(
550 &self,
551 chat_id: CID,
552 msg: M,
553 ) -> MessageBuilder<'_, C, M::Kind> {
554 self.client.send_message(chat_id.into(), msg)
555 }
556
557 pub fn multicast<I, M>(&self, chat_ids: I, msg: M) -> MulticastBuilder<'_, I, C, M::Kind>
559 where
560 I: IntoIterator<Item = ChatId>,
561 M: MessageLike,
562 {
563 self.client.multicast_message(chat_ids, msg)
564 }
565
566 pub async fn chat_ids(&self) -> Result<impl Iterator<Item = ChatId>, C::Error> {
568 self.chat_ids_with(|_| true).await
569 }
570
571 pub async fn chat_ids_with<F>(
573 &self,
574 f: F,
575 ) -> Result<impl 'static + Send + Iterator<Item = ChatId>, C::Error>
576 where
577 F: 'static + Send + FnMut(&ChatId) -> bool,
578 {
579 let (contacts, groups) = futures::future::try_join(self.contacts(), self.groups()).await?;
580
581 Ok(contacts
582 .into_iter()
583 .map(ChatId::from)
584 .chain(groups.into_iter().map(ChatId::from))
585 .filter(f))
586 }
587
588 pub async fn prepare_broadcast<M: MessageLike>(
597 &self,
598 msg: M,
599 ) -> Result<
600 MulticastBuilder<'_, impl 'static + Send + Iterator<Item = ChatId>, C, M::Kind>,
601 C::Error,
602 > {
603 self.prepare_broadcast_with(msg, |_| true).await
604 }
605
606 pub async fn prepare_broadcast_with<M, F>(
616 &self,
617 msg: M,
618 f: F,
619 ) -> Result<
620 MulticastBuilder<'_, impl 'static + Send + Iterator<Item = ChatId>, C, M::Kind>,
621 C::Error,
622 >
623 where
624 F: 'static + Send + FnMut(&ChatId) -> bool,
625 M: MessageLike,
626 {
627 let ids = self.chat_ids_with(f).await?;
628 let (msg, kind) = msg.into_builder_parts();
629
630 Ok(MulticastBuilder {
631 client: self.client(),
632 chat_ids: ids,
633 ttl: None,
634 msg,
635 kind,
636 })
637 }
638
639 pub async fn update_msg<CID: Into<ChatId>, MID: Into<MessageId>>(
640 &self,
641 chat_id: CID,
642 message_id: MID,
643 new_content: MsgContent,
644 ) -> Result<ApiUpdateChatItemResponse, C::Error> {
645 self.client
646 .update_message(chat_id, message_id, new_content)
647 .await
648 }
649
650 pub async fn delete_msg<CID: Into<ChatId>, MID: Into<MessageId>>(
651 &self,
652 chat_id: CID,
653 message_id: MID,
654 mode: CIDeleteMode,
655 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
656 self.client.delete_message(chat_id, message_id, mode).await
657 }
658
659 pub async fn batch_delete_msgs<CID: Into<ChatId>, I: IntoIterator<Item = MessageId>>(
660 &self,
661 chat_id: CID,
662 message_ids: I,
663 mode: CIDeleteMode,
664 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
665 self.client
666 .batch_delete_messages(chat_id, message_ids, mode)
667 .await
668 }
669
670 pub async fn batch_msg_reactions<
672 CID: Into<ChatId>,
673 MID: Into<MessageId>,
674 I: IntoIterator<Item = Reaction>,
675 >(
676 &self,
677 chat_id: CID,
678 message_id: MID,
679 reactions: I,
680 ) -> Vec<Result<Arc<ChatItemReactionResponse>, C::Error>> {
681 self.client
682 .batch_message_reactions(chat_id, message_id, reactions)
683 .await
684 }
685
686 pub async fn update_msg_reaction<CID: Into<ChatId>, MID: Into<MessageId>>(
687 &self,
688 chat_id: CID,
689 message_id: MID,
690 reaction: Reaction,
691 ) -> Vec<Result<Arc<ChatItemReactionResponse>, C::Error>> {
692 self.client
693 .update_message_reaction(chat_id, message_id, reaction)
694 .await
695 }
696
697 pub fn accept_file<FID: Into<FileId>>(&self, file_id: FID) -> AcceptFileBuilder<'_, C> {
699 self.client.accept_file(file_id)
700 }
701
702 pub async fn reject_file<FID: Into<FileId>>(
703 &self,
704 file_id: FID,
705 ) -> Result<CancelFileResponse, C::Error> {
706 self.client.reject_file(file_id).await
707 }
708
709 pub async fn delete_chat<CID: Into<ChatId>>(
710 &self,
711 chat_id: CID,
712 mode: DeleteMode,
713 ) -> Result<ApiDeleteChatResponse, C::Error> {
714 self.client.delete_chat(chat_id, mode).await
715 }
716
717 pub async fn create_group(
719 &self,
720 mut profile: GroupProfile,
721 ) -> Result<Arc<GroupCreatedResponse>, C::Error> {
722 match self
723 .client
724 .api_new_group(ApiNewGroup::new(self.user_id, profile.clone()))
725 .await
726 {
727 Ok(resp) => Ok(resp),
728 Err(e) => match e.bad_response().and_then(|e| {
729 e.chat_error()
730 .and_then(|e| e.error().and_then(|e| e.invalid_display_name()))
731 }) {
732 Some(err) => {
733 profile.display_name = err.valid_name.clone();
734 self.client
735 .api_new_group(ApiNewGroup::new(self.user_id, profile))
736 .await
737 }
738 None => Err(e),
739 },
740 }
741 }
742
743 pub async fn create_public_group<I: IntoIterator<Item = RelayId>>(
746 &self,
747 relay_ids: I,
748 mut profile: GroupProfile,
749 ) -> Result<ApiNewPublicGroupResponse, C::Error> {
750 let relays: Vec<_> = relay_ids.into_iter().map(|id| id.raw()).collect();
751
752 match self
753 .client
754 .api_new_public_group(ApiNewPublicGroup::new(
755 self.user_id,
756 relays.clone(),
757 profile.clone(),
758 ))
759 .await
760 {
761 Ok(resp) => Ok(resp),
762 Err(e) => match e.bad_response().and_then(|e| {
763 e.chat_error()
764 .and_then(|e| e.error().and_then(|e| e.invalid_display_name()))
765 }) {
766 Some(err) => {
767 profile.display_name = err.valid_name.clone();
768 self.client
769 .api_new_public_group(ApiNewPublicGroup::new(self.user_id, relays, profile))
770 .await
771 }
772 None => Err(e),
773 },
774 }
775 }
776
777 pub async fn set_auto_accept_member_contacts(
779 &self,
780 on: bool,
781 ) -> Result<Arc<CmdOkResponse>, C::Error> {
782 self.client
783 .api_set_user_auto_accept_member_contacts(ApiSetUserAutoAcceptMemberContacts {
784 user_id: self.user_id,
785 on_off: on,
786 })
787 .await
788 }
789
790 pub async fn add_member<GID: Into<GroupId>, CID: Into<ContactId>>(
792 &self,
793 group_id: GID,
794 contact_id: CID,
795 role: GroupMemberRole,
796 ) -> Result<Arc<SentGroupInvitationResponse>, C::Error> {
797 self.client.add_member(group_id, contact_id, role).await
798 }
799
800 pub async fn join_group<GID: Into<GroupId>>(
802 &self,
803 group_id: GID,
804 ) -> Result<Arc<UserAcceptedGroupSentResponse>, C::Error> {
805 self.client.join_group(group_id).await
806 }
807
808 pub async fn accept_member<GID: Into<GroupId>, MID: Into<MemberId>>(
810 &self,
811 group_id: GID,
812 member_id: MID,
813 role: GroupMemberRole,
814 ) -> Result<Arc<MemberAcceptedResponse>, C::Error> {
815 self.client.accept_member(group_id, member_id, role).await
816 }
817
818 pub async fn set_members_role<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
819 &self,
820 group_id: GID,
821 member_ids: I,
822 role: GroupMemberRole,
823 ) -> Result<Arc<MembersRoleUserResponse>, C::Error> {
824 self.client
825 .set_members_role(group_id, member_ids, role)
826 .await
827 }
828
829 pub async fn set_member_role<GID: Into<GroupId>, MID: Into<MemberId>>(
830 &self,
831 group_id: GID,
832 member_id: MID,
833 role: GroupMemberRole,
834 ) -> Result<Arc<MembersRoleUserResponse>, C::Error> {
835 self.client.set_member_role(group_id, member_id, role).await
836 }
837
838 pub async fn block_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
840 &self,
841 group_id: GID,
842 member_ids: I,
843 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
844 self.client
845 .block_members_for_all(group_id, member_ids)
846 .await
847 }
848
849 pub async fn unblock_members_for_all<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
851 &self,
852 group_id: GID,
853 member_ids: I,
854 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
855 self.client
856 .unblock_members_for_all(group_id, member_ids)
857 .await
858 }
859
860 pub async fn block_member_for_all<GID: Into<GroupId>, MID: Into<MemberId>>(
862 &self,
863 group_id: GID,
864 member_id: MID,
865 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
866 self.client.block_member_for_all(group_id, member_id).await
867 }
868
869 pub async fn unblock_member_for_all<GID: Into<GroupId>, MID: Into<MemberId>>(
871 &self,
872 group_id: GID,
873 member_id: MID,
874 ) -> Result<Arc<MembersBlockedForAllUserResponse>, C::Error> {
875 self.client
876 .unblock_member_for_all(group_id, member_id)
877 .await
878 }
879
880 pub async fn remove_members<GID: Into<GroupId>, I: IntoIterator<Item = MemberId>>(
882 &self,
883 group_id: GID,
884 member_ids: I,
885 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
886 self.client.remove_members(group_id, member_ids).await
887 }
888
889 pub async fn remove_members_with_messages<
891 GID: Into<GroupId>,
892 I: IntoIterator<Item = MemberId>,
893 >(
894 &self,
895 group_id: GID,
896 member_ids: I,
897 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
898 self.client
899 .remove_members_with_messages(group_id, member_ids)
900 .await
901 }
902
903 pub async fn remove_member<GID: Into<GroupId>, MID: Into<MemberId>>(
905 &self,
906 group_id: GID,
907 member_id: MID,
908 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
909 self.client.remove_member(group_id, member_id).await
910 }
911
912 pub async fn remove_member_with_messages<GID: Into<GroupId>, MID: Into<MemberId>>(
914 &self,
915 group_id: GID,
916 member_id: MID,
917 ) -> Result<Arc<UserDeletedMembersResponse>, C::Error> {
918 self.client
919 .remove_member_with_messages(group_id, member_id)
920 .await
921 }
922
923 pub async fn leave_group<GID: Into<GroupId>>(
924 &self,
925 group_id: GID,
926 ) -> Result<Arc<LeftMemberUserResponse>, C::Error> {
927 self.client.leave_group(group_id).await
928 }
929
930 pub async fn list_members<GID: Into<GroupId>>(
931 &self,
932 group_id: GID,
933 ) -> Result<Vec<GroupMember>, C::Error> {
934 self.client.list_members(group_id).await
935 }
936
937 pub async fn moderate_messages<GID: Into<GroupId>, I: IntoIterator<Item = MessageId>>(
939 &self,
940 group_id: GID,
941 message_ids: I,
942 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
943 self.client.moderate_messages(group_id, message_ids).await
944 }
945
946 pub async fn moderate_message<GID: Into<GroupId>, MID: Into<MessageId>>(
948 &self,
949 group_id: GID,
950 message_id: MID,
951 ) -> Result<Arc<ChatItemsDeletedResponse>, C::Error> {
952 self.client.moderate_message(group_id, message_id).await
953 }
954
955 pub async fn update_group_profile<GID: Into<GroupId>>(
956 &self,
957 group_id: GID,
958 profile: GroupProfile,
959 ) -> Result<Arc<GroupUpdatedResponse>, C::Error> {
960 self.client.update_group_profile(group_id, profile).await
961 }
962
963 pub async fn set_group_custom_data<GID: Into<GroupId>>(
965 &self,
966 group_id: GID,
967 data: Option<JsonObject>,
968 ) -> Result<Arc<CmdOkResponse>, C::Error> {
969 self.client.set_group_custom_data(group_id, data).await
970 }
971
972 pub async fn set_contact_custom_data<CID: Into<ContactId>>(
974 &self,
975 contact_id: CID,
976 data: Option<JsonObject>,
977 ) -> Result<Arc<CmdOkResponse>, C::Error> {
978 self.client.set_contact_custom_data(contact_id, data).await
979 }
980
981 pub async fn create_group_link<GID: Into<GroupId>>(
982 &self,
983 group_id: GID,
984 role: GroupMemberRole,
985 ) -> Result<Arc<GroupLinkCreatedResponse>, C::Error> {
986 self.client.create_group_link(group_id, role).await
987 }
988
989 pub async fn set_group_link_role<GID: Into<GroupId>>(
991 &self,
992 group_id: GID,
993 role: GroupMemberRole,
994 ) -> GroupLinkResult<C> {
995 self.client.set_group_link_role(group_id, role).await
996 }
997
998 pub async fn delete_group_link<GID: Into<GroupId>>(
999 &self,
1000 group_id: GID,
1001 ) -> Result<Arc<GroupLinkDeletedResponse>, C::Error> {
1002 self.client.delete_group_link(group_id).await
1003 }
1004
1005 pub async fn get_group_link<GID: Into<GroupId>>(&self, group_id: GID) -> GroupLinkResult<C> {
1006 self.client.get_group_link(group_id).await
1007 }
1008
1009 pub async fn get_group_relays<GID: Into<GroupId>>(
1010 &self,
1011 group_id: GID,
1012 ) -> GetGroupRelaysResponse<C> {
1013 self.client.get_group_relays(group_id).await
1014 }
1015
1016 pub async fn add_group_relays<GID: Into<GroupId>, I: IntoIterator<Item = RelayId>>(
1017 &self,
1018 group_id: GID,
1019 relay_ids: I,
1020 ) -> AddGroupRelaysResponse<C> {
1021 self.client.add_group_relays(group_id, relay_ids).await
1022 }
1023
1024 pub async fn add_group_relay<GID: Into<GroupId>, RID: Into<RelayId>>(
1025 &self,
1026 group_id: GID,
1027 relay_id: RID,
1028 ) -> AddGroupRelaysResponse<C> {
1029 self.client.add_group_relay(group_id, relay_id).await
1030 }
1031
1032 pub async fn get_chats(
1035 &self,
1036 pagination: PaginationByTime,
1037 query: ChatListQuery,
1038 ) -> Result<Arc<ApiChatsResponse>, C::Error> {
1039 self.client
1040 .api_get_chats(ApiGetChats::new(self.user_id, pagination, query))
1041 .await
1042 }
1043
1044 pub async fn default_relays(&self) -> Result<Vec<RelayId>, C::Error> {
1046 self.client.default_relays().await
1047 }
1048}
1049
1050#[cfg(feature = "xftp")]
1051impl<C: crate::xftp::XftpExt> Bot<C> {
1052 pub fn download_file<FID: Into<FileId>>(
1053 &self,
1054 file_id: FID,
1055 ) -> crate::xftp::DownloadFileBuilder<'_, C> {
1056 self.client.download_file(file_id)
1057 }
1058}
1059
1060#[cfg(feature = "websocket")]
1061impl crate::ws::Bot {
1062 pub fn shutdown(self) -> impl Future<Output = ()> {
1063 self.client.disconnect()
1064 }
1065}
1066
1067#[cfg(feature = "ffi")]
1068impl crate::ffi::Bot {
1069 pub fn shutdown(self) -> impl Future<Output = ()> {
1070 self.client.disconnect()
1071 }
1072}
1073
1074#[derive(Debug, Clone)]
1076pub struct BotSettings {
1077 pub display_name: String,
1078 pub auto_accept: Option<String>,
1081 pub profile_settings: Option<BotProfileSettings>,
1082 pub avatar: Option<ImagePreview>,
1083}
1084
1085impl BotSettings {
1086 pub fn new(name: impl Into<String>) -> Self {
1087 Self {
1088 display_name: name.into(),
1089 auto_accept: None,
1090 profile_settings: None,
1091 avatar: None,
1092 }
1093 }
1094
1095 pub fn with_avatar(mut self, avatar: ImagePreview) -> Self {
1096 self.avatar = Some(avatar);
1097 self
1098 }
1099
1100 pub fn auto_accept(mut self) -> Self {
1102 self.auto_accept = Some(String::default());
1103 self
1104 }
1105
1106 pub fn auto_accept_with(mut self, welcome_message: impl Into<String>) -> Self {
1108 self.auto_accept = Some(welcome_message.into());
1109 self
1110 }
1111
1112 pub fn with_profile_settings(mut self, settings: BotProfileSettings) -> Self {
1113 self.profile_settings = Some(settings);
1114 self
1115 }
1116}
1117
1118#[derive(Debug, Clone)]
1119pub enum BotProfileSettings {
1120 Preferences(Preferences),
1122 FullProfile(Profile),
1124}
1125
1126pub enum Connection {
1127 Initiated(UndocumentedResponse<ConnectResponse>),
1128 Rejected(Arc<ConnectionPlanResponse>),
1129}
1130
1131impl Connection {
1132 pub fn rejected(&self) -> Option<&ConnectionPlan> {
1133 if let Self::Rejected(resp) = self {
1134 Some(&resp.connection_plan)
1135 } else {
1136 None
1137 }
1138 }
1139
1140 pub fn initiated(&self) -> Option<&UndocumentedResponse<ConnectResponse>> {
1141 if let Self::Initiated(resp) = self {
1142 Some(resp)
1143 } else {
1144 None
1145 }
1146 }
1147
1148 pub fn is_rejected(&self) -> bool {
1149 self.rejected().is_some()
1150 }
1151
1152 pub fn is_initiated(&self) -> bool {
1153 self.initiated().is_some()
1154 }
1155}
1156
1157fn extract_address(link: &CreatedConnLink) -> String {
1158 link.conn_short_link
1159 .clone()
1160 .unwrap_or_else(|| link.conn_full_link.clone())
1161}
1162
1163fn extract_profile(local: &mut LocalProfile) -> Profile {
1164 Profile {
1165 display_name: std::mem::take(&mut local.display_name),
1166 full_name: std::mem::take(&mut local.full_name),
1167 short_descr: local.short_descr.take(),
1168 image: local.image.take(),
1169 contact_link: local.contact_link.take(),
1170 preferences: local.preferences.take(),
1171 peer_type: local.peer_type.take(),
1172 undocumented: std::mem::take(&mut local.undocumented),
1173 }
1174}
1175
1176fn extract_preferences(preferences: &mut Option<Preferences>) -> Preferences {
1177 match preferences.as_mut() {
1178 Some(prefs) => Preferences {
1179 timed_messages: prefs.timed_messages.take(),
1180 full_delete: prefs.full_delete.take(),
1181 reactions: prefs.reactions.take(),
1182 voice: prefs.voice.take(),
1183 files: prefs.files.take(),
1184 calls: prefs.calls.take(),
1185 sessions: prefs.sessions.take(),
1186 commands: prefs.commands.take(),
1187 undocumented: std::mem::take(&mut prefs.undocumented),
1188 },
1189 None => Preferences {
1190 timed_messages: None,
1191 full_delete: None,
1192 reactions: None,
1193 voice: None,
1194 files: None,
1195 calls: None,
1196 sessions: None,
1197 commands: None,
1198 undocumented: Default::default(),
1199 },
1200 }
1201}