forcebot_rs/src/core/botmodules.rs

2677 lines
91 KiB
Rust
Raw Normal View History

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<BotModule,Vec<ModStatusType>>,
botactions: HashMap<BotModule,Vec<BotAction>>,
2023-12-22 09:52:01 -05:00
}
- statusdb: HashMap<BotModule,Vec<ModStatusType>> - Defines Modules and their ModStatusType (e.g., Enabled at an Instance level, Disabled at a Channel Level)
- botactions: HashMap<BotModule,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-25 17:46:57 -04:00
const OF_CMD_CHANNEL:Channel = Channel(String::new());
2024-03-02 10:24:13 -05:00
use core::panic;
2023-12-21 00:48:09 -05:00
2024-03-27 17:03:23 -04:00
use std::borrow::Borrow;
use std::borrow::BorrowMut;
2024-03-02 10:24:13 -05:00
use std::collections::HashMap;
use std::sync::Arc;
2023-12-21 00:48:09 -05:00
2024-03-22 17:06:09 -04:00
use casual_logger::Log;
2024-03-21 14:01:54 -04:00
2024-03-26 11:29:47 -04:00
use chrono::DateTime;
2024-03-26 14:30:13 -04:00
// use chrono::Duration;
2024-03-26 11:29:47 -04:00
use chrono::Local;
2024-03-02 10:24:13 -05:00
use tokio::sync::RwLock;
use async_trait::async_trait;
2024-03-26 11:29:47 -04:00
use tokio::task::JoinHandle;
2024-03-02 10:24:13 -05:00
2024-03-26 14:30:13 -04:00
use tokio::time::Instant;
use tokio::time::{sleep, Duration};
2024-03-21 21:20:16 -04:00
use crate::core::bot_actions::actions_util;
2024-03-24 14:38:09 -04:00
use crate::core::bot_actions::ExecBodyParams;
use crate::core::botinstance::{BotInstance, Channel,ChangeResult};
2024-03-02 10:24:13 -05:00
use crate::core::botlog;
2024-03-22 17:06:09 -04:00
use crate::core::identity::{self, Permissible,IdentityManager};
2024-02-12 01:25:12 -05:00
2024-03-02 12:21:18 -05:00
use crate::core::bot_actions;
2024-03-22 20:18:02 -04:00
use std::hash::{Hash, Hasher};
2024-03-27 17:03:23 -04:00
use super::bot_actions::ActAR;
use super::bot_actions::RoutineAR;
2024-03-21 21:20:16 -04:00
use super::identity::ChatBadge;
2024-03-21 00:05:52 -04:00
2024-03-21 21:20:16 -04:00
pub async fn init(mgr: Arc<ModulesManager>) {
// 1. Define the BotAction
let botc1 = BotCommand {
module: BotModule(String::from("core")),
command: String::from("enable"), // command call name
alias: vec![
String::from("e"),
String::from("en")], // String of alternative names
exec_body: actions_util::asyncbox(cmd_enable),
help: String::from("Test Command tester"),
required_roles: vec![
identity::UserRole::BotAdmin,
identity::UserRole::Mod(OF_CMD_CHANNEL),
identity::UserRole::SupMod(OF_CMD_CHANNEL),
identity::UserRole::Broadcaster,
],
};
// 2. Add the BotAction to ModulesManager
botc1.add_core_to_modmgr(Arc::clone(&mgr)).await;
2024-03-24 14:38:09 -04:00
async fn cmd_enable(params : ExecBodyParams) {
2024-03-21 21:20:16 -04:00
/*
There should be additional validation checks
- BotAdmins can only run instance level (-i) enables
2024-03-22 08:21:10 -04:00
- If BotAdmins need to enable/disable at Channel level, they must Promote themselves to be a Mod at least
2024-03-21 21:20:16 -04:00
- Other Special Roles (Mod,SupMod,Broadcaster) can run without issues to enable the module at Channel Level
*/
/*
enable -i <module> // enables at Instance
enable <module> // enables at Channel
*/
/*
1. Parse out Message Arguments
exec_enable()
2. Get Special Roles of CmdSender
3. If CmdSender is BotAdmin but not (Mod,SupMod,Broadcaster)
3a. , and is not -i (to instance) , return a Failure recommending BotAdmin promote themselves first
3b. , and is -i (to instance) , return a Success
4. If CmdSender not a BotAdmin but is (Mod,SupMod,Broadcaster)
4a. , and is not -i (to instance) , return a Success
4b. , and is -i (to instance) , return a Failure they are not allowed
5. If CmdSender is (Mod,SupMod,Broadcaster) and a BotAdmin
5a. , and is not -i (to instance) , return a Success
5b. , and is -i (to instance) , return a Success
*/
2024-03-22 08:21:10 -04:00
2024-03-22 18:02:31 -04:00
// [x] Unwraps arguments from message
2024-03-22 08:21:10 -04:00
let (arg1, arg2) = {
2024-03-24 14:38:09 -04:00
let mut argv = params.msg.message_text.split(' ');
2024-03-22 08:21:10 -04:00
argv.next(); // Skip the command name
let arg1 = argv.next();
let arg2 = argv.next();
(arg1, arg2)
};
/* -- Related function to call later
exec_enable(
&self,
requestor: String,
requestor_badge: Option<ChatBadge>,
trg_module: BotModule,
// channel: Option<Channel>,
2024-03-22 08:21:10 -04:00
trg_level: StatusLvl,
bot: BotAR,
) -> ChangeResult
2024-03-21 21:20:16 -04:00
*/
2024-03-22 18:02:31 -04:00
// [x] requestor: String,
2024-03-24 14:38:09 -04:00
let requestor = params.msg.clone().sender.name;
2024-03-22 08:21:10 -04:00
2024-03-22 18:02:31 -04:00
// [x] requestor_badge: Option<ChatBadge>,
2024-03-22 08:21:10 -04:00
let mut requestor_badge_mut: Option<ChatBadge> = None;
2024-03-24 14:38:09 -04:00
for b in &params.msg.badges {
2024-03-22 08:21:10 -04:00
if b.name == "moderator" {
requestor_badge_mut = Some(ChatBadge::Mod);
} else if b.name == "broadcaster" {
requestor_badge_mut = Some(ChatBadge::Broadcaster);
2024-03-25 17:01:27 -04:00
} else if b.name == "vip" {
requestor_badge_mut = Some(ChatBadge::VIP);
2024-03-22 08:21:10 -04:00
}
}
let requestor_badge = requestor_badge_mut;
// [x] trg_module: BotModule,
// - [x] Need to validate an actual BotModule - otherwise, fail or exit the cmd
2024-03-22 08:21:10 -04:00
let trg_module = if (arg1 == Some("-i")) || (arg1 == Some("-f")) { arg2 } else { arg1 };
2024-03-22 18:02:31 -04:00
// if no trg_module was passed
2024-03-22 20:18:02 -04:00
// if let None = trg_module {
if trg_module.is_none() {
2024-03-22 08:21:10 -04:00
2024-03-25 15:46:37 -04:00
// let botlock = params.bot.read().await;
2024-03-22 18:02:31 -04:00
let outmsg = "uuh You need to pass a module";
2024-03-22 08:21:10 -04:00
2024-03-22 18:02:31 -04:00
botlog::debug(
outmsg,
Some("botmodules.rs > cmd_enable()".to_string()),
2024-03-24 14:38:09 -04:00
Some(&params.msg),
2024-03-22 18:02:31 -04:00
);
2024-03-24 16:59:50 -04:00
2024-03-25 14:11:21 -04:00
// We should call a notification around here
let bot = params.clone().bot;
let botclone = Arc::clone(&bot);
let botlock = botclone.read().await;
botlock.botmgrs.chat.send_botmsg(super::chat::BotMsgType::Notif(
outmsg.to_string()
),
params.clone(),
).await;
2024-03-22 18:02:31 -04:00
return;
2024-03-22 08:21:10 -04:00
}
2024-03-22 18:02:31 -04:00
// [x] trg_level: StatusLvl,
2024-03-22 08:21:10 -04:00
2024-03-24 14:38:09 -04:00
let currchnl = params.msg.channel_login.to_lowercase();
2024-03-22 08:21:10 -04:00
let trg_level =
2024-03-22 20:18:02 -04:00
if arg1 == Some("-i") || arg1 == Some("-f") { StatusLvl::Instance }
else { StatusLvl::Ch(Channel(currchnl)) }
2024-03-22 08:21:10 -04:00
;
2024-03-22 18:02:31 -04:00
2024-03-24 14:38:09 -04:00
let botlock = params.bot.read().await;
2024-03-22 18:02:31 -04:00
let modmgr = Arc::clone(&botlock.botmodules);
let id = botlock.get_identity();
let rslt = modmgr.exec_enable(
requestor,
requestor_badge,
BotModule(trg_module.unwrap().to_string()),
2024-03-22 18:02:31 -04:00
trg_level,
id).await;
2024-03-25 14:11:21 -04:00
// We should call a notification around here
2024-03-22 18:02:31 -04:00
let outmsg = match rslt.clone() {
ChangeResult::Failed(a) => format!("Stare Failed : {}",a),
ChangeResult::NoChange(a) => format!("Hmm No Change : {}",a),
ChangeResult::Success(a) => format!("YAAY Success : {}",a),
};
2024-03-22 08:21:10 -04:00
2024-03-25 14:11:21 -04:00
botlock.botmgrs.chat.send_botmsg(super::chat::BotMsgType::Notif(
outmsg.to_string()
),
params.clone(),
).await;
2024-03-25 16:08:24 -04:00
2024-03-22 18:16:41 -04:00
2024-03-21 21:20:16 -04:00
}
// 1. Define the BotAction
let botc1 = BotCommand {
module: BotModule(String::from("core")),
command: String::from("disable"), // command call name
alias: vec![
String::from("d")], // String of alternative names
exec_body: actions_util::asyncbox(cmd_disable),
help: String::from("Test Command tester"),
required_roles: vec![
identity::UserRole::BotAdmin,
identity::UserRole::Mod(OF_CMD_CHANNEL),
identity::UserRole::SupMod(OF_CMD_CHANNEL),
identity::UserRole::Broadcaster,
],
};
// 2. Add the BotAction to ModulesManager
botc1.add_core_to_modmgr(Arc::clone(&mgr)).await;
2024-03-24 14:38:09 -04:00
async fn cmd_disable(params : ExecBodyParams) {
2024-03-21 21:20:16 -04:00
/*
There should be additional validation checks
2024-03-22 08:21:10 -04:00
- BotAdmins can only run instance level (-i) disables and (-f) force disable
- If BotAdmins need to enable/disable at Channel level, they must Promote themselves to be a Mod at least
2024-03-21 21:20:16 -04:00
- Other Special Roles (Mod,SupMod,Broadcaster) can run without issues to disable the module at Channel Level
2024-03-22 08:21:10 -04:00
*/
2024-03-21 21:20:16 -04:00
/*
disable -i <module> // disables at Instance
disable <module> // disables at Channel
disable -f <module> // force disables (instance and enabled are removed)
*/
2024-03-22 08:21:10 -04:00
/*
1. If CmdSender is BotAdmin but not (Mod,SupMod,Broadcaster)
1. can_user_run for cmdreqRoles including BotAdmin & not can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
1a. , and has no special flags (-i / -f) , return a Failure recommending BotAdmin promote themselves first
1b. , and is -i (to instance) , return a Success
1c. , and is -f (forced) , return a Success
2. If CmdSender not a BotAdmin but is (Mod,SupMod,Broadcaster)
2. not can_user_run for cmdreqRoles including BotAdmin & can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
2a. , and has no special flags (-i / -f) , return a Success
2b. , and is -i (to instance) , return a Failure they are not allowed
2c. , and is -f (forced) , return a Failure they are not allowed
3. If CmdSender is (Mod,SupMod,Broadcaster) and a BotAdmin
3. can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster) & can_user_run for cmdreqRoles including BotAdmin
3a. , and has no special flags (-i / -f) , return a Success
3b. , and is -i (to instance) , return a Success
3c. , and is -f (forced) , return a Success
*/
2024-03-21 21:20:16 -04:00
2024-03-24 16:59:50 -04:00
2024-03-25 16:08:24 -04:00
// [x] Unwraps arguments from message
2024-03-22 18:16:41 -04:00
let (arg1, arg2) = {
2024-03-24 14:38:09 -04:00
let mut argv = params.msg.message_text.split(' ');
2024-03-22 18:16:41 -04:00
argv.next(); // Skip the command name
let arg1 = argv.next();
let arg2 = argv.next();
(arg1, arg2)
};
/* -- Related function to call later
exec_disable(
&self,
requestor: String,
requestor_badge: Option<ChatBadge>,
trg_module: BotModule,
// channel: Option<Channel>,
2024-03-22 18:16:41 -04:00
trg_level: StatusLvl,
force: bool,
// bot: BotAR,
id: Arc<RwLock<IdentityManager>>,
) -> ChangeResult
*/
// [x] requestor: String,
2024-03-24 14:38:09 -04:00
let requestor = params.msg.clone().sender.name;
2024-03-22 18:16:41 -04:00
// [x] requestor_badge: Option<ChatBadge>,
let mut requestor_badge_mut: Option<ChatBadge> = None;
2024-03-24 14:38:09 -04:00
for b in &params.msg.badges {
2024-03-22 18:16:41 -04:00
if b.name == "moderator" {
requestor_badge_mut = Some(ChatBadge::Mod);
} else if b.name == "broadcaster" {
requestor_badge_mut = Some(ChatBadge::Broadcaster);
2024-03-25 17:01:27 -04:00
} else if b.name == "vip" {
requestor_badge_mut = Some(ChatBadge::VIP);
2024-03-22 18:16:41 -04:00
}
}
let requestor_badge = requestor_badge_mut;
// [x] trg_module: BotModule,
// - [x] Need to validate an actual BotModule - otherwise, fail or exit the cmd
2024-03-22 18:16:41 -04:00
let trg_module = if (arg1 == Some("-i")) || (arg1 == Some("-f")) { arg2 } else { arg1 };
// if no trg_module was passed
2024-03-22 20:18:02 -04:00
if trg_module.is_none() {
2024-03-22 18:16:41 -04:00
2024-03-24 14:38:09 -04:00
let botlock = params.bot.read().await;
2024-03-22 18:16:41 -04:00
let outmsg = "uuh You need to pass a module";
botlog::debug(
outmsg,
Some("botmodules.rs > cmd_disable()".to_string()),
2024-03-24 14:38:09 -04:00
Some(&params.msg),
2024-03-22 18:16:41 -04:00
);
2024-03-24 16:59:50 -04:00
2024-03-25 14:11:21 -04:00
// We should call a notification around here
2024-03-24 16:59:50 -04:00
2024-03-25 14:11:21 -04:00
botlock.botmgrs.chat.send_botmsg(super::chat::BotMsgType::Notif(
outmsg.to_string()
),
params.clone(),
).await;
2024-03-24 16:59:50 -04:00
2024-03-22 18:16:41 -04:00
return;
}
// [x] trg_level: StatusLvl,
2024-03-24 14:38:09 -04:00
let currchnl = params.msg.channel_login.to_lowercase();
2024-03-22 18:16:41 -04:00
let trg_level =
2024-03-22 20:18:02 -04:00
if arg1 == Some("-i") || arg1 == Some("-f") { StatusLvl::Instance }
// else if arg1 == Some("-f") { StatusLvl::Instance }
else { StatusLvl::Ch(Channel(currchnl)) }
2024-03-22 18:16:41 -04:00
;
2024-03-24 14:38:09 -04:00
let botlock = params.bot.read().await;
2024-03-22 18:16:41 -04:00
let modmgr = Arc::clone(&botlock.botmodules);
let id = botlock.get_identity();
let force = arg1 == Some("-f");
let rslt = modmgr.exec_disable(
requestor,
requestor_badge,
BotModule(trg_module.unwrap().to_string()),
2024-03-22 18:16:41 -04:00
trg_level,
force,
id).await;
2024-03-22 18:49:07 -04:00
2024-03-22 18:16:41 -04:00
let outmsg = match rslt.clone() {
ChangeResult::Failed(a) => format!("Stare Failed : {}",a),
ChangeResult::NoChange(a) => format!("Hmm No Change : {}",a),
ChangeResult::Success(a) => format!("YAAY Success : {}",a),
};
2024-03-25 14:11:21 -04:00
// We should call a notification around here
2024-03-24 16:59:50 -04:00
2024-03-25 14:11:21 -04:00
botlock.botmgrs.chat.send_botmsg(super::chat::BotMsgType::Notif(
outmsg.to_string()
),
params.clone(),
).await;
2024-03-24 16:59:50 -04:00
2024-03-21 21:20:16 -04:00
}
}
2024-03-22 20:18:02 -04:00
#[derive(Debug, Clone)]
2024-03-23 14:00:20 -04:00
pub struct BotModule(pub String);
impl PartialEq for BotModule {
2024-03-22 08:40:09 -04:00
fn eq(&self, other: &Self) -> bool {
let BotModule(name1) = self.clone();
let BotModule(name2) = other.clone();
name1.to_lowercase() == name2.to_lowercase()
}
}
impl Eq for BotModule {}
2024-03-22 08:40:09 -04:00
impl Hash for BotModule{
2024-03-22 20:18:02 -04:00
fn hash<H: Hasher>(&self, state: &mut H) {
let BotModule(name) = self.clone();
name.to_lowercase().hash(state);
}
}
2024-03-22 08:40:09 -04:00
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,
Ch(Channel),
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),
L(Listener),
2024-03-27 01:29:25 -04:00
R(Arc<RwLock<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-24 14:38:09 -04:00
pub async fn execute(&self, params : ExecBodyParams) {
2024-01-29 02:27:11 -05:00
match self {
2024-03-24 14:38:09 -04:00
BotAction::L(a) => a.execute(params).await,
BotAction::C(a) => a.execute(params).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 {
pub module: BotModule,
2024-02-25 10:40:54 -05:00
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-24 14:38:09 -04:00
pub async fn execute(&self, params : ExecBodyParams) {
(*self.exec_body)(params).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: BotModule,
2024-02-25 10:40:54 -05:00
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-24 14:38:09 -04:00
pub async fn execute(&self, params : ExecBodyParams) {
(self.exec_body)(params).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
}
2024-03-26 14:30:13 -04:00
// #[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[derive(PartialEq, Eq, Hash)]
pub enum RoutineAttr {
2024-03-26 11:29:47 -04:00
DelayedStart,
ScheduledStart(DateTime<Local>), // Scheduled Date (if any) after which, if not started, may trigger
LoopDuration(Duration), // How long to wait between iterations
LoopInfinitely,
RunOnce,
MaxTimeThreshold(DateTime<Local>), // DateTime after which, it will abort/cancel or stop
MaxIterations(i64),
}
/*
a Routine can be given different combinations of the above, but business logic may validate
these at Routine construction
For example, a Routine could have the following characteristics
- DelayedStart (so it skips the first iteration)
- ScheduledStart(DateTime<Local>) - With a Start Date-Time for the first iteration to trigger
- LoopDuration(Duration) - How long to wait between iterations
The above without any other thresholds would loop infinitely with the above characteristics
Another example,
- LoopDuration(Duration) - How long to wait between iterations
- MaxTimeThreshold(DateTime<Local>)
- MaxIterations(i64)
The above has thresholds , so if either are reached, it would abort/cancel or stop . Since there is no
ScheduledStart, the routine would have to be started manually elsewhere
Another example ,
- (no RoutineAttr)
The above would only run once, and only when the Start() is called
*/
// #[derive(Debug)]
pub struct Routine {
2024-03-26 14:30:13 -04:00
pub name : String ,
2024-03-26 16:56:30 -04:00
pub module : BotModule , // from() can determine this if passed parents_params
pub channel : Channel , // Requiring some channel context
2024-03-26 11:29:47 -04:00
exec_body: bot_actions::actions_util::ExecBody,
2024-03-27 17:28:47 -04:00
pub parent_params : ExecBodyParams ,
2024-03-27 17:03:23 -04:00
pub join_handle : Option<Arc<RwLock<JoinHandle<RoutineAR>>>> ,
2024-03-26 11:29:47 -04:00
start_time : Option<DateTime<Local>> ,
2024-03-27 17:28:47 -04:00
pub complete_iterations : i64 ,
2024-03-26 14:30:13 -04:00
remaining_iterations : Option<i64> ,
2024-03-26 11:29:47 -04:00
routine_attr : Vec<RoutineAttr> ,
}
impl Routine {
2024-03-27 18:29:14 -04:00
pub async fn validate_attr(routine_attr : &Vec<RoutineAttr>)
-> Result<String,String>
// [ ] => 03.27 - WIP
{
/*
GENERAL LOGIC :
[ ] 1. Define RoutineAttr in a broad level that are known to be implented or are work in progress
[ ] 2. Built in Logic will check these vectors, and return if Not Implemented
[ ] 3. If Implemented , then there are additional internal validation based on combination done later
*/
// [x] 1. Define RoutineAttr in a broad level that are known to be implented or are work in progress
// adjust the below for those that are work in progress or that are implemented
// - This will allow other functions to validate that it is implemented
// WORK IN PROGRESS VECTOR - Vec<$RoutineAttr>
let wip_attr:Vec<&RoutineAttr> = vec![
&RoutineAttr::RunOnce
];
let implemented_attr:Vec<&RoutineAttr> = vec![
];
// [x] 2. Built in Logic will check these vectors, and return if Not Implemented
let mut unimplemented = routine_attr.iter().filter(|x| !wip_attr.contains(x) && !implemented_attr.contains(x));
if unimplemented.next().is_some() {
botlog::trace(
"[ERROR][Routine Feature NOT IMPLEMENTED]",
Some("Routine > Validate_attr()".to_string()),
None,
);
Log::flush();
return Err("NOT IMPLEMENTED".to_string());
}
// [ ] 3. If Implemented , then there are additional internal validation based on combination done later
if routine_attr.contains(&RoutineAttr::RunOnce) {
return Ok("Valid & Implemented Setup".to_string())
}
botlog::trace(
"[ERROR][Routine Feature NOT IMPLEMENTED]",
Some("Routine > Validate_attr()".to_string()),
None,
);
Log::flush();
return Err("NOT IMPLEMENTED".to_string())
}
pub async fn validate_self_attr(self)
-> Result<String,String>
// [x] => 03.27 - COMPLETED
{
Routine::validate_attr(&self.routine_attr).await
}
2024-03-26 11:29:47 -04:00
// Constructor
2024-03-27 18:29:14 -04:00
pub async fn from(
2024-03-26 14:30:13 -04:00
name : String ,
module : BotModule ,
channel : Channel,
routine_attr : Vec<RoutineAttr> ,
exec_body : bot_actions::actions_util::ExecBody ,
parent_params : ExecBodyParams
2024-03-27 01:29:25 -04:00
) -> Result<
Arc<RwLock<Routine>>,
String
2024-03-27 18:29:14 -04:00
>
// [x] => 03.27 - COMPLETED
{
2024-03-26 11:29:47 -04:00
2024-03-27 18:29:14 -04:00
Routine::validate_attr(&routine_attr).await?;
return Ok(Arc::new(RwLock::new(Routine {
name ,
module ,
channel ,
exec_body ,
parent_params ,
join_handle : None ,
start_time : None ,
complete_iterations : 0 ,
remaining_iterations : None ,
routine_attr : routine_attr
}))) ;
2024-03-26 14:30:13 -04:00
2024-03-26 16:56:30 -04:00
2024-03-26 11:29:47 -04:00
}
pub fn change_channel(
&self,
_channel : Channel
2024-03-27 18:29:14 -04:00
) -> Result<String,String>
// [ ] => 03.27 - WIP - NOT IMPLEMENTED
{
2024-03-26 11:29:47 -04:00
// [ ] Think Ideally it should try to
// change the target channel of the
// internal process too if possible?
Err("NOT IMPLEMENTED".to_string())
}
2024-03-27 01:29:25 -04:00
pub async fn start(
trg_routine_ar : Arc<RwLock<Routine>>
2024-03-27 10:18:13 -04:00
// ) -> Result<String,String>
2024-03-27 16:04:15 -04:00
) -> Result<Arc<RwLock<Routine>>,String>
2024-03-27 18:29:14 -04:00
// [ ] => 03.27 - WIP
2024-03-26 11:29:47 -04:00
{
// [ ] Asyncio Spawn likely around here
// [ ] & Assigns self.join_handle
2024-03-26 14:30:13 -04:00
2024-03-27 16:04:15 -04:00
/*
2024-03-27 17:03:23 -04:00
UMBRELLA ROUTINE LOGIC
2024-03-27 16:04:15 -04:00
1. Create a loop scenario based on routine_attr such as RunOnce
2. Run the loop depending on how the Routine is setup
2024-03-27 17:03:23 -04:00
START LOGIC :
1. Ideally validate the routineattr that they're not a problem scenario (However, this should have been validated At Setup)
a. Extra helper validation function though would help in case the attributes were changes between setup
2. Use these attributes only in Critical areas of the loop logic to determine changes in Loop logic
2024-03-27 16:04:15 -04:00
*/
2024-03-26 16:56:30 -04:00
2024-03-26 14:30:13 -04:00
2024-03-27 17:03:23 -04:00
// Use the following to stop the function from going any further if not implemented
2024-03-27 10:18:13 -04:00
2024-03-27 18:29:14 -04:00
Routine::validate_attr(&trg_routine_ar.read().await.routine_attr).await?;
// if !trg_routine_ar.read().await.routine_attr.contains(&RoutineAttr::RunOnce) {
2024-03-26 14:30:13 -04:00
2024-03-27 18:29:14 -04:00
// botlog::trace(
// format!(
// "[ERROR][Routine Feature NOT IMPLEMENTED] {} in {}",
// trg_routine_ar.read().await.name,
// trg_routine_ar.read().await.channel.0
// )
// .as_str(),
// Some(format!(
// "Routine > start() > (In Tokio Spawn) > {:?}",
// trg_routine_ar.read().await.module
// )),
// Some(&trg_routine_ar.read().await.parent_params.msg),
// );
// Log::flush();
// return Err("NOT IMPLEMENTED".to_string())
// }
2024-03-27 17:03:23 -04:00
2024-03-26 14:30:13 -04:00
2024-03-27 17:03:23 -04:00
let trg_routine_arout = Arc::clone(&trg_routine_ar);
2024-03-26 14:30:13 -04:00
2024-03-26 16:56:30 -04:00
botlog::trace(
2024-03-27 17:03:23 -04:00
"innerhelper() started",
Some(format!(
"Routine > start() > (In Tokio Spawn)",
2024-03-26 16:56:30 -04:00
)),
2024-03-27 01:29:25 -04:00
Some(&trg_routine_ar.read().await.parent_params.msg),
2024-03-26 16:56:30 -04:00
);
Log::flush();
2024-03-26 14:30:13 -04:00
2024-03-27 17:03:23 -04:00
// Spawn the task
let join_handle = tokio::spawn(async move {
botlog::trace(
">> Within Spawn",
Some(format!(
"Routine > start() > (In Tokio Spawn)",
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
Log::flush();
{ // Prior to Loop that calls Custom Routine Execution Body
let mut a = trg_routine_ar.write().await;
a.start_time = Some(chrono::offset::Local::now());
}
loop { // Routine loop
// execution body
trg_routine_ar.read().await.loopbody().await;
{ // End of Loop iteration
let mut a = trg_routine_ar.write().await;
a.complete_iterations += 1;
if let Some(i) = a.remaining_iterations {
if i > 0 { a.remaining_iterations = Some(i-1) ; }
}
}
// End of Loop Validation
2024-03-27 18:29:14 -04:00
// These generally may include routine_attr related checks
2024-03-27 17:03:23 -04:00
if trg_routine_ar.read().await.routine_attr.contains(&RoutineAttr::RunOnce) {
if trg_routine_ar.read().await.complete_iterations > 0 { break; }
}
}
botlog::trace(
format!(
"[TRACE][Routine Completed] {} in {}",
trg_routine_ar.read().await.name,
trg_routine_ar.read().await.channel.0
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
trg_routine_ar.read().await.module
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
botlog::trace(
format!(
"[TRACE][Routine Completed][Routine Header Test] {} in {} > Completed Iterations : {}",
trg_routine_ar.read().await.name,
trg_routine_ar.read().await.channel.0 ,
trg_routine_ar.read().await.complete_iterations,
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
trg_routine_ar.read().await.module
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
Log::flush();
trg_routine_ar
});
trg_routine_arout.write().await.join_handle = Some(Arc::new(RwLock::new(join_handle)));
// }
return Ok(trg_routine_arout);
2024-03-26 11:29:47 -04:00
}
2024-03-26 14:30:13 -04:00
2024-03-27 18:29:14 -04:00
async fn loopbody(&self)
// [x] => 03.27 - COMPLETED
2024-03-26 14:30:13 -04:00
{
2024-03-27 10:18:13 -04:00
botlog::trace(
"loopbody() started",
Some(format!(
"Routine > start() > (During Tokio Spawn) > Execution body",
)),
None,
);
Log::flush();
2024-03-26 14:30:13 -04:00
(self.exec_body)(
self.parent_params.clone()
).await;
}
2024-03-26 16:56:30 -04:00
pub async fn stop(&self) -> Result<String,String>
2024-03-27 18:29:14 -04:00
// [ ] => 03.27 - WIP - NOT IMPLEMENTED
2024-03-26 11:29:47 -04:00
{
2024-03-26 16:56:30 -04:00
let self_rw = Arc::new(RwLock::new(self));
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ERROR][Routine NOT IMPLEMENTED] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
2024-03-26 11:29:47 -04:00
Err("NOT IMPLEMENTED".to_string())
}
2024-03-26 16:56:30 -04:00
pub async fn restart(
2024-03-26 11:29:47 -04:00
&self,
_force : bool
) -> Result<String,String>
2024-03-27 18:29:14 -04:00
// [ ] => 03.27 - WIP - NOT IMPLEMENTED
2024-03-26 11:29:47 -04:00
{
// force flag aborts the routine immediately (like cancel())
2024-03-26 16:56:30 -04:00
let self_rw = Arc::new(RwLock::new(self));
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ERROR][Routine NOT IMPLEMENTED] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
2024-03-26 11:29:47 -04:00
Err("NOT IMPLEMENTED".to_string())
}
2024-03-26 16:56:30 -04:00
pub async fn cancel(&self) -> Result<String,String>
2024-03-27 18:29:14 -04:00
// [ ] => 03.27 - WIP - NOT IMPLEMENTED
2024-03-26 11:29:47 -04:00
{
// [ ] Likely calls abort()
// Related :
// https://docs.rs/tokio/latest/tokio/task/struct.JoinHandle.html#method.abort
2024-03-26 16:56:30 -04:00
let self_rw = Arc::new(RwLock::new(self));
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ERROR][Routine NOT IMPLEMENTED] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
2024-03-26 11:29:47 -04:00
Err("NOT IMPLEMENTED".to_string())
}
}
2023-12-21 00:48:09 -05:00
2024-03-22 20:18:02 -04:00
type StatusdbEntry = (ModGroup, Vec<StatusType>);
2024-03-24 15:46:08 -04:00
type ModuleActions = Vec<Arc<RwLock<BotAction>>>;
2024-03-22 20:18:02 -04:00
2024-02-25 10:40:54 -05:00
pub struct ModulesManager {
statusdb: Arc<RwLock<HashMap<BotModule, StatusdbEntry>>>,
2024-03-24 15:46:08 -04:00
pub botactions: Arc<RwLock<HashMap<BotModule, ModuleActions>>>,
2023-12-21 00:48:09 -05:00
}
/*
statusdb
2024-02-25 10:40:54 -05:00
<HashMap
<BotModule, <-- e.g., BotModule(String::from("experiments001"))
Vec<ModStatusType> <-- shows Enabled/Disabled per Status level
2024-02-25 10:40:54 -05:00
botactions
HashMap<
BotModule, <-- e.g., BotModule(String::from("experiments001"))
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;
2024-03-22 18:49:07 -04:00
crate::core::botmodules::init(Arc::clone(&mgrarc)).await;
2024-03-02 11:55:16 -05:00
// 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-22 08:21:10 -04:00
pub async fn moduleslist(&self) -> HashMap<BotModule,ModGroup>
2024-03-22 08:21:10 -04:00
{
// let db = Arc::clone(&self.statusdb);
let db = self.statusdb.clone();
let dblock = db.read().await;
let mut outmap = HashMap::new();
for (k,v) in &(*dblock) {
let (mgrp,_) = v;
let mtype = k;
outmap.insert((*mtype).clone(), (*mgrp).clone());
}
outmap
}
pub async fn modstatus(&self, in_module: BotModule, in_chnl: Channel) -> 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 (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 => {
/*
[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 21:20:16 -04:00
pub async fn exec_enable(
&self,
requestor: String,
requestor_badge: Option<ChatBadge>,
trg_module: BotModule,
2024-03-21 21:20:16 -04:00
trg_level: StatusLvl,
2024-03-22 17:06:09 -04:00
id: Arc<RwLock<IdentityManager>>,
2024-03-21 21:20:16 -04:00
) -> ChangeResult
{
/*
1. If CmdSender is BotAdmin but not (Mod,SupMod,Broadcaster)
1. can_user_run for cmdreqRoles including BotAdmin & not can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
1a. , and is not -i (to instance) , return a Failure recommending BotAdmin promote themselves first
1b. , and is -i (to instance) , return a Success
2. If CmdSender not a BotAdmin but is (Mod,SupMod,Broadcaster)
2. not can_user_run for cmdreqRoles including BotAdmin & can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
2a. , and is not -i (to instance) , return a Success
2b. , and is -i (to instance) , return a Failure they are not allowed
3. If CmdSender is (Mod,SupMod,Broadcaster) and a BotAdmin
3. can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster) & can_user_run for cmdreqRoles including BotAdmin
3a. , and is not -i (to instance) , return a Success
3b. , and is -i (to instance) , return a Success
*/
/*
2024-03-22 21:06:15 -04:00
[x] 1. If CmdSender is BotAdmin but not (Mod,SupMod,Broadcaster)
2024-03-21 21:20:16 -04:00
1. can_user_run for cmdreqRoles including BotAdmin & not can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
1a. , and is -i (to instance) , return a Success
1b. , and is not -i (to instance) , return a Failure recommending BotAdmin promote themselves first
*/
2024-03-22 17:06:09 -04:00
// [x] Validate in trg_module first
let modlist = self.moduleslist().await;
let rslt = modlist.get(&trg_module);
2024-03-22 20:18:02 -04:00
if rslt.is_none() {
2024-03-22 17:06:09 -04:00
return ChangeResult::Failed("Module doesn't exist".to_string());
}
2024-03-24 21:50:28 -04:00
botlog::trace(
"ACQUIRING WRITE LOCK : ID",
Some("ModulesManager > Exec_enable".to_string()),
None,
);
2024-03-22 17:06:09 -04:00
2024-03-21 21:20:16 -04:00
let mut idlock = id.write().await;
// if trg_level = StatusLvl::Instance , the temp_chnl = the broadcaster's or the chatter's
let arb_chnl = match trg_level.clone() {
StatusLvl::Instance => Channel(requestor.to_lowercase()),
2024-03-21 21:20:16 -04:00
StatusLvl::Ch(a) => a,
};
const OF_CMD_CHANNEL:Channel = Channel(String::new());
2024-03-21 21:20:16 -04:00
let (admin_level_access,_) = idlock.can_user_run(requestor.clone(), arb_chnl.clone(), requestor_badge.clone(),
vec![
identity::UserRole::BotAdmin,
]).await;
let (chnl_elevated_access,_) = idlock.can_user_run(requestor, arb_chnl, requestor_badge.clone(),
vec![
identity::UserRole::Mod(OF_CMD_CHANNEL),
identity::UserRole::SupMod(OF_CMD_CHANNEL),
identity::UserRole::Broadcaster,
]).await;
2024-03-22 17:06:09 -04:00
2024-03-21 21:20:16 -04:00
if let Permissible::Allow = admin_level_access {
if let Permissible::Block = chnl_elevated_access {
2024-03-22 17:06:09 -04:00
botlog::debug(
&format!("?? REACHED INNER TIER :
admin_level_access : {:?} ; chnl_elevated_access : {:?}",
admin_level_access , chnl_elevated_access),
Some("botmodules.rs > exec_enable()".to_string()),
None,
);
2024-03-21 21:20:16 -04:00
match trg_level {
StatusLvl::Instance => {
self.set_instance_enabled(trg_module.clone()).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Enabled at Instance Level".to_string());
2024-03-21 21:20:16 -04:00
},
StatusLvl::Ch(_) => {
2024-03-22 17:06:09 -04:00
return ChangeResult::Failed("Promote yourself Temporarily First".to_string());
2024-03-21 21:20:16 -04:00
},
};
2024-03-22 17:06:09 -04:00
2024-03-21 21:20:16 -04:00
}
}
/*
2024-03-22 21:06:15 -04:00
[x] 2. If CmdSender not a BotAdmin but is (Mod,SupMod,Broadcaster)
2024-03-21 21:20:16 -04:00
2. not can_user_run for cmdreqRoles including BotAdmin & can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
2a. , and is -i (to instance) , return a Failure they are not allowed
2b. , and is not -i (to instance) , return a Success
*/
if let Permissible::Block = admin_level_access {
if let Permissible::Allow = chnl_elevated_access {
match trg_level.clone() {
StatusLvl::Instance => {
2024-03-22 17:06:09 -04:00
return ChangeResult::Failed("You're not allowed".to_string());
2024-03-21 21:20:16 -04:00
},
StatusLvl::Ch(in_chnl) => {
self.set_ch_enabled(trg_module.clone(), in_chnl).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Enabled at Channel Level".to_string());
2024-03-21 21:20:16 -04:00
},
};
}
}
/*
2024-03-22 21:06:15 -04:00
[x] 3. If CmdSender is (Mod,SupMod,Broadcaster) and a BotAdmin
2024-03-21 21:20:16 -04:00
3. can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster) & can_user_run for cmdreqRoles including BotAdmin
3a. , and is not -i (to instance) , return a Success
3b. , and is -i (to instance) , return a Success
*/
if let Permissible::Allow = admin_level_access {
if let Permissible::Allow = chnl_elevated_access {
match trg_level {
StatusLvl::Instance => {
self.set_instance_enabled(trg_module.clone()).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Enabled at Instance Level".to_string());
2024-03-21 21:20:16 -04:00
},
StatusLvl::Ch(in_chnl) => {
self.set_ch_enabled(trg_module.clone(), in_chnl).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Enabled at Channel Level".to_string());
2024-03-21 21:20:16 -04:00
},
};
}
}
2024-03-22 17:06:09 -04:00
// Respond in case of General Chatter
// The below should NOT be required , as current internal logic would prevent
// a BotCommand to be ran by a Chatter if it requires any special roles and
// that chatter does not have htose roles
// However, below is added to satisfy unit tests
if let Permissible::Block = admin_level_access {
if let Permissible::Block = chnl_elevated_access {
match trg_level {
StatusLvl::Instance => {
return ChangeResult::Failed("You're not allowed".to_string());
},
StatusLvl::Ch(_) => {
return ChangeResult::Failed("You're not allowed".to_string());
},
};
}
}
2024-03-21 21:20:16 -04:00
// =======================
// =======================
// =======================
2024-03-22 17:06:09 -04:00
botlog::debug(
&format!("FAILURE involves :
admin_level_access : {:?} ; chnl_elevated_access : {:?}",
admin_level_access , chnl_elevated_access),
Some("botmodules.rs > exec_enable()".to_string()),
None,
);
2024-03-21 21:20:16 -04:00
2024-03-22 17:06:09 -04:00
Log::flush();
2024-03-21 21:20:16 -04:00
2024-03-22 08:21:10 -04:00
ChangeResult::Failed("ERROR : Not implemented yet".to_string())
}
pub async fn exec_disable(
&self,
requestor: String,
requestor_badge: Option<ChatBadge>,
trg_module: BotModule,
2024-03-22 08:21:10 -04:00
trg_level: StatusLvl,
force: bool,
2024-03-22 17:06:09 -04:00
id: Arc<RwLock<IdentityManager>>,
2024-03-22 08:21:10 -04:00
) -> ChangeResult
{
/*
1. If CmdSender is BotAdmin but not (Mod,SupMod,Broadcaster)
1. can_user_run for cmdreqRoles including BotAdmin & not can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
1a. , and has no special flags (-i / -f) , return a Failure recommending BotAdmin promote themselves first
1b. , and is -i (to instance) , return a Success
1c. , and is -f (forced) , return a Success
2. If CmdSender not a BotAdmin but is (Mod,SupMod,Broadcaster)
2. not can_user_run for cmdreqRoles including BotAdmin & can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
2a. , and has no special flags (-i / -f) , return a Success
2b. , and is -i (to instance) , return a Failure they are not allowed
2c. , and is -f (forced) , return a Failure they are not allowed
3. If CmdSender is (Mod,SupMod,Broadcaster) and a BotAdmin
3. can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster) & can_user_run for cmdreqRoles including BotAdmin
3a. , and has no special flags (-i / -f) , return a Success
3b. , and is -i (to instance) , return a Success
3c. , and is -f (forced) , return a Success
*/
2024-03-22 17:06:09 -04:00
// [x] Validate in trg_module first
let modlist = self.moduleslist().await;
let rslt = modlist.get(&trg_module);
2024-03-22 20:18:02 -04:00
if rslt.is_none() {
2024-03-22 17:06:09 -04:00
return ChangeResult::Failed("Module doesn't exist".to_string());
}
2024-03-24 21:50:28 -04:00
botlog::trace(
"ACQUIRING WRITE LOCK : ID",
Some("ModulesManager > Exec_disable".to_string()),
None,
);
2024-03-22 08:21:10 -04:00
let mut idlock = id.write().await;
// if trg_level = StatusLvl::Instance , the temp_chnl = the broadcaster's or the chatter's
let arb_chnl = match trg_level.clone() {
StatusLvl::Instance => Channel(requestor.to_lowercase()),
2024-03-22 08:21:10 -04:00
StatusLvl::Ch(a) => a,
};
const OF_CMD_CHANNEL:Channel = Channel(String::new());
2024-03-22 08:21:10 -04:00
let (admin_level_access,_) = idlock.can_user_run(requestor.clone(), arb_chnl.clone(), requestor_badge.clone(),
vec![
identity::UserRole::BotAdmin,
]).await;
let (chnl_elevated_access,_) = idlock.can_user_run(requestor, arb_chnl, requestor_badge.clone(),
vec![
identity::UserRole::Mod(OF_CMD_CHANNEL),
identity::UserRole::SupMod(OF_CMD_CHANNEL),
identity::UserRole::Broadcaster,
]).await;
/*
2024-03-22 21:06:15 -04:00
[x] 1. If CmdSender is BotAdmin but not (Mod,SupMod,Broadcaster)
2024-03-22 08:21:10 -04:00
1. can_user_run for cmdreqRoles including BotAdmin & not can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
1a. , and is -f (forced) , return a Success
1b. , and is -i (to instance) , return a Success
1c. , and has no special flags (-i / -f) , return a Failure recommending BotAdmin promote themselves first
*/
if let Permissible::Allow = admin_level_access {
if let Permissible::Block = chnl_elevated_access {
if force {
self.force_disable(trg_module.clone()).await;
return ChangeResult::Success("Forced Disable".to_string());
} else {
match trg_level {
StatusLvl::Instance => {
self.set_instance_disabled(trg_module.clone()).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Disabled at Instance Level".to_string());
2024-03-22 08:21:10 -04:00
},
StatusLvl::Ch(_) => {
2024-03-22 17:06:09 -04:00
return ChangeResult::Failed("Promote yourself Temporarily First".to_string());
2024-03-22 08:21:10 -04:00
},
};
}
}
}
/*
2024-03-22 21:06:15 -04:00
[x] 2. If CmdSender not a BotAdmin but is (Mod,SupMod,Broadcaster)
2. not can_user_run for cmdreqRoles including BotAdmin & can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster)
2a. , and is -f (forced) , return a Failure they are not allowed
2b. , and is -i (to instance) , return a Failure they are not allowed
2c. , and has no special flags (-i / -f) , return a Success
2024-03-22 08:21:10 -04:00
*/
if let Permissible::Block = admin_level_access {
if let Permissible::Allow = chnl_elevated_access {
if force {
return ChangeResult::Failed("You're not allowed".to_string());
} else {
match trg_level.clone() {
StatusLvl::Instance => {
2024-03-22 17:06:09 -04:00
return ChangeResult::Failed("You're not allowed".to_string());
2024-03-22 08:21:10 -04:00
},
StatusLvl::Ch(in_chnl) => {
self.set_ch_disabled(trg_module.clone(), in_chnl).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Disabled at Channel Level".to_string());
2024-03-22 08:21:10 -04:00
},
};
}
}
}
/*
2024-03-22 21:06:15 -04:00
[x] 3. If CmdSender is (Mod,SupMod,Broadcaster) and a BotAdmin
3. can_user_run for cmdreqRoles (Mod,SupMod,Broadcaster) & can_user_run for cmdreqRoles including BotAdmin
3a. , and is -f (forced) , return a Success
3b. , and is -i (to instance) , return a Success
3c. , and has no special flags (-i / -f) , return a Success
2024-03-22 08:21:10 -04:00
*/
if let Permissible::Allow = admin_level_access {
if let Permissible::Allow = chnl_elevated_access {
if force {
self.force_disable(trg_module.clone()).await;
return ChangeResult::Success("Forced Disable".to_string());
} else {
match trg_level {
StatusLvl::Instance => {
self.set_instance_disabled(trg_module.clone()).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Disabled at Instance Level".to_string());
2024-03-22 08:21:10 -04:00
},
StatusLvl::Ch(in_chnl) => {
self.set_ch_disabled(trg_module.clone(), in_chnl).await;
2024-03-22 17:06:09 -04:00
return ChangeResult::Success("Disabled at Channel Level".to_string());
2024-03-22 08:21:10 -04:00
},
};
}
}
}
2024-03-22 17:06:09 -04:00
// Respond in case of General Chatter
// The below should NOT be required , as current internal logic would prevent
// a BotCommand to be ran by a Chatter if it requires any special roles and
// that chatter does not have htose roles
// However, below is added to satisfy unit tests
if let Permissible::Block = admin_level_access {
if let Permissible::Block = chnl_elevated_access {
match trg_level {
StatusLvl::Instance => {
return ChangeResult::Failed("You're not allowed".to_string());
},
StatusLvl::Ch(_) => {
return ChangeResult::Failed("You're not allowed".to_string());
},
};
}
}
2024-03-22 08:21:10 -04:00
ChangeResult::Failed("ERROR : Not implemented yet".to_string())
2024-03-21 21:20:16 -04:00
}
pub async fn set_instance_disabled(&self, in_module: BotModule) -> (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
let mut dbt = self.statusdb.write().await;
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
)
},
}
2023-12-21 00:48:09 -05:00
}
pub async fn force_disable(&self, in_module: BotModule) -> (StatusType,ChangeResult) {
2024-03-21 02:13:23 -04:00
// 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 (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
2024-03-22 20:18:02 -04:00
} else {
matches!((*x).clone(), StatusType::Enabled(StatusLvl::Ch(_)))
}
2024-03-21 02:13:23 -04:00
)
{
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
)
},
}
2023-12-21 00:48:09 -05:00
}
pub async fn set_instance_enabled(&self, in_module: BotModule) -> (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 (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())
)
},
}
2024-03-21 00:05:52 -04:00
}
pub async fn set_ch_disabled(&self, in_module: BotModule , in_chnl: Channel) -> (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 (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())
)
},
}
2024-03-21 00:05:52 -04:00
}
pub async fn set_ch_enabled(&self, in_module: BotModule , in_chnl: Channel) -> (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 (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())
)
},
}
2024-03-21 00:05:52 -04:00
}
pub async fn add_botaction(&self, in_module: BotModule, 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: BotModule, in_action: BotAction) {
2024-03-20 23:20:46 -04:00
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:BotModule,in_modgroup: ModGroup) {
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()));
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)),
}
}
}
async fn int_add_botaction(&self, in_module: BotModule, 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
async fn find_conflict_module(mgr: &ModulesManager, act: &BotAction) -> Option<BotModule> {
2024-03-24 15:46:08 -04: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-03-24 15:46:08 -04:00
// for modact in moduleactions.iter() {
for modact_prelock in moduleactions.iter() {
let modact = modact_prelock.read().await;
// if let BotAction::C(dbcmd) = &modact {
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
// [x] check if given botcommand c.command:String conflicts with any in botactions
2023-12-21 20:11:32 -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
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
return Some(module.clone()); // works
2024-02-25 10:40:54 -05:00
}
}
2023-12-21 20:11:32 -05:00
// [x] Then do the same check except for each c.alias
2023-12-21 20:11:32 -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
return Some(module.clone()); // works
2024-02-25 10:40:54 -05:00
}
2023-12-21 20:11:32 -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
return Some(module.clone()); // works
2024-02-25 10:40:54 -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
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());
2024-03-24 15:46:08 -04:00
modactions.push(Arc::new(RwLock::new(in_action)));
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
);
}
fn _statuscleanup(&self, _: Option<Channel>) {
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<Channel> can pass Some(Channel("m")) (as an example) so statuscleanup only works on the given channel
2023-12-21 12:10:50 -05:00
// 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::*;
2024-03-22 08:21:10 -04:00
#[test]
2024-03-22 08:40:09 -04:00
fn case_insensitive_test() {
2024-03-22 08:21:10 -04:00
Log::set_file_ext(Extension::Log);
assert_eq!(
2024-03-22 17:06:09 -04:00
BotModule("TEST".to_string()),
BotModule("test".to_string())
2024-03-22 08:21:10 -04:00
);
}
2024-03-21 14:01:54 -04:00
/*
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: BotModule ,
2024-03-21 14:01:54 -04:00
in_modgroup : ModGroup ,
in_chnl1 : Channel,
in_chnl2 : Channel)
2024-03-21 14:01:54 -04:00
{
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) =
(Channel("TestChannel01".to_string()),Channel("TestChannel02".to_string()));
2024-03-21 14:01:54 -04:00
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) =
(Channel("TestChannel01".to_string()),Channel("TestChannel02".to_string()));
2024-03-21 14:01:54 -04:00
complex_workflow(in_module, in_modgroup, in_chnl1, in_chnl2).await;
}
2024-03-22 17:06:09 -04:00
/*
1. Create new ModulesManager & Identity Manager
2. modmgr.affirm_in_statusdb(Experiments01,Custom)
3. affirm when BotAdmin attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
4. affirm when BotAdmin attempts to exec_disable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
d. force disable
1. Create new ModulesManager & Identity Manager
2. modmgr.affirm_in_statusdb(Experiments01,Custom)
3. affirm when Mod attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
4. affirm when Mod attempts to exec_disable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
d. force disable
*/
async fn inner_enable_disable_complex(
requestor:String,
channel:Channel,
2024-03-22 17:06:09 -04:00
idmgr:IdentityManager,
modsmgr:Arc<ModulesManager>)
{
/*
Parent Tests would involve :
- Testing with a BotAdmin User
- Testing with a Mod User
- Testing with a Regular Chatter
*/
enum TestScenarios {
BotadminUser,
ModUser,
RegularChatter,
// ModuleDoesNotExist, // preferring instead to handle in it's own smaller test
}
let mut idlock = idmgr.clone();
let requestor_badge = None; // If they are a Mod on the Given Channel already, that can be evaluated without the current badge
const OF_CMD_CHANNEL:Channel = Channel(String::new());
2024-03-22 17:06:09 -04:00
let (admin_level_access,_) = idlock.can_user_run(requestor.clone(), channel.clone(), requestor_badge.clone(),
vec![
identity::UserRole::BotAdmin,
]).await;
let (chnl_elevated_access,_) = idlock.can_user_run(requestor.clone(), channel.clone(), requestor_badge.clone(),
vec![
identity::UserRole::Mod(OF_CMD_CHANNEL),
identity::UserRole::SupMod(OF_CMD_CHANNEL),
identity::UserRole::Broadcaster,
]).await;
let current_test_scenario =
match admin_level_access {
Permissible::Allow => {
match chnl_elevated_access {
Permissible::Allow => { TestScenarios::BotadminUser },
Permissible::Block => { TestScenarios::BotadminUser }
}
},
Permissible::Block => {
match chnl_elevated_access {
Permissible::Allow => { TestScenarios::ModUser },
Permissible::Block => { TestScenarios::RegularChatter }
}
}
};
// [x] 2. modmgr.affirm_in_statusdb(Experiments01,Custom)
let in_module = BotModule("Experiments01".to_string());
let in_modgroup = ModGroup::Custom;
modsmgr.affirm_in_statusdb(in_module.clone(), in_modgroup.clone()).await;
/*
[x] 3. affirm when BotAdmin attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
*/
// [-] requestor_badge: Option<ChatBadge>,
// [x] trg_module: BotModule,
2024-03-22 17:06:09 -04:00
let trg_module = in_module;
// [x] trg_level: StatusLvl,
let trg_level = StatusLvl::Ch(channel.clone()); // setting to Channel Level
// [x] id: Arc<RwLock<IdentityManager>>,
let id = Arc::new(RwLock::new(idmgr.clone()));
let rslt = modsmgr.exec_enable(requestor.clone(),
None,
trg_module.clone(),
trg_level.clone(),
id.clone()).await;
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Failed("Promote yourself Temporarily First".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Success("Enabled at Channel Level".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
/*
[x] 3. affirm when BotAdmin attempts to exec_enable on the following
b. Channel Level , when they are a Mod
*/
// [x] requestor_badge: Option<ChatBadge>,
2024-03-22 21:06:15 -04:00
2024-03-22 17:06:09 -04:00
let requestor_badge = match current_test_scenario {
TestScenarios::BotadminUser =>
Some(ChatBadge::Mod), // setting badge to Mod -- for the Problem Scenario . They are both BotAdmin & Mod
TestScenarios::ModUser =>
Some(ChatBadge::Mod), // setting badge to Mod
TestScenarios::RegularChatter =>
None, // setting badge to None
} ;
let rslt = modsmgr.exec_enable(requestor.clone(),
requestor_badge,
trg_module.clone(),
trg_level.clone(),
id.clone()).await;
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Success("Enabled at Channel Level".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Success("Enabled at Channel Level".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
/*
[x] 3. affirm when BotAdmin attempts to exec_enable on the following
c. Instance Level
*/
let trg_level = StatusLvl::Instance; // setting to Instance level
let requestor_badge = match current_test_scenario {
TestScenarios::BotadminUser =>
None,
TestScenarios::ModUser =>
Some(ChatBadge::Mod),
TestScenarios::RegularChatter =>
None, // setting badge to None
};
let rslt = modsmgr.exec_enable(requestor.clone(),
requestor_badge, // passing based on scenario
trg_module.clone(),
trg_level.clone(),
id.clone()).await;
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Success("Enabled at Instance Level".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
/*
[x] 4. affirm when BotAdmin attempts to exec_disable on the following
a. Channel Level , where they are not a Mod
*/
let trg_level = StatusLvl::Ch(channel.clone()); // setting to Channel Level
let rslt: ChangeResult = modsmgr.exec_disable(requestor.clone(),
None, // Does not have a ChatBadge like Mod
trg_module.clone(),
trg_level.clone(),
false,
id.clone()).await;
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Success("Disabled at Channel Level".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Success("Disabled at Channel Level".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
/*
[x] 4. affirm when BotAdmin attempts to exec_disable on the following
b. Channel Level , when they are a Mod
*/
let trg_level = StatusLvl::Ch(channel.clone()); // setting to Channel Level
2024-03-22 21:06:15 -04:00
2024-03-22 17:06:09 -04:00
let requestor_badge = match current_test_scenario {
TestScenarios::BotadminUser =>
None,
TestScenarios::ModUser =>
Some(ChatBadge::Mod),
TestScenarios::RegularChatter =>
None, // setting badge to None
};
let rslt: ChangeResult = modsmgr.exec_disable(requestor.clone(),
requestor_badge,
trg_module.clone(),
trg_level.clone(),
false,
id.clone()).await;
2024-03-22 21:06:15 -04:00
2024-03-22 17:06:09 -04:00
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Success("Disabled at Channel Level".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Success("Disabled at Channel Level".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
/*
[x] 4. affirm when BotAdmin attempts to exec_disable on the following
c. Instance Level
*/
let trg_level = StatusLvl::Instance; // setting to Instance level
2024-03-22 21:06:15 -04:00
2024-03-22 17:06:09 -04:00
let rslt: ChangeResult = modsmgr.exec_disable(requestor.clone(),
None, // Does not have a ChatBadge like Mod
trg_module.clone(),
trg_level.clone(),
false,
id.clone()).await;
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Success("Disabled at Instance Level".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
/*
[ ] 4. affirm when BotAdmin attempts to exec_disable on the following
d. force disable
*/
let trg_level = StatusLvl::Instance; // setting to Instance level
let rslt: ChangeResult = modsmgr.exec_disable(requestor.clone(),
None, // Does not have a ChatBadge like Mod
trg_module.clone(),
trg_level.clone(),
true, // force flag - true
id.clone()).await;
match current_test_scenario {
TestScenarios::BotadminUser =>
assert_eq!(rslt,ChangeResult::Success("Forced Disable".to_string())),
TestScenarios::ModUser =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
TestScenarios::RegularChatter =>
assert_eq!(rslt,ChangeResult::Failed("You're not allowed".to_string())),
}
}
#[tokio::test]
async fn enable_disable_bot_admin_workflow() {
Log::set_file_ext(Extension::Log);
/*
1. Create new ModulesManager & Identity Manager
2. modmgr.affirm_in_statusdb(Experiments01,Custom)
3. affirm when BotAdmin attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
4. affirm when BotAdmin attempts to exec_disable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
d. force disable
*/
// [x] 1. Create new ModulesManager & Identity Manager
let idmgr = IdentityManager::init();
let modsmgr = ModulesManager::init().await;
/*
[x] 3. affirm when BotAdmin attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
*/
// [x] Create BotAdmin first
let requestor = "botadministrator".to_string();
idmgr.affirm_chatter_in_db(requestor.clone()).await;
idmgr
.add_role(requestor.clone(), identity::UserRole::BotAdmin)
.await;
let rslt = idmgr
.getspecialuserroles(requestor.clone(), None)
.await;
assert!(rslt.contains(&identity::UserRole::BotAdmin));
let channel = Channel("somechannel".to_string());
2024-03-22 17:06:09 -04:00
inner_enable_disable_complex(requestor, channel, idmgr, modsmgr).await;
}
#[tokio::test]
async fn enable_disable_mod_workflow() {
Log::set_file_ext(Extension::Log);
/*
1. Create new ModulesManager & Identity Manager
2. modmgr.affirm_in_statusdb(Experiments01,Custom)
3. affirm when Mod attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
4. affirm when Mod attempts to exec_disable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
d. force disable
*/
// [x] 1. Create new ModulesManager & Identity Manager
let idmgr = IdentityManager::init();
let modsmgr = ModulesManager::init().await;
let requestor = "mod_user".to_string();
// let botadmin_badge = &None;
let channel = Channel("somechannel".to_string());
2024-03-22 17:06:09 -04:00
idmgr.affirm_chatter_in_db(requestor.clone()).await;
idmgr
.add_role(requestor.clone(), identity::UserRole::Mod(channel.clone()))
.await;
let rslt = idmgr
.getspecialuserroles(
requestor.clone(),
Some(channel.clone()) // None if BotAdmin ; Otherwise, pass Some(Channel)
)
.await;
assert!(rslt.contains(&identity::UserRole::Mod(channel.clone())));
inner_enable_disable_complex(requestor, channel, idmgr, modsmgr).await;
}
#[tokio::test]
async fn enable_disable_chatter_workflow() {
Log::set_file_ext(Extension::Log);
/*
1. Create new ModulesManager & Identity Manager
2. modmgr.affirm_in_statusdb(Experiments01,Custom)
3. affirm when Mod attempts to exec_enable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
4. affirm when Mod attempts to exec_disable on the following
a. Channel Level , where they are not a Mod
b. Channel Level , when they are a Mod
c. Instance Level
d. force disable
*/
// [x] 1. Create new ModulesManager & Identity Manager
let idmgr = IdentityManager::init();
let modsmgr = ModulesManager::init().await;
let requestor = "regular_user".to_string();
let channel = Channel("somechannel".to_string());
2024-03-22 17:06:09 -04:00
idmgr.affirm_chatter_in_db(requestor.clone()).await;
2024-03-22 21:06:15 -04:00
2024-03-22 17:06:09 -04:00
let rslt = idmgr
.getspecialuserroles(
requestor.clone(),
Some(channel.clone()) // None if BotAdmin ; Otherwise, pass Some(Channel)
)
.await;
assert!(!rslt.contains(&identity::UserRole::Mod(channel.clone())) ||
!rslt.contains(&identity::UserRole::BotAdmin));
inner_enable_disable_complex(requestor, channel, idmgr, modsmgr).await;
}
#[tokio::test]
async fn enable_disable_modulenotexist_workflow() {
Log::set_file_ext(Extension::Log);
// [x] 1. Create new ModulesManager & Identity Manager
let idmgr = IdentityManager::init();
let modsmgr = ModulesManager::init().await;
let requestor = "regular_user".to_string();
2024-03-22 21:06:15 -04:00
let channel = Channel("somechannel".to_string());
2024-03-22 17:06:09 -04:00
idmgr.affirm_chatter_in_db(requestor.clone()).await;
2024-03-22 21:06:15 -04:00
2024-03-22 17:06:09 -04:00
let rslt = idmgr
.getspecialuserroles(
requestor.clone(),
Some(channel.clone()) // None if BotAdmin ; Otherwise, pass Some(Channel)
)
.await;
assert!(!rslt.contains(&identity::UserRole::Mod(channel.clone())) ||
!rslt.contains(&identity::UserRole::BotAdmin));
// After above, regular chatter is created
// [x] 2. modmgr.affirm_in_statusdb(Existing_Module,Custom)
let in_module = BotModule("Existing_Module".to_string());
let in_modgroup = ModGroup::Custom;
modsmgr.affirm_in_statusdb(in_module.clone(), in_modgroup.clone()).await;
let trg_level = StatusLvl::Ch(channel.clone()); // setting to Channel Level
// [x] Test with Non Existing module > exec
let trg_module = BotModule("Non_Existent_Module".to_string());
let rslt = modsmgr.exec_enable(requestor.clone(),
None,
trg_module.clone(),
trg_level.clone(),
Arc::new(RwLock::new(idmgr.clone()))).await;
assert_eq!(rslt,ChangeResult::Failed("Module doesn't exist".to_string()));
2024-03-22 21:06:15 -04:00
// [x] Test with Non Existing module > disable
2024-03-22 17:06:09 -04:00
let trg_module = BotModule("Non_Existent_Module".to_string());
let rslt = modsmgr.exec_disable(requestor.clone(),
None,
trg_module.clone(),
trg_level.clone(),
false,
Arc::new(RwLock::new(idmgr))).await;
assert_eq!(rslt,ChangeResult::Failed("Module doesn't exist".to_string()));
}
2024-03-21 14:01:54 -04:00
}