1use serde::Deserialize;
4use simploxide_api_types::{
5 NewUser, User, UserInfo,
6 client_api::ClientApi,
7 commands::{ApiDeleteUser, ApiSetActiveUser, CancelFile, ListUsers, ReceiveFile},
8 responses::{CancelFileResponse, ListUsersResponse, ReceiveFileResponse, UsersListResponse},
9};
10use tokio::sync::{
11 mpsc::{self, UnboundedReceiver, UnboundedSender},
12 oneshot,
13};
14
15use std::{
16 collections::{HashMap, hash_map::Entry},
17 sync::Arc,
18};
19
20use crate::{
21 EventParser, EventStream,
22 bot::{BotName, BotSettings},
23 ext::ClientApiExt as _,
24 id::UserId,
25};
26
27mod demux;
28mod mux;
29
30use demux::{BotMap, Channel};
31
32use super::Bot;
33
34#[cfg(feature = "xftp")]
35pub type FarmBot<C> = Bot<crate::xftp::XftpClient<DelegateClient<C>>>;
36
37#[cfg(not(feature = "xftp"))]
38pub type FarmBot<C> = Bot<DelegateClient<C>>;
39
40pub type InitFarm<C, P> = BotFarm<Init<C, P>>;
41
42pub type RunningFarm<C, P> = BotFarm<Running<C, P>>;
43
44#[derive(Clone)]
45pub struct BotFarm<S> {
46 state: S,
47}
48
49impl<C: ClientApi, P: EventParser> BotFarm<Init<C, P>> {
50 pub async fn init(
53 farm_name: impl Into<String>,
54 client: C,
55 events: EventStream<P>,
56 ) -> Result<Self, C::Error> {
57 let farm_name = farm_name.into();
58 let mut farm_id = BotId::anybot();
59 let mut active_name = String::new();
60 let mut cache = HashMap::new();
61 let bots = demux::FxDashMap::with_hasher(rustc_hash::FxBuildHasher);
62
63 let resp = client.users().await?;
64
65 for info in &resp {
66 let bot_id: BotId = UserId::from(info).into();
67
68 if info.user.active_user {
69 active_name = info.user.profile.display_name.clone();
70 }
71
72 if info.user.profile.display_name == farm_name {
73 farm_id = bot_id;
74 continue;
75 }
76
77 bots.insert(bot_id, Channel::Ghost);
78 cache.insert(info.user.profile.display_name.clone(), info.user.clone());
79 }
80
81 let farm_id = match farm_id.get() {
82 Some(user_id) => user_id,
83 None => {
84 let resp = client
85 .create_active_user(NewUser {
86 profile: Some(Bot::<C>::default_profile(farm_name.clone())),
87 client_service: false,
88 past_timestamp: false,
89 user_chat_relay: false,
90 undocumented: Default::default(),
91 })
92 .await?;
93
94 active_name = farm_name.clone();
95 UserId::from(&resp.user)
96 }
97 };
98
99 let state = Init {
100 client,
101 events,
102 farm_id,
103 farm_name,
104 active_name,
105 bots,
106 cache: Cache(cache),
107 };
108
109 Ok(Self { state })
110 }
111
112 pub fn users_count(&self) -> usize {
114 self.state.cache.len()
115 }
116
117 pub fn users(&self) -> impl Iterator<Item = &User> {
119 self.state.cache.values()
120 }
121
122 pub fn user(&self, name: &str) -> Option<&User> {
123 self.state.cache.get(name)
124 }
125
126 pub async fn remove(&mut self, user_id: UserId) -> Result<(), C::Error> {
127 self.state
128 .client
129 .api_set_active_user(ApiSetActiveUser::new(self.state.farm_id.raw()))
130 .await?;
131
132 self.state.active_name = self.state.farm_name.clone();
133
134 let resp = self
135 .state
136 .client
137 .api_delete_user(ApiDeleteUser {
138 user_id: user_id.raw(),
139 del_smp_queues: true,
140 view_pwd: None,
141 })
142 .await?;
143
144 self.state.bots.remove(&user_id.into());
145
146 let Some(user) = resp.user.as_ref() else {
147 return Ok(());
148 };
149
150 self.state.cache.remove(&user.profile.display_name);
151
152 Ok(())
153 }
154
155 pub async fn remove_by_name(&mut self, name: &str) -> Result<(), C::Error> {
156 let Some(user) = self.state.cache.remove(name) else {
157 return Ok(());
158 };
159
160 let result = self.remove(UserId::from(&user)).await;
161
162 if result.is_err() {
163 self.state.cache.insert(name.to_owned(), user);
164 }
165
166 result
167 }
168
169 pub async fn prepare_bot(
171 &mut self,
172 settings: BotSettings,
173 ) -> Result<UserId, CreateError<C::Error>>
174 where
175 C: Clone,
176 {
177 let user_id = self.prepare_inner(settings).await?;
178 self.state.bots.insert(user_id.into(), Channel::new_bot());
179
180 Ok(user_id)
181 }
182
183 pub async fn prepare_ghost(
186 &mut self,
187 settings: BotSettings,
188 ) -> Result<UserId, CreateError<C::Error>>
189 where
190 C: Clone,
191 {
192 let user_id = self.prepare_inner(settings).await?;
193 self.state.bots.insert(user_id.into(), Channel::Ghost);
194
195 Ok(user_id)
196 }
197
198 pub fn run(self) -> (BotFarm<Running<C, P>>, EventStream<P>)
208 where
209 C: 'static + Send,
210 C::Error: Send,
211 P: 'static + Send,
212 {
213 let (delegate_client, rx) = DelegateClient::new(self.state.farm_id.into());
214 mux::start(self.state.client, rx);
215
216 let bots = Arc::new(self.state.bots);
217 let (suspender, mut unmuxed_events) = demux::start(bots.clone(), self.state.events);
218
219 unmuxed_events.exclude_user(self.state.farm_id);
220
221 #[cfg(feature = "xftp")]
222 let (xftp_client, unmuxed_events) = unmuxed_events.hook_xftp(delegate_client.clone());
223
224 let state = Running {
225 farm_name: self.state.farm_name,
226 client: delegate_client,
227 suspender,
228 bots,
229 #[cfg(feature = "xftp")]
230 xftp: xftp_client.manager(),
231 };
232
233 (BotFarm { state }, unmuxed_events)
234 }
235
236 async fn prepare_inner(
237 &mut self,
238 settings: BotSettings,
239 ) -> Result<UserId, CreateError<C::Error>>
240 where
241 C: Clone,
242 {
243 if settings.display_name.matches(&self.state.farm_name) {
244 return Err(CreateError::FarmUser);
245 }
246
247 match self.state.cache.user_by_name(settings.display_name.clone()) {
248 Entry::Occupied(mut occupied) => {
249 let old_key = occupied.key().clone();
250 let bot = Bot::<C>::init_existing(
251 self.state.client.clone(),
252 occupied.get_mut(),
253 settings,
254 )
255 .await?;
256 let update = bot.info().await?;
257 let new_name = update.user.profile.display_name.clone();
258
259 if old_key == new_name {
260 *occupied.get_mut() = update.user.clone();
261 } else {
262 occupied.remove();
263 self.state
264 .cache
265 .insert(new_name.clone(), update.user.clone());
266 }
267
268 self.change_active_user(new_name);
269 Ok(bot.user_id())
270 }
271 Entry::Vacant(vacant) => {
272 let bot = Bot::<C>::init_new(self.state.client.clone(), settings).await?;
273 let update = bot.info().await?;
274
275 vacant.insert(update.user.clone());
276
277 self.change_active_user(update.user.profile.display_name.clone());
278 Ok(bot.user_id())
279 }
280 }
281 }
282
283 fn change_active_user(&mut self, new_active_username: String) {
284 if new_active_username == self.state.active_name {
285 return;
286 }
287
288 if let Some(user) = self.state.cache.get_mut(&self.state.active_name) {
289 user.active_user = false;
290 }
291
292 self.state.active_name = new_active_username;
293 }
294}
295
296impl<C: 'static + ClientApi, P: EventParser> BotFarm<Running<C, P>>
297where
298 C::Error: Send,
299{
300 pub fn ghost(&self, user_id: UserId) -> Option<FarmBot<C>> {
307 let chan = self.state.bots.get(&user_id.into())?;
308
309 if let Channel::Ghost = chan.value() {
310 Some(self.make_ghost(user_id))
311 } else {
312 None
313 }
314 }
315
316 pub fn take_bot(&self, user_id: UserId) -> (FarmBot<C>, EventStream<P>) {
322 let mut chan = self.state.bots.get_mut(&user_id.into()).unwrap();
323
324 if chan.is_ghost() {
325 panic!("The {user_id:?} was not initialized as bot");
326 }
327
328 let receiver = chan
329 .take_receiver()
330 .unwrap_or_else(|| panic!("The {user_id:?} was already taken"));
331
332 self.make_bot(user_id, receiver)
333 }
334
335 pub fn take_bot_checked(&self, user_id: UserId) -> Option<(FarmBot<C>, EventStream<P>)> {
338 self.state
339 .bots
340 .get_mut(&user_id.into())
341 .and_then(|mut chan| chan.take_receiver())
342 .map(|receiver| self.make_bot(user_id, receiver))
343 }
344
345 #[allow(clippy::type_complexity)]
346 pub fn create_bot(
352 &self,
353 settings: BotSettings,
354 ) -> impl Future<Output = Result<(FarmBot<C>, EventStream<P>), CreateError<C::Error>>> {
355 self.create_bot_with_hint(settings, None)
356 }
357
358 async fn create_bot_with_hint(
359 &self,
360 settings: BotSettings,
361 hint: Option<Vec<UserInfo>>,
362 ) -> Result<(FarmBot<C>, EventStream<P>), CreateError<C::Error>> {
363 let user_id = self
364 .create_inner(settings, Channel::new_bot(), hint)
365 .await?;
366
367 let (bot, stream) = self.take_bot(user_id);
368 Ok((bot, stream))
369 }
370
371 pub async fn get_or_create_bot(
380 &self,
381 settings: BotSettings,
382 ) -> Result<(FarmBot<C>, EventStream<P>), CreateError<C::Error>> {
383 let resp = self.state.client.users().await?;
384
385 match resp.iter().find_map(|info| {
386 (settings
387 .display_name
388 .matches_new(&info.user.profile.display_name))
389 .then_some(UserId::from(info))
390 }) {
391 Some(user_id) => match self.state.bots.get_mut(&user_id.into()) {
392 Some(mut entry) => match entry.value_mut() {
393 Channel::Bot(pipe) => {
394 let receiver = pipe.take_receiver().ok_or(CreateError::BotAlreadyTaken)?;
395 Ok(self.make_bot(user_id, receiver))
396 }
397 Channel::Ghost => Err(CreateError::BotIsGhost),
398 },
399 None => Err(CreateError::Desync),
400 },
401 None => self.create_bot_with_hint(settings, Some(resp)).await,
402 }
403 }
404
405 #[allow(clippy::type_complexity)]
406 pub fn create_ghost(
410 &self,
411 settings: BotSettings,
412 ) -> impl Future<Output = Result<FarmBot<C>, CreateError<C::Error>>> {
413 self.create_ghost_with_hint(settings, None)
414 }
415
416 async fn create_ghost_with_hint(
417 &self,
418 settings: BotSettings,
419 hint: Option<Vec<UserInfo>>,
420 ) -> Result<FarmBot<C>, CreateError<C::Error>> {
421 let user_id = self.create_inner(settings, Channel::Ghost, hint).await?;
422 Ok(self.ghost(user_id).unwrap())
423 }
424
425 pub async fn get_or_create_ghost(
430 &self,
431 settings: BotSettings,
432 ) -> Result<FarmBot<C>, CreateError<C::Error>> {
433 let resp = self.state.client.users().await?;
434
435 match resp.iter().find_map(|info| {
436 (settings
437 .display_name
438 .matches_new(&info.user.profile.display_name))
439 .then_some(UserId::from(&info.user))
440 }) {
441 Some(user_id) => match self.state.bots.get(&user_id.into()) {
442 Some(entry) => match entry.value() {
443 Channel::Bot(_) => Err(CreateError::GhostIsBot),
444 Channel::Ghost => Ok(self.make_ghost(user_id)),
445 },
446 None => Err(CreateError::Desync),
447 },
448 None => self.create_ghost_with_hint(settings, Some(resp)).await,
449 }
450 }
451
452 pub async fn delete(&self, user_id: UserId) -> Result<(), C::Error> {
457 self.state
458 .client
459 .api_delete_user(ApiDeleteUser {
460 user_id: user_id.raw(),
461 del_smp_queues: true,
462 view_pwd: None,
463 })
464 .await?;
465
466 self.state.bots.remove(&user_id.into());
467 Ok(())
468 }
469
470 async fn create_inner(
471 &self,
472 settings: BotSettings,
473 channel: Channel<P>,
474 hint: Option<Vec<UserInfo>>,
475 ) -> Result<UserId, CreateError<C::Error>> {
476 if settings.display_name.matches(&self.state.farm_name) {
477 return Err(CreateError::FarmUser);
478 }
479
480 let (_guard, suspension) = oneshot::channel();
481 let _ = self.state.suspender.send(suspension);
482
483 let mut known_users = match hint {
484 Some(hint) => hint,
485 None => self.state.client.users().await?,
486 };
487
488 match settings.display_name.match_user(&mut known_users) {
489 Some(user) => {
490 let user_id = UserId::from(&*user);
491
492 self.state.bots.insert(user_id.into(), channel);
493 let client = self.state.client.delegate_to(user_id);
494
495 match Bot::init_existing(client, user, settings).await {
496 Ok(bot) => Ok(bot.user_id()),
497 Err(e) => {
498 self.state.bots.remove(&user_id.into());
499 Err(e.into())
500 }
501 }
502 }
503 None => {
504 let mut resp = self
505 .state
506 .client
507 .new_user(NewUser {
508 profile: Some(Bot::<C>::default_profile(settings.display_name.current())),
509 client_service: false,
510 past_timestamp: false,
511 user_chat_relay: false,
512 undocumented: Default::default(),
513 })
514 .await?;
515
516 let resp = Arc::get_mut(&mut resp).unwrap();
517 let user_id = UserId::from(&resp.user);
518
519 self.state.bots.insert(user_id.into(), channel);
520 let client = self.state.client.delegate_to(user_id);
521
522 self.init_new(client, &mut resp.user, settings).await
523 }
524 }
525 }
526
527 async fn init_new(
528 &self,
529 client: DelegateClient<C>,
530 user: &mut User,
531 settings: BotSettings,
532 ) -> Result<UserId, CreateError<C::Error>> {
533 match Bot::init_existing(client, user, settings).await {
534 Ok(bot) => Ok(bot.user_id()),
535 Err(e) => {
536 if let Err(err) = self.delete(UserId::from(&*user)).await {
537 log::warn!("Failed to delete incorrectly initialized bot: {err}")
538 }
539 Err(e.into())
540 }
541 }
542 }
543
544 fn make_bot(
545 &self,
546 user_id: UserId,
547 receiver: UnboundedReceiver<P>,
548 ) -> (FarmBot<C>, EventStream<P>) {
549 let bot_client = self.state.client.delegate_to(user_id);
550 let stream = EventStream::from(receiver);
551
552 #[cfg(feature = "xftp")]
553 let (bot_client, stream) = stream.hook_xftp(bot_client);
554
555 (Bot::new(bot_client, user_id), stream)
556 }
557
558 fn make_ghost(&self, user_id: UserId) -> FarmBot<C> {
559 let bot_client = self.state.client.delegate_to(user_id);
560
561 #[cfg(feature = "xftp")]
562 let bot_client = crate::xftp::XftpClient::new(bot_client, self.state.xftp.clone());
563
564 Bot::new(bot_client, user_id)
565 }
566}
567
568pub struct Init<C, P> {
569 client: C,
570 events: EventStream<P>,
571 farm_id: UserId,
572 farm_name: String,
573 active_name: String,
574 bots: BotMap<P>,
575 cache: Cache,
576}
577
578struct Cache(HashMap<String, User>);
579
580impl Cache {
581 fn user_by_name(&mut self, name: BotName) -> Entry<'_, String, User> {
582 match name {
583 BotName::Current(name) => self.0.entry(name),
584 BotName::Rename { from, to } => {
585 if self.0.contains_key(&to) {
586 self.0.entry(to)
587 } else {
588 for old in from {
589 if self.0.contains_key(&old) {
590 return self.0.entry(old);
591 }
592 }
593
594 self.0.entry(to)
595 }
596 }
597 }
598 }
599}
600
601impl std::ops::Deref for Cache {
602 type Target = HashMap<String, User>;
603
604 fn deref(&self) -> &Self::Target {
605 &self.0
606 }
607}
608
609impl std::ops::DerefMut for Cache {
610 fn deref_mut(&mut self) -> &mut Self::Target {
611 &mut self.0
612 }
613}
614
615#[derive(Clone)]
616pub struct Running<C: ClientApi, P> {
617 farm_name: String,
618 client: DelegateClient<C>,
619 suspender: demux::Suspender,
620 bots: Arc<BotMap<P>>,
621 #[cfg(feature = "xftp")]
622 xftp: Arc<crate::xftp::XftpManager>,
623}
624
625pub struct DelegateClient<C: ClientApi> {
626 bot_id: BotId,
627 sender: DelegateSender<C>,
628}
629
630impl<C: ClientApi> Clone for DelegateClient<C> {
631 fn clone(&self) -> Self {
632 Self {
633 bot_id: self.bot_id,
634 sender: self.sender.clone(),
635 }
636 }
637}
638
639impl<C: ClientApi> DelegateClient<C> {
640 fn new(bot_id: BotId) -> (Self, DelegateReceiver<C>) {
641 let (sender, receiver) = mpsc::unbounded_channel();
642 (Self { bot_id, sender }, receiver)
643 }
644
645 fn delegate_to(&self, bot_id: impl Into<BotId>) -> Self {
646 Self {
647 bot_id: bot_id.into(),
648 sender: self.sender.clone(),
649 }
650 }
651}
652
653impl<C: ClientApi> ClientApi for DelegateClient<C>
654where
655 C::Error: Send,
656{
657 type ResponseShape<'de, T: 'de + Deserialize<'de>> = C::ResponseShape<'de, T>;
658 type Error = C::Error;
659
660 async fn send_raw(&self, cmd: String) -> Result<String, Self::Error> {
661 let (responder, response) = oneshot::channel();
662
663 let request = DelegateRequest {
664 bot_id: self.bot_id,
665 cmd,
666 responder,
667 };
668
669 self.sender
670 .send(request)
671 .expect("Delegate client cannot outlive background task");
672
673 response
674 .await
675 .expect("Delegate client cannot outlive background task")
676 }
677
678 async fn list_users(&self) -> Result<Arc<UsersListResponse>, Self::Error> {
679 let client = self.delegate_to(BotId::anybot());
680 let response: ListUsersResponse = client.send(ListUsers {}).await?;
681 Ok(response.into_inner())
682 }
683
684 async fn receive_file(&self, cmd: ReceiveFile) -> Result<ReceiveFileResponse, Self::Error> {
685 let client = self.delegate_to(BotId::anybot());
686 client.send(cmd).await
687 }
688
689 async fn cancel_file(&self, file_id: i64) -> Result<CancelFileResponse, Self::Error> {
690 let client = self.delegate_to(BotId::anybot());
691 client.send(CancelFile { file_id }).await
692 }
693}
694
695#[derive(Debug)]
696pub enum CreateError<E> {
697 FarmUser,
699 BotIsGhost,
701 GhostIsBot,
703 BotAlreadyTaken,
705 Desync,
707 Api(E),
708}
709
710impl<E> From<E> for CreateError<E> {
711 fn from(value: E) -> Self {
712 Self::Api(value)
713 }
714}
715
716impl<E> std::fmt::Display for CreateError<E>
717where
718 E: std::fmt::Display,
719{
720 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
721 match self {
722 Self::FarmUser => write!(
723 f,
724 "Attempt to create a farm user. Farm user is special and cannot be interacted with directly"
725 ),
726 Self::BotIsGhost => write!(
727 f,
728 "Cannot create a bot because the ghost user with the same name already exists"
729 ),
730 Self::GhostIsBot => write!(
731 f,
732 "Cannot create a ghost because the bot user with the same name already exists"
733 ),
734 Self::BotAlreadyTaken => {
735 write!(
736 f,
737 "The bot already exists and has been taken from the farm. Cannot recreate operational bots"
738 )
739 }
740 Self::Desync => {
741 write!(
742 f,
743 "The DB state was not in sync with the memory state, try again"
744 )
745 }
746 Self::Api(e) => write!(f, "{e:#}"),
747 }
748 }
749}
750
751impl<E: 'static + std::error::Error> std::error::Error for CreateError<E> {
752 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
753 if let Self::Api(err) = self {
754 Some(err)
755 } else {
756 None
757 }
758 }
759}
760
761struct DelegateRequest<C: ClientApi> {
762 bot_id: BotId,
763 cmd: String,
764 responder: oneshot::Sender<Result<String, C::Error>>,
765}
766
767type DelegateSender<C> = UnboundedSender<DelegateRequest<C>>;
768type DelegateReceiver<C> = UnboundedReceiver<DelegateRequest<C>>;
769
770#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
771#[repr(transparent)]
772struct BotId(Option<UserId>);
773
774impl BotId {
775 fn anybot() -> Self {
777 Self(None)
778 }
779
780 fn get(&self) -> Option<UserId> {
781 self.0
782 }
783}
784
785impl From<UserId> for BotId {
786 fn from(user_id: UserId) -> Self {
787 Self(Some(user_id))
788 }
789}