2310 lines
82 KiB
Rust
2310 lines
82 KiB
Rust
/*
|
|
|
|
ModulesManager is used to manage Modules and BotActions associated with those modules
|
|
|
|
pub struct ModulesManager {
|
|
statusdb: HashMap<BotModule,Vec<ModStatusType>>,
|
|
botactions: HashMap<BotModule,Vec<BotAction>>,
|
|
}
|
|
|
|
- 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)
|
|
|
|
Example
|
|
{
|
|
ModulesManager {
|
|
statusdb: {BotModule("experiments 004"): [Enabled(Instance)]},
|
|
botactions: {BotModule("experiments 004"): [C(BotCommand { module: BotModule("experiments 004"), command: "DUPCMD4", alias: ["DUPALIAS4A", "DUPALIAS4B"], help: "DUPCMD4 tester" })]} }
|
|
}
|
|
|
|
*/
|
|
|
|
use core::panic;
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
|
|
use twitch_irc::message::PrivmsgMessage;
|
|
|
|
use casual_logger::Log;
|
|
|
|
use tokio::sync::RwLock;
|
|
|
|
use async_trait::async_trait;
|
|
|
|
// use self::bot_actions::actions_util::BotAR;
|
|
use crate::core::bot_actions::BotAR;
|
|
use crate::core::bot_actions::actions_util;
|
|
use crate::core::bot_actions::ExecBodyParams;
|
|
use crate::core::botinstance::{BotInstance, Channel,ChangeResult};
|
|
use crate::core::botlog;
|
|
use crate::core::identity::{self, Permissible,IdentityManager};
|
|
|
|
use crate::core::bot_actions;
|
|
|
|
use std::hash::{Hash, Hasher};
|
|
|
|
use super::identity::ChatBadge;
|
|
|
|
|
|
pub async fn init(mgr: Arc<ModulesManager>) {
|
|
|
|
const OF_CMD_CHANNEL:Channel = Channel(String::new());
|
|
|
|
// 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;
|
|
|
|
// async fn cmd_enable(bot: BotAR, msg: PrivmsgMessage) {
|
|
async fn cmd_enable(params : ExecBodyParams) {
|
|
/*
|
|
There should be additional validation checks
|
|
- BotAdmins can only run instance level (-i) enables
|
|
- If BotAdmins need to enable/disable at Channel level, they must Promote themselves to be a Mod at least
|
|
- 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
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
Get parent module
|
|
|
|
*/
|
|
|
|
// let params_clone = Arc::clone(¶ms.parent_act);
|
|
// let actlock = params_clone.read().await;
|
|
// let act = &(*actlock);
|
|
// let parent_module = match act {
|
|
// BotAction::C(c) => Some(&(*c).module),
|
|
// BotAction::L(l) => Some(&(*l).module),
|
|
// _ => None,
|
|
// };
|
|
|
|
let parent_module = params.get_parent_module().await;
|
|
|
|
|
|
|
|
|
|
// [x] Unwraps arguments from message
|
|
|
|
let (arg1, arg2) = {
|
|
|
|
let mut argv = params.msg.message_text.split(' ');
|
|
|
|
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>,
|
|
trg_level: StatusLvl,
|
|
bot: BotAR,
|
|
) -> ChangeResult
|
|
*/
|
|
|
|
|
|
// [x] requestor: String,
|
|
let requestor = params.msg.clone().sender.name;
|
|
|
|
|
|
// [x] requestor_badge: Option<ChatBadge>,
|
|
|
|
let mut requestor_badge_mut: Option<ChatBadge> = None;
|
|
|
|
for b in ¶ms.msg.badges {
|
|
if b.name == "moderator" {
|
|
requestor_badge_mut = Some(ChatBadge::Mod);
|
|
} else if b.name == "broadcaster" {
|
|
requestor_badge_mut = Some(ChatBadge::Broadcaster);
|
|
}
|
|
}
|
|
|
|
let requestor_badge = requestor_badge_mut;
|
|
|
|
|
|
// [x] trg_module: BotModule,
|
|
// - [x] Need to validate an actual BotModule - otherwise, fail or exit the cmd
|
|
|
|
let trg_module = if (arg1 == Some("-i")) || (arg1 == Some("-f")) { arg2 } else { arg1 };
|
|
|
|
// if no trg_module was passed
|
|
// if let None = trg_module {
|
|
if trg_module.is_none() {
|
|
|
|
let botlock = params.bot.read().await;
|
|
|
|
let outmsg = "uuh You need to pass a module";
|
|
|
|
botlog::debug(
|
|
outmsg,
|
|
Some("botmodules.rs > cmd_enable()".to_string()),
|
|
Some(¶ms.msg),
|
|
);
|
|
|
|
// Only call Say if there is a parent module passed
|
|
if parent_module.is_some() {
|
|
|
|
|
|
botlock
|
|
.botmgrs
|
|
.chat
|
|
.say_in_reply_to(
|
|
¶ms.msg,
|
|
outmsg.to_string() ,
|
|
// parent_module.unwrap().clone()
|
|
params.clone(),
|
|
).await;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// [x] trg_level: StatusLvl,
|
|
|
|
let currchnl = params.msg.channel_login.to_lowercase();
|
|
|
|
let trg_level =
|
|
if arg1 == Some("-i") || arg1 == Some("-f") { StatusLvl::Instance }
|
|
// else if arg1 == Some("-f") { StatusLvl::Instance }
|
|
else { StatusLvl::Ch(Channel(currchnl)) }
|
|
;
|
|
|
|
|
|
|
|
let botlock = params.bot.read().await;
|
|
let modmgr = Arc::clone(&botlock.botmodules);
|
|
let id = botlock.get_identity();
|
|
|
|
|
|
// modmgr.exec_enable(requestor, requestor_badge, trg_module, trg_level, id)
|
|
let rslt = modmgr.exec_enable(
|
|
requestor,
|
|
requestor_badge,
|
|
BotModule(trg_module.unwrap().to_string()),
|
|
trg_level,
|
|
id).await;
|
|
|
|
|
|
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),
|
|
};
|
|
|
|
|
|
// Only call Say if there is a parent module passed
|
|
if parent_module.is_some() {
|
|
|
|
|
|
botlock
|
|
.botmgrs
|
|
.chat
|
|
.say_in_reply_to(
|
|
¶ms.msg,
|
|
outmsg.to_string(),
|
|
// parent_module.unwrap().clone()
|
|
params.clone(),
|
|
).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
// async fn cmd_disable(bot: BotAR, msg: PrivmsgMessage) {
|
|
async fn cmd_disable(params : ExecBodyParams) {
|
|
/*
|
|
There should be additional validation checks
|
|
- 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
|
|
- Other Special Roles (Mod,SupMod,Broadcaster) can run without issues to disable the module at Channel Level
|
|
*/
|
|
|
|
/*
|
|
disable -i <module> // disables at Instance
|
|
disable <module> // disables at Channel
|
|
disable -f <module> // force disables (instance and enabled are removed)
|
|
*/
|
|
|
|
/*
|
|
|
|
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
|
|
*/
|
|
|
|
/*
|
|
[x] Get the parent module
|
|
*/
|
|
|
|
// let params_clone = Arc::clone(¶ms.parent_act);
|
|
// let actlock = params_clone.read().await;
|
|
// let act = &(*actlock);
|
|
// let parent_module = match act {
|
|
// BotAction::C(c) => Some(&(*c).module),
|
|
// BotAction::L(l) => Some(&(*l).module),
|
|
// _ => None,
|
|
// };
|
|
|
|
let parent_module = params.get_parent_module().await;
|
|
|
|
// [x] Unwraps arguments from message
|
|
|
|
let (arg1, arg2) = {
|
|
|
|
let mut argv = params.msg.message_text.split(' ');
|
|
|
|
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>,
|
|
trg_level: StatusLvl,
|
|
force: bool,
|
|
// bot: BotAR,
|
|
id: Arc<RwLock<IdentityManager>>,
|
|
) -> ChangeResult
|
|
*/
|
|
|
|
|
|
// [x] requestor: String,
|
|
let requestor = params.msg.clone().sender.name;
|
|
|
|
|
|
// [x] requestor_badge: Option<ChatBadge>,
|
|
|
|
let mut requestor_badge_mut: Option<ChatBadge> = None;
|
|
|
|
for b in ¶ms.msg.badges {
|
|
if b.name == "moderator" {
|
|
requestor_badge_mut = Some(ChatBadge::Mod);
|
|
} else if b.name == "broadcaster" {
|
|
requestor_badge_mut = Some(ChatBadge::Broadcaster);
|
|
}
|
|
}
|
|
|
|
let requestor_badge = requestor_badge_mut;
|
|
|
|
// [x] trg_module: BotModule,
|
|
// - [x] Need to validate an actual BotModule - otherwise, fail or exit the cmd
|
|
|
|
let trg_module = if (arg1 == Some("-i")) || (arg1 == Some("-f")) { arg2 } else { arg1 };
|
|
|
|
// if no trg_module was passed
|
|
// if let None = trg_module {
|
|
if trg_module.is_none() {
|
|
|
|
let botlock = params.bot.read().await;
|
|
|
|
let outmsg = "uuh You need to pass a module";
|
|
|
|
botlog::debug(
|
|
outmsg,
|
|
Some("botmodules.rs > cmd_disable()".to_string()),
|
|
Some(¶ms.msg),
|
|
);
|
|
|
|
|
|
// let params_clone = Arc::clone(¶ms.parent_act);
|
|
// let actlock = params_clone.read().await;
|
|
// let act = &(*actlock);
|
|
// let parent_module = match act {
|
|
// BotAction::C(c) => Some(&(*c).module),
|
|
// BotAction::L(l) => Some(&(*l).module),
|
|
// _ => None,
|
|
// };
|
|
|
|
|
|
|
|
// Only call Say if there is a parent module passed
|
|
if parent_module.is_some() {
|
|
|
|
botlock
|
|
.botmgrs
|
|
.chat
|
|
.say_in_reply_to(
|
|
¶ms.msg,
|
|
outmsg.to_string(),
|
|
// ,parent_module.unwrap().clone()
|
|
params.clone()
|
|
).await;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [x] trg_level: StatusLvl,
|
|
|
|
let currchnl = params.msg.channel_login.to_lowercase();
|
|
|
|
let trg_level =
|
|
if arg1 == Some("-i") || arg1 == Some("-f") { StatusLvl::Instance }
|
|
// else if arg1 == Some("-f") { StatusLvl::Instance }
|
|
else { StatusLvl::Ch(Channel(currchnl)) }
|
|
;
|
|
|
|
|
|
|
|
let botlock = params.bot.read().await;
|
|
let modmgr = Arc::clone(&botlock.botmodules);
|
|
let id = botlock.get_identity();
|
|
|
|
let force = arg1 == Some("-f");
|
|
|
|
// modmgr.exec_enable(requestor, requestor_badge, trg_module, trg_level, id)
|
|
let rslt = modmgr.exec_disable(
|
|
requestor,
|
|
requestor_badge,
|
|
BotModule(trg_module.unwrap().to_string()),
|
|
trg_level,
|
|
force,
|
|
id).await;
|
|
|
|
|
|
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),
|
|
};
|
|
|
|
|
|
// Only call Say if there is a parent module passed
|
|
if parent_module.is_some() {
|
|
|
|
botlock
|
|
.botmgrs
|
|
.chat
|
|
.say_in_reply_to(
|
|
¶ms.msg,
|
|
outmsg.to_string(),
|
|
// parent_module.unwrap().clone()
|
|
params.clone(),
|
|
).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct BotModule(pub String);
|
|
|
|
impl PartialEq for BotModule {
|
|
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 {}
|
|
|
|
impl Hash for BotModule{
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
// self.id.hash(state);
|
|
// self.phone.hash(state);
|
|
let BotModule(name) = self.clone();
|
|
name.to_lowercase().hash(state);
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
pub enum ModGroup {
|
|
Core,
|
|
Custom,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
pub enum StatusLvl {
|
|
Instance,
|
|
Ch(Channel),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
pub enum StatusType {
|
|
Enabled(StatusLvl),
|
|
Disabled(StatusLvl),
|
|
}
|
|
|
|
pub enum BotAction {
|
|
C(BotCommand),
|
|
L(Listener),
|
|
R(Routine),
|
|
}
|
|
|
|
impl BotAction {
|
|
// pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
|
|
pub async fn execute(&self, params : ExecBodyParams) {
|
|
match self {
|
|
BotAction::L(a) => a.execute(params).await,
|
|
BotAction::C(a) => a.execute(params).await,
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait BotActionTrait {
|
|
async fn add_to_bot(self, bot: BotInstance);
|
|
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>);
|
|
async fn add_core_to_bot(self, bot: BotInstance);
|
|
async fn add_core_to_modmgr(self, modmgr: Arc<ModulesManager>);
|
|
}
|
|
|
|
pub struct BotCommand {
|
|
pub module: BotModule,
|
|
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>,
|
|
}
|
|
|
|
impl BotCommand {
|
|
// pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
|
|
pub async fn execute(&self, params : ExecBodyParams) {
|
|
(*self.exec_body)(params).await;
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl BotActionTrait for BotCommand {
|
|
async fn add_to_bot(self, bot: BotInstance) {
|
|
self.add_to_modmgr(bot.botmodules).await;
|
|
}
|
|
|
|
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
|
|
modmgr
|
|
.add_botaction(self.module.clone(), BotAction::C(self))
|
|
.await
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
pub struct Listener {
|
|
pub module: BotModule,
|
|
pub name: String,
|
|
pub exec_body: bot_actions::actions_util::ExecBody,
|
|
pub help: String,
|
|
}
|
|
|
|
impl Listener {
|
|
// pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
|
|
pub async fn execute(&self, params : ExecBodyParams) {
|
|
(self.exec_body)(params).await;
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl BotActionTrait for Listener {
|
|
async fn add_to_bot(self, bot: BotInstance) {
|
|
botlog::trace(
|
|
"Adding action to bot",
|
|
Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
|
|
None,
|
|
);
|
|
self.add_to_modmgr(bot.botmodules).await;
|
|
}
|
|
|
|
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
|
|
botlog::trace(
|
|
"Adding action to module manager",
|
|
Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
|
|
None,
|
|
);
|
|
|
|
modmgr
|
|
.add_botaction(self.module.clone(), BotAction::L(self))
|
|
.await;
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Routine {}
|
|
|
|
type StatusdbEntry = (ModGroup, Vec<StatusType>);
|
|
type ModuleActions = Vec<Arc<RwLock<BotAction>>>;
|
|
|
|
pub struct ModulesManager {
|
|
statusdb: Arc<RwLock<HashMap<BotModule, StatusdbEntry>>>,
|
|
pub botactions: Arc<RwLock<HashMap<BotModule, ModuleActions>>>,
|
|
}
|
|
|
|
/*
|
|
|
|
statusdb
|
|
<HashMap
|
|
<BotModule, <-- e.g., BotModule(String::from("experiments001"))
|
|
Vec<ModStatusType> <-- shows Enabled/Disabled per Status level
|
|
|
|
botactions
|
|
HashMap<
|
|
BotModule, <-- e.g., BotModule(String::from("experiments001"))
|
|
Vec<BotAction>> BotCommand, Listener
|
|
|
|
*/
|
|
|
|
impl ModulesManager {
|
|
pub async fn init() -> Arc<ModulesManager> {
|
|
let mgr = ModulesManager {
|
|
statusdb: Arc::new(RwLock::new(HashMap::new())),
|
|
botactions: Arc::new(RwLock::new(HashMap::new())),
|
|
};
|
|
|
|
// :: [x] initialize core modules
|
|
botlog::trace(
|
|
"ModulesManager > init() > Adding modules",
|
|
Some("ModulesManager > init()".to_string()),
|
|
None,
|
|
);
|
|
|
|
let mgrarc = Arc::new(mgr);
|
|
|
|
// 1. load core modules
|
|
crate::core::identity::init(Arc::clone(&mgrarc)).await;
|
|
crate::core::botmodules::init(Arc::clone(&mgrarc)).await;
|
|
|
|
// 2. load custom modules
|
|
crate::custom::init(Arc::clone(&mgrarc)).await;
|
|
|
|
botlog::trace(
|
|
">> Modules Manager : End of Init",
|
|
Some("ModulesManager > init()".to_string()),
|
|
None,
|
|
);
|
|
|
|
mgrarc
|
|
}
|
|
|
|
|
|
pub async fn moduleslist(&self) -> HashMap<BotModule,ModGroup>
|
|
{
|
|
|
|
// 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 {
|
|
// Example usage : botmanager.modstatus(
|
|
// BotModule("GambaCore"),
|
|
// Channel("modulatingforce")
|
|
// )
|
|
// - The ModStatusType checks in the context of the given channel ,
|
|
// but also validates based on wheher the module is disabled at a bot instance
|
|
// level as well
|
|
|
|
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 => {
|
|
|
|
/*
|
|
|
|
[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)
|
|
}
|
|
|
|
pub async fn exec_enable(
|
|
&self,
|
|
requestor: String,
|
|
requestor_badge: Option<ChatBadge>,
|
|
trg_module: BotModule,
|
|
trg_level: StatusLvl,
|
|
id: Arc<RwLock<IdentityManager>>,
|
|
) -> 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
|
|
*/
|
|
|
|
|
|
/*
|
|
[x] 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 -i (to instance) , return a Success
|
|
1b. , and is not -i (to instance) , return a Failure recommending BotAdmin promote themselves first
|
|
|
|
|
|
*/
|
|
|
|
|
|
// [x] Validate in trg_module first
|
|
|
|
let modlist = self.moduleslist().await;
|
|
let rslt = modlist.get(&trg_module);
|
|
|
|
if rslt.is_none() {
|
|
return ChangeResult::Failed("Module doesn't exist".to_string());
|
|
}
|
|
|
|
botlog::trace(
|
|
"ACQUIRING WRITE LOCK : ID",
|
|
Some("ModulesManager > Exec_enable".to_string()),
|
|
None,
|
|
);
|
|
|
|
|
|
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()),
|
|
StatusLvl::Ch(a) => a,
|
|
};
|
|
|
|
const OF_CMD_CHANNEL:Channel = Channel(String::new());
|
|
|
|
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;
|
|
|
|
|
|
if let Permissible::Allow = admin_level_access {
|
|
if let Permissible::Block = chnl_elevated_access {
|
|
|
|
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,
|
|
);
|
|
match trg_level {
|
|
StatusLvl::Instance => {
|
|
self.set_instance_enabled(trg_module.clone()).await;
|
|
return ChangeResult::Success("Enabled at Instance Level".to_string());
|
|
},
|
|
StatusLvl::Ch(_) => {
|
|
return ChangeResult::Failed("Promote yourself Temporarily First".to_string());
|
|
},
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
[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 -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 => {
|
|
return ChangeResult::Failed("You're not allowed".to_string());
|
|
},
|
|
StatusLvl::Ch(in_chnl) => {
|
|
self.set_ch_enabled(trg_module.clone(), in_chnl).await;
|
|
return ChangeResult::Success("Enabled at Channel Level".to_string());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
[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 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;
|
|
return ChangeResult::Success("Enabled at Instance Level".to_string());
|
|
},
|
|
StatusLvl::Ch(in_chnl) => {
|
|
self.set_ch_enabled(trg_module.clone(), in_chnl).await;
|
|
return ChangeResult::Success("Enabled at Channel Level".to_string());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
// 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());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =======================
|
|
// =======================
|
|
// =======================
|
|
|
|
|
|
|
|
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,
|
|
);
|
|
|
|
|
|
Log::flush();
|
|
|
|
ChangeResult::Failed("ERROR : Not implemented yet".to_string())
|
|
}
|
|
|
|
|
|
pub async fn exec_disable(
|
|
&self,
|
|
requestor: String,
|
|
requestor_badge: Option<ChatBadge>,
|
|
trg_module: BotModule,
|
|
trg_level: StatusLvl,
|
|
force: bool,
|
|
id: Arc<RwLock<IdentityManager>>,
|
|
) -> 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
|
|
*/
|
|
|
|
// [x] Validate in trg_module first
|
|
|
|
let modlist = self.moduleslist().await;
|
|
let rslt = modlist.get(&trg_module);
|
|
|
|
if rslt.is_none() {
|
|
return ChangeResult::Failed("Module doesn't exist".to_string());
|
|
}
|
|
|
|
botlog::trace(
|
|
"ACQUIRING WRITE LOCK : ID",
|
|
Some("ModulesManager > Exec_disable".to_string()),
|
|
None,
|
|
);
|
|
|
|
|
|
|
|
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()),
|
|
StatusLvl::Ch(a) => a,
|
|
};
|
|
|
|
const OF_CMD_CHANNEL:Channel = Channel(String::new());
|
|
|
|
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;
|
|
|
|
|
|
/*
|
|
|
|
[x] 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 -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;
|
|
return ChangeResult::Success("Disabled at Instance Level".to_string());
|
|
},
|
|
StatusLvl::Ch(_) => {
|
|
return ChangeResult::Failed("Promote yourself Temporarily First".to_string());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
[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
|
|
|
|
*/
|
|
|
|
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 => {
|
|
return ChangeResult::Failed("You're not allowed".to_string());
|
|
},
|
|
StatusLvl::Ch(in_chnl) => {
|
|
self.set_ch_disabled(trg_module.clone(), in_chnl).await;
|
|
return ChangeResult::Success("Disabled at Channel Level".to_string());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
[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
|
|
|
|
*/
|
|
|
|
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;
|
|
return ChangeResult::Success("Disabled at Instance Level".to_string());
|
|
},
|
|
StatusLvl::Ch(in_chnl) => {
|
|
self.set_ch_disabled(trg_module.clone(), in_chnl).await;
|
|
return ChangeResult::Success("Disabled at Channel Level".to_string());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 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());
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ChangeResult::Failed("ERROR : Not implemented yet".to_string())
|
|
}
|
|
|
|
pub async fn set_instance_disabled(&self, in_module: BotModule) -> (StatusType,ChangeResult) {
|
|
// at Instance level
|
|
// - If core module, do nothing
|
|
|
|
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),
|
|
ChangeResult::Success("Set Disabled at Instance".to_string())
|
|
)
|
|
},
|
|
}
|
|
|
|
// (StatusType::Disabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
|
|
}
|
|
|
|
pub async fn force_disable(&self, in_module: BotModule) -> (StatusType,ChangeResult) {
|
|
// Disables the module at Instance level, and removes all Enabled at Channel level
|
|
// - Bot Moderators MUST Re-enable if they were enabled before
|
|
// - If core module, do nothing
|
|
|
|
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
|
|
} else {
|
|
matches!((*x).clone(), StatusType::Enabled(StatusLvl::Ch(_)))
|
|
}
|
|
)
|
|
{
|
|
statusvector.remove(index);
|
|
}
|
|
statusvector.push(StatusType::Disabled(StatusLvl::Instance));
|
|
|
|
(
|
|
StatusType::Disabled(StatusLvl::Instance),
|
|
ChangeResult::Success("Forced Disabled".to_string())
|
|
)
|
|
},
|
|
}
|
|
|
|
// (StatusType::Disabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
|
|
}
|
|
|
|
pub async fn set_instance_enabled(&self, in_module: BotModule) -> (StatusType,ChangeResult) {
|
|
// at Instance level
|
|
// - If core module, do nothing
|
|
|
|
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())
|
|
)
|
|
},
|
|
}
|
|
|
|
// (StatusType::Enabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
|
|
}
|
|
|
|
pub async fn set_ch_disabled(&self, in_module: BotModule , in_chnl: Channel) -> (StatusType,ChangeResult) {
|
|
// at Instance level
|
|
// - If core module, do nothing
|
|
|
|
|
|
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())
|
|
)
|
|
},
|
|
}
|
|
|
|
// (StatusType::Disabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
|
|
}
|
|
|
|
pub async fn set_ch_enabled(&self, in_module: BotModule , in_chnl: Channel) -> (StatusType,ChangeResult) {
|
|
// at Instance level
|
|
// - If core module, do nothing
|
|
|
|
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())
|
|
)
|
|
},
|
|
}
|
|
|
|
|
|
// (StatusType::Enabled(StatusLvl::Instance),ChangeResult::NoChange("Nothing needed".to_string()))
|
|
}
|
|
|
|
|
|
|
|
pub async fn add_botaction(&self, in_module: BotModule, in_action: BotAction) {
|
|
self.int_add_botaction(in_module,ModGroup::Custom,in_action).await;
|
|
}
|
|
|
|
pub async fn add_core_act(&self, in_module: BotModule, in_action: BotAction) {
|
|
self.int_add_botaction(in_module,ModGroup::Core,in_action).await;
|
|
}
|
|
|
|
|
|
pub async fn affirm_in_statusdb(&self,in_module:BotModule,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)),
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
async fn int_add_botaction(&self, in_module: BotModule, in_modgroup: ModGroup, in_action: BotAction) {
|
|
botlog::trace(
|
|
"Add botaction called",
|
|
Some("ModulesManager > init()".to_string()),
|
|
None,
|
|
);
|
|
|
|
/*
|
|
adds a BotAction to the Modules Manager - This will require a BotModule passed as well
|
|
This will including the logic of a valid add
|
|
If it fails to add, either a PANIC or some default coded business rules that handles the botaction add
|
|
For example, this Should PANIC (ideally Panic?) if it does not successfully add a bot module
|
|
-- Being unable to indicates a Programming/Developer code logic issue : They cannot add botactions that already exists (?)
|
|
-- In particular to BotCommands, which must have Unique command call names and aliases that to not conflict with any other
|
|
already BotCommand added name or alias
|
|
Other types might be fine? For example, if 2 modules have their own listeners but each have the name "targetchatter" ,
|
|
both would be called separately, even if they both have the same or different logic
|
|
*/
|
|
|
|
// [x] Before Adding, validate the following :
|
|
// - If BotAction to Add is a BotCommand , In Module Manager DB (botactions),
|
|
// Check All Other BotAction Command Names & Aliases to ensure they don't conflict
|
|
|
|
async fn find_conflict_module(mgr: &ModulesManager, act: &BotAction) -> Option<BotModule> {
|
|
|
|
if let BotAction::C(incmd) = act {
|
|
let actdb = mgr.botactions.read().await;
|
|
|
|
for (module, moduleactions) in &(*actdb) {
|
|
|
|
|
|
// 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
|
|
|
|
// [x] check if given botcommand c.command:String conflicts with any in botactions
|
|
|
|
if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() {
|
|
// Returning State - with the identified module
|
|
return Some(module.clone()); // works
|
|
}
|
|
|
|
for a in &dbcmd.alias {
|
|
if incmd.command.to_lowercase() == a.to_lowercase() {
|
|
// Returning State - with the identified module
|
|
|
|
return Some(module.clone()); // works
|
|
}
|
|
}
|
|
|
|
// [x] Then do the same check except for each c.alias
|
|
|
|
for inalias in &incmd.alias {
|
|
if inalias.to_lowercase() == dbcmd.command.to_lowercase() {
|
|
// Returning State - with the identified module
|
|
|
|
return Some(module.clone()); // works
|
|
}
|
|
|
|
for a in &dbcmd.alias {
|
|
if inalias.to_lowercase() == a.to_lowercase() {
|
|
// Returning State - with the identified module
|
|
|
|
return Some(module.clone()); // works
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// for all other scenarios (e.g., Listener, Routine), find no conflicts
|
|
None
|
|
}
|
|
|
|
if let Some(c) = find_conflict_module(self, &in_action).await {
|
|
panic!(
|
|
"ERROR: Could not add module; there was a conflict with existing module {:?}",
|
|
c
|
|
)
|
|
}
|
|
|
|
self.affirm_in_statusdb(in_module.clone(),in_modgroup).await;
|
|
|
|
let mut a = self.botactions.write().await;
|
|
let modactions = a.entry(in_module.clone()).or_insert(Vec::new());
|
|
|
|
modactions.push(Arc::new(RwLock::new(in_action)));
|
|
|
|
botlog::trace(
|
|
format!(
|
|
"Modules Manager> add_botaction called - botactions size : {}",
|
|
modactions.len()
|
|
)
|
|
.as_str(),
|
|
Some("ModulesManager > init()".to_string()),
|
|
None,
|
|
);
|
|
}
|
|
|
|
fn _statuscleanup(&self, _: Option<Channel>) {
|
|
// internal cleans up statusdb . For example :
|
|
// - remove redudancies . If we see several Enabled("m"), only keep 1x
|
|
// - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m")
|
|
// the IDEAL is that this is ran before every read/update operation to ensure quality
|
|
// Option<Channel> 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
|
|
}
|
|
}
|
|
|
|
|
|
// =====================
|
|
// =====================
|
|
// =====================
|
|
// =====================
|
|
// =====================
|
|
|
|
#[cfg(test)]
|
|
mod core_modulesmanager {
|
|
|
|
|
|
use casual_logger::Log;
|
|
use casual_logger::Extension;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn case_insensitive_test() {
|
|
Log::set_file_ext(Extension::Log);
|
|
assert_eq!(
|
|
BotModule("TEST".to_string()),
|
|
BotModule("test".to_string())
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
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 ,
|
|
in_modgroup : ModGroup ,
|
|
in_chnl1 : Channel,
|
|
in_chnl2 : Channel)
|
|
{
|
|
|
|
|
|
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()));
|
|
|
|
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()));
|
|
|
|
complex_workflow(in_module, in_modgroup, in_chnl1, in_chnl2).await;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
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,
|
|
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());
|
|
|
|
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,
|
|
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>,
|
|
|
|
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
|
|
|
|
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;
|
|
|
|
|
|
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
|
|
|
|
|
|
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());
|
|
|
|
|
|
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());
|
|
|
|
|
|
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());
|
|
|
|
|
|
idmgr.affirm_chatter_in_db(requestor.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())) ||
|
|
!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();
|
|
|
|
let channel = Channel("somechannel".to_string());
|
|
|
|
|
|
idmgr.affirm_chatter_in_db(requestor.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())) ||
|
|
!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()));
|
|
|
|
// [x] Test with Non Existing module > disable
|
|
|
|
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()));
|
|
|
|
}
|
|
|
|
}
|