2023-12-21 00:48:09 -05:00
/*
2024-02-25 10:40:54 -05:00
ModulesManager is used to manage Modules and BotActions associated with those modules
2023-12-22 09:52:01 -05:00
pub struct ModulesManager {
statusdb : HashMap < ModType , Vec < ModStatusType > > ,
2024-02-25 10:40:54 -05:00
botactions : HashMap < ModType , Vec < BotAction > > ,
2023-12-22 09:52:01 -05:00
}
- statusdb : HashMap < ModType , Vec < ModStatusType > > - Defines Modules and their ModStatusType ( e . g . , Enabled at an Instance level , Disabled at a Channel Level )
2024-02-25 10:40:54 -05:00
- botactions : HashMap < ModType , Vec < BotAction > > - Defines Modules and their BotActions ( e . g . , BotCommand , Listener , Routine )
2023-12-21 00:48:09 -05:00
Example
{
2024-02-25 10:40:54 -05:00
ModulesManager {
statusdb : { BotModule ( " experiments 004 " ) : [ Enabled ( Instance ) ] } ,
2023-12-22 09:52:01 -05:00
botactions : { BotModule ( " experiments 004 " ) : [ C ( BotCommand { module : BotModule ( " experiments 004 " ) , command : " DUPCMD4 " , alias : [ " DUPALIAS4A " , " DUPALIAS4B " ] , help : " DUPCMD4 tester " } ) ] } }
2023-12-21 00:48:09 -05:00
}
* /
2024-03-02 10:24:13 -05:00
use core ::panic ;
2023-12-21 00:48:09 -05:00
2024-03-02 10:24:13 -05:00
use std ::collections ::HashMap ;
2024-03-21 00:05:52 -04:00
// use std::error::Error;
2024-03-02 10:24:13 -05:00
use std ::sync ::Arc ;
2023-12-21 00:48:09 -05:00
2024-03-21 09:37:08 -04:00
// use futures::stream::iter;
2023-12-26 20:00:32 -05:00
use twitch_irc ::message ::PrivmsgMessage ;
2023-12-21 00:48:09 -05:00
2024-03-21 14:01:54 -04:00
// use casual_logger::Log;
2024-03-02 10:24:13 -05:00
use tokio ::sync ::RwLock ;
use async_trait ::async_trait ;
2024-03-02 11:41:24 -05:00
use self ::bot_actions ::actions_util ::BotAR ;
2024-03-21 00:05:52 -04:00
use crate ::core ::botinstance ::{ BotInstance , ChType , ChangeResult } ;
2024-03-02 10:24:13 -05:00
use crate ::core ::botlog ;
use crate ::core ::identity ;
2024-02-12 01:25:12 -05:00
2024-03-02 12:21:18 -05:00
use crate ::core ::bot_actions ;
2024-03-02 10:24:13 -05:00
pub use ChType ::Channel ;
pub use ModType ::BotModule ;
2024-03-21 00:05:52 -04:00
// use super::identity::ChangeResult;
2024-03-02 10:24:13 -05:00
#[ derive(Debug, PartialEq, Eq, Hash, Clone) ]
pub enum ModType {
BotModule ( String ) ,
}
2024-03-20 23:20:46 -04:00
#[ derive(Debug, PartialEq, Eq, Hash, Clone) ]
pub enum ModGroup {
Core ,
Custom ,
}
2024-03-21 02:13:23 -04:00
#[ derive(Debug, PartialEq, Eq, Hash, Clone) ]
2024-03-01 23:36:37 -05:00
pub enum StatusLvl {
2023-12-21 00:48:09 -05:00
Instance ,
2024-03-21 02:13:23 -04:00
Ch ( ChType ) ,
2023-12-21 00:48:09 -05:00
}
2024-03-21 02:13:23 -04:00
#[ derive(Debug, PartialEq, Eq, Hash, Clone) ]
2024-03-20 23:35:56 -04:00
pub enum StatusType {
2023-12-21 00:48:09 -05:00
Enabled ( StatusLvl ) ,
Disabled ( StatusLvl ) ,
}
2024-02-04 14:28:37 -05:00
2024-02-25 10:40:54 -05:00
pub enum BotAction {
C ( BotCommand ) ,
2024-01-29 01:13:56 -05:00
L ( Listener ) ,
2024-02-25 10:40:54 -05:00
R ( Routine ) ,
2023-12-21 00:48:09 -05:00
}
2023-12-26 20:00:32 -05:00
2024-01-29 02:27:11 -05:00
impl BotAction {
2024-03-01 23:36:37 -05:00
pub async fn execute ( & self , m : BotAR , n : PrivmsgMessage ) {
2024-01-29 02:27:11 -05:00
match self {
2024-02-25 10:40:54 -05:00
BotAction ::L ( a ) = > a . execute ( m , n ) . await ,
BotAction ::C ( a ) = > a . execute ( m , n ) . await ,
2024-01-29 02:27:11 -05:00
_ = > ( ) ,
}
}
}
2024-02-04 14:28:37 -05:00
#[ async_trait ]
2024-02-25 10:40:54 -05:00
pub trait BotActionTrait {
async fn add_to_bot ( self , bot : BotInstance ) ;
async fn add_to_modmgr ( self , modmgr : Arc < ModulesManager > ) ;
2024-03-20 23:20:46 -04:00
async fn add_core_to_bot ( self , bot : BotInstance ) ;
async fn add_core_to_modmgr ( self , modmgr : Arc < ModulesManager > ) ;
2023-12-26 20:00:32 -05:00
}
2024-01-29 04:09:53 -05:00
pub struct BotCommand {
2024-02-25 10:40:54 -05:00
pub module : ModType ,
pub command : String , // command call name
pub alias : Vec < String > , // String of alternative names
pub exec_body : bot_actions ::actions_util ::ExecBody ,
pub help : String ,
pub required_roles : Vec < identity ::UserRole > ,
2024-01-29 04:09:53 -05:00
}
2024-02-25 10:40:54 -05:00
impl BotCommand {
2024-03-01 23:36:37 -05:00
pub async fn execute ( & self , m : BotAR , n : PrivmsgMessage ) {
( * self . exec_body ) ( m , n ) . await ;
2024-01-29 04:09:53 -05:00
}
}
2024-02-04 14:28:37 -05:00
#[ async_trait ]
2024-02-25 10:40:54 -05:00
impl BotActionTrait for BotCommand {
async fn add_to_bot ( self , bot : BotInstance ) {
2024-02-12 05:25:38 -05:00
self . add_to_modmgr ( bot . botmodules ) . await ;
2024-01-29 04:09:53 -05:00
}
2024-02-25 10:40:54 -05:00
async fn add_to_modmgr ( self , modmgr : Arc < ModulesManager > ) {
modmgr
. add_botaction ( self . module . clone ( ) , BotAction ::C ( self ) )
. await
2024-01-29 04:09:53 -05:00
}
2024-03-20 23:20:46 -04:00
async fn add_core_to_bot ( self , bot : BotInstance ) {
self . add_core_to_modmgr ( bot . botmodules ) . await ;
}
async fn add_core_to_modmgr ( self , modmgr : Arc < ModulesManager > ) {
modmgr
. add_core_act ( self . module . clone ( ) , BotAction ::C ( self ) )
. await
}
2024-01-29 04:09:53 -05:00
}
2024-02-25 10:40:54 -05:00
pub struct Listener {
pub module : ModType ,
pub name : String ,
pub exec_body : bot_actions ::actions_util ::ExecBody ,
pub help : String ,
2023-12-21 00:48:09 -05:00
}
2024-02-25 10:40:54 -05:00
impl Listener {
2024-03-01 23:36:37 -05:00
pub async fn execute ( & self , m : BotAR , n : PrivmsgMessage ) {
( self . exec_body ) ( m , n ) . await ;
2023-12-26 20:00:32 -05:00
}
}
2024-02-04 14:28:37 -05:00
#[ async_trait ]
2024-02-25 10:40:54 -05:00
impl BotActionTrait for Listener {
async fn add_to_bot ( self , bot : BotInstance ) {
2024-03-02 10:06:26 -05:00
botlog ::trace (
2024-02-25 10:40:54 -05:00
" Adding action to bot " ,
Some ( " BotModules > BotActionTrait > add_to_bot() " . to_string ( ) ) ,
None ,
) ;
2024-02-12 02:34:32 -05:00
self . add_to_modmgr ( bot . botmodules ) . await ;
2023-12-26 20:00:32 -05:00
}
2024-02-25 10:40:54 -05:00
async fn add_to_modmgr ( self , modmgr : Arc < ModulesManager > ) {
2024-03-02 10:06:26 -05:00
botlog ::trace (
2024-02-25 10:40:54 -05:00
" Adding action to module manager " ,
Some ( " BotModules > BotActionTrait > add_to_bot() " . to_string ( ) ) ,
None ,
) ;
2024-02-13 07:54:35 -05:00
2024-02-25 10:40:54 -05:00
modmgr
. add_botaction ( self . module . clone ( ) , BotAction ::L ( self ) )
. await ;
2023-12-26 20:00:32 -05:00
}
2024-03-20 23:20:46 -04:00
async fn add_core_to_bot ( self , bot : BotInstance ) {
self . add_core_to_modmgr ( bot . botmodules ) . await ;
}
async fn add_core_to_modmgr ( self , modmgr : Arc < ModulesManager > ) {
modmgr
. add_core_act ( self . module . clone ( ) , BotAction ::L ( self ) )
. await
}
2023-12-26 20:00:32 -05:00
}
2023-12-21 00:48:09 -05:00
#[ derive(Debug) ]
2024-03-01 23:36:37 -05:00
pub struct Routine { }
2023-12-21 00:48:09 -05:00
2024-02-25 10:40:54 -05:00
pub struct ModulesManager {
2024-03-20 23:20:46 -04:00
// statusdb: Arc<RwLock<HashMap<ModType, Vec<ModStatusType>>>>,
2024-03-21 00:11:24 -04:00
// statusdb: Arc<RwLock<HashMap<(ModType,ModGroup), Vec<StatusType>>>>,
statusdb : Arc < RwLock < HashMap < ModType , ( ModGroup , Vec < StatusType > ) > > > ,
2024-02-25 10:40:54 -05:00
pub botactions : Arc < RwLock < HashMap < ModType , Vec < BotAction > > > > ,
2023-12-21 00:48:09 -05:00
}
2024-02-18 15:23:58 -05:00
/*
statusdb
2024-02-25 10:40:54 -05:00
< HashMap
< ModType , < - - e . g . , BotModule ( String ::from ( " experiments001 " ) )
2024-02-18 15:23:58 -05:00
Vec < ModStatusType > < - - shows Enabled / Disabled per Status level
2024-02-25 10:40:54 -05:00
botactions
2024-02-18 15:23:58 -05:00
HashMap <
2024-02-25 10:40:54 -05:00
ModType , < - - e . g . , BotModule ( String ::from ( " experiments001 " ) )
2024-02-18 15:23:58 -05:00
Vec < BotAction > > BotCommand , Listener
* /
2024-02-25 10:40:54 -05:00
impl ModulesManager {
pub async fn init ( ) -> Arc < ModulesManager > {
2024-03-01 23:36:37 -05:00
let mgr = ModulesManager {
2024-03-02 11:55:16 -05:00
statusdb : Arc ::new ( RwLock ::new ( HashMap ::new ( ) ) ) ,
botactions : Arc ::new ( RwLock ::new ( HashMap ::new ( ) ) ) ,
2023-12-21 00:48:09 -05:00
} ;
2024-02-04 14:28:37 -05:00
// :: [x] initialize core modules
2024-03-02 12:21:18 -05:00
botlog ::trace (
2024-02-25 10:40:54 -05:00
" ModulesManager > init() > Adding modules " ,
2024-02-13 10:11:49 -05:00
Some ( " ModulesManager > init() " . to_string ( ) ) ,
2024-02-25 10:40:54 -05:00
None ,
2024-02-13 10:11:49 -05:00
) ;
2023-12-22 09:21:49 -05:00
2024-03-02 11:55:16 -05:00
let mgrarc = Arc ::new ( mgr ) ;
2024-03-02 12:21:18 -05:00
2024-03-02 11:55:16 -05:00
// 1. load core modules
crate ::core ::identity ::init ( Arc ::clone ( & mgrarc ) ) . await ;
// 2. load custom modules
crate ::custom ::init ( Arc ::clone ( & mgrarc ) ) . await ;
2024-02-25 10:40:54 -05:00
botlog ::trace (
" >> Modules Manager : End of Init " ,
2024-02-13 10:11:49 -05:00
Some ( " ModulesManager > init() " . to_string ( ) ) ,
2024-02-25 10:40:54 -05:00
None ,
2024-02-13 10:11:49 -05:00
) ;
2023-12-21 00:48:09 -05:00
2024-03-02 11:55:16 -05:00
mgrarc
2024-02-25 10:40:54 -05:00
}
2023-12-21 00:48:09 -05:00
2024-03-21 12:21:00 -04:00
pub async fn modstatus ( & self , in_module : ModType , in_chnl : ChType ) -> StatusType {
2023-12-21 00:48:09 -05:00
// Example usage : botmanager.modstatus(
// BotModule("GambaCore"),
// Channel("modulatingforce")
2024-02-25 10:40:54 -05:00
// )
// - The ModStatusType checks in the context of the given channel ,
2023-12-21 00:48:09 -05:00
// but also validates based on wheher the module is disabled at a bot instance
// level as well
2024-03-21 12:21:00 -04:00
let dbt = self . statusdb . read ( ) . await ;
// let a = dbt.entry(in_module.clone()).;
let ( mgrp , statusvector ) = dbt . get ( & in_module ) . unwrap ( ) ;
match mgrp {
ModGroup ::Core = > {
StatusType ::Enabled ( StatusLvl ::Instance ) // This forces core to be validated as Enabled, even if undesired scenario of missing StatusLvl::Instance or empty vectors
} ,
ModGroup ::Custom = > {
// // remove all instance level pattern for the module
// while let Some(index) = statusvector
// .iter()
// .position(|x| (*x == StatusType::Enabled(StatusLvl::Instance)) || (*x == StatusType::Disabled(StatusLvl::Instance))) {
// statusvector.remove(index);
// }
// statusvector.push(StatusType::Disabled(StatusLvl::Instance));
// (
// StatusType::Disabled(StatusLvl::Instance),
// ChangeResult::Success("Set Disabled at Instance".to_string())
// )
/*
[ x ] 1. If Disabled at Instance Level ,
[ x ] a . And Enabled at a Channel Level > return Enabled ( Channel )
[ x ] b . And Disabled at a Channel Level > return Disabled ( Channel )
[ x ] c . And Not Defined at Channel Level > return Disabled ( Instance )
[ x ] 2. If Enabled at Instance Level ,
[ x ] a . And Enabled at a Channel Level > return Enabled ( Channel )
[ x ] b . And Disabled at a Channel Level > return Disabled ( Channel )
[ x ] c . And Not Defined at Channel Level > return Enabled ( Instance )
* /
if statusvector . contains ( & StatusType ::Disabled ( StatusLvl ::Instance ) ) {
// [x] 1. If Disabled at Instance Level ,
if statusvector . contains ( & StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) {
// [x] a. And Enabled at a Channel Level > return Enabled(Channel)
StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) )
} else if statusvector . contains ( & StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) {
// [x] b. And Disabled at a Channel Level > return Disabled(Channel)
StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) )
} else {
// [x] c. And Not Defined at Channel Level > return Disabled(Instance)
StatusType ::Disabled ( StatusLvl ::Instance )
}
} else if statusvector . contains ( & StatusType ::Enabled ( StatusLvl ::Instance ) ) {
// [x] 2. If Enabled at Instance Level ,
if statusvector . contains ( & StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) {
// [x] a. And Enabled at a Channel Level > return Enabled(Channel)
StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) )
} else if statusvector . contains ( & StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) {
// [x] b. And Disabled at a Channel Level > return Disabled(Channel)
StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) )
} else {
// [x] c. And Not Defined at Channel Level > return Enabled(Instance)
StatusType ::Enabled ( StatusLvl ::Instance )
}
} else {
// ? In some unexpected scenario (e.g., not define at instance level), assume Disabled at Instance level and set as this way
self . set_instance_disabled ( in_module ) . await ;
StatusType ::Disabled ( StatusLvl ::Instance )
}
} ,
}
//StatusType::Enabled(StatusLvl::Instance)
2023-12-21 00:48:09 -05:00
}
2024-03-21 00:05:52 -04:00
// pub fn togglestatus(&self, _: ModType, _: ChType) -> StatusType {
// // enables or disables based on current status
// StatusType::Enabled(StatusLvl::Instance)
// }
// pub fn setstatus(&self, _: ModType, _: StatusType) -> Result<&str, Box<dyn Error>> {
// // sets the status based given ModSatusType
// // e.g., b.setstatus(BodModule("GambaCore"), Enabled(Channel("modulatingforce"))).expect("ERROR")
// Ok("")
// }
2024-03-21 02:13:23 -04:00
pub async fn set_instance_disabled ( & self , in_module : ModType ) -> ( StatusType , ChangeResult ) {
2024-03-21 00:05:52 -04:00
// at Instance level
// - If core module, do nothing
2024-03-21 02:13:23 -04:00
// self.satusdb.
let mut dbt = self . statusdb . write ( ) . await ;
// let a = dbt.entry(in_module.clone()).;
let ( mgrp , statusvector ) = dbt . get_mut ( & in_module ) . unwrap ( ) ;
match mgrp {
ModGroup ::Core = > {
(
StatusType ::Enabled ( StatusLvl ::Instance ) ,
ChangeResult ::Failed ( " Core Modules cannot be disabled " . to_string ( ) )
)
} ,
ModGroup ::Custom = > {
// remove all instance level pattern for the module
while let Some ( index ) = statusvector
. iter ( )
. position ( | x | ( * x = = StatusType ::Enabled ( StatusLvl ::Instance ) ) | | ( * x = = StatusType ::Disabled ( StatusLvl ::Instance ) ) ) {
statusvector . remove ( index ) ;
}
statusvector . push ( StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
(
StatusType ::Disabled ( StatusLvl ::Instance ) ,
2024-03-21 12:21:00 -04:00
ChangeResult ::Success ( " Set Disabled at Instance " . to_string ( ) )
2024-03-21 02:13:23 -04:00
)
} ,
}
// (StatusType::Disabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
2023-12-21 00:48:09 -05:00
}
2024-03-21 02:13:23 -04:00
pub async fn force_disable ( & self , in_module : ModType ) -> ( StatusType , ChangeResult ) {
// Disables the module at Instance level, and removes all Enabled at Channel level
2024-03-21 00:05:52 -04:00
// - Bot Moderators MUST Re-enable if they were enabled before
// - If core module, do nothing
2024-03-21 02:13:23 -04:00
let mut dbt = self . statusdb . write ( ) . await ;
// let a = dbt.entry(in_module.clone()).;
let ( mgrp , statusvector ) = dbt . get_mut ( & in_module ) . unwrap ( ) ;
match mgrp {
ModGroup ::Core = > {
(
StatusType ::Enabled ( StatusLvl ::Instance ) ,
ChangeResult ::Failed ( " Core Modules cannot be disabled " . to_string ( ) )
)
} ,
ModGroup ::Custom = > {
// remove all instance level pattern & Enabled Channel patterns for the module
// Disabled at Channel level might be fine? That way if it gets Enabled at instance level, channel level disables are uninterrupted
while let Some ( index ) = statusvector
. iter ( )
. position ( | x |
if ( * x = = StatusType ::Enabled ( StatusLvl ::Instance ) )
| | ( * x = = StatusType ::Disabled ( StatusLvl ::Instance ) ) {
true
} else if let StatusType ::Enabled ( StatusLvl ::Ch ( _ ) ) = ( * x ) . clone ( ) {
true
} else { false }
)
{
statusvector . remove ( index ) ;
}
statusvector . push ( StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
(
StatusType ::Disabled ( StatusLvl ::Instance ) ,
2024-03-21 09:37:08 -04:00
ChangeResult ::Success ( " Forced Disabled " . to_string ( ) )
2024-03-21 02:13:23 -04:00
)
} ,
}
// (StatusType::Disabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
2023-12-21 00:48:09 -05:00
}
2024-03-21 12:21:00 -04:00
pub async fn set_instance_enabled ( & self , in_module : ModType ) -> ( StatusType , ChangeResult ) {
2024-03-21 00:05:52 -04:00
// at Instance level
// - If core module, do nothing
2024-03-21 12:21:00 -04:00
let mut dbt = self . statusdb . write ( ) . await ;
// let a = dbt.entry(in_module.clone()).;
let ( mgrp , statusvector ) = dbt . get_mut ( & in_module ) . unwrap ( ) ;
match mgrp {
ModGroup ::Core = > {
(
StatusType ::Enabled ( StatusLvl ::Instance ) ,
ChangeResult ::NoChange ( " Core Modules are always Enabled " . to_string ( ) )
)
} ,
ModGroup ::Custom = > {
// remove all instance level pattern for the module
while let Some ( index ) = statusvector
. iter ( )
. position ( | x | ( * x = = StatusType ::Enabled ( StatusLvl ::Instance ) ) | | ( * x = = StatusType ::Disabled ( StatusLvl ::Instance ) ) ) {
statusvector . remove ( index ) ;
}
statusvector . push ( StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
(
StatusType ::Enabled ( StatusLvl ::Instance ) ,
ChangeResult ::Success ( " Set Enabled at Instance " . to_string ( ) )
)
} ,
}
// (StatusType::Enabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
2024-03-21 00:05:52 -04:00
}
2024-03-21 12:21:00 -04:00
pub async fn set_ch_disabled ( & self , in_module : ModType , in_chnl : ChType ) -> ( StatusType , ChangeResult ) {
2024-03-21 00:05:52 -04:00
// at Instance level
// - If core module, do nothing
2024-03-21 12:21:00 -04:00
let mut dbt = self . statusdb . write ( ) . await ;
// let a = dbt.entry(in_module.clone()).;
let ( mgrp , statusvector ) = dbt . get_mut ( & in_module ) . unwrap ( ) ;
match mgrp {
ModGroup ::Core = > {
(
StatusType ::Enabled ( StatusLvl ::Instance ) ,
ChangeResult ::Failed ( " Core Modules cannot be disabled " . to_string ( ) )
)
} ,
ModGroup ::Custom = > {
// remove all channel level pattern for the module
while let Some ( index ) = statusvector
. iter ( )
. position ( | x |
( * x = = StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) | | ( * x = = StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) )
{
statusvector . remove ( index ) ;
}
statusvector . push ( StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) ;
(
StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ,
ChangeResult ::Success ( " Set Disabled at Channel Level " . to_string ( ) )
)
} ,
}
// (StatusType::Disabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
2024-03-21 00:05:52 -04:00
}
2024-03-21 12:21:00 -04:00
pub async fn set_ch_enabled ( & self , in_module : ModType , in_chnl : ChType ) -> ( StatusType , ChangeResult ) {
2024-03-21 00:05:52 -04:00
// at Instance level
// - If core module, do nothing
2024-03-21 12:21:00 -04:00
let mut dbt = self . statusdb . write ( ) . await ;
// let a = dbt.entry(in_module.clone()).;
let ( mgrp , statusvector ) = dbt . get_mut ( & in_module ) . unwrap ( ) ;
match mgrp {
ModGroup ::Core = > {
(
StatusType ::Enabled ( StatusLvl ::Instance ) ,
ChangeResult ::NoChange ( " Core Modules are always Enabled " . to_string ( ) )
)
} ,
ModGroup ::Custom = > {
// remove all channel level pattern for the module
while let Some ( index ) = statusvector
. iter ( )
. position ( | x |
( * x = = StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) | | ( * x = = StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) )
{
statusvector . remove ( index ) ;
}
statusvector . push ( StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ) ;
(
StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl . clone ( ) ) ) ,
ChangeResult ::Success ( " Set Enabled at Channel Level " . to_string ( ) )
)
} ,
}
// (StatusType::Enabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
2024-03-21 00:05:52 -04:00
}
2024-02-25 10:40:54 -05:00
pub async fn add_botaction ( & self , in_module : ModType , in_action : BotAction ) {
2024-03-20 23:20:46 -04:00
self . int_add_botaction ( in_module , ModGroup ::Custom , in_action ) . await ;
}
pub async fn add_core_act ( & self , in_module : ModType , in_action : BotAction ) {
self . int_add_botaction ( in_module , ModGroup ::Core , in_action ) . await ;
}
2024-03-21 02:13:23 -04:00
pub async fn affirm_in_statusdb ( & self , in_module :ModType , in_modgroup : ModGroup ) {
let mut dbt = self . statusdb . write ( ) . await ;
let ( _ , statusvector ) = dbt . entry ( in_module . clone ( ) ) . or_insert ( ( in_modgroup . clone ( ) , Vec ::new ( ) ) ) ;
if ! statusvector . contains ( & StatusType ::Enabled ( StatusLvl ::Instance ) ) & & ! statusvector . contains ( & StatusType ::Disabled ( StatusLvl ::Instance ) )
{
match in_modgroup {
ModGroup ::Core = > statusvector . push ( StatusType ::Enabled ( StatusLvl ::Instance ) ) , // Pushes the Module as Enabled at Instance Level
ModGroup ::Custom = > statusvector . push ( StatusType ::Disabled ( StatusLvl ::Instance ) ) ,
}
}
}
2024-03-20 23:20:46 -04:00
async fn int_add_botaction ( & self , in_module : ModType , in_modgroup : ModGroup , in_action : BotAction ) {
2024-02-25 10:40:54 -05:00
botlog ::trace (
" Add botaction called " ,
Some ( " ModulesManager > init() " . to_string ( ) ) ,
None ,
2024-02-13 10:11:49 -05:00
) ;
2023-12-21 20:11:32 -05:00
/*
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 " ,
2024-02-25 10:40:54 -05:00
both would be called separately , even if they both have the same or different logic
2023-12-21 20:11:32 -05:00
* /
2024-02-25 10:40:54 -05:00
// [x] Before Adding, validate the following :
// - If BotAction to Add is a BotCommand , In Module Manager DB (botactions),
2023-12-21 20:11:32 -05:00
// Check All Other BotAction Command Names & Aliases to ensure they don't conflict
2024-02-25 10:40:54 -05:00
async fn find_conflict_module ( mgr : & ModulesManager , act : & BotAction ) -> Option < ModType > {
2024-01-29 04:28:58 -05:00
if let BotAction ::C ( incmd ) = act {
2024-03-02 11:55:16 -05:00
let actdb = mgr . botactions . read ( ) . await ;
2024-03-02 12:21:18 -05:00
2024-03-02 11:55:16 -05:00
for ( module , moduleactions ) in & ( * actdb ) {
2024-01-29 04:28:58 -05:00
for modact in moduleactions . iter ( ) {
if let BotAction ::C ( dbcmd ) = & modact {
// At this point, there is an command incmd and looked up dbcmd
2023-12-21 20:11:32 -05:00
2024-01-29 04:28:58 -05:00
// [x] check if given botcommand c.command:String conflicts with any in botactions
2023-12-21 20:11:32 -05:00
2024-01-29 04:28:58 -05:00
if incmd . command . to_lowercase ( ) = = dbcmd . command . to_lowercase ( ) {
// Returning State - with the identified module
return Some ( module . clone ( ) ) ; // works
2024-02-25 10:40:54 -05:00
}
2023-12-21 20:11:32 -05:00
2024-01-29 04:28:58 -05:00
for a in & dbcmd . alias {
if incmd . command . to_lowercase ( ) = = a . to_lowercase ( ) {
// Returning State - with the identified module
2024-03-02 12:21:18 -05:00
2024-01-29 04:28:58 -05:00
return Some ( module . clone ( ) ) ; // works
2024-02-25 10:40:54 -05:00
}
}
2023-12-21 20:11:32 -05:00
2024-01-29 04:28:58 -05:00
// [x] Then do the same check except for each c.alias
2023-12-21 20:11:32 -05:00
2024-01-29 04:28:58 -05:00
for inalias in & incmd . alias {
if inalias . to_lowercase ( ) = = dbcmd . command . to_lowercase ( ) {
// Returning State - with the identified module
2024-03-02 12:21:18 -05:00
2024-01-29 04:28:58 -05:00
return Some ( module . clone ( ) ) ; // works
2024-02-25 10:40:54 -05:00
}
2023-12-21 20:11:32 -05:00
2024-01-29 04:28:58 -05:00
for a in & dbcmd . alias {
if inalias . to_lowercase ( ) = = a . to_lowercase ( ) {
// Returning State - with the identified module
2024-03-02 12:21:18 -05:00
2024-01-29 04:28:58 -05:00
return Some ( module . clone ( ) ) ; // works
2024-02-25 10:40:54 -05:00
}
}
2024-01-29 04:28:58 -05:00
}
}
}
}
}
2023-12-21 20:11:32 -05:00
// for all other scenarios (e.g., Listener, Routine), find no conflicts
None
}
2024-03-01 23:36:37 -05:00
if let Some ( c ) = find_conflict_module ( self , & in_action ) . await {
panic! (
2024-02-25 10:40:54 -05:00
" ERROR: Could not add module; there was a conflict with existing module {:?} " ,
c
2024-03-01 23:36:37 -05:00
)
2023-12-21 20:11:32 -05:00
}
2024-03-21 02:13:23 -04:00
// let mut dbt = self.statusdb.write().await;
// //
// let statusvector = dbt.entry(in_module.clone()).or_insert((in_modgroup.clone(),Vec::new()));
2023-12-21 17:22:40 -05:00
2024-03-21 02:13:23 -04:00
// match in_modgroup {
// ModGroup::Core => statusvector.1.push(StatusType::Enabled(StatusLvl::Instance)) , // Pushes the Module as Enabled at Instance Level
// ModGroup::Custom => statusvector.1.push(StatusType::Disabled(StatusLvl::Instance)),
// }
2024-03-20 23:32:05 -04:00
// statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level
2023-12-21 17:22:40 -05:00
2024-03-21 02:13:23 -04:00
self . affirm_in_statusdb ( in_module . clone ( ) , in_modgroup ) . await ;
2024-02-12 01:25:12 -05:00
let mut a = self . botactions . write ( ) . await ;
2024-03-02 12:21:18 -05:00
let modactions = a . entry ( in_module . clone ( ) ) . or_insert ( Vec ::new ( ) ) ;
2023-12-21 17:22:40 -05:00
2023-12-21 20:11:32 -05:00
modactions . push ( in_action ) ;
2023-12-21 17:22:40 -05:00
2024-02-25 10:40:54 -05:00
botlog ::trace (
2024-03-02 12:21:18 -05:00
format! (
" Modules Manager> add_botaction called - botactions size : {} " ,
modactions . len ( )
)
. as_str ( ) ,
2024-02-25 10:40:54 -05:00
Some ( " ModulesManager > init() " . to_string ( ) ) ,
None ,
2024-02-13 10:11:49 -05:00
) ;
2023-12-21 17:22:40 -05:00
}
2024-03-01 23:36:37 -05:00
fn _statuscleanup ( & self , _ : Option < ChType > ) {
2024-02-25 10:40:54 -05:00
// internal cleans up statusdb . For example :
2023-12-21 12:10:50 -05:00
// - 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<ChType> 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
}
2024-02-25 10:40:54 -05:00
}
2024-03-21 14:01:54 -04:00
// =====================
// =====================
// =====================
// =====================
// =====================
#[ cfg(test) ]
mod core_modulesmanager {
use casual_logger ::Log ;
use casual_logger ::Extension ;
use super ::* ;
/*
Possible Tests
[ x ] Test 1 - Custom ModGroup Workflow
1. affirm_in_statusdb ( Experiments01 , Custom )
2. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
3. set_instance_enabled ( Experiments01 )
4. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
5. set_ch_disabled ( Experiments01 , TestChannel01 )
6. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
7. set_ch_enabled ( Experiments01 , TestChannel01 ) & set_ch_disabled ( Experiments01 , TestChannel02 )
8. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
9. set_instance_disabled ( Experiments01 )
10. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
11. force_disable ( Experiments01 )
12. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
[ x ] Test 2 - Core ModGroup Workflow
1. affirm_in_statusdb ( CoreModule01 , Core )
2. modstatus ( CoreModule01 , TestChannel01 ) & modstatus ( CoreModule01 , TestChannel02 )
3. set_instance_enabled ( CoreModule01 )
4. modstatus ( CoreModule01 , TestChannel01 ) & modstatus ( CoreModule01 , TestChannel02 )
5. set_ch_disabled ( CoreModule01 , TestChannel01 )
6. modstatus ( CoreModule01 , TestChannel01 ) & modstatus ( CoreModule01 , TestChannel02 )
7. set_ch_enabled ( CoreModule01 , TestChannel01 ) & set_ch_disabled ( CoreModule01 , TestChannel02 )
8. modstatus ( CoreModule01 , TestChannel01 ) & modstatus ( CoreModule01 , TestChannel02 )
9. set_instance_disabled ( CoreModule01 )
10. modstatus ( CoreModule01 , TestChannel01 ) & modstatus ( CoreModule01 , TestChannel02 )
11. force_disable ( CoreModule01 )
12. modstatus ( CoreModule01 , TestChannel01 ) & modstatus ( CoreModule01 , TestChannel02 )
* /
async fn complex_workflow (
in_module : ModType ,
in_modgroup : ModGroup ,
in_chnl1 : ChType ,
in_chnl2 : ChType )
{
let mgr = ModulesManager ::init ( ) . await ;
/*
1. affirm_in_statusdb ( Experiments01 , Custom )
2. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
mgr . affirm_in_statusdb ( in_module . clone ( ) , in_modgroup . clone ( ) ) . await ;
match in_modgroup {
ModGroup ::Custom = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
} ,
ModGroup ::Core = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
}
/*
3. set_instance_enabled ( Experiments01 )
4. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
mgr . set_instance_enabled ( in_module . clone ( ) ) . await ;
match in_modgroup {
ModGroup ::Custom = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
ModGroup ::Core = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
}
/*
5. set_ch_disabled ( Experiments01 , TestChannel01 )
6. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
mgr . set_ch_disabled ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ;
//StatusType::Disabled(StatusLvl::Ch(in_chnl1.clone()))
match in_modgroup {
ModGroup ::Custom = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Disabled ( StatusLvl ::Ch ( in_chnl1 . clone ( ) ) ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
ModGroup ::Core = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
}
/*
7. set_ch_enabled ( Experiments01 , TestChannel01 ) & set_ch_disabled ( Experiments01 , TestChannel02 )
8. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
mgr . set_ch_enabled ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ;
//StatusType::Disabled(StatusLvl::Ch(in_chnl1.clone()))
match in_modgroup {
ModGroup ::Custom = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl1 . clone ( ) ) ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
ModGroup ::Core = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
}
/*
9. set_instance_disabled ( Experiments01 )
10. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
mgr . set_instance_disabled ( in_module . clone ( ) ) . await ;
// StatusType::Disabled(StatusLvl::Ch(in_chnl1.clone()))
match in_modgroup {
ModGroup ::Custom = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Ch ( in_chnl1 . clone ( ) ) ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
} ,
ModGroup ::Core = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
}
/*
11. force_disable ( Experiments01 )
12. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
mgr . force_disable ( in_module . clone ( ) ) . await ;
match in_modgroup {
ModGroup ::Custom = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Disabled ( StatusLvl ::Instance ) ) ;
} ,
ModGroup ::Core = > {
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl1 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
assert_eq! ( mgr . modstatus ( in_module . clone ( ) , in_chnl2 . clone ( ) ) . await ,
StatusType ::Enabled ( StatusLvl ::Instance ) ) ;
} ,
}
}
#[ tokio::test ]
async fn custom_modgroup_workflow ( ) {
Log ::set_file_ext ( Extension ::Log ) ;
/*
[ x ] Test 1 - Custom ModGroup Workflow
1. affirm_in_statusdb ( Experiments01 , Custom )
2. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
3. set_instance_enabled ( Experiments01 )
4. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
5. set_ch_disabled ( Experiments01 , TestChannel01 )
6. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
7. set_ch_enabled ( Experiments01 , TestChannel01 ) & set_ch_disabled ( Experiments01 , TestChannel02 )
8. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
9. set_instance_disabled ( Experiments01 )
10. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
11. force_disable ( Experiments01 )
12. modstatus ( Experiments01 , TestChannel01 ) & modstatus ( Experiments01 , TestChannel02 )
* /
let in_module = BotModule ( " Experiments01 " . to_string ( ) ) ;
let in_modgroup = ModGroup ::Custom ;
let ( in_chnl1 , in_chnl2 ) =
( ChType ::Channel ( " TestChannel01 " . to_string ( ) ) , ChType ::Channel ( " TestChannel02 " . to_string ( ) ) ) ;
complex_workflow ( in_module , in_modgroup , in_chnl1 , in_chnl2 ) . await ;
}
#[ tokio::test ]
async fn core_modgroup_workflow ( ) {
Log ::set_file_ext ( Extension ::Log ) ;
let in_module = BotModule ( " CoreModule01 " . to_string ( ) ) ;
let in_modgroup = ModGroup ::Core ;
let ( in_chnl1 , in_chnl2 ) =
( ChType ::Channel ( " TestChannel01 " . to_string ( ) ) , ChType ::Channel ( " TestChannel02 " . to_string ( ) ) ) ;
complex_workflow ( in_module , in_modgroup , in_chnl1 , in_chnl2 ) . await ;
}
}