2023-12-21 00:48:09 -05:00
|
|
|
/*
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
ModulesManager is used to manage Modules and BotActions associated with those modules
|
2023-12-22 09:52:01 -05:00
|
|
|
|
|
|
|
pub struct ModulesManager {
|
|
|
|
statusdb: HashMap<ModType,Vec<ModStatusType>>,
|
2024-02-25 10:40:54 -05:00
|
|
|
botactions: HashMap<ModType,Vec<BotAction>>,
|
2023-12-22 09:52:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
- statusdb: HashMap<ModType,Vec<ModStatusType>> - Defines Modules and their ModStatusType (e.g., Enabled at an Instance level, Disabled at a Channel Level)
|
2024-02-25 10:40:54 -05:00
|
|
|
- botactions: HashMap<ModType,Vec<BotAction>> - Defines Modules and their BotActions (e.g., BotCommand , Listener, Routine)
|
2023-12-21 00:48:09 -05:00
|
|
|
|
|
|
|
Example
|
|
|
|
{
|
2024-02-25 10:40:54 -05:00
|
|
|
ModulesManager {
|
|
|
|
statusdb: {BotModule("experiments 004"): [Enabled(Instance)]},
|
2023-12-22 09:52:01 -05:00
|
|
|
botactions: {BotModule("experiments 004"): [C(BotCommand { module: BotModule("experiments 004"), command: "DUPCMD4", alias: ["DUPALIAS4A", "DUPALIAS4B"], help: "DUPCMD4 tester" })]} }
|
2023-12-21 00:48:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2024-03-02 10:24:13 -05:00
|
|
|
use core::panic;
|
2023-12-21 00:48:09 -05:00
|
|
|
|
2024-03-02 10:24:13 -05:00
|
|
|
use std::collections::HashMap;
|
2024-03-02 11:41:24 -05:00
|
|
|
use std::error::Error;
|
2024-03-02 10:24:13 -05:00
|
|
|
use std::sync::Arc;
|
2023-12-21 00:48:09 -05:00
|
|
|
|
2023-12-26 20:00:32 -05:00
|
|
|
use twitch_irc::message::PrivmsgMessage;
|
2023-12-21 00:48:09 -05:00
|
|
|
|
2024-03-02 10:24:13 -05:00
|
|
|
use tokio::sync::RwLock;
|
|
|
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
2024-03-02 11:41:24 -05:00
|
|
|
use self::bot_actions::actions_util::BotAR;
|
|
|
|
use crate::core::botinstance::{BotInstance, ChType};
|
2024-03-02 10:24:13 -05:00
|
|
|
use crate::core::botlog;
|
|
|
|
use crate::core::identity;
|
2024-02-12 01:25:12 -05:00
|
|
|
|
2024-03-02 11:41:24 -05:00
|
|
|
pub use crate::core::bot_actions;
|
2024-03-02 10:24:13 -05:00
|
|
|
pub use ChType::Channel;
|
|
|
|
pub use ModType::BotModule;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
|
|
pub enum ModType {
|
|
|
|
BotModule(String),
|
|
|
|
}
|
|
|
|
|
2023-12-21 00:48:09 -05:00
|
|
|
#[derive(Debug)]
|
2024-03-01 23:36:37 -05:00
|
|
|
pub enum StatusLvl {
|
2023-12-21 00:48:09 -05:00
|
|
|
Instance,
|
2024-03-01 23:36:37 -05:00
|
|
|
_Ch(ChType),
|
2023-12-21 00:48:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ModStatusType {
|
|
|
|
Enabled(StatusLvl),
|
|
|
|
Disabled(StatusLvl),
|
|
|
|
}
|
2024-02-04 14:28:37 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub enum BotAction {
|
|
|
|
C(BotCommand),
|
2024-01-29 01:13:56 -05:00
|
|
|
L(Listener),
|
2024-02-25 10:40:54 -05:00
|
|
|
R(Routine),
|
2023-12-21 00:48:09 -05:00
|
|
|
}
|
2023-12-26 20:00:32 -05:00
|
|
|
|
2024-01-29 02:27:11 -05:00
|
|
|
impl BotAction {
|
2024-03-01 23:36:37 -05:00
|
|
|
pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
|
2024-01-29 02:27:11 -05:00
|
|
|
match self {
|
2024-02-25 10:40:54 -05:00
|
|
|
BotAction::L(a) => a.execute(m, n).await,
|
|
|
|
BotAction::C(a) => a.execute(m, n).await,
|
2024-01-29 02:27:11 -05:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-04 14:28:37 -05:00
|
|
|
#[async_trait]
|
2024-02-25 10:40:54 -05:00
|
|
|
pub trait BotActionTrait {
|
|
|
|
async fn add_to_bot(self, bot: BotInstance);
|
|
|
|
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>);
|
2023-12-26 20:00:32 -05:00
|
|
|
}
|
|
|
|
|
2024-01-29 04:09:53 -05:00
|
|
|
pub struct BotCommand {
|
2024-02-25 10:40:54 -05:00
|
|
|
pub module: ModType,
|
|
|
|
pub command: String, // command call name
|
|
|
|
pub alias: Vec<String>, // String of alternative names
|
|
|
|
pub exec_body: bot_actions::actions_util::ExecBody,
|
|
|
|
pub help: String,
|
|
|
|
pub required_roles: Vec<identity::UserRole>,
|
2024-01-29 04:09:53 -05:00
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
impl BotCommand {
|
2024-03-01 23:36:37 -05:00
|
|
|
pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
|
|
|
|
(*self.exec_body)(m, n).await;
|
2024-01-29 04:09:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-04 14:28:37 -05:00
|
|
|
#[async_trait]
|
2024-02-25 10:40:54 -05:00
|
|
|
impl BotActionTrait for BotCommand {
|
|
|
|
async fn add_to_bot(self, bot: BotInstance) {
|
2024-02-12 05:25:38 -05:00
|
|
|
self.add_to_modmgr(bot.botmodules).await;
|
2024-01-29 04:09:53 -05:00
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
|
|
|
|
modmgr
|
|
|
|
.add_botaction(self.module.clone(), BotAction::C(self))
|
|
|
|
.await
|
2024-01-29 04:09:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub struct Listener {
|
|
|
|
pub module: ModType,
|
|
|
|
pub name: String,
|
|
|
|
pub exec_body: bot_actions::actions_util::ExecBody,
|
|
|
|
pub help: String,
|
2023-12-21 00:48:09 -05:00
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
impl Listener {
|
2024-03-01 23:36:37 -05:00
|
|
|
pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
|
|
|
|
(self.exec_body)(m, n).await;
|
2023-12-26 20:00:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-04 14:28:37 -05:00
|
|
|
#[async_trait]
|
2024-02-25 10:40:54 -05:00
|
|
|
impl BotActionTrait for Listener {
|
|
|
|
async fn add_to_bot(self, bot: BotInstance) {
|
2024-03-02 10:06:26 -05:00
|
|
|
botlog::trace(
|
2024-02-25 10:40:54 -05:00
|
|
|
"Adding action to bot",
|
|
|
|
Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
|
|
|
|
None,
|
|
|
|
);
|
2024-02-12 02:34:32 -05:00
|
|
|
self.add_to_modmgr(bot.botmodules).await;
|
2023-12-26 20:00:32 -05:00
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
|
2024-03-02 10:06:26 -05:00
|
|
|
botlog::trace(
|
2024-02-25 10:40:54 -05:00
|
|
|
"Adding action to module manager",
|
|
|
|
Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
|
|
|
|
None,
|
|
|
|
);
|
2024-02-13 07:54:35 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
modmgr
|
|
|
|
.add_botaction(self.module.clone(), BotAction::L(self))
|
|
|
|
.await;
|
2023-12-26 20:00:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-21 00:48:09 -05:00
|
|
|
#[derive(Debug)]
|
2024-03-01 23:36:37 -05:00
|
|
|
pub struct Routine {}
|
2023-12-21 00:48:09 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub struct ModulesManager {
|
|
|
|
statusdb: Arc<RwLock<HashMap<ModType, Vec<ModStatusType>>>>,
|
|
|
|
pub botactions: Arc<RwLock<HashMap<ModType, Vec<BotAction>>>>,
|
2023-12-21 00:48:09 -05:00
|
|
|
}
|
|
|
|
|
2024-02-18 15:23:58 -05:00
|
|
|
/*
|
|
|
|
|
|
|
|
statusdb
|
2024-02-25 10:40:54 -05:00
|
|
|
<HashMap
|
|
|
|
<ModType, <-- e.g., BotModule(String::from("experiments001"))
|
2024-02-18 15:23:58 -05:00
|
|
|
Vec<ModStatusType> <-- shows Enabled/Disabled per Status level
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
botactions
|
2024-02-18 15:23:58 -05:00
|
|
|
HashMap<
|
2024-02-25 10:40:54 -05:00
|
|
|
ModType, <-- e.g., BotModule(String::from("experiments001"))
|
2024-02-18 15:23:58 -05:00
|
|
|
Vec<BotAction>> BotCommand, Listener
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
impl ModulesManager {
|
|
|
|
pub async fn init() -> Arc<ModulesManager> {
|
2023-12-21 00:48:09 -05:00
|
|
|
|
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-02-25 10:40:54 -05:00
|
|
|
botlog::debug(
|
|
|
|
"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);
|
|
|
|
|
|
|
|
// 1. load core modules
|
|
|
|
crate::core::identity::init(Arc::clone(&mgrarc)).await;
|
|
|
|
|
|
|
|
// 2. load custom modules
|
|
|
|
crate::custom::init(Arc::clone(&mgrarc)).await;
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
botlog::trace(
|
|
|
|
">> Modules Manager : End of Init",
|
2024-02-13 10:11:49 -05:00
|
|
|
Some("ModulesManager > init()".to_string()),
|
2024-02-25 10:40:54 -05:00
|
|
|
None,
|
2024-02-13 10:11:49 -05:00
|
|
|
);
|
2023-12-21 00:48:09 -05:00
|
|
|
|
2024-03-02 11:55:16 -05:00
|
|
|
mgrarc
|
2024-02-25 10:40:54 -05:00
|
|
|
}
|
2023-12-21 00:48:09 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub fn modstatus(&self, _: ModType, _: ChType) -> ModStatusType {
|
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
|
|
|
|
ModStatusType::Enabled(StatusLvl::Instance)
|
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub fn togglestatus(&self, _: ModType, _: ChType) -> ModStatusType {
|
2023-12-21 00:48:09 -05:00
|
|
|
// enables or disables based on current status
|
|
|
|
ModStatusType::Enabled(StatusLvl::Instance)
|
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub fn setstatus(&self, _: ModType, _: ModStatusType) -> Result<&str, Box<dyn Error>> {
|
2023-12-21 00:48:09 -05:00
|
|
|
// sets the status based given ModSatusType
|
|
|
|
// e.g., b.setstatus(BodModule("GambaCore"), Enabled(Channel("modulatingforce"))).expect("ERROR")
|
|
|
|
Ok("")
|
|
|
|
}
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
pub async fn add_botaction(&self, in_module: ModType, in_action: BotAction) {
|
2024-02-13 10:11:49 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
botlog::trace(
|
|
|
|
"Add botaction called",
|
|
|
|
Some("ModulesManager > init()".to_string()),
|
|
|
|
None,
|
2024-02-13 10:11:49 -05:00
|
|
|
);
|
|
|
|
|
2023-12-21 20:11:32 -05:00
|
|
|
/*
|
|
|
|
adds a BotAction to the Modules Manager - This will require a BotModule passed as well
|
|
|
|
This will including the logic of a valid add
|
|
|
|
If it fails to add, either a PANIC or some default coded business rules that handles the botaction add
|
|
|
|
For example, this Should PANIC (ideally Panic?) if it does not successfully add a bot module
|
|
|
|
-- Being unable to indicates a Programming/Developer code logic issue : They cannot add botactions that already exists (?)
|
|
|
|
-- In particular to BotCommands, which must have Unique command call names and aliases that to not conflict with any other
|
|
|
|
already BotCommand added name or alias
|
|
|
|
Other types might be fine? For example, if 2 modules have their own listeners but each have the name "targetchatter" ,
|
2024-02-25 10:40:54 -05:00
|
|
|
both would be called separately, even if they both have the same or different logic
|
2023-12-21 20:11:32 -05:00
|
|
|
*/
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
// [x] Before Adding, validate the following :
|
|
|
|
// - If BotAction to Add is a BotCommand , In Module Manager DB (botactions),
|
2023-12-21 20:11:32 -05:00
|
|
|
// Check All Other BotAction Command Names & Aliases to ensure they don't conflict
|
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
async fn find_conflict_module(mgr: &ModulesManager, act: &BotAction) -> Option<ModType> {
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
if let BotAction::C(incmd) = act {
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-03-02 11:55:16 -05:00
|
|
|
let actdb = mgr.botactions.read().await;
|
|
|
|
|
|
|
|
for (module, moduleactions) in &(*actdb) {
|
2024-01-29 04:28:58 -05:00
|
|
|
for modact in moduleactions.iter() {
|
|
|
|
if let BotAction::C(dbcmd) = &modact {
|
|
|
|
// At this point, there is an command incmd and looked up dbcmd
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
// [x] check if given botcommand c.command:String conflicts with any in botactions
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() {
|
|
|
|
// Returning State - with the identified module
|
|
|
|
return Some(module.clone()); // works
|
2024-03-02 11:55:16 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
}
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
for a in &dbcmd.alias {
|
|
|
|
if incmd.command.to_lowercase() == a.to_lowercase() {
|
|
|
|
// Returning State - with the identified module
|
2024-03-02 11:55:16 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
return Some(module.clone()); // works
|
2024-02-25 10:40:54 -05:00
|
|
|
}
|
|
|
|
}
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
// [x] Then do the same check except for each c.alias
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
for inalias in &incmd.alias {
|
|
|
|
if inalias.to_lowercase() == dbcmd.command.to_lowercase() {
|
|
|
|
// Returning State - with the identified module
|
2024-03-02 11:55:16 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
return Some(module.clone()); // works
|
2024-02-25 10:40:54 -05:00
|
|
|
}
|
2023-12-21 20:11:32 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
for a in &dbcmd.alias {
|
|
|
|
if inalias.to_lowercase() == a.to_lowercase() {
|
|
|
|
// Returning State - with the identified module
|
2024-03-02 11:55:16 -05:00
|
|
|
|
2024-01-29 04:28:58 -05:00
|
|
|
return Some(module.clone()); // works
|
2024-02-25 10:40:54 -05:00
|
|
|
}
|
|
|
|
}
|
2024-01-29 04:28:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-12-21 20:11:32 -05:00
|
|
|
|
|
|
|
// for all other scenarios (e.g., Listener, Routine), find no conflicts
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2024-03-01 23:36:37 -05:00
|
|
|
if let Some(c) = find_conflict_module(self, &in_action).await {
|
|
|
|
panic!(
|
2024-02-25 10:40:54 -05:00
|
|
|
"ERROR: Could not add module; there was a conflict with existing module {:?}",
|
|
|
|
c
|
2024-03-01 23:36:37 -05:00
|
|
|
)
|
2023-12-21 20:11:32 -05:00
|
|
|
}
|
|
|
|
|
2024-02-12 01:25:12 -05:00
|
|
|
let mut dbt = self.statusdb.write().await;
|
|
|
|
let statusvector = dbt
|
2023-12-21 17:22:40 -05:00
|
|
|
.entry(in_module.clone())
|
|
|
|
.or_insert(Vec::new());
|
|
|
|
|
2023-12-21 20:11:32 -05:00
|
|
|
statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level
|
2023-12-21 17:22:40 -05:00
|
|
|
|
2024-02-12 01:25:12 -05:00
|
|
|
let mut a = self.botactions.write().await;
|
|
|
|
let modactions = a
|
2024-02-25 10:40:54 -05:00
|
|
|
.entry(in_module.clone())
|
2023-12-21 17:22:40 -05:00
|
|
|
.or_insert(Vec::new());
|
|
|
|
|
2023-12-21 20:11:32 -05:00
|
|
|
modactions.push(in_action);
|
2023-12-21 17:22:40 -05:00
|
|
|
|
2024-02-25 10:40:54 -05:00
|
|
|
botlog::trace(
|
2024-03-02 11:55:16 -05:00
|
|
|
format!("Modules Manager> add_botaction called - botactions size : {}", modactions.len()).as_str(),
|
2024-02-25 10:40:54 -05:00
|
|
|
Some("ModulesManager > init()".to_string()),
|
|
|
|
None,
|
2024-02-13 10:11:49 -05:00
|
|
|
);
|
2023-12-21 17:22:40 -05:00
|
|
|
}
|
|
|
|
|
2024-03-01 23:36:37 -05:00
|
|
|
fn _statuscleanup(&self, _: Option<ChType>) {
|
2024-02-25 10:40:54 -05:00
|
|
|
// internal cleans up statusdb . For example :
|
2023-12-21 12:10:50 -05:00
|
|
|
// - remove redudancies . If we see several Enabled("m"), only keep 1x
|
|
|
|
// - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m")
|
|
|
|
// the IDEAL is that this is ran before every read/update operation to ensure quality
|
|
|
|
// Option<ChType> can pass Some(Channel("m")) (as an example) so statuscleanup only works on the given channel
|
|
|
|
// Passing None to chnl may be a heavy operation, as this will review and look at the whole table
|
|
|
|
}
|
2024-02-25 10:40:54 -05:00
|
|
|
}
|