diff --git a/src/bot_manager.rs b/src/bot_manager.rs new file mode 100644 index 0000000..7c3766f --- /dev/null +++ b/src/bot_manager.rs @@ -0,0 +1,106 @@ +use std::{collections::HashMap, sync::RwLock, thread::JoinHandle}; + +use lazy_static::lazy_static; +use teloxide::{ + dispatching::dialogue::serializer::Json, + dptree, + prelude::{Dispatcher, Requester}, + Bot, +}; + +use crate::{ + bot_handler::script_handler, + db::{bots::BotInstance, DbError, DB}, + mongodb_storage::MongodbStorage, + BotController, BotError, BotResult, +}; + +pub struct BotRunner { + controller: BotController, + info: BotInfo, + thread: JoinHandle>, +} + +unsafe impl Sync for BotRunner {} +unsafe impl Send for BotRunner {} + +#[derive(Clone)] +pub struct BotInfo { + pub name: String, +} + +lazy_static! { + static ref BOT_POOL: RwLock> = RwLock::new(HashMap::new()); +} + +static DEFAULT_SCRIPT: &str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/default_script.js")); + +pub async fn deploy_bot(db: &mut DB, token: &str) -> BotResult { + let bot = Bot::new(token); + let name = bot.get_me().await?.username().to_string(); + + let bi = BotInstance::new(name.clone(), token.to_string(), DEFAULT_SCRIPT.to_string()) + .store(db) + .await?; + + start_bot(bi, &mut db.clone().with_name(name)).await +} + +pub async fn start_bot(bi: BotInstance, db: &mut DB) -> BotResult { + let controller = BotController::with_db(db.clone(), &bi.token, &bi.script).await?; + + let thread = spawn_bot_thread(controller.clone(), db).await?; + + let info = BotInfo { + name: bi.name.clone(), + }; + let runner = BotRunner { + controller, + info: info.clone(), + thread, + }; + + BOT_POOL + .write() + .map_or_else( + |err| { + Err(BotError::RwLockError(format!( + "Failed to lock BOT_POOL because previous thread paniced, err: {err}" + ))) + }, + Ok, + )? + .insert(bi.name.clone(), runner); + + Ok(info) +} + +pub async fn spawn_bot_thread( + bc: BotController, + db: &mut DB, +) -> BotResult>> { + let state_mgr = MongodbStorage::from_db(db, Json) + .await + .map_err(DbError::from)?; + let thread = std::thread::spawn(move || -> BotResult<()> { + let state_mgr = state_mgr; + + let handler = script_handler(bc.rc); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build()?; + + rt.block_on( + Dispatcher::builder(bc.bot, handler) + .dependencies(dptree::deps![bc.db, state_mgr]) + .build() + .dispatch(), + ); + + Ok(()) + }); + + Ok(thread) +} diff --git a/src/main.rs b/src/main.rs index 8ef3f30..bdd6aab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ pub mod admin; +pub mod bot_manager; pub mod botscript; pub mod commands; pub mod db;