1use serde::Deserialize;
4use simploxide_api_types::{
5 NewUser, User,
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::{EventParser, EventStream, bot::BotSettings, ext::ClientApiExt as _, id::UserId};
21
22mod demux;
23mod mux;
24
25use demux::{BotMap, Channel};
26
27use super::Bot;
28
29#[cfg(feature = "xftp")]
30pub type FarmBot<C> = Bot<crate::xftp::XftpClient<DelegateClient<C>>>;
31
32#[cfg(not(feature = "xftp"))]
33pub type FarmBot<C> = Bot<DelegateClient<C>>;
34
35pub type InitFarm<C, P> = BotFarm<Init<C, P>>;
36
37pub type RunningFarm<C, P> = BotFarm<Running<C, P>>;
38
39#[derive(Clone)]
40pub struct BotFarm<S> {
41 state: S,
42}
43
44impl<C: ClientApi, P: EventParser> BotFarm<Init<C, P>> {
45 pub async fn init(
48 farm_name: String,
49 client: C,
50 events: EventStream<P>,
51 ) -> Result<Self, C::Error> {
52 let mut farm_id = BotId::anybot();
53 let mut active_name = String::new();
54 let mut cache = HashMap::new();
55 let bots = demux::FxDashMap::with_hasher(rustc_hash::FxBuildHasher);
56
57 let resp = client.list_users().await?;
58
59 for info in &resp.users {
60 let bot_id: BotId = UserId::from(info).into();
61
62 if info.user.active_user {
63 active_name = info.user.profile.display_name.clone();
64 }
65
66 if info.user.profile.display_name == farm_name {
67 farm_id = bot_id;
68 continue;
69 }
70
71 bots.insert(bot_id, Channel::Ghost);
72 cache.insert(info.user.profile.display_name.clone(), info.user.clone());
73 }
74
75 let farm_id = match farm_id.get() {
76 Some(user_id) => user_id,
77 None => {
78 let resp = client
79 .create_active_user(NewUser {
80 profile: Some(Bot::<C>::default_profile(farm_name.clone())),
81 past_timestamp: false,
82 user_chat_relay: false,
83 undocumented: Default::default(),
84 })
85 .await?;
86
87 active_name = farm_name.clone();
88 UserId::from(&resp.user)
89 }
90 };
91
92 let state = Init {
93 client,
94 events,
95 farm_id,
96 farm_name,
97 active_name,
98 bots,
99 cache,
100 };
101
102 Ok(Self { state })
103 }
104
105 pub fn users_count(&self) -> usize {
107 self.state.cache.len()
108 }
109
110 pub fn users(&self) -> impl Iterator<Item = &User> {
112 self.state.cache.values()
113 }
114
115 pub fn user(&self, name: &str) -> Option<&User> {
116 self.state.cache.get(name)
117 }
118
119 pub async fn remove(&mut self, user_id: UserId) -> Result<(), C::Error> {
120 self.state
121 .client
122 .api_set_active_user(ApiSetActiveUser::new(self.state.farm_id.raw()))
123 .await?;
124
125 self.state.active_name = self.state.farm_name.clone();
126
127 let resp = self
128 .state
129 .client
130 .api_delete_user(ApiDeleteUser {
131 user_id: user_id.raw(),
132 del_smp_queues: true,
133 view_pwd: None,
134 })
135 .await?;
136
137 let user = resp.user.as_ref().unwrap();
138
139 self.state.bots.remove(&user_id.into());
140 self.state.cache.remove(&user.profile.display_name);
141
142 Ok(())
143 }
144
145 pub async fn remove_by_name(&mut self, name: &str) -> Result<(), C::Error> {
146 let Some(user) = self.state.cache.remove(name) else {
147 return Ok(());
148 };
149
150 let result = self.remove(UserId::from(&user)).await;
151
152 if result.is_err() {
153 self.state.cache.insert(name.to_owned(), user);
154 }
155
156 result
157 }
158
159 pub async fn prepare_bot(
161 &mut self,
162 settings: BotSettings,
163 ) -> Result<UserId, CreateError<C::Error>>
164 where
165 C: Clone,
166 {
167 let user_id = self.prepare_inner(settings).await?;
168 self.state.bots.insert(user_id.into(), Channel::new_bot());
169
170 Ok(user_id)
171 }
172
173 pub async fn prepare_ghost(
176 &mut self,
177 settings: BotSettings,
178 ) -> Result<UserId, CreateError<C::Error>>
179 where
180 C: Clone,
181 {
182 let user_id = self.prepare_inner(settings).await?;
183 self.state.bots.insert(user_id.into(), Channel::Ghost);
184
185 Ok(user_id)
186 }
187
188 pub fn run(self) -> (BotFarm<Running<C, P>>, EventStream<P>)
198 where
199 C: 'static + Send,
200 C::Error: Send,
201 P: 'static + Send,
202 {
203 let (delegate_client, rx) = DelegateClient::new(self.state.farm_id.into());
204 mux::start(self.state.client, rx);
205
206 let bots = Arc::new(self.state.bots);
207 let (suspender, mut unmuxed_events) = demux::start(bots.clone(), self.state.events);
208
209 unmuxed_events.exclude_user(self.state.farm_id);
210
211 #[cfg(feature = "xftp")]
212 let (xftp_client, unmuxed_events) = unmuxed_events.hook_xftp(delegate_client.clone());
213
214 let state = Running {
215 farm_name: self.state.farm_name,
216 client: delegate_client,
217 suspender,
218 bots,
219 #[cfg(feature = "xftp")]
220 xftp: xftp_client.manager(),
221 };
222
223 (BotFarm { state }, unmuxed_events)
224 }
225
226 async fn prepare_inner(
227 &mut self,
228 settings: BotSettings,
229 ) -> Result<UserId, CreateError<C::Error>>
230 where
231 C: Clone,
232 {
233 if settings.display_name == self.state.farm_name {
234 return Err(CreateError::FarmUser);
235 }
236
237 match self.state.cache.entry(settings.display_name.clone()) {
238 Entry::Occupied(mut occupied) => {
239 let bot = Bot::<C>::init_existing(
240 self.state.client.clone(),
241 occupied.get_mut(),
242 settings,
243 )
244 .await?;
245 let update = bot.info().await?;
246
247 *occupied.get_mut() = update.user.clone();
248 self.change_active_user(update.user.profile.display_name.clone());
249
250 Ok(bot.user_id())
251 }
252 Entry::Vacant(vacant) => {
253 let bot = Bot::<C>::init_new(self.state.client.clone(), settings).await?;
254 let update = bot.info().await?;
255
256 vacant.insert(update.user.clone());
257
258 self.change_active_user(update.user.profile.display_name.clone());
259 Ok(bot.user_id())
260 }
261 }
262 }
263
264 fn change_active_user(&mut self, new_active_username: String) {
265 if new_active_username == self.state.active_name {
266 return;
267 }
268
269 if let Some(user) = self.state.cache.get_mut(&self.state.active_name) {
270 user.active_user = false;
271 }
272
273 self.state.active_name = new_active_username;
274 }
275}
276
277impl<C: 'static + ClientApi, P: EventParser> BotFarm<Running<C, P>>
278where
279 C::Error: Send,
280{
281 pub fn ghost(&self, user_id: UserId) -> Option<FarmBot<C>> {
288 let chan = self.state.bots.get(&user_id.into())?;
289
290 if let Channel::Ghost = chan.value() {
291 Some(self.make_ghost(user_id))
292 } else {
293 None
294 }
295 }
296
297 pub fn take_bot(&self, user_id: UserId) -> (FarmBot<C>, EventStream<P>) {
303 let mut chan = self.state.bots.get_mut(&user_id.into()).unwrap();
304
305 if chan.is_ghost() {
306 panic!("The {user_id:?} was not initialized as bot");
307 }
308
309 let receiver = chan
310 .take_receiver()
311 .unwrap_or_else(|| panic!("The {user_id:?} was already taken"));
312
313 self.make_bot(user_id, receiver)
314 }
315
316 pub fn take_bot_checked(&self, user_id: UserId) -> Option<(FarmBot<C>, EventStream<P>)> {
319 self.state
320 .bots
321 .get_mut(&user_id.into())
322 .and_then(|mut chan| chan.take_receiver())
323 .map(|receiver| self.make_bot(user_id, receiver))
324 }
325
326 pub async fn create_bot(
332 &self,
333 settings: BotSettings,
334 ) -> Result<(FarmBot<C>, EventStream<P>), CreateError<C::Error>> {
335 let user_id = self.create_inner(settings, true).await?;
336 let (bot, stream) = self.take_bot(user_id);
337 Ok((bot, stream))
338 }
339
340 pub async fn get_or_create_bot(
349 &self,
350 settings: BotSettings,
351 ) -> Result<(FarmBot<C>, EventStream<P>), CreateError<C::Error>> {
352 let resp = self.state.client.list_users().await?;
353
354 match resp.users.iter().find_map(|info| {
355 (info.user.profile.display_name == settings.display_name).then_some(UserId::from(info))
356 }) {
357 Some(user_id) => match self.state.bots.get_mut(&user_id.into()) {
358 Some(mut entry) => match entry.value_mut() {
359 Channel::Bot(pipe) => {
360 let receiver = pipe.take_receiver().ok_or(CreateError::BotAlreadyTaken)?;
361 Ok(self.make_bot(user_id, receiver))
362 }
363 Channel::Ghost => Err(CreateError::BotIsGhost),
364 },
365 None => Err(CreateError::Desync),
366 },
367 None => self.create_bot(settings).await,
368 }
369 }
370
371 pub async fn create_ghost(
375 &self,
376 settings: BotSettings,
377 ) -> Result<FarmBot<C>, CreateError<C::Error>> {
378 let user_id = self.create_inner(settings, false).await?;
379 Ok(self.ghost(user_id).unwrap())
380 }
381
382 pub async fn get_or_create_ghost(
387 &self,
388 settings: BotSettings,
389 ) -> Result<FarmBot<C>, CreateError<C::Error>> {
390 let resp = self.state.client.list_users().await?;
391
392 match resp.users.iter().find_map(|info| {
393 (info.user.profile.display_name == settings.display_name)
394 .then_some(UserId::from(&info.user))
395 }) {
396 Some(user_id) => match self.state.bots.get(&user_id.into()) {
397 Some(entry) => match entry.value() {
398 Channel::Bot(_) => Err(CreateError::GhostIsBot),
399 Channel::Ghost => Ok(self.make_ghost(user_id)),
400 },
401 None => Err(CreateError::Desync),
402 },
403 None => self.create_ghost(settings).await,
404 }
405 }
406
407 pub async fn delete(&self, user_id: UserId) -> Result<(), C::Error> {
412 self.state
413 .client
414 .api_delete_user(ApiDeleteUser {
415 user_id: user_id.raw(),
416 del_smp_queues: true,
417 view_pwd: None,
418 })
419 .await?;
420
421 self.state.bots.remove(&user_id.into());
422 Ok(())
423 }
424
425 async fn create_inner(
426 &self,
427 settings: BotSettings,
428 is_bot: bool,
429 ) -> Result<UserId, CreateError<C::Error>> {
430 if settings.display_name == self.state.farm_name {
431 return Err(CreateError::FarmUser);
432 }
433
434 let (_guard, suspension) = oneshot::channel();
435 let _ = self.state.suspender.send(suspension);
436
437 let mut resp = self
438 .state
439 .client
440 .new_user(NewUser {
441 profile: Some(Bot::<C>::default_profile(&settings.display_name)),
442 past_timestamp: false,
443 user_chat_relay: false,
444 undocumented: Default::default(),
445 })
446 .await?;
447
448 if is_bot {
449 self.state
450 .bots
451 .insert(UserId::from(&resp.user).into(), Channel::new_bot());
452 } else {
453 self.state
454 .bots
455 .insert(UserId::from(&resp.user).into(), Channel::Ghost);
456 }
457
458 let resp = Arc::get_mut(&mut resp).unwrap();
459 let client = self.state.client.delegate_to(UserId::from(&resp.user));
460
461 match Bot::init_existing(client, &mut resp.user, settings).await {
462 Ok(bot) => Ok(bot.user_id()),
463 Err(e) => {
464 if let Err(err) = self.delete(UserId::from(&resp.user)).await {
465 log::warn!("Failed to delete incorrectly initialized bot: {err}")
466 }
467 Err(e.into())
468 }
469 }
470 }
471
472 fn make_bot(
473 &self,
474 user_id: UserId,
475 receiver: UnboundedReceiver<P>,
476 ) -> (FarmBot<C>, EventStream<P>) {
477 let bot_client = self.state.client.delegate_to(user_id);
478 let stream = EventStream::from(receiver);
479
480 #[cfg(feature = "xftp")]
481 let (bot_client, stream) = stream.hook_xftp(bot_client);
482
483 (Bot::new(bot_client, user_id), stream)
484 }
485
486 fn make_ghost(&self, user_id: UserId) -> FarmBot<C> {
487 let bot_client = self.state.client.delegate_to(user_id);
488
489 #[cfg(feature = "xftp")]
490 let bot_client = crate::xftp::XftpClient::new(bot_client, self.state.xftp.clone());
491
492 Bot::new(bot_client, user_id)
493 }
494}
495
496pub struct Init<C, P> {
497 client: C,
498 events: EventStream<P>,
499 farm_id: UserId,
500 farm_name: String,
501 active_name: String,
502 bots: BotMap<P>,
503 cache: HashMap<String, User>,
504}
505
506#[derive(Clone)]
507pub struct Running<C: ClientApi, P> {
508 farm_name: String,
509 client: DelegateClient<C>,
510 suspender: demux::Suspender,
511 bots: Arc<BotMap<P>>,
512 #[cfg(feature = "xftp")]
513 xftp: Arc<crate::xftp::XftpManager>,
514}
515
516pub struct DelegateClient<C: ClientApi> {
517 bot_id: BotId,
518 sender: DelegateSender<C>,
519}
520
521impl<C: ClientApi> Clone for DelegateClient<C> {
522 fn clone(&self) -> Self {
523 Self {
524 bot_id: self.bot_id,
525 sender: self.sender.clone(),
526 }
527 }
528}
529
530impl<C: ClientApi> DelegateClient<C> {
531 fn new(bot_id: BotId) -> (Self, DelegateReceiver<C>) {
532 let (sender, receiver) = mpsc::unbounded_channel();
533 (Self { bot_id, sender }, receiver)
534 }
535
536 fn delegate_to(&self, bot_id: impl Into<BotId>) -> Self {
537 Self {
538 bot_id: bot_id.into(),
539 sender: self.sender.clone(),
540 }
541 }
542}
543
544impl<C: ClientApi> ClientApi for DelegateClient<C>
545where
546 C::Error: Send,
547{
548 type ResponseShape<'de, T: 'de + Deserialize<'de>> = C::ResponseShape<'de, T>;
549 type Error = C::Error;
550
551 async fn send_raw(&self, cmd: String) -> Result<String, Self::Error> {
552 let (responder, response) = oneshot::channel();
553
554 let request = DelegateRequest {
555 bot_id: self.bot_id,
556 cmd,
557 responder,
558 };
559
560 self.sender
561 .send(request)
562 .expect("Delegate client cannot outlive background task");
563
564 response
565 .await
566 .expect("Delegate client cannot outlive background task")
567 }
568
569 async fn list_users(&self) -> Result<Arc<UsersListResponse>, Self::Error> {
570 let client = self.delegate_to(BotId::anybot());
571 let response: ListUsersResponse = client.send(ListUsers {}).await?;
572 Ok(response.into_inner())
573 }
574
575 async fn receive_file(&self, cmd: ReceiveFile) -> Result<ReceiveFileResponse, Self::Error> {
576 let client = self.delegate_to(BotId::anybot());
577 client.send(cmd).await
578 }
579
580 async fn cancel_file(&self, file_id: i64) -> Result<CancelFileResponse, Self::Error> {
581 let client = self.delegate_to(BotId::anybot());
582 client.send(CancelFile { file_id }).await
583 }
584}
585
586#[derive(Debug)]
587pub enum CreateError<E> {
588 FarmUser,
590 BotIsGhost,
592 GhostIsBot,
594 BotAlreadyTaken,
596 Desync,
598 Api(E),
599}
600
601impl<E> From<E> for CreateError<E> {
602 fn from(value: E) -> Self {
603 Self::Api(value)
604 }
605}
606
607impl<E> std::fmt::Display for CreateError<E>
608where
609 E: std::fmt::Display,
610{
611 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
612 match self {
613 Self::FarmUser => write!(
614 f,
615 "Attempt to create a farm user. Farm user is special and cannot be interacted with directly"
616 ),
617 Self::BotIsGhost => write!(
618 f,
619 "Cannot create a bot because the ghost user with the same name already exists"
620 ),
621 Self::GhostIsBot => write!(
622 f,
623 "Cannot create a ghost because the bot user with the same name already exists"
624 ),
625 Self::BotAlreadyTaken => {
626 write!(
627 f,
628 "The bot already exists and has been taken from the farm. Cannot recreate operational bots"
629 )
630 }
631 Self::Desync => {
632 write!(
633 f,
634 "The DB state was not in sync with the memory state, try again"
635 )
636 }
637 Self::Api(e) => write!(f, "{e:#}"),
638 }
639 }
640}
641
642impl<E: 'static + std::error::Error> std::error::Error for CreateError<E> {
643 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
644 if let Self::Api(err) = self {
645 Some(err)
646 } else {
647 None
648 }
649 }
650}
651
652struct DelegateRequest<C: ClientApi> {
653 bot_id: BotId,
654 cmd: String,
655 responder: oneshot::Sender<Result<String, C::Error>>,
656}
657
658type DelegateSender<C> = UnboundedSender<DelegateRequest<C>>;
659type DelegateReceiver<C> = UnboundedReceiver<DelegateRequest<C>>;
660
661#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
662#[repr(transparent)]
663struct BotId(Option<UserId>);
664
665impl BotId {
666 fn anybot() -> Self {
668 Self(None)
669 }
670
671 fn get(&self) -> Option<UserId> {
672 self.0
673 }
674}
675
676impl From<UserId> for BotId {
677 fn from(user_id: UserId) -> Self {
678 Self(Some(user_id))
679 }
680}