use core::{panic}; use std::error::Error; use std::collections::HashMap; /* ModulesManager is used to manage Modules and BotActions associated with those modules pub struct ModulesManager { statusdb: HashMap>, botactions: HashMap>, } - statusdb: HashMap> - Defines Modules and their ModStatusType (e.g., Enabled at an Instance level, Disabled at a Channel Level) - botactions: HashMap> - Defines Modules and their BotActions (e.g., BotCommand , Listener, Routine) Example { ModulesManager { statusdb: {BotModule("experiments 004"): [Enabled(Instance)]}, botactions: {BotModule("experiments 004"): [C(BotCommand { module: BotModule("experiments 004"), command: "DUPCMD4", alias: ["DUPALIAS4A", "DUPALIAS4B"], help: "DUPCMD4 tester" })]} } } */ #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum ModType { BotModule(String), } pub use ModType::BotModule; #[derive(Debug, PartialEq, Eq, Hash)] pub enum ChType { Channel(String), } pub use ChType::Channel; use twitch_irc::message::PrivmsgMessage; use crate::core::botinstance::{self, BotInstance}; #[derive(Debug)] enum StatusLvl { Instance, Ch(ChType), } #[derive(Debug)] pub enum ModStatusType { Enabled(StatusLvl), Disabled(StatusLvl), } // pub use EnType::Enabled; //#[derive(Debug)] // pub enum BotAction // where // //F: std::future::Future + ?Sized, // // F: std::future::Future, // //F: std::future::Future + Send, // // F: Send, // F: Send + ?Sized, pub enum BotAction { //C(BotCommand), // L(Listener), L(Listener), R(Routine), } impl BotAction { pub async fn execute(&self,m:botinstance::Chat,n:PrivmsgMessage){ match self { BotAction::L(a) => a.execute(m,n).await, _ => (), } // (self.exec_body)(m,n).await; } } // pub trait BotActionTrait // where // // F: std::future::Future, // //F: std::future::Future + ?Sized, // //: std::future::Future + Send, // // F: Send, // F : Send + ?Sized pub trait BotActionTrait { // async fn execute(&self,m:&mut BotInstance,n:&PrivmsgMessage); // fn add_to_bot(self, bot:BotInstance) -> Result>; fn add_to_bot(self, bot:BotInstance); //fn add_to_modmgr(self, modmgr:ModulesManager) -> ModulesManager; //fn add_to_modmgr(self,modmgr:&mut ModulesManager) -> ModulesManager; fn add_to_modmgr(self,modmgr:&mut ModulesManager); } // #[derive(Debug)] // pub struct BotCommand { // pub module : ModType, // pub command : String, // command call name // pub alias : Vec, // String of alternative names // // bot_prefix : char, // although should be global? // exec_body : fn(&BotInstance,&PrivmsgMessage), // pub help : String, // } // impl BotCommand { // // pub fn add_to_bot(self, bot:BotInstance) { // // // let mut mgr = bot.botmodules; // // // let nmod = self.module.clone(); // // // mgr.add_botaction(nmod, BotAction::C(self)); // // let mut mgr = bot.botmodules; // // //let nmod = self.module.clone(); // // // mgr.add_botaction(self.module.clone(), BotAction::C(self)); // // self.add_to_modmgr(&mut mgr); // // } // // pub fn add_to_modmgr(self, modmgr:&mut ModulesManager) { // // // // let mut mgr = bot.botmodules; // // // // let nmod = self.module.clone(); // // // // mgr.add_botaction(nmod, BotAction::C(self)); // // // let mut mgr = modmgr; // // // //let nmod = self.module.clone(); // // // mgr.add_botaction(self.module.clone(), BotAction::C(self)); // // // let mut mgr = modmgr; // // // //let nmod = self.module.clone(); // // modmgr.add_botaction(self.module.clone(), BotAction::C(self)); // // } // } // impl BotActionTrait for BotCommand { // // fn add_to_bot(&self) -> Result> { // // println!("Calling Add to Bot"); // // Ok(String::from("Hello")) // // } // fn add_to_bot(self, bot:BotInstance) { // // let mut mgr = bot.botmodules; // // let nmod = self.module.clone(); // // mgr.add_botaction(nmod, BotAction::C(self)); // let mut mgr = bot.botmodules; // //let nmod = self.module.clone(); // // mgr.add_botaction(self.module.clone(), BotAction::C(self)); // self.add_to_modmgr(&mut mgr); // } // fn add_to_modmgr(self, modmgr:&mut ModulesManager) { // // // let mut mgr = bot.botmodules; // // // let nmod = self.module.clone(); // // // mgr.add_botaction(nmod, BotAction::C(self)); // // let mut mgr = modmgr; // // //let nmod = self.module.clone(); // // mgr.add_botaction(self.module.clone(), BotAction::C(self)); // // let mut mgr = modmgr; // // //let nmod = self.module.clone(); // modmgr.add_botaction(self.module.clone(), BotAction::C(self)); // } // // => 12.23 - MF: NOTE : The exec body is defined ad module level , not this core level // // fn exec_body(&self,m:&BotInstance,n:&PrivmsgMessage){ // // // => 12.23 - [ ] #todo requires field // // // (&self.exec)(String::from("Hello from BotAction Trait")) // // //self.exec_body(m,n) // // } // // fn execute(&self,m:&mut BotInstance,n:&PrivmsgMessage){ // // // => 12.23 - [ ] #todo requires field // // // (&self.exec)(String::from("Hello from BotAction Trait")) // // //self.exec_body(m,n) // // (self.exec_body)(m,n) // // } // } pub mod bot_actions { // use std::boxed::Box; // use std::pin::Pin; // use std::future::Future; // use crate::core::botinstance::Chat; // use twitch_irc::message::PrivmsgMessage; // type ExecBody = Box Pin>>>; pub mod actions_util { use std::future::Future; use std::boxed::Box; use std::pin::Pin; use crate::core::botinstance::Chat; use twitch_irc::message::PrivmsgMessage; // pub type ExecBody = Box Pin>>>; // pub type ExecBody = Box Pin + Send>> + Send + Sync>; pub type ExecBody = Box Pin + Send>> + Send + Sync>; //pub type ExecBody = Box Pin + Send>> + Send + Sync>; // pub fn asyncbox(f: fn(Chat,PrivmsgMessage) -> T) -> ExecBody // where // //T: Future + 'static, // //T: Future , // T: Future + Send + 'static, // { // Box::new(move |a,b| Box::pin(f(a,b))) // } pub fn asyncbox(f: fn(Chat,PrivmsgMessage) -> T) -> ExecBody //pub fn asyncbox(f: fn(Chat,&PrivmsgMessage) -> T) -> ExecBody where //T: Future + 'static, //T: Future , // T: Future + Send + 'static, T: Future + Send + 'static, { Box::new(move |a,b| Box::pin(f(a,b))) } } } // #[derive(Debug)] // pub struct Listener { // pub module : ModType, // pub name : String, // pub exec_body : fn(&mut BotInstance,&PrivmsgMessage) , // pub help : String // } // #[derive(Debug)] // pub struct Listener pub struct Listener // where // // F: std::future::Future + ?Sized, // //F: std::future::Future, // //F: std::future::Future + ?Sized + Send, // //F: Send, // F: Send + ?Sized, { pub module : ModType, pub name : String, //pub exec_body : fn(&mut BotInstance,&PrivmsgMessage) , // pub exec_body : fn(&mut Box>,&PrivmsgMessage) -> F , //pub exec_body : fn(&mut botinstance::Chat,&PrivmsgMessage) -> F , // pub exec_body : fn(botinstance::Chat,PrivmsgMessage) -> F , //pub exec_body : fn(String,&PrivmsgMessage) -> F , //pub exec_body : bot_actions::actions_util::ExecBody, pub exec_body : bot_actions::actions_util::ExecBody, pub help : String } // impl Listener // where // // F: std::future::Future, // //F: std::future::Future + Sized, // F: std::future::Future + Sized + Send, impl Listener { // async fn execute(&self,m:&mut Box>,n:&PrivmsgMessage){ // async fn execute(&self,m:&mut botinstance::Chat,n:&PrivmsgMessage){ pub async fn execute(&self,m:botinstance::Chat,n:PrivmsgMessage){ // => 12.23 - [ ] #todo requires field // (&self.exec)(String::from("Hello from BotAction Trait")) //self.exec_body(m,n) (self.exec_body)(m,n).await; // (self.exec_body)(m,n); } } // impl BotActionTrait for Listener // where // //F: std::future::Future, // //F: std::future::Future + Sized, // // F: std::future::Future + Send, // // F : Send, // F : Send + ?Sized, impl BotActionTrait for Listener { // fn add_to_bot(&self) -> Result> { // println!("Calling Add to Bot"); // Ok(String::from("Hello")) // } // fn add_to_bot(self, mut bot:BotInstance) { fn add_to_bot(self, mut bot:BotInstance) { // let mut mgr = bot.botmodules; // let nmod = self.module.clone(); // mgr.add_botaction(nmod, BotAction::C(self)); //let mgr = &mut bot.botmodules; //let nmod = self.module.clone(); // mgr.add_botaction(self.module.clone(), BotAction::C(self)); let mgr = &mut bot.botmodules; self.add_to_modmgr(mgr); } //fn add_to_modmgr(self, modmgr:&mut ModulesManager) -> ModulesManager { fn add_to_modmgr(self, modmgr:&mut ModulesManager) { //fn add_to_modmgr(self, mut modmgr:ModulesManager) -> ModulesManager { // // let mut mgr = bot.botmodules; // // let nmod = self.module.clone(); // // mgr.add_botaction(nmod, BotAction::C(self)); // let mut mgr = modmgr; // //let nmod = self.module.clone(); // mgr.add_botaction(self.module.clone(), BotAction::C(self)); // let mut mgr = modmgr; // //let nmod = self.module.clone(); // modmgr.add_botaction(self.module.clone(), BotAction::L(self)); modmgr.add_botaction(self.module.clone(), BotAction::L(self)) } // => 12.23 - MF: NOTE : The exec body is defined ad module level , not this core level // fn exec_body(&self,m:&BotInstance,n:&PrivmsgMessage){ // // => 12.23 - [ ] #todo requires field // // (&self.exec)(String::from("Hello from BotAction Trait")) // //self.exec_body(m,n) // } // fn execute(&self,m:&mut BotInstance,n:&PrivmsgMessage){ // // => 12.23 - [ ] #todo requires field // // (&self.exec)(String::from("Hello from BotAction Trait")) // //self.exec_body(m,n) // (self.exec_body)(m,n) // } // // => 12.23 - MF : There are issues trying to define async at trait level // async fn execute(&self,m:&mut BotInstance,n:&PrivmsgMessage) -> Result<>{ // // => 12.23 - [ ] #todo requires field // // (&self.exec)(String::from("Hello from BotAction Trait")) // //self.exec_body(m,n) // (self.exec_body)(m,n) // } } #[derive(Debug)] struct Routine {} //#[derive(Debug)] // pub struct ModulesManager // where // //F: std::future::Future + ?Sized, // // F: std::future::Future, // //F: std::future::Future + Send, // // F: Send, // // F: Send + ?Sized, pub struct ModulesManager { statusdb: HashMap>, pub botactions: HashMap>, } // impl ModulesManager // where // // F: std::future::Future, // //F: std::future::Future + Send, // // F : Send, // F : Send + ?Sized, impl ModulesManager { // pub fn init() -> ModulesManager // where // F: std::future::Future , // pub fn init() -> &'static mut ModulesManager pub fn init() -> ModulesManager { // initializes the modulers manager // Ideally, this should have added known modules based on // directory structure and API user recommendations let mut m = HashMap::new(); let mut act = HashMap::new(); // -- some processing including adding into the hashmap // let newmodule = BotModule(String::from("GambaCore")); // let newlistener = Listener { // module : BotModule(String::from("experiments").to_owned()), // name : String::from("socklistener"), // help : String::from("This will listen and react to sock randomly"), // }; // // As a Demonstration, the listener's Module is added and Enabled at Instance level // let statusvector = m // .entry(BotModule(String::from("experiments"))) // .or_insert(Vec::new()); // statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // let modactions = act // .entry( BotModule(String::from("experiments"))) // .or_insert(Vec::new()); // modactions.push(BotAction::L(newlistener)); // let mgr = &mut ModulesManager { // statusdb : m, // botactions : act, // }; let mut mgr = ModulesManager { statusdb : m, botactions : act, }; // // -- This area can be where bot actions are imported to the bot's module manager // // -- Below can be used to validate what occurs when dup BotCommands are added // let bcmd = BotCommand { // module : BotModule(String::from("experiments 001")), // command : String::from("DUPCMD1"), // command call name // alias : vec![String::from("DUPALIAS1A"),String::from("DUPALIAS1B")], // String of alternative names // // bot_prefix : char, // although should be global? // // exec_body : fn, // help : String::from("DUPCMD1 tester"), // }; // mgr.add_botaction(BotModule(String::from("experiments 001")), BotAction::C(bcmd)); // let bcmd = BotCommand { // module : BotModule(String::from("experiments 002")), // command : String::from("DUPCMD2"), // command call name // alias : vec![String::from("DUPALIAS2A"),String::from("DUPALIAS2B")], // String of alternative names // // bot_prefix : char, // although should be global? // // exec_body : fn, // help : String::from("DUPCMD2 tester"), // }; // mgr.add_botaction(BotModule(String::from("experiments 002")), BotAction::C(bcmd)); // -- Below working demonstration of BotCommand.add_to_modmgr() // BotCommand { // module : BotModule(String::from("experiments 003")), // command : String::from("DUPCMD3"), // command call name // alias : vec![String::from("DUPALIAS3A"),String::from("DUPALIAS3B")], // String of alternative names // // bot_prefix : char, // although should be global? // // exec_body : fn, // help : String::from("DUPCMD3 tester"), // }.add_to_modmgr(&mut mgr); // crate::core::botmodules::BotCommand{ // module : BotModule(String::from("experiments 003")), // command : String::from("DUPCMD3"), // command call name // alias : vec![String::from("DUPALIAS3A"),String::from("DUPALIAS3B")], // String of alternative names // // bot_prefix : char, // although should be global? // // exec_body : fn, // help : String::from("DUPCMD3 tester"), // }; // // => 2023.12.22 - [x] MF : How can I call submods:init() ? // // => 2023.12.22 - this was answered - needed to pub modules in main.rs // crate::core::botinstance::init(); // works // crate::core::init(); // works //crate::submods:: //crate::arbfile; crate::modules::init(&mut mgr); println!(">> Modules Manager : End of Init"); //println!(">> Modules Manager : {:?}",mgr); // let &mut mgr = mgr; // &mgr mgr } pub fn modstatus(&self, _:ModType, _:ChType) -> ModStatusType { // Example usage : botmanager.modstatus( // BotModule("GambaCore"), // Channel("modulatingforce") // ) // - The ModStatusType checks in the context of the given channel , // but also validates based on wheher the module is disabled at a bot instance // level as well ModStatusType::Enabled(StatusLvl::Instance) } pub fn togglestatus(&self, _:ModType, _:ChType) -> ModStatusType { // enables or disables based on current status ModStatusType::Enabled(StatusLvl::Instance) } pub fn setstatus(&self, _:ModType, _:ModStatusType) -> Result<&str,Box> { // sets the status based given ModSatusType // e.g., b.setstatus(BodModule("GambaCore"), Enabled(Channel("modulatingforce"))).expect("ERROR") Ok("") } //pub fn add_botaction(mut self, in_module:ModType, in_action:BotAction ) -> ModulesManager { // pub fn add_botaction(mut self, in_module:ModType, in_action:BotAction ) -> ModulesManager { //pub fn add_botaction(&mut self, in_module:ModType, in_action:BotAction ) -> () { pub fn add_botaction(&mut self, in_module:ModType, in_action:BotAction ) { /* adds a BotAction to the Modules Manager - This will require a BotModule passed as well This will including the logic of a valid add If it fails to add, either a PANIC or some default coded business rules that handles the botaction add For example, this Should PANIC (ideally Panic?) if it does not successfully add a bot module -- Being unable to indicates a Programming/Developer code logic issue : They cannot add botactions that already exists (?) -- In particular to BotCommands, which must have Unique command call names and aliases that to not conflict with any other already BotCommand added name or alias Other types might be fine? For example, if 2 modules have their own listeners but each have the name "targetchatter" , both would be called separately, even if they both have the same or different logic */ // let newlistener = Listener { // // module : BotModule(String::from("experiments").to_owned()), // module : in_module.clone(), // name : String::from("socklistener"), // help : String::from("This will listen and react to sock randomly"), // }; // As a Demonstration, the listener's Module is added and Enabled at Instance level // [x] Before Adding, validate the following : // - If BotAction to Add is a BotCommand , In Module Manager DB (botactions), // Check All Other BotAction Command Names & Aliases to ensure they don't conflict // fn find_conflict_module(mgr:& ModulesManager, act:& BotAction) -> Option // where // // F: std::future::Future, // // F: std::future::Future + Send, // // F : Send, // F : Send + ?Sized, fn find_conflict_module(mgr:& ModulesManager, act:& BotAction) -> Option { // Some(BotModule(String::from("GambaCore"))) // match act { // BotAction::C(c) => { // Some(BotModule(String::from("GambaCore"))) // }, // BotAction::L(l) => None, // BotAction::R(r) => None, // } // if let BotAction::C(incmd) = act { // // let n = & mgr.botactions; // let d = &mgr.botactions; // for (module,moduleactions) in d { // for modact in moduleactions.iter() { // if let BotAction::C(dbcmd) = &modact { // // At this point, there is an command incmd and looked up dbcmd // // [x] check if given botcommand c.command:String conflicts with any in botactions // if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() { // // Returning State - with the identified module // // return Some((module.clone(),BotAction::C(*dbcmd.clone()))); // // return Some(incmd); // for some reason I keep getting issues // //return Some(BotModule(String::from("GambaCore"))); // works // return Some(module.clone()); // works // // return Some(dbcmd.clone()); // } // for a in &dbcmd.alias { // if incmd.command.to_lowercase() == a.to_lowercase() { // // Returning State - with the identified module // // return Some((module.clone(),BotAction::C(dbcmd))); // return Some(module.clone()); // works // } // } // // [x] Then do the same check except for each c.alias // for inalias in &incmd.alias { // if inalias.to_lowercase() == dbcmd.command.to_lowercase() { // // Returning State - with the identified module // // return Some((module.clone(),BotAction::C(dbcmd))); // return Some(module.clone()); // works // } // for a in &dbcmd.alias { // if inalias.to_lowercase() == a.to_lowercase() { // // Returning State - with the identified module // // return Some((module.clone(),BotAction::C(dbcmd))); // return Some(module.clone()); // works // } // } // } // } // } // } // // return Some(BotModule(String::from("GambaCore"))) // } // for all other scenarios (e.g., Listener, Routine), find no conflicts None } // if let probmod = find_conflict_module(&self, &in_action) { // // () // return because there was a conflict? // panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , probmod ); // } match find_conflict_module(&self, &in_action) { // Some(c) => panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , c ), Some(c) => panic!("ERROR: Could not add module; there was a conflict with existing module {:?}", c ), None => (), } let statusvector = self.statusdb // .entry(BotModule(String::from("experiments"))) .entry(in_module.clone()) .or_insert(Vec::new()); statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level let modactions = self.botactions //.entry( BotModule(String::from("experiments"))) .entry( in_module.clone()) .or_insert(Vec::new()); // modactions.push(BotAction::L(newlistener)); modactions.push(in_action); println!(">> Modules Manager : Called Add bot Action"); //println!(">> Modules Manager : {:?}",&self); //(); //let mgr = self; //mgr //self } fn statuscleanup(&self,_:Option) -> () { // internal cleans up statusdb . For example : // - remove redudancies . If we see several Enabled("m"), only keep 1x // - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m") // the IDEAL is that this is ran before every read/update operation to ensure quality // Option can pass Some(Channel("m")) (as an example) so statuscleanup only works on the given channel // Passing None to chnl may be a heavy operation, as this will review and look at the whole table () } }