From 2a884f95716cac2dc9c708a9a0a155e132013b2a Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Wed, 29 Jan 2025 11:54:25 -0500
Subject: [PATCH 01/14] multi command

---
 src/bin/fun_bot.rs                 |  2 +-
 src/bin/simple_command_bot.rs      |  2 +-
 src/bin/simple_module.rs           |  2 +-
 src/botcore/bot_objects.rs         | 51 ++++++++++++++++++++++++++++--
 src/botcore/bot_objects/command.rs | 28 ++++++++++++----
 5 files changed, 74 insertions(+), 11 deletions(-)

diff --git a/src/bin/fun_bot.rs b/src/bin/fun_bot.rs
index de67fda..f7bc67f 100644
--- a/src/bin/fun_bot.rs
+++ b/src/bin/fun_bot.rs
@@ -49,7 +49,7 @@ pub mod funbot_objs {
     /// Create a Command Object
     fn create_cmd_test() -> Command {
     
-        let mut cmd = Command::new("remind besty".to_string(),"annytfYandere ".to_string());
+        let mut cmd = Command::new(vec!["remind besty".to_string()],"annytfYandere ".to_string());
 
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
diff --git a/src/bin/simple_command_bot.rs b/src/bin/simple_command_bot.rs
index a6ec70e..934ddf7 100644
--- a/src/bin/simple_command_bot.rs
+++ b/src/bin/simple_command_bot.rs
@@ -29,7 +29,7 @@ pub async fn main() {
     let mut bot = Bot::new();
 
     /* 1. Create a new blank cmd */
-    let mut cmd = Command::new("test".to_string(),"".to_string());
+    let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
 
     /* 2. Define an async fn callback execution */
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
diff --git a/src/bin/simple_module.rs b/src/bin/simple_module.rs
index 61d66e5..ae483c5 100644
--- a/src/bin/simple_module.rs
+++ b/src/bin/simple_module.rs
@@ -55,7 +55,7 @@ pub mod custom_mod {
 
     pub fn cmd_test() -> Command {
         /* 1. Create a new cmd */
-        let mut cmd = Command::new("test".to_string(),"".to_string());
+        let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
 
         /* 2. Define exec callback  */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
diff --git a/src/botcore/bot_objects.rs b/src/botcore/bot_objects.rs
index 9fb6985..a563d84 100644
--- a/src/botcore/bot_objects.rs
+++ b/src/botcore/bot_objects.rs
@@ -59,7 +59,7 @@ pub mod built_in_objects {
 
     fn create_disable_cmd() -> Command {
         /* 1. Create a new blank cmd */
-        let mut cmd = Command::new("disable".to_string(),"".to_string());
+        let mut cmd = Command::new(vec!["disable".to_string()],"".to_string());
 
         /* 2. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
@@ -88,7 +88,7 @@ pub mod built_in_objects {
 
     fn create_enable_cmd() -> Command {
         /* 1. Create a new blank cmd */
-        let mut cmd = Command::new("enable".to_string(),"".to_string());
+        let mut cmd = Command::new(vec!["enable".to_string()],"".to_string());
 
         /* 2. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
@@ -116,4 +116,51 @@ pub mod built_in_objects {
     }
 
 
+    /// adminonly command that grants a temporary role
+    fn create_iam_role_cmd() -> Command {
+        /* 1. Create a new blank cmd */
+        let mut cmd = Command::new(vec![
+            "I am ".to_string(), 
+            "I'm ".to_string()
+            ],"".to_string());
+
+        /* 2. Define an async fn callback execution */
+        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+            if let ServerMessage::Privmsg(msg) = message {
+                for (i,arg) in msg.message_text.split(" ").enumerate() {
+                    if i > 1 {
+                        // bot.disable_module(msg.channel_login.clone(), arg.to_string()).await;
+                        // #todo
+                        // if not dont have the badge or have a lower priviledge badge 
+                        // and they dont have an active guest badge, ths admin can be
+                        // recognzed wth that badge
+                        if arg == "mod" || arg == "moderator" {
+
+                        }
+                        if arg == "vip" {
+
+                        }
+                        if arg == "broadcaster" || arg == "strimmer" || arg == "streamer" {
+
+                        }
+                    }
+                }
+                let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+            }
+            Result::Err("Not Valid message type".to_string()) 
+        }
+
+        /* 3. Set and Store the execution body using `async_box()`  */
+        cmd.set_exec_fn(asyncfn_box(execbody));
+
+        /* 4. optionally, remove admin only default flag */
+        cmd.set_admin_only(true);
+
+        // /* 5. optionally, set min badge*/
+        // cmd.set_min_badge(Badge::Moderator);
+        cmd
+        
+    } 
+
+
 }
\ No newline at end of file
diff --git a/src/botcore/bot_objects/command.rs b/src/botcore/bot_objects/command.rs
index 574a22e..9100df5 100644
--- a/src/botcore/bot_objects/command.rs
+++ b/src/botcore/bot_objects/command.rs
@@ -20,7 +20,7 @@ use super::ExecBody;
 #[derive(Clone)]
 pub struct Command
 {
-    command : String,
+    commands : Vec<String>,
     exec_fn : Arc<ExecBody>,
     min_badge : Badge,
     admin_only : bool,
@@ -38,13 +38,13 @@ impl Command
     /// if a blank prefix is given, the bot will look for the bot prefix instead
     /// 
     /// By default, the new command is admin_only
-    pub fn new(command:String,prefix:String) -> Command {
+    pub fn new(commands:Vec<String>,prefix:String) -> Command {
 
         async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> 
         { Result::Ok("success".to_string()) }
 
         Command {
-            command ,
+            commands ,
             prefix ,
             exec_fn : Arc::new(asyncfn_box(execbody)),
             min_badge : Badge::Vip,
@@ -79,12 +79,28 @@ impl Command
                 } else { 
                     prefixed_cmd.push_str(&cmd.prefix); 
                 }
-                prefixed_cmd.push_str(&cmd.command);
-                return message.message_text.starts_with(prefixed_cmd.as_str())
+                for cmd_nm in &cmd.commands {
+                    prefixed_cmd.push_str(cmd_nm);
+                    if message.message_text.starts_with(prefixed_cmd.as_str()) {
+                        return true;
+                    }
+                };
+                return false;
         }
 
         
-        fn caller_badge_ok(cmd:&Command,_bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
+        fn caller_badge_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
+
+            // senders that are admins skip badge check if the command is adminonly
+            if cmd.admin_only && bot.get_admins().contains(&message.sender.login)  {
+                return true;
+            } ;
+
+            // adminonly commands will can only be ran by admins
+            if cmd.admin_only && bot.get_admins().contains(&message.sender.login) {
+                return false;
+            }
+
             for badge in message.badges {
                 
                 match cmd.min_badge {
-- 
2.49.0


From a9da9f41929897435cfef3c0784bcaa6aaae4b27 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Wed, 29 Jan 2025 20:00:20 -0500
Subject: [PATCH 02/14] guest_badge module

---
 src/bin/fun_bot.rs                 |  15 +++-
 src/bin/simple_module.rs           |   4 +-
 src/botcore/bot.rs                 | 122 +++++++++++++++++++++----
 src/botcore/bot_objects.rs         | 120 +++++++++++++++++++++----
 src/botcore/bot_objects/command.rs |  29 +++++-
 src/botcore/modules.rs             |  23 +++--
 src/custom_mods.rs                 |   1 +
 src/custom_mods/guest_badge.rs     | 140 +++++++++++++++++++++++++++++
 src/lib.rs                         |   5 +-
 9 files changed, 413 insertions(+), 46 deletions(-)
 create mode 100644 src/custom_mods/guest_badge.rs

diff --git a/src/bin/fun_bot.rs b/src/bin/fun_bot.rs
index f7bc67f..eaf0487 100644
--- a/src/bin/fun_bot.rs
+++ b/src/bin/fun_bot.rs
@@ -1,7 +1,9 @@
 //! WIP Fun forcebot with catered customizations #todo
 //! 
 //! Custom modules that can be managed in chat through `disable` and `enable` commands
-//! - funbot 
+//! - funbot
+//! - guests
+//! 
 //! 
 //! Be sure the followig is defined in `.env` 
 //! - login_name
@@ -14,7 +16,7 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_rs_v2::Bot;
+use forcebot_rs_v2::{custom_mods::guest_badge, Bot};
 
 #[tokio::main]
 pub async fn main() {
@@ -24,8 +26,11 @@ pub async fn main() {
 
     /* 1. Load the module into the bot */
     bot.load_module(funbot_objs::create_module());
+
+    /* 2. Load Custom Modules */
+    bot.load_module(guest_badge::create_module());
     
-    /* 2. Run the bot */
+    /* 3. Run the bot */
     bot.run().await;
 
 }
@@ -39,7 +44,9 @@ pub mod funbot_objs {
 
     /// Create a Module with a loaded Command object
     pub fn create_module() -> Module {
-        let mut custom_mod = Module::new("funbot".to_string(), "".to_string());
+        let mut custom_mod = Module::new(
+            vec!["funbot".to_string()], 
+            "".to_string());
 
         custom_mod.load_command(create_cmd_test());
 
diff --git a/src/bin/simple_module.rs b/src/bin/simple_module.rs
index ae483c5..002ca22 100644
--- a/src/bin/simple_module.rs
+++ b/src/bin/simple_module.rs
@@ -44,7 +44,9 @@ pub mod custom_mod {
     /// Module with a loaded command
     pub fn new() -> Module {
         /* 1. Create a new module */
-        let mut custom_mod = Module::new("test".to_string(), "".to_string());
+        let mut custom_mod = Module::new(
+            vec!["test".to_string()], 
+            "".to_string());
 
         /* 2. Load the cmd into a new module */
         custom_mod.load_command(cmd_test());
diff --git a/src/botcore/bot.rs b/src/botcore/bot.rs
index 89598af..ac19db2 100644
--- a/src/botcore/bot.rs
+++ b/src/botcore/bot.rs
@@ -3,11 +3,11 @@
 use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
 use twitch_irc::{login::StaticLoginCredentials, message::ServerMessage, SecureTCPTransport, TwitchIRCClient};
 use dotenv::dotenv;
-use std::{env, sync::Arc};
+use std::{env, sync::Arc, time::{Duration, Instant}};
 
-use crate::{Command, Listener, Module};
+use crate::{Badge, Command, Listener, Module};
 
-use super::bot_objects::built_in_objects;
+use super::{bot_objects::built_in_objects, modules::{self, Status}};
 
 
 /// Twitch chat bot
@@ -30,7 +30,9 @@ pub struct Bot
     /// modules
     modules: Vec<Module>,
     /// channel module status
-    channel_module_status: Mutex<Vec<(String,String,String)>>
+    channel_module_status: Mutex<Vec<(String,String,modules::Status)>>,
+    /// chatter guest badges - chatter,channel,Badge,start_time,duration
+    chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>
 }
 
 
@@ -46,6 +48,8 @@ impl Bot
     /// - bot_admins
     pub fn new() -> Bot {
 
+
+        
         dotenv().ok();
         let bot_login_name = env::var("login_name").unwrap().to_owned();
         let oauth_token = env::var("access_token").unwrap().to_owned();
@@ -107,6 +111,7 @@ impl Bot
              admins,
              modules:   vec![],
              channel_module_status: Mutex::new(vec![]),
+             chatter_guest_badges: Mutex::new(vec![]),
         };
 
         for cmd in built_in_objects::create_commands() {
@@ -145,18 +150,28 @@ impl Bot
                     for cmd in &(*bot).commands {
                     
                         let a = cmd.clone();
-                        if a.command_triggered(bot.clone(),msg.clone()) {
+                        if a.command_triggered(bot.clone(),msg.clone()).await {
                              
                             let _ = cmd.execute_fn(bot.clone(),message.clone()).await;
                         }
                     }
 
 
-                    for module in &(*bot).modules {
+                    'module_loop: for module in &(*bot).modules {
                         
+                        // skip modules that are disable 
                         let cms_lock = bot.channel_module_status.lock().await;
-                        if cms_lock.contains(&(msg.channel_login.clone(),module.get_name(),"disabled".to_string())) 
-                        { continue; }
+
+                        for channel_flags in cms_lock.iter() {
+                            if channel_flags.0 == msg.channel_login.clone() {
+                                
+                                if module.get_names().contains(&channel_flags.1) && channel_flags.2 == Status::Disabled {
+                                    continue 'module_loop;
+                                }
+                            }
+                        }
+                    
+
 
                         for listener in module.get_listeners() {
                         
@@ -169,7 +184,7 @@ impl Bot
                         for cmd in module.get_commands() {
                             
                             let a = cmd.clone();
-                            if a.command_triggered(bot.clone(),msg.clone()) {
+                            if a.command_triggered(bot.clone(),msg.clone()).await {
                                 
                                 let _ = cmd.execute_fn(bot.clone(),message.clone()).await;
                             }
@@ -196,6 +211,16 @@ impl Bot
     pub fn load_command(&mut self,c : Command) {
         self.commands.push(c);
     }
+
+    
+    pub fn get_module(&self,module:String) -> Option<Module> {
+        for modl in self.modules.clone() {
+            if modl.get_names().contains(&module) {
+                return Some(modl);
+            }
+        }
+        None
+    }
     
 
 
@@ -212,26 +237,93 @@ impl Bot
         self.modules.push(m)
     }
 
+    pub async fn get_channel_module_status(&self,channel:String,module:String) -> Status {
+        let found_disabled:bool = {
+            let cms_lock = self.channel_module_status.lock().await;
+
+            let mut found = false;
+
+            for channel_flags in cms_lock.iter() {
+                if channel_flags.0 == channel {
+                    if channel_flags.1 == module && channel_flags.2 == Status::Disabled {
+                        found = true;
+                    }
+                }
+            }
+            found
+        };
+        if found_disabled { return Status::Disabled;}
+        else { return Status::Enabled; };
+
+    }
+
     pub async fn disable_module(&self,channel:String,module:String){
-        let mut lock = self.channel_module_status.lock().await;
-        if !lock.contains(&(channel.clone(),module.clone(),"disabled".to_string())) {
-            lock.push((channel,module,"disabled".to_string()));
-        }
+
+        let found_disabled:bool = {
+            let cms_lock = self.channel_module_status.lock().await;
+
+            let mut found = false;
+
+            for channel_flags in cms_lock.iter() {
+                if channel_flags.0 == channel {
+                    if channel_flags.1 == module && channel_flags.2 == Status::Disabled {
+                        found = true;
+                    }
+                }
+            }
+            found
+        };
+
+        if !found_disabled {
+
+            let mut cms_lock = self.channel_module_status.lock().await;
+            cms_lock.push((channel,module,Status::Disabled));
+            
+
+        } 
+
+        
     }
 
     pub async fn enable_module(&self,channel:String,module:String){
         let mut lock = self.channel_module_status.lock().await;
-        if lock.contains(&(channel.clone(),module.clone(),"disabled".to_string())) {
+        if lock.contains(&(channel.clone(),module.clone(),Status::Disabled)) {
             
             let index = lock
                 .iter()
                 .position(|x| *x == 
-                    (channel.clone(),module.clone(),"disabled".to_string()))
+                    (channel.clone(),module.clone(),Status::Disabled))
                 .unwrap();
             lock.remove(index);
         }
     }
 
+    pub async fn get_channel_guest_badges(&self,chatter:String,channel:String) ->  Vec<(Badge,Instant,Duration)> {
+        
+        let bot = Arc::new(self);
+        let guest_badges_lock = bot.chatter_guest_badges.lock().await;
+
+        let mut badges = vec![];
+        for temp_badge in guest_badges_lock.iter() {
+            if  temp_badge.0 == chatter && temp_badge.1 == channel &&
+            temp_badge.3 + temp_badge.4 > Instant::now()
+            {
+                badges.push((temp_badge.2.clone(),temp_badge.3,temp_badge.4));
+            }
+        }
+
+        badges
+    }
+
+
+    pub async fn issue_new_guest_badge(&self,chatter:String,channel:String,badge:Badge,start:Instant,dur:Duration) {
+        let bot = Arc::new(self);
+        let mut guest_badges_lock = bot.chatter_guest_badges.lock().await;
+
+        guest_badges_lock.push((chatter,channel,badge,start,dur));
+
+    }
+
 
 }
 
diff --git a/src/botcore/bot_objects.rs b/src/botcore/bot_objects.rs
index a563d84..065e08a 100644
--- a/src/botcore/bot_objects.rs
+++ b/src/botcore/bot_objects.rs
@@ -13,7 +13,7 @@ use super::bot::Bot;
 
 
 /// chat badge
-#[derive(Clone)]
+#[derive(Clone,PartialEq, Eq,Debug)]
 pub enum Badge {
     Moderator,
     Broadcaster,
@@ -36,13 +36,13 @@ where
 
 /// collection of functions to create built in objects
 pub mod built_in_objects {
-    
+    const TEMP_BADGE_DUR_MIN:u64 = 30;
 
-    use std::sync::Arc;
+    use std::{sync::Arc, time::{Duration, Instant}};
 
     use twitch_irc::message::ServerMessage;
 
-    use crate::{asyncfn_box, Badge, Bot, Command};
+    use crate::{asyncfn_box, modules::Status, Badge, Bot, Command};
 
 
     /// create a vector of command build in objects
@@ -52,6 +52,7 @@ pub mod built_in_objects {
 
         cmds.push(create_disable_cmd());
         cmds.push(create_enable_cmd());
+        cmds.push(create_iam_role_cmd());
 
         cmds
 
@@ -64,12 +65,19 @@ pub mod built_in_objects {
         /* 2. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                for (i,arg) in msg.message_text.split(" ").enumerate() {
+                let mut action_taken = false;
+                for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
                     if i > 1 {
-                        bot.disable_module(msg.channel_login.clone(), arg.to_string()).await;
+                        if bot.get_channel_module_status(msg.channel_login.clone(), arg.to_string()).await == Status::Enabled {
+                            action_taken = true;
+                            bot.disable_module(msg.channel_login.clone(), arg.to_string()).await;
+                        }
+                        
                     }
                 }
-                let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+                if action_taken {
+                    let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+                }
             }
             Result::Err("Not Valid message type".to_string()) 
         }
@@ -93,13 +101,38 @@ pub mod built_in_objects {
         /* 2. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                for (i,arg) in msg.message_text.split(" ").enumerate() {
+
+                let mut bot_message="".to_string();
+                let mut re_enabled = false;
+
+                for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
                     if i > 1 {
-                        bot.enable_module(msg.channel_login.clone(), arg.to_string()).await;
+
+                        if Status::Disabled == bot.get_channel_module_status(msg.channel_login.clone(), arg.to_string()).await {
+                            bot.enable_module(msg.channel_login.clone(), arg.to_string()).await;
+                                
+                            //bot.get_modules()
+                            if let Some(found_mod) = bot.get_module(arg.to_string()) {
+                                bot_message = bot_message.to_string() + found_mod.get_bot_read_description().as_str();
+                            }
+                            re_enabled = true;
+                        }
+
                     }
                 }
+
+                if re_enabled {
+                    
+                    if bot_message.len() > 250 { 
+                        bot_message = bot_message[..250].to_string();
+                    }
+
+                    let _ = bot.client.say_in_reply_to(&msg, 
+                        format!("Enabled! {}", bot_message)
+                    ).await ;
+
+                }
                 
-                let _ = bot.client.say_in_reply_to(&msg, String::from("Enabled!")).await ;
          }
             Result::Err("Not Valid message type".to_string()) 
         }
@@ -113,7 +146,7 @@ pub mod built_in_objects {
         /* 5. optionally, set min badge*/
         cmd.set_min_badge(Badge::Moderator);
         cmd
-    }
+    } 
 
 
     /// adminonly command that grants a temporary role
@@ -121,31 +154,88 @@ pub mod built_in_objects {
         /* 1. Create a new blank cmd */
         let mut cmd = Command::new(vec![
             "I am ".to_string(), 
-            "I'm ".to_string()
+            "I'm ".to_string(),
+            "Im a ".to_string(),
             ],"".to_string());
 
         /* 2. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                for (i,arg) in msg.message_text.split(" ").enumerate() {
+                for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
                     if i > 1 {
                         // bot.disable_module(msg.channel_login.clone(), arg.to_string()).await;
                         // #todo
                         // if not dont have the badge or have a lower priviledge badge 
                         // and they dont have an active guest badge, ths admin can be
                         // recognzed wth that badge
+
                         if arg == "mod" || arg == "moderator" {
+                            let curr_temp_badges = bot.get_channel_guest_badges(msg.sender.login.clone(), msg.channel_login.clone()).await;
+                            let mut found = false;
+                            for temp_badge in curr_temp_badges {
+                                if temp_badge.0 == Badge::Moderator {
+                                    found = true;
+                                } 
+                            }
+                            if found {
+                                /* do nothing */
+                            } else {
+                                bot.issue_new_guest_badge(
+                                    msg.sender.login.clone(), 
+                                msg.channel_login.clone(), 
+                                Badge::Moderator, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
+
+                                let _ = bot.client.say_in_reply_to(&msg, 
+                                    format!("Temp {:?} issued for {:?} minutes",Badge::Moderator,TEMP_BADGE_DUR_MIN)
+                                    ).await ;
+                            }
 
                         }
                         if arg == "vip" {
+                            let curr_temp_badges = bot.get_channel_guest_badges(msg.sender.login.clone(), msg.channel_login.clone()).await;
+                            let mut found = false;
+                            for temp_badge in curr_temp_badges {
+                                if temp_badge.0 == Badge::Vip {
+                                    found = true;
+                                } 
+                            }
+                            if found {
+                                /* do nothing */
+                            } else {
+                                bot.issue_new_guest_badge(
+                                    msg.sender.login.clone(), 
+                                msg.channel_login.clone(), 
+                                Badge::Vip, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
 
+                                let _ = bot.client.say_in_reply_to(&msg, 
+                                    format!("Temp {:?} issued for {:?} minutes",Badge::Vip,TEMP_BADGE_DUR_MIN)
+                                    ).await ;
+                            }
                         }
                         if arg == "broadcaster" || arg == "strimmer" || arg == "streamer" {
-
+                            let curr_temp_badges = bot.get_channel_guest_badges(msg.sender.login.clone(), msg.channel_login.clone()).await;
+                            let mut found = false;
+                            for temp_badge in curr_temp_badges {
+                                if temp_badge.0 == Badge::Broadcaster {
+                                    found = true;
+                                } 
+                            }
+                            if found {
+                                /* do nothing */
+                            } else {
+                                bot.issue_new_guest_badge(
+                                    msg.sender.login.clone(), 
+                                msg.channel_login.clone(), 
+                                Badge::Broadcaster, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
+                                
+                                let _ = bot.client.say_in_reply_to(&msg, 
+                                format!("Temp {:?} issued for {:?} minutes",Badge::Broadcaster,TEMP_BADGE_DUR_MIN)
+                                ).await ;
+                            }
                         }
                     }
                 }
-                let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+                // let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
             }
             Result::Err("Not Valid message type".to_string()) 
         }
diff --git a/src/botcore/bot_objects/command.rs b/src/botcore/bot_objects/command.rs
index 9100df5..0cb4249 100644
--- a/src/botcore/bot_objects/command.rs
+++ b/src/botcore/bot_objects/command.rs
@@ -24,6 +24,8 @@ pub struct Command
     exec_fn : Arc<ExecBody>,
     min_badge : Badge,
     admin_only : bool,
+    /// admin role overrides channel badgen- default : false
+    admin_override : bool,
     prefix : String,
     custom_cond_fn : fn(Arc<Bot>,PrivmsgMessage) -> bool,
 }
@@ -49,6 +51,7 @@ impl Command
             exec_fn : Arc::new(asyncfn_box(execbody)),
             min_badge : Badge::Vip,
             admin_only : true,
+            admin_override : false ,
             custom_cond_fn : |_:Arc<Bot>,_:PrivmsgMessage| true,
         }
     }
@@ -70,7 +73,7 @@ impl Command
     /// checks if the trigger condition is met
     /// specifically if the message is a valid command and min badge roles provided
     /// 
-    pub fn command_triggered(&self,bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
+    pub async fn command_triggered(&self,bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
 
         fn cmd_called(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
                 let mut prefixed_cmd = "".to_string();
@@ -89,18 +92,21 @@ impl Command
         }
 
         
-        fn caller_badge_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
+        async fn caller_badge_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
 
             // senders that are admins skip badge check if the command is adminonly
             if cmd.admin_only && bot.get_admins().contains(&message.sender.login)  {
                 return true;
             } ;
 
-            // adminonly commands will can only be ran by admins
+            // adminOnly commands will can only be ran by admins
             if cmd.admin_only && bot.get_admins().contains(&message.sender.login) {
                 return false;
             }
 
+            // admin role overrides badge check if enabled
+            if cmd.admin_override && bot.get_admins().contains( &message.sender.login) { return true; }
+
             for badge in message.badges {
                 
                 match cmd.min_badge {
@@ -123,6 +129,15 @@ impl Command
                 }
             }
 
+            for temp_badge in bot.get_channel_guest_badges(message.sender.login, message.channel_login).await {
+                match (cmd.min_badge.clone(),temp_badge.0) {
+                    (Badge::Broadcaster,Badge::Broadcaster) => return true,
+                    (Badge::Moderator,Badge::Moderator) | (Badge::Moderator,Badge::Broadcaster) => return true,
+                    (Badge::Vip,Badge::Vip)|(Badge::Vip,Badge::Moderator)|(Badge::Vip,Badge::Broadcaster) => return true,
+                    _ => (),
+                }
+            }
+
             return false;
         }
 
@@ -153,7 +168,7 @@ impl Command
         // custom_cond_ok(self, bot.clone(), msg.clone()));
 
         cmd_called(self, bot.clone(), msg.clone()) && 
-        caller_badge_ok(self, bot.clone(), msg.clone()) &&
+        caller_badge_ok(self, bot.clone(), msg.clone()).await &&
         admin_only_ok(self, bot.clone(), msg.clone()) &&
         custom_cond_ok(self, bot, msg)
 
@@ -175,4 +190,10 @@ impl Command
     pub fn set_admin_only(&mut self,admin_only:bool) {
         self.admin_only = admin_only
     }
+
+    /// sets admin_override . This lets admins bypass 
+    /// badge restrictions   
+    pub fn set_admin_override(&mut self,admin_override:bool) {
+        self.admin_override = admin_override
+    }
 }
diff --git a/src/botcore/modules.rs b/src/botcore/modules.rs
index e57ecc2..8a342c8 100644
--- a/src/botcore/modules.rs
+++ b/src/botcore/modules.rs
@@ -2,14 +2,21 @@
 
 use crate::{Command, Listener};
 
+#[derive(PartialEq, Eq,Debug)]
+pub enum Status {
+    Disabled,
+    Enabled
+}
 
 /// Bot `Module` that groups a set of `bot_objects`
 /// 
 /// Elevated chatters can disable modules by their name or chat alias
+#[derive(Clone)]
 pub struct Module
 {
-    name: String,
-    _alias: String,
+    name: Vec<String>,
+    // _alias: String,
+    bot_read_description : String,
     listeners: Vec<Listener>,
     commands: Vec<Command>,
 }
@@ -17,10 +24,11 @@ pub struct Module
 impl Module 
 {
     /// create a new module
-    pub fn new(name:String,alias:String) -> Module {
+    pub fn new(name:Vec<String>,bot_read_description:String) -> Module {
         Module {
             name,
-            _alias: alias,
+            // _alias: alias,
+            bot_read_description,
             listeners: vec![],
             commands: vec![]
         }
@@ -44,8 +52,13 @@ impl Module
         self.commands.clone()
     }
 
-    pub fn get_name(&self) -> String {
+    pub fn get_names(&self) -> Vec<String> {
         self.name.clone()
     }
 
+    pub fn get_bot_read_description(&self) -> String {
+        self.bot_read_description.clone()
+    }
+
+
 }
\ No newline at end of file
diff --git a/src/custom_mods.rs b/src/custom_mods.rs
index e69de29..cd79787 100644
--- a/src/custom_mods.rs
+++ b/src/custom_mods.rs
@@ -0,0 +1 @@
+pub mod guest_badge;
\ No newline at end of file
diff --git a/src/custom_mods/guest_badge.rs b/src/custom_mods/guest_badge.rs
new file mode 100644
index 0000000..175a0e1
--- /dev/null
+++ b/src/custom_mods/guest_badge.rs
@@ -0,0 +1,140 @@
+use std::{sync::Arc, time::{Duration, Instant}};
+
+use twitch_irc::message::ServerMessage;
+
+use crate::{asyncfn_box, Badge, Bot, Command, Module};
+
+/// guest_badge / guest module
+/// 
+/// Temporary badges can be issued to chatters. The bot then opens functionality
+/// to that chatter baded on the recognized role
+///  
+/// Chatters with real badge roles will be able to share guest 
+/// badges based on their role
+/// 
+/// 
+/// 
+/// 
+/// 
+/// 
+/// 
+
+const VIP_GIVEN_DUR_MIN:u64 = 15;
+const MOD_GIVEN_DUR_MIN:u64 = 30;
+
+
+
+pub fn create_module() -> Module {
+
+    let mut custom_mod = Module::new(
+        vec!["guests".to_string()], 
+        "Temp Guest badges can be given by chatters with badges. ".to_string());
+
+    custom_mod.load_command(create_cmd_mod());
+    custom_mod.load_command(create_cmd_vip());
+
+    custom_mod
+
+}
+
+fn create_cmd_vip() -> Command {
+
+    let mut cmd = Command::new(vec!["vip".to_string()],"".to_string());
+
+
+    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        if let ServerMessage::Privmsg(msg) = message {
+
+            let guest_dur_min = {
+                let mut result=VIP_GIVEN_DUR_MIN;
+                for badge in msg.clone().badges {
+                    if badge.name == "vip" { result = VIP_GIVEN_DUR_MIN ; }
+                    if badge.name == "moderator" { result = MOD_GIVEN_DUR_MIN ; }
+                }
+                result
+            };
+            
+       
+            let mut badges_issued =false;
+            for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+                if i > 1 {
+                    let mut already_vip = false;
+                  
+                
+                    for guest_badge in bot.get_channel_guest_badges(arg.trim().to_string(), msg.channel_login.clone()).await {
+                        if guest_badge.0 == Badge::Vip { already_vip = true }
+                    }
+                    if !already_vip {
+                        badges_issued= true;
+                        bot.issue_new_guest_badge(
+                            arg.trim().to_string(), 
+                        msg.channel_login.clone(), 
+                        Badge::Vip, Instant::now(), Duration::from_secs(60*guest_dur_min)).await;
+                     
+                    }
+                }
+            }
+            if badges_issued { 
+           
+                
+            let _= bot.client.say_in_reply_to(
+                &msg.clone(), format!("Guest badges issued for {} min",guest_dur_min)).await;
+                return Result::Ok("Success".to_string());
+            }
+
+        }
+        Result::Err("Not Valid message type".to_string()) 
+    }
+
+    cmd.set_exec_fn(asyncfn_box(execbody));
+
+    cmd.set_admin_only(false);
+    cmd.set_admin_override(true);
+    cmd.set_min_badge(Badge::Vip);
+    cmd
+
+}
+
+fn create_cmd_mod() -> Command {
+
+    let mut cmd = Command::new(vec!["mod".to_string()],"".to_string());
+
+    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        if let ServerMessage::Privmsg(msg) = message {
+
+       
+            let mut badges_issued =false;
+            for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+                if i > 1 {
+                    
+                    let mut already_mod = false;
+                    for guest_badge in bot.get_channel_guest_badges(arg.trim().to_string(), msg.channel_login.clone()).await {
+                        if guest_badge.0 == Badge::Moderator { already_mod = true }
+                    }
+                    if !already_mod {
+                        badges_issued= true;
+                        bot.issue_new_guest_badge(
+                            arg.trim().to_string(), 
+                        msg.channel_login.clone(), 
+                        Badge::Moderator, Instant::now(), Duration::from_secs(60*MOD_GIVEN_DUR_MIN)).await;
+                    }
+                }
+            } 
+            if badges_issued {
+            let _= bot.client.say_in_reply_to(
+                &msg, format!("Guest badges issued for {} min",MOD_GIVEN_DUR_MIN)).await;
+                return Result::Ok("Success".to_string());
+            }
+        }
+        Result::Err("Not Valid message type".to_string()) 
+    }
+
+    cmd.set_exec_fn(asyncfn_box(execbody));
+
+    cmd.set_admin_only(false);
+    cmd.set_admin_override(true);
+    cmd.set_min_badge(Badge::Moderator);
+    cmd
+
+
+}
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index 9ecb36f..3bb703a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -137,7 +137,7 @@
 //! 
 //! ```
 //! 
-//! ## Modertor Reactor
+//! ## Moderator Reactor
 //! 
 //! ```
 //! 
@@ -197,10 +197,11 @@
 
 
 pub mod botcore;
+pub mod custom_mods;
 pub use crate::botcore::bot::Bot;
 pub use crate::botcore::bot_objects::asyncfn_box;
 pub use crate::botcore::bot_objects::listener::Listener;
 pub use crate::botcore::bot_objects::command::Command;
 pub use crate::botcore::modules::Module;
 pub use crate::botcore::bot_objects::Badge;
-
+pub use crate::botcore::modules;
-- 
2.49.0


From 6b09aeed694f519971d04a24f5eb590efa49bad1 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Thu, 30 Jan 2025 10:46:59 -0500
Subject: [PATCH 03/14] custom pyramid

---
 .gitignore                     |   3 +
 Cargo.lock                     |   7 +
 Cargo.toml                     |   2 +
 readme.md                      |  17 ++-
 src/bin/fun_bot.rs             |   4 +-
 src/bin/simple_module.rs       |   5 +-
 src/botcore/bot.rs             |  25 ++-
 src/custom_mods.rs             |   3 +-
 src/custom_mods/guest_badge.rs |  14 +-
 src/custom_mods/pyramid.rs     | 270 +++++++++++++++++++++++++++++++++
 src/lib.rs                     |  11 +-
 11 files changed, 337 insertions(+), 24 deletions(-)
 create mode 100644 src/custom_mods/pyramid.rs

diff --git a/.gitignore b/.gitignore
index c77774d..8e6827f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@
 
 # env
 .env
+
+# temp
+/tmp
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 11ba1db..3ee755c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -146,6 +146,7 @@ name = "forcebot-rs-v2"
 version = "0.1.0"
 dependencies = [
  "dotenv",
+ "lazy_static",
  "tokio",
  "twitch-irc",
 ]
@@ -214,6 +215,12 @@ version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
 [[package]]
 name = "libc"
 version = "0.2.169"
diff --git a/Cargo.toml b/Cargo.toml
index 53faa8d..e9d2e5e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,9 +6,11 @@ default-run = "forcebot-rs-v2"
 
 [dependencies]
 dotenv = "0.15.0"
+lazy_static = "1.5.0"
 tokio = { version = "1.33.0", features = ["full"] }
 twitch-irc = "5.0.1"
 
+
 # [[bin]]
 # name = "simple_bot"
 # path = "src/simple_bot.rs"
diff --git a/readme.md b/readme.md
index d192a1b..045e712 100644
--- a/readme.md
+++ b/readme.md
@@ -128,16 +128,19 @@ pub async fn main() {
 
 
 pub mod custom_mod {
+    
     use std::sync::Arc;
 
     use forcebot_rs_v2::{asyncfn_box, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
 
-    /// Module with a loaded command
+    /// Module definition with a loaded command
     pub fn new() -> Module {
         /* 1. Create a new module */
-        let mut custom_mod = Module::new("test".to_string(), "".to_string());
+        let mut custom_mod = Module::new(
+            vec!["test".to_string()], 
+            "".to_string());
 
         /* 2. Load the cmd into a new module */
         custom_mod.load_command(cmd_test());
@@ -146,9 +149,10 @@ pub mod custom_mod {
 
     }
 
+    /// Command definition 
     pub fn cmd_test() -> Command {
         /* 1. Create a new cmd */
-        let mut cmd = Command::new("test".to_string(),"".to_string());
+        let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
 
         /* 2. Define exec callback  */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
@@ -165,7 +169,6 @@ pub mod custom_mod {
         cmd.set_min_badge(Badge::Moderator);
 
         cmd
-        }
 }
 
 ```
@@ -290,7 +293,7 @@ pub async fn main() {
     let mut bot = Bot::new();
 
     /* 1. Create a new blank cmd */
-    let mut cmd = Command::new("test".to_string(),"".to_string());
+    let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
 
     /* 2. Define an async fn callback execution */
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
@@ -308,8 +311,8 @@ pub async fn main() {
     cmd.set_admin_only(false);
 
     /* 5. optionally, set min badge*/
-    cmd.set_min_badge("broadcaster".to_string());
-// 
+    cmd.set_min_badge(Badge::Moderator);
+
     /* 6. Load the cmd into the bot */
     bot.load_command(cmd);
 
diff --git a/src/bin/fun_bot.rs b/src/bin/fun_bot.rs
index eaf0487..196ba05 100644
--- a/src/bin/fun_bot.rs
+++ b/src/bin/fun_bot.rs
@@ -3,6 +3,7 @@
 //! Custom modules that can be managed in chat through `disable` and `enable` commands
 //! - funbot
 //! - guests
+//! - pyramid
 //! 
 //! 
 //! Be sure the followig is defined in `.env` 
@@ -16,7 +17,7 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_rs_v2::{custom_mods::guest_badge, Bot};
+use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
 
 #[tokio::main]
 pub async fn main() {
@@ -29,6 +30,7 @@ pub async fn main() {
 
     /* 2. Load Custom Modules */
     bot.load_module(guest_badge::create_module());
+    bot.load_module(pyramid::create_module());
     
     /* 3. Run the bot */
     bot.run().await;
diff --git a/src/bin/simple_module.rs b/src/bin/simple_module.rs
index 002ca22..1204e3a 100644
--- a/src/bin/simple_module.rs
+++ b/src/bin/simple_module.rs
@@ -41,7 +41,7 @@ pub mod custom_mod {
     use twitch_irc::message::ServerMessage;
 
 
-    /// Module with a loaded command
+    /// Module definition with a loaded command
     pub fn new() -> Module {
         /* 1. Create a new module */
         let mut custom_mod = Module::new(
@@ -55,6 +55,7 @@ pub mod custom_mod {
 
     }
 
+    /// Command definition 
     pub fn cmd_test() -> Command {
         /* 1. Create a new cmd */
         let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
@@ -74,5 +75,5 @@ pub mod custom_mod {
         cmd.set_min_badge(Badge::Moderator);
 
         cmd
-        }
+    }
 }
\ No newline at end of file
diff --git a/src/botcore/bot.rs b/src/botcore/bot.rs
index ac19db2..b70b09c 100644
--- a/src/botcore/bot.rs
+++ b/src/botcore/bot.rs
@@ -1,7 +1,7 @@
 
 
 use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
-use twitch_irc::{login::StaticLoginCredentials, message::ServerMessage, SecureTCPTransport, TwitchIRCClient};
+use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, ServerMessage}, SecureTCPTransport, TwitchIRCClient};
 use dotenv::dotenv;
 use std::{env, sync::Arc, time::{Duration, Instant}};
 
@@ -32,7 +32,10 @@ pub struct Bot
     /// channel module status
     channel_module_status: Mutex<Vec<(String,String,modules::Status)>>,
     /// chatter guest badges - chatter,channel,Badge,start_time,duration
-    chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>
+    chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
+    /// Message cache
+    message_cache: Mutex<Vec<PrivmsgMessage>>,
+    // message_cache: Vec<PrivmsgMessage>
 }
 
 
@@ -112,6 +115,7 @@ impl Bot
              modules:   vec![],
              channel_module_status: Mutex::new(vec![]),
              chatter_guest_badges: Mutex::new(vec![]),
+             message_cache : Mutex::new(vec![]),
         };
 
         for cmd in built_in_objects::create_commands() {
@@ -135,7 +139,10 @@ impl Bot
             let a = bot.clone();
             let mut in_msgs_lock = a.incoming_msgs.lock().await;
             
-            while let Some(message) = in_msgs_lock.recv().await {                
+            while let Some(message) = in_msgs_lock.recv().await {   
+
+                
+
                 for listener in &(*bot).listeners {
                     
                     let a = listener.clone();
@@ -147,6 +154,15 @@ impl Bot
 
                 if let ServerMessage::Privmsg(msg) = message.clone() {
 
+                    // let mut cache_lock = bot.message_cache.lock().await;
+                    let mut cache_lock = bot.message_cache.lock().await;
+                    cache_lock.push(msg.clone());
+                    // dbg!(cache_lock.clone());
+                    drop(cache_lock);
+
+                    // let a = bot.clone();
+                    // dbg!(bot.get_message_cache());
+
                     for cmd in &(*bot).commands {
                     
                         let a = cmd.clone();
@@ -324,6 +340,9 @@ impl Bot
 
     }
 
+    pub fn get_message_cache(&self) -> &Mutex<Vec<PrivmsgMessage>> {
+        &self.message_cache
+    }
 
 }
 
diff --git a/src/custom_mods.rs b/src/custom_mods.rs
index cd79787..099cee1 100644
--- a/src/custom_mods.rs
+++ b/src/custom_mods.rs
@@ -1 +1,2 @@
-pub mod guest_badge;
\ No newline at end of file
+pub mod guest_badge;
+pub mod pyramid;
\ No newline at end of file
diff --git a/src/custom_mods/guest_badge.rs b/src/custom_mods/guest_badge.rs
index 175a0e1..9e9807c 100644
--- a/src/custom_mods/guest_badge.rs
+++ b/src/custom_mods/guest_badge.rs
@@ -14,16 +14,18 @@ use crate::{asyncfn_box, Badge, Bot, Command, Module};
 /// 
 /// 
 /// 
-/// 
-/// 
-/// 
-/// 
 
 const VIP_GIVEN_DUR_MIN:u64 = 15;
 const MOD_GIVEN_DUR_MIN:u64 = 30;
 
 
-
+/// Use this function when loading modules into the bot
+/// 
+/// For example
+/// ```rust
+/// bot.load_module(guest_badge::create_module());
+/// ```
+/// 
 pub fn create_module() -> Module {
 
     let mut custom_mod = Module::new(
@@ -32,7 +34,7 @@ pub fn create_module() -> Module {
 
     custom_mod.load_command(create_cmd_mod());
     custom_mod.load_command(create_cmd_vip());
-
+ 
     custom_mod
 
 }
diff --git a/src/custom_mods/pyramid.rs b/src/custom_mods/pyramid.rs
new file mode 100644
index 0000000..5874a07
--- /dev/null
+++ b/src/custom_mods/pyramid.rs
@@ -0,0 +1,270 @@
+use std::sync::{Arc, Mutex};
+
+use twitch_irc::message::{PrivmsgMessage, ServerMessage};
+
+use crate::{asyncfn_box, Bot, Listener, Module};
+
+/// pyramid module
+/// 
+/// for detecting & handling pyramids
+/// 
+/// 
+/// 
+/// 
+use lazy_static::lazy_static;
+
+
+/// Use this function when loading modules into the bot
+/// 
+/// For example
+/// ```rust
+/// bot.load_module(pyramid::create_module());
+/// ```
+/// 
+pub fn create_module() -> Module {
+
+    let mut custom_mod = Module::new(
+        vec!["pyramid".to_string(),
+        "pyramids".to_string()], 
+        "o7 I can handle pyramids".to_string());
+    custom_mod.load_listener(create_pyramid_detector());
+
+    custom_mod
+
+}
+
+fn create_pyramid_detector() -> Listener {
+
+        /* 1. Create a new blank Listener */
+        let mut listener = Listener::new();
+
+        /* 2. Set a trigger condition function for listener */
+        listener.set_trigger_cond_fn(
+            |_bot:Arc<Bot>,_message:ServerMessage| 
+                true
+        );
+    
+        /* 3. Define an async fn callback execution */
+        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+            if let ServerMessage::Privmsg(msg) = message {
+                if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
+                    let _ = bot.client.say_in_reply_to(&msg, "Clap".to_string()).await ;
+                    return Result::Ok("Success".to_string()) ;
+                }
+            }
+            Result::Err("Not Valid message type".to_string()) 
+        }
+    
+        /* 4. Set and Store the execution body using `async_box()`  */
+        listener.set_exec_fn(asyncfn_box(execbody));
+
+        listener
+
+}
+
+
+async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
+
+    let msgtext = msg.message_text.replace("\u{e0000}","").trim().to_string();
+    let msgchannel = msg.channel_login;
+
+    // 1. Check if Pyramid started in chat > and recognize pyramid started
+    if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
+        set_pyramid_started(msgchannel.clone(),true);
+        push_to_compare(msgchannel.clone(),get_start_pattern(msgchannel.clone()));
+                
+    }
+
+    if is_pyramid_started(msgchannel.clone()) {
+        push_to_compare(msgchannel.clone(),msgtext.clone());
+    }
+
+    // 2a. If Pyramid Not Started, Assume message is a potential start pattern
+    if !is_pyramid_started(msgchannel.clone()) {
+        set_start_pattern(msgchannel.clone(),msgtext.clone());
+    }
+
+    // 2b. If Pyramid is Started, and the latest message is the pattern, check for
+    // symmetry to determine pyramid 
+    if is_pyramid_started(msgchannel.clone()) && msgtext.clone() == get_start_pattern(msgchannel.clone()) {
+        if symmetry_ok(msgchannel.clone()) {
+            return true;
+        } else {
+            return false ; 
+        }
+    } else {
+        return false;
+    }
+
+}
+
+
+lazy_static!{
+    /// Message Compare stack per channel (channel:String,msgstack:Vec<String>)
+    pub static ref COMPARE_MSG_STACK_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<String>>)>>  = Mutex::new(vec![]);
+    #[derive(Debug)]
+    /// Pyramid Started per channel (channel:String,started:bool)
+    pub static ref PYRAMID_STARTED_PER_CHNL:  Mutex<Vec<(String,Mutex<bool>)>> = Mutex::new(vec![]);
+    /// Start patterns per channel (channel:String,pattern:String)
+    pub static ref START_PATTERNS_PER_CHNL: Mutex<Vec<(String,Mutex<String>)>> = Mutex::new(vec![]);
+    /// temp message stack checker 
+    pub static ref TEMP_MSG_STACK: Mutex<Vec<String>> = Mutex::new(vec![]);
+}
+
+fn read_top_of_compare(channel:String) -> Option<String> {
+
+    let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
+
+    for rec in comp_perchnl.iter() {
+        if rec.0 == channel {
+            let msg_stack = rec.1.lock().unwrap();
+            
+            return msg_stack.last().cloned();
+        }
+    }
+
+    None
+
+}
+
+fn pop_top_of_compare(channel:String) -> Option<String> {
+    let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
+
+    for rec in comp_perchnl.iter() {
+        if rec.0 == channel {
+            let mut msg_stack = rec.1.lock().unwrap();
+            
+            let popped = msg_stack.pop();
+            return popped;
+        }
+    }
+    
+    None
+}
+
+fn set_pyramid_started(channel:String,started:bool) {
+    let mut start_perchnl = PYRAMID_STARTED_PER_CHNL.lock().unwrap();
+    let mut found = false;
+    for rec in start_perchnl.iter() {
+        if rec.0 == channel {
+            found = true;
+            let mut rec_started = rec.1.lock().unwrap();
+            *rec_started = started;
+        } 
+    }
+    if !found {
+        start_perchnl.push((channel,Mutex::new(started)));
+    }
+}
+
+fn is_pyramid_started(channel:String) -> bool {
+    let start_perchnl = PYRAMID_STARTED_PER_CHNL.lock().unwrap();
+    for rec in start_perchnl.iter() {
+        if rec.0 == channel {
+            let rec_started = rec.1.lock().unwrap();
+            return *rec_started;
+        }
+    }
+    false
+}
+
+fn set_start_pattern(channel:String,pattern:String) {
+    let mut start_patterns = START_PATTERNS_PER_CHNL.lock().unwrap();
+
+    let mut found = false;
+    for rec in start_patterns.iter() {
+
+        if rec.0 == channel {
+            found = true;
+            let mut patternlock = rec.1.lock().unwrap();
+            *patternlock = pattern.clone();
+        } 
+        
+    }  
+    if !found {
+        start_patterns.push((channel.clone(),Mutex::new(pattern.clone())));
+    }
+}
+
+
+fn get_start_pattern(channel:String) -> String {
+    let start_patterns = START_PATTERNS_PER_CHNL.lock().unwrap();
+
+    for rec in start_patterns.iter() {
+
+        if rec.0 == channel {
+            let patternlock = rec.1.lock().unwrap();
+            return patternlock.clone();
+        } 
+    }  
+
+    return "".to_string();
+}
+
+
+/// pushes message to compare stack
+fn push_to_compare(channel:String,message:String) {
+    let mut comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
+
+    let mut found = false;
+    for rec in comp_perchnl.iter() {
+        if rec.0 == channel {
+            found = true;
+            let mut msg_stack = rec.1.lock().unwrap();
+            msg_stack.push(message.clone());
+            // dbg!("Push message to cmp stack ; result last cmp_pchnl - ",comp_perchnl.last());
+        }
+    }
+    if !found {
+        comp_perchnl.push((channel,Mutex::new(vec![message])));
+    }
+
+}
+
+
+/// checks latest and next latest messages for potential start
+fn check_start_pyramid(channel:String,msgtext: String) -> bool {
+    msgtext == format!("{} {}",get_start_pattern(channel.clone()),get_start_pattern(channel.clone()))
+}
+
+
+/// pops the compare stack to determine symmetry
+fn symmetry_ok(channel:String) -> bool {
+    let mut temp_stack = TEMP_MSG_STACK.lock().unwrap();
+    let mut checking_started = false;
+    if !(read_top_of_compare(channel.clone()).unwrap_or("".to_string()) == get_start_pattern(channel.clone())) {
+        return false;
+    }
+    loop {
+        
+        if !checking_started && read_top_of_compare(channel.clone()).unwrap_or("".to_string()) == get_start_pattern(channel.clone()) {  
+            checking_started = true;
+        } 
+        if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or("".to_string()).len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
+            temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or("".to_string()));
+            
+        } else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or("".to_string()).len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
+    
+            temp_stack.pop();
+            if temp_stack.last().unwrap_or(&"".to_string()).clone() == read_top_of_compare(channel.clone()).unwrap_or("".to_string()) {
+                temp_stack.pop();
+                
+                continue;
+            } else {
+                
+                temp_stack.clear();
+                return false;
+            }
+
+        } else { /* dbg!("failed catchall"); */ return false; }
+        if checking_started && read_top_of_compare(channel.clone()).unwrap() == get_start_pattern(channel.clone()) {  
+            
+            temp_stack.clear();
+            return true;
+        } 
+    
+    }
+
+
+}
+
diff --git a/src/lib.rs b/src/lib.rs
index 3bb703a..cf5fdc9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -59,10 +59,12 @@
 //!     use twitch_irc::message::ServerMessage;
 //! 
 //! 
-//!     /// Module with a loaded command
+//!     /// Module definition with a loaded command
 //!     pub fn new() -> Module {
 //!         /* 1. Create a new module */
-//!         let mut custom_mod = Module::new("test".to_string(), "".to_string());
+//!         let mut custom_mod = Module::new(
+//!         vec!["test".to_string()], 
+//!          "".to_string());
 //! 
 //!         /* 2. Load the cmd into a new module */
 //!         custom_mod.load_command(cmd_test());
@@ -71,9 +73,10 @@
 //! 
 //!     }
 //! 
+//!     /// Command definition 
 //!     pub fn cmd_test() -> Command {
 //!         /* 1. Create a new cmd */
-//!         let mut cmd = Command::new("test".to_string(),"".to_string());
+//!         let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
 //! 
 //!         /* 2. Define exec callback  */
 //!         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
@@ -83,7 +86,7 @@
 //!             }
 //!             Result::Err("Not Valid message type".to_string()) 
 //!         }
-//! 
+//! z
 //!         /* 3. Set Command flags */
 //!         cmd.set_exec_fn(asyncfn_box(execbody));
 //!         cmd.set_admin_only(false);
-- 
2.49.0


From 57f1a3c914e76f4c2855e859fe0de3519c554d61 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Fri, 31 Jan 2025 13:47:12 -0500
Subject: [PATCH 04/14] msg cache per channel

---
 src/botcore/bot.rs | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/botcore/bot.rs b/src/botcore/bot.rs
index b70b09c..71b5cb9 100644
--- a/src/botcore/bot.rs
+++ b/src/botcore/bot.rs
@@ -344,6 +344,18 @@ impl Bot
         &self.message_cache
     }
 
+    /// get message cache newest to oldest for a channel
+    pub async fn get_message_cache_per_channel(&self,channel:String) -> Vec<PrivmsgMessage> {
+        let cache = self.message_cache.lock().await;
+        let mut rslt = vec![];
+        for a in cache.iter().rev().filter(|x| { x.channel_login==channel }).into_iter() {
+            rslt.push(a.clone());
+
+        }
+        rslt
+    }
+
+
 }
 
 
-- 
2.49.0


From 739f7aeeef28cb1b6ac0cb65c51057d239e4203a Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Fri, 31 Jan 2025 21:56:56 -0500
Subject: [PATCH 05/14] async trigger cond

---
 readme.md                           |  26 +++---
 src/bin/fun_bot.rs                  |   4 +-
 src/bin/moderator_reactor.rs        |   4 +-
 src/bin/simple_command_bot.rs       |   4 +-
 src/bin/simple_debug_listener.rs    |   4 +-
 src/bin/simple_module.rs            |   4 +-
 src/botcore/bot.rs                  |   2 +-
 src/botcore/bot_objects.rs          |  22 +++--
 src/botcore/bot_objects/command.rs  |  38 ++++++--
 src/botcore/bot_objects/listener.rs |  42 +++++++--
 src/custom_mods/guest_badge.rs      |   6 +-
 src/custom_mods/pyramid.rs          |  78 ++++++++++++++---
 src/lib.rs                          | 129 ++++++++++++++--------------
 13 files changed, 248 insertions(+), 115 deletions(-)

diff --git a/readme.md b/readme.md
index 045e712..55aa65f 100644
--- a/readme.md
+++ b/readme.md
@@ -126,12 +126,10 @@ pub async fn main() {
 
 }
 
-
 pub mod custom_mod {
-    
     use std::sync::Arc;
 
-    use forcebot_rs_v2::{asyncfn_box, Badge, Bot, Command, Module};
+    use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
 
@@ -164,11 +162,12 @@ pub mod custom_mod {
         }
 
         /* 3. Set Command flags */
-        cmd.set_exec_fn(asyncfn_box(execbody));
+        cmd.set_exec_fn(execution_async(execbody));
         cmd.set_admin_only(false);
         cmd.set_min_badge(Badge::Moderator);
 
         cmd
+    }
 }
 
 ```
@@ -179,7 +178,7 @@ Bot with a simple listener that listens for all messages and prints in output
 ```rust
 use std::sync::Arc;
 
-use forcebot_rs_v2::{asyncfn_box, Bot, Listener};
+use forcebot_rs_v2::{execution_async, Bot, Listener};
 use twitch_irc::message::ServerMessage;
 
 #[tokio::main]
@@ -203,7 +202,7 @@ pub async fn main() {
     }
 
     /* 2d. Set and Store the execution body using `async_box()`  */
-    listener.set_exec_fn(asyncfn_box(execbody));
+    listener.set_exec_fn(execution_async(execbody));
 
     /* 3. Load the listener into the bot */
     bot.load_listener(listener);
@@ -220,10 +219,11 @@ pub async fn main() {
 Example listener listens for a moderator badge and reply in chat 
 
 ```rust
+
 use std::sync::Arc;
 
 use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::asyncfn_box;
+use forcebot_rs_v2::execution_async;
 use forcebot_rs_v2::Listener;
 use twitch_irc::message::ServerMessage;
 
@@ -238,10 +238,11 @@ pub async fn main() {
     let mut listener = Listener::new();
 
     /* 2. Set a trigger condition function for listener */
+
     listener.set_trigger_cond_fn(
         |_:Arc<Bot>,message:ServerMessage| 
             if let ServerMessage::Privmsg(msg) = message {
-                
+                // dbg!(msg.clone());
                 for badge in msg.badges {
                     if matches!(badge, x if x.name == "moderator") {
                         // dbg!("moderator found");
@@ -262,7 +263,7 @@ pub async fn main() {
     }
 
     /* 4. Set and Store the execution body using `async_box()`  */
-    listener.set_exec_fn(asyncfn_box(execbody));
+    listener.set_exec_fn(execution_async(execbody));
 
     /* 5. Load the listener into the bot */
     bot.load_listener(listener);
@@ -273,6 +274,7 @@ pub async fn main() {
 }
 
 
+
 ```
 
 ## Simple Test Command
@@ -280,8 +282,9 @@ pub async fn main() {
 ```rust
 use std::sync::Arc;
 
+use forcebot_rs_v2::Badge;
 use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::asyncfn_box;
+use forcebot_rs_v2::execution_async;
 use forcebot_rs_v2::Command;
 use twitch_irc::message::ServerMessage;
 
@@ -305,7 +308,7 @@ pub async fn main() {
     }
 
     /* 3. Set and Store the execution body using `async_box()`  */
-    cmd.set_exec_fn(asyncfn_box(execbody));
+    cmd.set_exec_fn(execution_async(execbody));
 
     /* 4. optionally, remove admin only default flag */
     cmd.set_admin_only(false);
@@ -321,6 +324,7 @@ pub async fn main() {
 
 }
 
+
 ```
 
 # Crate Rust Documentation 
diff --git a/src/bin/fun_bot.rs b/src/bin/fun_bot.rs
index 196ba05..27c67a0 100644
--- a/src/bin/fun_bot.rs
+++ b/src/bin/fun_bot.rs
@@ -41,7 +41,7 @@ pub async fn main() {
 pub mod funbot_objs {
     use std::sync::Arc;
 
-    use forcebot_rs_v2::{asyncfn_box, Badge, Bot, Command, Module};
+    use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
     /// Create a Module with a loaded Command object
@@ -69,7 +69,7 @@ pub mod funbot_objs {
             Result::Err("Not Valid message type".to_string()) 
         }
 
-        cmd.set_exec_fn(asyncfn_box(execbody));
+        cmd.set_exec_fn(execution_async(execbody));
 
         cmd.set_admin_only(false);
         cmd.set_min_badge(Badge::Vip);
diff --git a/src/bin/moderator_reactor.rs b/src/bin/moderator_reactor.rs
index a7bb62c..279d204 100644
--- a/src/bin/moderator_reactor.rs
+++ b/src/bin/moderator_reactor.rs
@@ -14,7 +14,7 @@
 use std::sync::Arc;
 
 use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::asyncfn_box;
+use forcebot_rs_v2::execution_async;
 use forcebot_rs_v2::Listener;
 use twitch_irc::message::ServerMessage;
 
@@ -54,7 +54,7 @@ pub async fn main() {
     }
 
     /* 4. Set and Store the execution body using `async_box()`  */
-    listener.set_exec_fn(asyncfn_box(execbody));
+    listener.set_exec_fn(execution_async(execbody));
 
     /* 5. Load the listener into the bot */
     bot.load_listener(listener);
diff --git a/src/bin/simple_command_bot.rs b/src/bin/simple_command_bot.rs
index 934ddf7..9c2f2ff 100644
--- a/src/bin/simple_command_bot.rs
+++ b/src/bin/simple_command_bot.rs
@@ -17,7 +17,7 @@ use std::sync::Arc;
 
 use forcebot_rs_v2::Badge;
 use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::asyncfn_box;
+use forcebot_rs_v2::execution_async;
 use forcebot_rs_v2::Command;
 use twitch_irc::message::ServerMessage;
 
@@ -41,7 +41,7 @@ pub async fn main() {
     }
 
     /* 3. Set and Store the execution body using `async_box()`  */
-    cmd.set_exec_fn(asyncfn_box(execbody));
+    cmd.set_exec_fn(execution_async(execbody));
 
     /* 4. optionally, remove admin only default flag */
     cmd.set_admin_only(false);
diff --git a/src/bin/simple_debug_listener.rs b/src/bin/simple_debug_listener.rs
index ee129bd..791c5d0 100644
--- a/src/bin/simple_debug_listener.rs
+++ b/src/bin/simple_debug_listener.rs
@@ -12,7 +12,7 @@
 
 use std::sync::Arc;
 
-use forcebot_rs_v2::{asyncfn_box, Bot, Listener};
+use forcebot_rs_v2::{execution_async, Bot, Listener};
 use twitch_irc::message::ServerMessage;
 
 #[tokio::main]
@@ -36,7 +36,7 @@ pub async fn main() {
     }
 
     /* 2d. Set and Store the execution body using `async_box()`  */
-    listener.set_exec_fn(asyncfn_box(execbody));
+    listener.set_exec_fn(execution_async(execbody));
 
     /* 3. Load the listener into the bot */
     bot.load_listener(listener);
diff --git a/src/bin/simple_module.rs b/src/bin/simple_module.rs
index 1204e3a..f9e7bec 100644
--- a/src/bin/simple_module.rs
+++ b/src/bin/simple_module.rs
@@ -37,7 +37,7 @@ pub async fn main() {
 pub mod custom_mod {
     use std::sync::Arc;
 
-    use forcebot_rs_v2::{asyncfn_box, Badge, Bot, Command, Module};
+    use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
 
@@ -70,7 +70,7 @@ pub mod custom_mod {
         }
 
         /* 3. Set Command flags */
-        cmd.set_exec_fn(asyncfn_box(execbody));
+        cmd.set_exec_fn(execution_async(execbody));
         cmd.set_admin_only(false);
         cmd.set_min_badge(Badge::Moderator);
 
diff --git a/src/botcore/bot.rs b/src/botcore/bot.rs
index 71b5cb9..e769efd 100644
--- a/src/botcore/bot.rs
+++ b/src/botcore/bot.rs
@@ -192,7 +192,7 @@ impl Bot
                         for listener in module.get_listeners() {
                         
                             let a = listener.clone();
-                            if a.cond_triggered(bot.clone(),message.clone()) {
+                            if a.cond_triggered(bot.clone(),message.clone()) && a.cond_async_triggered(bot.clone(), message.clone()).await {
                                 
                                 let _ = listener.execute_fn(bot.clone(),message.clone()).await;
                             }
diff --git a/src/botcore/bot_objects.rs b/src/botcore/bot_objects.rs
index 065e08a..cca0423 100644
--- a/src/botcore/bot_objects.rs
+++ b/src/botcore/bot_objects.rs
@@ -25,13 +25,25 @@ pub type ExecBody = Box<
     dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = Result<String,String>> + Send>> + Send + Sync,
 >;
 
-pub fn asyncfn_box<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> ExecBody
+pub fn execution_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> ExecBody
 where
     T: Future<Output = Result<String,String>> + Send + 'static,
 {
     Box::new(move |a,b| Box::pin(f(a,b)))
 }
 
+pub type TrigBody = Box<
+    dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
+>;
+
+pub fn condition_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> TrigBody
+where
+    T: Future<Output = bool> + Send + 'static,
+{
+    Box::new(move |a,b| Box::pin(f(a,b)))
+}
+
+
 
 
 /// collection of functions to create built in objects
@@ -42,7 +54,7 @@ pub mod built_in_objects {
 
     use twitch_irc::message::ServerMessage;
 
-    use crate::{asyncfn_box, modules::Status, Badge, Bot, Command};
+    use crate::{execution_async, modules::Status, Badge, Bot, Command};
 
 
     /// create a vector of command build in objects
@@ -83,7 +95,7 @@ pub mod built_in_objects {
         }
 
         /* 3. Set and Store the execution body using `async_box()`  */
-        cmd.set_exec_fn(asyncfn_box(execbody));
+        cmd.set_exec_fn(execution_async(execbody));
 
         /* 4. optionally, remove admin only default flag */
         cmd.set_admin_only(false);
@@ -138,7 +150,7 @@ pub mod built_in_objects {
         }
 
         /* 3. Set and Store the execution body using `async_box()`  */
-        cmd.set_exec_fn(asyncfn_box(execbody));
+        cmd.set_exec_fn(execution_async(execbody));
 
         /* 4. optionally, remove admin only default flag */
         cmd.set_admin_only(false);
@@ -241,7 +253,7 @@ pub mod built_in_objects {
         }
 
         /* 3. Set and Store the execution body using `async_box()`  */
-        cmd.set_exec_fn(asyncfn_box(execbody));
+        cmd.set_exec_fn(execution_async(execbody));
 
         /* 4. optionally, remove admin only default flag */
         cmd.set_admin_only(true);
diff --git a/src/botcore/bot_objects/command.rs b/src/botcore/bot_objects/command.rs
index 0cb4249..3314347 100644
--- a/src/botcore/bot_objects/command.rs
+++ b/src/botcore/bot_objects/command.rs
@@ -2,9 +2,9 @@ use std::sync::Arc;
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-use crate::{asyncfn_box, botcore::bot::Bot, Badge};
+use crate::{botcore::{bot::Bot, bot_objects::condition_async}, execution_async, Badge};
 
-use super::ExecBody;
+use super::{ExecBody, TrigBody};
 
 /// Bot `Command` that stores trigger condition callback and a execution functon
 /// 
@@ -16,7 +16,7 @@ use super::ExecBody;
 /// 
 /// AdminOnly commands can only be ran by admin 
 /// 
-/// Use `asyncfn_box()` on custom async execution bodies
+/// Use `execution_async()` on custom async execution bodies
 #[derive(Clone)]
 pub struct Command
 {
@@ -28,6 +28,7 @@ pub struct Command
     admin_override : bool,
     prefix : String,
     custom_cond_fn : fn(Arc<Bot>,PrivmsgMessage) -> bool,
+    custom_cond_async : Arc<TrigBody>,
 }
 
 impl Command
@@ -44,26 +45,51 @@ impl Command
 
         async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> 
         { Result::Ok("success".to_string()) }
+        async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
 
         Command {
             commands ,
             prefix ,
-            exec_fn : Arc::new(asyncfn_box(execbody)),
+            exec_fn : Arc::new(execution_async(execbody)),
             min_badge : Badge::Vip,
             admin_only : true,
             admin_override : false ,
             custom_cond_fn : |_:Arc<Bot>,_:PrivmsgMessage| true,
+            custom_cond_async : Arc::new(condition_async(condition01)),
         }
     }
 
-    /// set a trigger conditin callback that returns true if the listener shoud trigger
+    /// set a trigger conditin callback that returns true if the command should trigger
     pub fn set_custom_cond_fn(&mut self,cond_fn: fn(Arc<Bot>,PrivmsgMessage) -> bool) {
         self.custom_cond_fn = cond_fn;
     }
 
+    
+    /// sets the async trigger condition for listener
+    /// 
+    /// Same as `set_custom_cond_fn()` , but async define
+    /// 
+    /// Use`execution_async()` on the async fn when storing
+    /// 
+    /// Example - 
+    /// ```rust
+    /// /* 1. Create a new blank Listener */
+    /// let mut cmd = Command::new();
+    ///
+    /// /* 2. define an async function */
+    /// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
+    /// 
+    /// /* 3. Set and Store the execution body using `async_box()`  */
+    /// cmd.set_custom_cond_async(condition_async(condition01));
+    /// ```
+    /// 
+    pub fn set_custom_cond_async(&mut self,condition:TrigBody ) {
+        self.custom_cond_async = Arc::new(condition);
+    }
+
     /// sets the execution body of the listener for when it triggers
     /// 
-    /// Use`asyncfn_box()` on the async fn when storing
+    /// Use`execution_async()` on the async fn when storing
     /// 
     /// 
     pub fn set_exec_fn(&mut self,exec_fn:ExecBody ) {
diff --git a/src/botcore/bot_objects/listener.rs b/src/botcore/bot_objects/listener.rs
index 2bebb8e..cc170df 100644
--- a/src/botcore/bot_objects/listener.rs
+++ b/src/botcore/bot_objects/listener.rs
@@ -2,9 +2,9 @@ use std::sync::Arc;
 
 use twitch_irc::message::ServerMessage;
 
-use crate::{asyncfn_box, Bot};
+use crate::{botcore::bot_objects::condition_async, execution_async, Bot};
 
-use super::ExecBody;
+use super::{ExecBody, TrigBody};
 
 /// Bot `Listener`` that stores trigger condition callback and a execution functon
 /// 
@@ -14,6 +14,8 @@ pub struct Listener
 {
     /// trigger condition
     trigger_cond_fn : fn(Arc<Bot>,ServerMessage) -> bool,
+    /// trigger condition for async
+    trigger_cond_async : Arc<TrigBody> ,
     /// execution body
     exec_fn : Arc<ExecBody>,
 }
@@ -26,10 +28,11 @@ impl Listener
     pub fn new() -> Listener {
 
         async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
-
+        async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
         Listener {
             trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| false,
-            exec_fn : Arc::new(asyncfn_box(execbody))
+            trigger_cond_async : Arc::new(condition_async(condition01)),
+            exec_fn : Arc::new(execution_async(execbody)),
         }
     }
 
@@ -38,6 +41,29 @@ impl Listener
         self.trigger_cond_fn = cond_fn;
     }
 
+    /// sets the async trigger condition for listener
+    /// 
+    /// Same as `set_trigger_cond_fn()` , but async define
+    /// 
+    /// Use`asyncfn_box()` on the async fn when storing
+    /// 
+    /// Example - 
+    /// ```rust
+    /// /* 1. Create a new blank Listener */
+    /// let mut listener = Listener::new();
+    ///
+    /// /* 2. define an async function */
+    /// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
+    /// 
+    /// /* 3. Set and Store the execution body using `async_box()`  */
+    /// listener.set_trigger_cond_async(condition_async(condition01));
+    /// ```
+    /// 
+    pub fn set_trigger_cond_async(&mut self,condition:TrigBody ) {
+        self.trigger_cond_async = Arc::new(condition);
+    }
+    
+
     /// sets the execution body of the listener for when it triggers
     /// 
     /// Use`asyncfn_box()` on the async fn when storing
@@ -60,7 +86,13 @@ impl Listener
 
     /// checks if the trigger condition is met
     pub fn cond_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
-        (self.trigger_cond_fn)(bot,msg)
+        (self.trigger_cond_fn)(bot,msg) 
+    }
+
+    /// executes the listeners executon body
+    pub async fn cond_async_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
+        // (self.exec_fn)(bot,msg)
+        (self.trigger_cond_async)(bot,msg).await
     }
 
     /// executes the listeners executon body
diff --git a/src/custom_mods/guest_badge.rs b/src/custom_mods/guest_badge.rs
index 9e9807c..ee1eb16 100644
--- a/src/custom_mods/guest_badge.rs
+++ b/src/custom_mods/guest_badge.rs
@@ -2,7 +2,7 @@ use std::{sync::Arc, time::{Duration, Instant}};
 
 use twitch_irc::message::ServerMessage;
 
-use crate::{asyncfn_box, Badge, Bot, Command, Module};
+use crate::{execution_async, Badge, Bot, Command, Module};
 
 /// guest_badge / guest module
 /// 
@@ -88,7 +88,7 @@ fn create_cmd_vip() -> Command {
         Result::Err("Not Valid message type".to_string()) 
     }
 
-    cmd.set_exec_fn(asyncfn_box(execbody));
+    cmd.set_exec_fn(execution_async(execbody));
 
     cmd.set_admin_only(false);
     cmd.set_admin_override(true);
@@ -131,7 +131,7 @@ fn create_cmd_mod() -> Command {
         Result::Err("Not Valid message type".to_string()) 
     }
 
-    cmd.set_exec_fn(asyncfn_box(execbody));
+    cmd.set_exec_fn(execution_async(execbody));
 
     cmd.set_admin_only(false);
     cmd.set_admin_override(true);
diff --git a/src/custom_mods/pyramid.rs b/src/custom_mods/pyramid.rs
index 5874a07..de633bb 100644
--- a/src/custom_mods/pyramid.rs
+++ b/src/custom_mods/pyramid.rs
@@ -1,14 +1,17 @@
+
+
 use std::sync::{Arc, Mutex};
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-use crate::{asyncfn_box, Bot, Listener, Module};
+use crate::{condition_async, Badge, Bot, Command, Listener, Module};
 
 /// pyramid module
 /// 
 /// for detecting & handling pyramids
 /// 
-/// 
+/// - listener - detects pyramid
+/// - cmd & listener - interrupts some chatters temporarily
 /// 
 /// 
 use lazy_static::lazy_static;
@@ -38,13 +41,21 @@ fn create_pyramid_detector() -> Listener {
         /* 1. Create a new blank Listener */
         let mut listener = Listener::new();
 
-        /* 2. Set a trigger condition function for listener */
-        listener.set_trigger_cond_fn(
-            |_bot:Arc<Bot>,_message:ServerMessage| 
-                true
-        );
+        /* 2. Define an async trigger condition callback */
+        async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
+            if let ServerMessage::Privmsg(msg) = message {
+                if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
+                    return true;
+                }
+            }
+            false 
+        }
+
+        /* 3. Set a trigger condition function for listener */
+        listener.set_trigger_cond_async(condition_async(condition01));
+
     
-        /* 3. Define an async fn callback execution */
+        /* 4. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
                 if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
@@ -55,8 +66,8 @@ fn create_pyramid_detector() -> Listener {
             Result::Err("Not Valid message type".to_string()) 
         }
     
-        /* 4. Set and Store the execution body using `async_box()`  */
-        listener.set_exec_fn(asyncfn_box(execbody));
+        /* 5. Set and Store the execution body using `async_box()`  */
+        listener.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
 
         listener
 
@@ -109,6 +120,10 @@ lazy_static!{
     pub static ref START_PATTERNS_PER_CHNL: Mutex<Vec<(String,Mutex<String>)>> = Mutex::new(vec![]);
     /// temp message stack checker 
     pub static ref TEMP_MSG_STACK: Mutex<Vec<String>> = Mutex::new(vec![]);
+    
+    /// interruptor targets - (channel:String,chatters:Vec<String>>)
+    pub static ref INTERRUPT_TRG_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<String>>)>>  = Mutex::new(vec![]);
+
 }
 
 fn read_top_of_compare(channel:String) -> Option<String> {
@@ -268,3 +283,46 @@ fn symmetry_ok(channel:String) -> bool {
 
 }
 
+
+/// pyramid interruptor #todo
+/// 
+/// pick chatters that will be interrupted if they solo build
+/// 
+/// takes in arguments as chatters
+/// 
+/// chatters are then interrupted for a random duration under 15m
+/// 
+/// if a duration is given, take that duration eg 15m , 25m 
+/// 
+/// 
+fn create_interruptor_cmd() -> Command {
+    let mut cmd = Command::new(vec![
+        "no pyramid".to_string(),
+        "no pyramids".to_string()
+        ], 
+    "".to_string());
+
+        /* 2. Define an async fn callback execution */
+        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+            if let ServerMessage::Privmsg(msg) = message {
+                let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
+                return Result::Ok("Success".to_string()) ;
+            }
+            Result::Err("Not Valid message type".to_string()) 
+        }
+
+        /* 3. Set and Store the execution body using `async_box()`  */
+        cmd.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
+
+        /* 4. optionally, remove admin only default flag */
+        cmd.set_admin_only(false);
+    
+        /* 5. optionally, set min badge*/
+        cmd.set_min_badge(Badge::Moderator);
+
+
+    cmd
+}
+
+
+
diff --git a/src/lib.rs b/src/lib.rs
index cf5fdc9..7981025 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -36,65 +36,65 @@
 //! 
 //! ```rust
 //! use forcebot_rs_v2::Bot;
-//! 
-//! #[tokio::main]
-//! pub async fn main() {
-//! 
-//!     /* Create the bot using env */
-//!     let mut bot = Bot::new();
-//! 
-//!     /* load the Module */
-//!     bot.load_module(custom_mod::new());
-//! 
-//!     /* Run the bot */
-//!     bot.run().await;
-//! 
-//! }
-//! 
-//! 
-//! pub mod custom_mod {
-//!     use std::sync::Arc;
-//! 
-//!     use forcebot_rs_v2::{asyncfn_box, Badge, Bot, Command, Module};
-//!     use twitch_irc::message::ServerMessage;
-//! 
-//! 
-//!     /// Module definition with a loaded command
-//!     pub fn new() -> Module {
-//!         /* 1. Create a new module */
-//!         let mut custom_mod = Module::new(
-//!         vec!["test".to_string()], 
-//!          "".to_string());
-//! 
-//!         /* 2. Load the cmd into a new module */
-//!         custom_mod.load_command(cmd_test());
-//! 
-//!         custom_mod
-//! 
-//!     }
-//! 
-//!     /// Command definition 
-//!     pub fn cmd_test() -> Command {
-//!         /* 1. Create a new cmd */
-//!         let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
-//! 
-//!         /* 2. Define exec callback  */
-//!         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-//!             if let ServerMessage::Privmsg(msg) = message {
-//!                 let _= bot.client.say_in_reply_to(
-//!                     &msg, "test return".to_string()).await;
-//!             }
-//!             Result::Err("Not Valid message type".to_string()) 
-//!         }
-//! z
-//!         /* 3. Set Command flags */
-//!         cmd.set_exec_fn(asyncfn_box(execbody));
-//!         cmd.set_admin_only(false);
-//!         cmd.set_min_badge(Badge::Moderator);
-//! 
-//!         cmd
-//!         }
-//! }
+//!    
+//!    #[tokio::main]
+//!    pub async fn main() {
+//!    
+//!        /* Create the bot using env */
+//!        let mut bot = Bot::new();
+//!    
+//!        /* load the Module */
+//!        bot.load_module(custom_mod::new());
+//!    
+//!        /* Run the bot */
+//!        bot.run().await;
+//!    
+//!    }
+//!    
+//!    pub mod custom_mod {
+//!        use std::sync::Arc;
+//!    
+//!        use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
+//!        use twitch_irc::message::ServerMessage;
+//!    
+//!    
+//!        /// Module definition with a loaded command
+//!        pub fn new() -> Module {
+//!            /* 1. Create a new module */
+//!            let mut custom_mod = Module::new(
+//!                vec!["test".to_string()], 
+//!                "".to_string());
+//!    
+//!            /* 2. Load the cmd into a new module */
+//!            custom_mod.load_command(cmd_test());
+//!    
+//!            custom_mod
+//!    
+//!        }
+//!    
+//!        /// Command definition 
+//!        pub fn cmd_test() -> Command {
+//!            /* 1. Create a new cmd */
+//!            let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
+//!    
+//!            /* 2. Define exec callback  */
+//!            async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+//!                if let ServerMessage::Privmsg(msg) = message {
+//!                    let _= bot.client.say_in_reply_to(
+//!                        &msg, "test return".to_string()).await;
+//!                }
+//!                Result::Err("Not Valid message type".to_string()) 
+//!            }
+//!    
+//!            /* 3. Set Command flags */
+//!            cmd.set_exec_fn(execution_async(execbody));
+//!            cmd.set_admin_only(false);
+//!            cmd.set_min_badge(Badge::Moderator);
+//!    
+//!            cmd
+//!        }
+//!    }
+//!    
 //! 
 //! ```
 //! 
@@ -104,7 +104,7 @@
 //! ```rust
 //! use std::sync::Arc;
 //! 
-//! use forcebot_rs_v2::{asyncfn_box, Bot, Listener};
+//! use forcebot_rs_v2::{execution_async, Bot, Listener};
 //! use twitch_irc::message::ServerMessage;
 //! 
 //! #[tokio::main]
@@ -128,7 +128,7 @@
 //!     }
 //! 
 //!     /* 2d. Set and Store the execution body using `async_box()`  */
-//!     listener.set_exec_fn(asyncfn_box(execbody));
+//!     listener.set_exec_fn(execution_async(execbody));
 //! 
 //!     /* 3. Load the listener into the bot */
 //!     bot.load_listener(listener);
@@ -147,7 +147,7 @@
 //! use std::sync::Arc;
 //! 
 //! use forcebot_rs_v2::Bot;
-//! use forcebot_rs_v2::asyncfn_box;
+//! use forcebot_rs_v2::execution_async;
 //! use forcebot_rs_v2::Listener;
 //! use twitch_irc::message::ServerMessage;
 //! 
@@ -187,7 +187,7 @@
 //!     }
 //! 
 //!     /* 4. Set and Store the execution body using `async_box()`  */
-//!     listener.set_exec_fn(asyncfn_box(execbody));
+//!     listener.set_exec_fn(execution_async(execbody));
 //! 
 //!     /* 5. Load the listener into the bot */
 //!     bot.load_listener(listener);
@@ -202,7 +202,8 @@
 pub mod botcore;
 pub mod custom_mods;
 pub use crate::botcore::bot::Bot;
-pub use crate::botcore::bot_objects::asyncfn_box;
+pub use crate::botcore::bot_objects::execution_async;
+pub use crate::botcore::bot_objects::condition_async;
 pub use crate::botcore::bot_objects::listener::Listener;
 pub use crate::botcore::bot_objects::command::Command;
 pub use crate::botcore::modules::Module;
-- 
2.49.0


From 802fd714c204b0f134d18f9beb856227c471e7c6 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Sat, 1 Feb 2025 03:43:53 -0500
Subject: [PATCH 06/14] module disable by default

---
 src/botcore/bot.rs     | 10 ++++++++--
 src/botcore/modules.rs | 15 +++++++++++++--
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/src/botcore/bot.rs b/src/botcore/bot.rs
index e769efd..1a54cc6 100644
--- a/src/botcore/bot.rs
+++ b/src/botcore/bot.rs
@@ -249,9 +249,15 @@ impl Bot
     }
     
     /// loads a `Module` and its bot objects
-    pub fn load_module(&mut self,m: Module) {
+    pub async fn load_module(&mut self,m: Module) {
+        if m.get_status_by_default() == Status::Disabled {
+            for chnl in &self.botchannels {
+                self.disable_module(chnl.clone(),  
+                m.get_names().first().unwrap().clone()).await
+            }
+        }
         self.modules.push(m)
-    }
+    }  
 
     pub async fn get_channel_module_status(&self,channel:String,module:String) -> Status {
         let found_disabled:bool = {
diff --git a/src/botcore/modules.rs b/src/botcore/modules.rs
index 8a342c8..2ed44dc 100644
--- a/src/botcore/modules.rs
+++ b/src/botcore/modules.rs
@@ -2,7 +2,7 @@
 
 use crate::{Command, Listener};
 
-#[derive(PartialEq, Eq,Debug)]
+#[derive(PartialEq, Eq,Debug,Clone)]
 pub enum Status {
     Disabled,
     Enabled
@@ -19,6 +19,8 @@ pub struct Module
     bot_read_description : String,
     listeners: Vec<Listener>,
     commands: Vec<Command>,
+    // disable module at load for bot channels
+    default_status_per_channel: Status,
 }
 
 impl Module 
@@ -30,7 +32,8 @@ impl Module
             // _alias: alias,
             bot_read_description,
             listeners: vec![],
-            commands: vec![]
+            commands: vec![],
+            default_status_per_channel: Status::Disabled,
         }
     }
 
@@ -60,5 +63,13 @@ impl Module
         self.bot_read_description.clone()
     }
 
+    pub fn set_status_by_default(&mut self,status:Status){
+        self.default_status_per_channel = status;
+    }
+
+    pub fn get_status_by_default(&self) -> Status {
+        self.default_status_per_channel.clone()
+    }
+
 
 }
\ No newline at end of file
-- 
2.49.0


From 34842d4c41420c654585d2e7d96dc954b3b62968 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Sat, 1 Feb 2025 07:18:23 -0500
Subject: [PATCH 07/14] exec body and listeners adj

---
 readme.md                           |  2 +-
 src/bin/fun_bot.rs                  |  6 +--
 src/bin/simple_module.rs            |  2 +-
 src/botcore/bot.rs                  | 10 ++---
 src/botcore/bot_objects.rs          | 60 +++++++++++++++++++++++++++--
 src/botcore/bot_objects/command.rs  | 23 +++++------
 src/botcore/bot_objects/listener.rs | 21 ++++------
 src/custom_mods/pyramid.rs          |  4 +-
 src/lib.rs                          |  3 +-
 9 files changed, 88 insertions(+), 43 deletions(-)

diff --git a/readme.md b/readme.md
index 55aa65f..769ba75 100644
--- a/readme.md
+++ b/readme.md
@@ -119,7 +119,7 @@ pub async fn main() {
     let mut bot = Bot::new();
 
     /* load the Module */
-    bot.load_module(custom_mod::new());
+    bot.load_module(custom_mod::new()).await;
 
     /* Run the bot */
     bot.run().await;
diff --git a/src/bin/fun_bot.rs b/src/bin/fun_bot.rs
index 27c67a0..4f119ce 100644
--- a/src/bin/fun_bot.rs
+++ b/src/bin/fun_bot.rs
@@ -26,11 +26,11 @@ pub async fn main() {
     let mut bot = Bot::new();
 
     /* 1. Load the module into the bot */
-    bot.load_module(funbot_objs::create_module());
+    bot.load_module(funbot_objs::create_module()).await;
 
     /* 2. Load Custom Modules */
-    bot.load_module(guest_badge::create_module());
-    bot.load_module(pyramid::create_module());
+    bot.load_module(guest_badge::create_module()).await;
+    bot.load_module(pyramid::create_module()).await;
     
     /* 3. Run the bot */
     bot.run().await;
diff --git a/src/bin/simple_module.rs b/src/bin/simple_module.rs
index f9e7bec..77bd5da 100644
--- a/src/bin/simple_module.rs
+++ b/src/bin/simple_module.rs
@@ -26,7 +26,7 @@ pub async fn main() {
     let mut bot = Bot::new();
 
     /* load the Module */
-    bot.load_module(custom_mod::new());
+    bot.load_module(custom_mod::new()).await;
 
     /* Run the bot */
     bot.run().await;
diff --git a/src/botcore/bot.rs b/src/botcore/bot.rs
index 1a54cc6..f1a8798 100644
--- a/src/botcore/bot.rs
+++ b/src/botcore/bot.rs
@@ -141,13 +141,11 @@ impl Bot
             
             while let Some(message) = in_msgs_lock.recv().await {   
 
-                
-
+        
                 for listener in &(*bot).listeners {
                     
                     let a = listener.clone();
-                    if a.cond_triggered(bot.clone(),message.clone()) {
-                        
+                    if a.cond_triggered(bot.clone(),message.clone()).await {
                         let _ = listener.execute_fn(bot.clone(),message.clone()).await;
                     }
                 }
@@ -160,8 +158,6 @@ impl Bot
                     // dbg!(cache_lock.clone());
                     drop(cache_lock);
 
-                    // let a = bot.clone();
-                    // dbg!(bot.get_message_cache());
 
                     for cmd in &(*bot).commands {
                     
@@ -192,7 +188,7 @@ impl Bot
                         for listener in module.get_listeners() {
                         
                             let a = listener.clone();
-                            if a.cond_triggered(bot.clone(),message.clone()) && a.cond_async_triggered(bot.clone(), message.clone()).await {
+                            if a.cond_triggered(bot.clone(),message.clone()).await {
                                 
                                 let _ = listener.execute_fn(bot.clone(),message.clone()).await;
                             }
diff --git a/src/botcore/bot_objects.rs b/src/botcore/bot_objects.rs
index cca0423..d5ae5b2 100644
--- a/src/botcore/bot_objects.rs
+++ b/src/botcore/bot_objects.rs
@@ -7,7 +7,7 @@ use std::future::Future;
 use std::pin::Pin;
 use std::sync::Arc;
 
-use twitch_irc::message::ServerMessage;
+use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
 use super::bot::Bot;
 
@@ -25,6 +25,21 @@ pub type ExecBody = Box<
     dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = Result<String,String>> + Send>> + Send + Sync,
 >;
 
+/// used to store async execution functions. Primarily used for `Command`
+/// 
+/// call this to store execution functions in `Commands`
+/// 
+/// # example
+/// ```
+/// /* 2. Define exec callback  */
+///  async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+///   /* do smth */
+///  }
+///
+/// /* 3. Set Command flags */
+///  cmd.set_exec_fn(execution_async(execbody));
+/// ```
+/// 
 pub fn execution_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> ExecBody
 where
     T: Future<Output = Result<String,String>> + Send + 'static,
@@ -32,11 +47,24 @@ where
     Box::new(move |a,b| Box::pin(f(a,b)))
 }
 
-pub type TrigBody = Box<
-    dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
+pub type CommandTrigger = Box<
+    dyn Fn(Arc<Bot>,PrivmsgMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
 >;
 
-pub fn condition_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> TrigBody
+/// used to store async trigger condition callback functions. Primarily used for `Command`
+/// 
+/// # example
+/// ```
+/// /* 2. Define condition callback  */
+/// async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
+///   /* do smth */
+/// }
+///
+/// /* 3. Set Command flags */
+/// cmd.set_custom_cond_async(command_condition_async(condition01));
+/// ```
+/// 
+pub fn command_condition_async<T>(f: fn(Arc<Bot>,PrivmsgMessage) -> T) -> CommandTrigger
 where
     T: Future<Output = bool> + Send + 'static,
 {
@@ -44,6 +72,30 @@ where
 }
 
 
+pub type ListenerTrigger = Box<
+    dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
+>;
+
+/// used to store async trigger condition callback functions. Primarily used for `Listener`
+/// 
+/// # example
+/// ```
+/// /* 2. Define condition callback  */
+/// async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
+///   /* do smth */
+/// }
+///
+/// /* 3. Set Command flags */
+/// cmd.set_custom_cond_async(listener_condition_async(condition01));
+/// ```
+/// 
+pub fn listener_condition_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> ListenerTrigger
+where
+    T: Future<Output = bool> + Send + 'static,
+{
+    Box::new(move |a,b| Box::pin(f(a,b)))
+}
+
 
 
 /// collection of functions to create built in objects
diff --git a/src/botcore/bot_objects/command.rs b/src/botcore/bot_objects/command.rs
index 3314347..001ac31 100644
--- a/src/botcore/bot_objects/command.rs
+++ b/src/botcore/bot_objects/command.rs
@@ -2,9 +2,9 @@ use std::sync::Arc;
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-use crate::{botcore::{bot::Bot, bot_objects::condition_async}, execution_async, Badge};
+use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
 
-use super::{ExecBody, TrigBody};
+use super::{CommandTrigger, ExecBody};
 
 /// Bot `Command` that stores trigger condition callback and a execution functon
 /// 
@@ -23,12 +23,13 @@ pub struct Command
     commands : Vec<String>,
     exec_fn : Arc<ExecBody>,
     min_badge : Badge,
+    /// only admins can run - default : `true`
     admin_only : bool,
-    /// admin role overrides channel badgen- default : false
+    /// admin role overrides channel badge - default : `false`
     admin_override : bool,
     prefix : String,
     custom_cond_fn : fn(Arc<Bot>,PrivmsgMessage) -> bool,
-    custom_cond_async : Arc<TrigBody>,
+    custom_cond_async : Arc<CommandTrigger>,
 }
 
 impl Command
@@ -45,7 +46,7 @@ impl Command
 
         async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> 
         { Result::Ok("success".to_string()) }
-        async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
+        async fn condition01(_:Arc<Bot>,_:PrivmsgMessage) -> bool { true }
 
         Command {
             commands ,
@@ -55,11 +56,11 @@ impl Command
             admin_only : true,
             admin_override : false ,
             custom_cond_fn : |_:Arc<Bot>,_:PrivmsgMessage| true,
-            custom_cond_async : Arc::new(condition_async(condition01)),
+            custom_cond_async : Arc::new(command_condition_async(condition01)),
         }
     }
 
-    /// set a trigger conditin callback that returns true if the command should trigger
+    /// set a trigger condition callback that returns true if the command should trigger
     pub fn set_custom_cond_fn(&mut self,cond_fn: fn(Arc<Bot>,PrivmsgMessage) -> bool) {
         self.custom_cond_fn = cond_fn;
     }
@@ -83,7 +84,7 @@ impl Command
     /// cmd.set_custom_cond_async(condition_async(condition01));
     /// ```
     /// 
-    pub fn set_custom_cond_async(&mut self,condition:TrigBody ) {
+    pub fn set_custom_cond_async(&mut self,condition:CommandTrigger ) {
         self.custom_cond_async = Arc::new(condition);
     }
 
@@ -181,8 +182,8 @@ impl Command
             }
         }
 
-        fn custom_cond_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-            (cmd.custom_cond_fn)(bot,message)
+        async fn custom_cond_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
+            (cmd.custom_cond_fn)(bot.clone(),message.clone()) && (cmd.custom_cond_async)(bot,message).await
         }
 
         // dbg!(msg.clone());
@@ -196,7 +197,7 @@ impl Command
         cmd_called(self, bot.clone(), msg.clone()) && 
         caller_badge_ok(self, bot.clone(), msg.clone()).await &&
         admin_only_ok(self, bot.clone(), msg.clone()) &&
-        custom_cond_ok(self, bot, msg)
+        custom_cond_ok(self, bot, msg).await
 
     }
 
diff --git a/src/botcore/bot_objects/listener.rs b/src/botcore/bot_objects/listener.rs
index cc170df..af96cf7 100644
--- a/src/botcore/bot_objects/listener.rs
+++ b/src/botcore/bot_objects/listener.rs
@@ -2,9 +2,9 @@ use std::sync::Arc;
 
 use twitch_irc::message::ServerMessage;
 
-use crate::{botcore::bot_objects::condition_async, execution_async, Bot};
+use crate::{execution_async, listener_condition_async, Bot};
 
-use super::{ExecBody, TrigBody};
+use super::{ExecBody, ListenerTrigger};
 
 /// Bot `Listener`` that stores trigger condition callback and a execution functon
 /// 
@@ -15,7 +15,7 @@ pub struct Listener
     /// trigger condition
     trigger_cond_fn : fn(Arc<Bot>,ServerMessage) -> bool,
     /// trigger condition for async
-    trigger_cond_async : Arc<TrigBody> ,
+    trigger_cond_async : Arc<ListenerTrigger> ,
     /// execution body
     exec_fn : Arc<ExecBody>,
 }
@@ -31,7 +31,7 @@ impl Listener
         async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
         Listener {
             trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| false,
-            trigger_cond_async : Arc::new(condition_async(condition01)),
+            trigger_cond_async : Arc::new(listener_condition_async(condition01)),
             exec_fn : Arc::new(execution_async(execbody)),
         }
     }
@@ -59,7 +59,7 @@ impl Listener
     /// listener.set_trigger_cond_async(condition_async(condition01));
     /// ```
     /// 
-    pub fn set_trigger_cond_async(&mut self,condition:TrigBody ) {
+    pub fn set_trigger_cond_async(&mut self,condition:ListenerTrigger ) {
         self.trigger_cond_async = Arc::new(condition);
     }
     
@@ -85,14 +85,9 @@ impl Listener
     }
 
     /// checks if the trigger condition is met
-    pub fn cond_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
-        (self.trigger_cond_fn)(bot,msg) 
-    }
-
-    /// executes the listeners executon body
-    pub async fn cond_async_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
-        // (self.exec_fn)(bot,msg)
-        (self.trigger_cond_async)(bot,msg).await
+    pub async fn cond_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
+        let list = Arc::new(self);
+        (list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
     }
 
     /// executes the listeners executon body
diff --git a/src/custom_mods/pyramid.rs b/src/custom_mods/pyramid.rs
index de633bb..fcc9da2 100644
--- a/src/custom_mods/pyramid.rs
+++ b/src/custom_mods/pyramid.rs
@@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-use crate::{condition_async, Badge, Bot, Command, Listener, Module};
+use crate::{listener_condition_async, Badge, Bot, Command, Listener, Module};
 
 /// pyramid module
 /// 
@@ -52,7 +52,7 @@ fn create_pyramid_detector() -> Listener {
         }
 
         /* 3. Set a trigger condition function for listener */
-        listener.set_trigger_cond_async(condition_async(condition01));
+        listener.set_trigger_cond_async(listener_condition_async(condition01));
 
     
         /* 4. Define an async fn callback execution */
diff --git a/src/lib.rs b/src/lib.rs
index 7981025..67e89ac 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -203,7 +203,8 @@ pub mod botcore;
 pub mod custom_mods;
 pub use crate::botcore::bot::Bot;
 pub use crate::botcore::bot_objects::execution_async;
-pub use crate::botcore::bot_objects::condition_async;
+pub use crate::botcore::bot_objects::command_condition_async;
+pub use crate::botcore::bot_objects::listener_condition_async;
 pub use crate::botcore::bot_objects::listener::Listener;
 pub use crate::botcore::bot_objects::command::Command;
 pub use crate::botcore::modules::Module;
-- 
2.49.0


From 2a6b512ce15bcf616c9ff7a63e3160abd4631214 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Sat, 1 Feb 2025 16:00:57 -0500
Subject: [PATCH 08/14] doc adj for modules

---
 readme.md  | 37 +++++++++++++++++++++++++++++--------
 src/lib.rs | 33 +++++++++++++++++++++++++++++----
 2 files changed, 58 insertions(+), 12 deletions(-)

diff --git a/readme.md b/readme.md
index 769ba75..6af83e2 100644
--- a/readme.md
+++ b/readme.md
@@ -98,10 +98,34 @@ pub async fn main() {
 
 ```
 
-## Customize with Modules
+## Customize by Loading Custom Modules 
 
 A `Module` is a group of bot objects (eg `Command`) that elevated users can manage through built in `disable` and `enable` commands
 
+Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
+
+```rust
+use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
+
+#[tokio::main]
+pub async fn main() {
+
+    /* 1. Create the bot using env */
+    let mut bot = Bot::new();
+
+    /* 2. Load Custom Modules */
+    bot.load_module(guest_badge::create_module()).await;
+    bot.load_module(pyramid::create_module()).await;
+    
+    /* 3. Run the bot */
+    bot.run().await;
+
+}
+```
+
+
+## Create your own Custom Modules
+
 Create a custom `Module` by :
 
 1. Defining Functions that create the Custom Bot Objects (eg `Command`)
@@ -329,14 +353,11 @@ pub async fn main() {
 
 # Crate Rust Documentation 
 
-Create Crate documentation
+Create `forcebot_rs_v2` Rust Crate documentation
 
-Clean Build Documentation 
+Documentation - Clean Build & Open in Default Browser  
 ```
-cargo clean && cargo doc
+cargo clean && cargo doc --open
 ```
 
-Open Crate Doc
-```
-cargo doc --open
-```
+
diff --git a/src/lib.rs b/src/lib.rs
index 67e89ac..817caac 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,7 @@
 //! 
 //! Customize by adding additional bot objects
 //! 
-//! ## New Bot
+//! # New Bot
 //! 
 //! Uses Env defined variables to create and run the bot
 //! 
@@ -23,10 +23,35 @@
 //! ```
 //! 
 //! 
-//! ## Customize with Modules
+//! # Customize with Modules
 //! 
 //! A `Module` is a group of bot objects (eg `Command`) that elevated users can manage through built in `disable` and `enable` commands
 //! 
+//! 
+//! Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
+//!    
+//! ```rust
+//! use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
+//!    
+//! #[tokio::main]
+//! pub async fn main() {
+//!    
+//!    /* 1. Create the bot using env */
+//!    let mut bot = Bot::new();
+//!    
+//!    /* 2. Load Custom Modules */
+//!    bot.load_module(guest_badge::create_module()).await;
+//!    bot.load_module(pyramid::create_module()).await;
+//!       
+//!    /* 3. Run the bot */
+//!    bot.run().await;
+//!    
+//! }
+//!    ```
+//!    
+//! 
+//! # Create your own Custom Modules
+//! 
 //! Create a custom `Module` by :
 //! 
 //! 1. Defining Functions that create the Custom Bot Objects (eg `Command`)
@@ -98,7 +123,7 @@
 //! 
 //! ```
 //! 
-//! ## Simple Debug Listener
+//! # Simple Debug Listener
 //! Bot with a simple listener that listens for all messages and prints in output
 //! 
 //! ```rust
@@ -140,7 +165,7 @@
 //! 
 //! ```
 //! 
-//! ## Moderator Reactor
+//! # Moderator Reactor
 //! 
 //! ```
 //! 
-- 
2.49.0


From 46cff68bc1116235123e9395340d2d34179070ac Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Sun, 2 Feb 2025 08:17:38 -0500
Subject: [PATCH 09/14] reorg workspaces

---
 Cargo.lock                                    | 120 ++++++++++++----
 Cargo.toml                                    |  31 +----
 forcebot_core/Cargo.toml                      |  11 ++
 {src => forcebot_core/src}/bin/fun_bot.rs     |  19 ++-
 {src => forcebot_core/src}/bin/new_bot.rs     |   2 +-
 .../src}/bin/simple_module.rs                 |   4 +-
 {src => forcebot_core/src}/botcore.rs         |   0
 {src => forcebot_core/src}/botcore/bot.rs     |  16 ++-
 .../src}/botcore/bot_objects.rs               |  18 ++-
 .../src}/botcore/bot_objects/command.rs       |   6 +-
 .../src}/botcore/bot_objects/listener.rs      |  41 ++++--
 {src => forcebot_core/src}/botcore/modules.rs |   5 +-
 {src => forcebot_core/src}/custom_mods.rs     |   0
 .../src}/custom_mods/guest_badge.rs           |   0
 .../src}/custom_mods/pyramid.rs               |  96 ++++++++++---
 {src => forcebot_core/src}/lib.rs             |  42 +++---
 moderator_reactor/Cargo.toml                  |  11 ++
 .../src/main.rs                               |   8 +-
 new_empty_bot/Cargo.toml                      |  11 ++
 {src => new_empty_bot/src}/main.rs            |   3 +-
 readme.md                                     | 129 ++++++++++++------
 simple_command_bot/Cargo.toml                 |  11 ++
 .../src/main.rs                               |  10 +-
 simple_debug_listener/Cargo.toml              |  11 ++
 .../src/main.rs                               |   4 +-
 simple_module_example/Cargo.toml              |  11 ++
 simple_module_example/src/main.rs             |  79 +++++++++++
 27 files changed, 519 insertions(+), 180 deletions(-)
 create mode 100644 forcebot_core/Cargo.toml
 rename {src => forcebot_core/src}/bin/fun_bot.rs (82%)
 rename {src => forcebot_core/src}/bin/new_bot.rs (95%)
 rename {src => forcebot_core/src}/bin/simple_module.rs (95%)
 rename {src => forcebot_core/src}/botcore.rs (100%)
 rename {src => forcebot_core/src}/botcore/bot.rs (96%)
 rename {src => forcebot_core/src}/botcore/bot_objects.rs (94%)
 rename {src => forcebot_core/src}/botcore/bot_objects/command.rs (97%)
 rename {src => forcebot_core/src}/botcore/bot_objects/listener.rs (63%)
 rename {src => forcebot_core/src}/botcore/modules.rs (91%)
 rename {src => forcebot_core/src}/custom_mods.rs (100%)
 rename {src => forcebot_core/src}/custom_mods/guest_badge.rs (100%)
 rename {src => forcebot_core/src}/custom_mods/pyramid.rs (73%)
 rename {src => forcebot_core/src}/lib.rs (85%)
 create mode 100644 moderator_reactor/Cargo.toml
 rename src/bin/moderator_reactor.rs => moderator_reactor/src/main.rs (91%)
 create mode 100644 new_empty_bot/Cargo.toml
 rename {src => new_empty_bot/src}/main.rs (93%)
 create mode 100644 simple_command_bot/Cargo.toml
 rename src/bin/simple_command_bot.rs => simple_command_bot/src/main.rs (88%)
 create mode 100644 simple_debug_listener/Cargo.toml
 rename src/bin/simple_debug_listener.rs => simple_debug_listener/src/main.rs (90%)
 create mode 100644 simple_module_example/Cargo.toml
 create mode 100644 simple_module_example/src/main.rs

diff --git a/Cargo.lock b/Cargo.lock
index 3ee755c..e770a80 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,9 +19,9 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
 
 [[package]]
 name = "async-trait"
-version = "0.1.85"
+version = "0.1.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
+checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -63,9 +63,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
 
 [[package]]
 name = "cc"
-version = "1.2.10"
+version = "1.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
+checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf"
 dependencies = [
  "shlex",
 ]
@@ -142,7 +142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
-name = "forcebot-rs-v2"
+name = "forcebot_core"
 version = "0.1.0"
 dependencies = [
  "dotenv",
@@ -200,13 +200,14 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.15"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi",
+ "wasi 0.13.3+wasi-0.2.2",
+ "windows-targets",
 ]
 
 [[package]]
@@ -271,15 +272,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
 dependencies = [
  "libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "moderator_reactor"
+version = "0.1.0"
+dependencies = [
+ "dotenv",
+ "forcebot_core",
+ "lazy_static",
+ "tokio",
+ "twitch-irc",
+]
+
 [[package]]
 name = "native-tls"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
+checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
 dependencies = [
  "libc",
  "log",
@@ -292,6 +304,17 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "new_empty_bot"
+version = "0.1.0"
+dependencies = [
+ "dotenv",
+ "forcebot_core",
+ "lazy_static",
+ "tokio",
+ "twitch-irc",
+]
+
 [[package]]
 name = "num-traits"
 version = "0.2.19"
@@ -318,9 +341,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
 name = "openssl"
-version = "0.10.68"
+version = "0.10.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
+checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e"
 dependencies = [
  "bitflags",
  "cfg-if",
@@ -344,9 +367,9 @@ dependencies = [
 
 [[package]]
 name = "openssl-probe"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
 
 [[package]]
 name = "openssl-sys"
@@ -436,9 +459,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
 [[package]]
 name = "rustix"
-version = "0.38.43"
+version = "0.38.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
 dependencies = [
  "bitflags",
  "errno",
@@ -500,6 +523,39 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "simple_command_bot"
+version = "0.1.0"
+dependencies = [
+ "dotenv",
+ "forcebot_core",
+ "lazy_static",
+ "tokio",
+ "twitch-irc",
+]
+
+[[package]]
+name = "simple_debug_listener"
+version = "0.1.0"
+dependencies = [
+ "dotenv",
+ "forcebot_core",
+ "lazy_static",
+ "tokio",
+ "twitch-irc",
+]
+
+[[package]]
+name = "simple_module_example"
+version = "0.1.0"
+dependencies = [
+ "dotenv",
+ "forcebot_core",
+ "lazy_static",
+ "tokio",
+ "twitch-irc",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -527,9 +583,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.96"
+version = "2.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -538,9 +594,9 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.15.0"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
+checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
 dependencies = [
  "cfg-if",
  "fastrand",
@@ -687,9 +743,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.14"
+version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
 
 [[package]]
 name = "vcpkg"
@@ -703,6 +759,15 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasi"
+version = "0.13.3+wasi-0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.52.0"
@@ -784,3 +849,12 @@ name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
+dependencies = [
+ "bitflags",
+]
diff --git a/Cargo.toml b/Cargo.toml
index e9d2e5e..7b027e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,24 +1,7 @@
-[package]
-name = "forcebot-rs-v2"
-version = "0.1.0"
-edition = "2021"
-default-run = "forcebot-rs-v2"
-
-[dependencies]
-dotenv = "0.15.0"
-lazy_static = "1.5.0"
-tokio = { version = "1.33.0", features = ["full"] }
-twitch-irc = "5.0.1"
-
-
-# [[bin]]
-# name = "simple_bot"
-# path = "src/simple_bot.rs"
-
-# [[bin]]
-# name = "simple_bot_listener"
-# path = "src/simple_bot_listener.rs"
-
-# [lib]
-# name = "botlib"
-# path = "src/lib.rs"
\ No newline at end of file
+[workspace]
+members = [
+    "forcebot_core",
+    "simple_module_example",
+    "new_empty_bot",
+    "simple_debug_listener", 
+    "moderator_reactor", "simple_command_bot"]
diff --git a/forcebot_core/Cargo.toml b/forcebot_core/Cargo.toml
new file mode 100644
index 0000000..082c28b
--- /dev/null
+++ b/forcebot_core/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "forcebot_core"
+version = "0.1.0"
+edition = "2021"
+default-run = "fun_bot"
+
+[dependencies]
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
diff --git a/src/bin/fun_bot.rs b/forcebot_core/src/bin/fun_bot.rs
similarity index 82%
rename from src/bin/fun_bot.rs
rename to forcebot_core/src/bin/fun_bot.rs
index 4f119ce..d9d8d45 100644
--- a/src/bin/fun_bot.rs
+++ b/forcebot_core/src/bin/fun_bot.rs
@@ -1,9 +1,9 @@
 //! WIP Fun forcebot with catered customizations #todo
 //! 
 //! Custom modules that can be managed in chat through `disable` and `enable` commands
-//! - funbot
-//! - guests
-//! - pyramid
+//! - `besty`
+//! - `guests`
+//! - `pyramid`
 //! 
 //! 
 //! Be sure the followig is defined in `.env` 
@@ -17,7 +17,10 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
+// use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
+use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
+
+
 
 #[tokio::main]
 pub async fn main() {
@@ -41,16 +44,17 @@ pub async fn main() {
 pub mod funbot_objs {
     use std::sync::Arc;
 
-    use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
+    use forcebot_core::{execution_async, modules::Status, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
     /// Create a Module with a loaded Command object
     pub fn create_module() -> Module {
         let mut custom_mod = Module::new(
-            vec!["funbot".to_string()], 
-            "".to_string());
+            vec!["besty".to_string()], 
+            "Now Aware of besty xdd666 ".to_string());
 
         custom_mod.load_command(create_cmd_test());
+        custom_mod.set_status_by_default(Status::Disabled);
 
         custom_mod
     }
@@ -73,6 +77,7 @@ pub mod funbot_objs {
 
         cmd.set_admin_only(false);
         cmd.set_min_badge(Badge::Vip);
+
         cmd
 
     }
diff --git a/src/bin/new_bot.rs b/forcebot_core/src/bin/new_bot.rs
similarity index 95%
rename from src/bin/new_bot.rs
rename to forcebot_core/src/bin/new_bot.rs
index cbe682b..4abdbc7 100644
--- a/src/bin/new_bot.rs
+++ b/forcebot_core/src/bin/new_bot.rs
@@ -10,7 +10,7 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_rs_v2::Bot;
+use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
diff --git a/src/bin/simple_module.rs b/forcebot_core/src/bin/simple_module.rs
similarity index 95%
rename from src/bin/simple_module.rs
rename to forcebot_core/src/bin/simple_module.rs
index 77bd5da..02756d7 100644
--- a/src/bin/simple_module.rs
+++ b/forcebot_core/src/bin/simple_module.rs
@@ -17,7 +17,7 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_rs_v2::Bot;
+use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
@@ -37,7 +37,7 @@ pub async fn main() {
 pub mod custom_mod {
     use std::sync::Arc;
 
-    use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
+    use forcebot_core::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
 
diff --git a/src/botcore.rs b/forcebot_core/src/botcore.rs
similarity index 100%
rename from src/botcore.rs
rename to forcebot_core/src/botcore.rs
diff --git a/src/botcore/bot.rs b/forcebot_core/src/botcore/bot.rs
similarity index 96%
rename from src/botcore/bot.rs
rename to forcebot_core/src/botcore/bot.rs
index f1a8798..96a66b6 100644
--- a/src/botcore/bot.rs
+++ b/forcebot_core/src/botcore/bot.rs
@@ -5,10 +5,16 @@ use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, Server
 use dotenv::dotenv;
 use std::{env, sync::Arc, time::{Duration, Instant}};
 
-use crate::{Badge, Command, Listener, Module};
+// use crate::{Badge, Command, Listener, Module};
+use super::bot_objects::command::Command;
 
-use super::{bot_objects::built_in_objects, modules::{self, Status}};
+use crate::botcore::bot_objects::Badge;
+use crate::botcore::bot_objects::listener::Listener;
+use super::super::botcore::modules::Module;
+// use super::
 
+use super:: {bot_objects::built_in_objects, modules::{self, Status}};
+ 
 
 /// Twitch chat bot
 pub struct Bot 
@@ -73,7 +79,7 @@ impl Bot
 
     /// Creates a new `Bot` using bot information
     /// 
-    /// Bot joined channels will include channels from `.env` and `botchannels` argument
+    /// Bot will join `botchannels` argument
     pub fn new_from(bot_login_name:String,oauth_token:String,prefix:String,botchannels:Vec<String>) -> Bot {
 
         dotenv().ok();
@@ -90,10 +96,6 @@ impl Bot
         let mut botchannels_all = Vec::new();
         botchannels_all.extend(botchannels);
 
-        for chnl in env::var("bot_channels").unwrap().split(',') {
-            botchannels_all.push(chnl.to_owned());
-        }
-
 
         let mut admins = Vec::new();
 
diff --git a/src/botcore/bot_objects.rs b/forcebot_core/src/botcore/bot_objects.rs
similarity index 94%
rename from src/botcore/bot_objects.rs
rename to forcebot_core/src/botcore/bot_objects.rs
index d5ae5b2..397d7fc 100644
--- a/src/botcore/bot_objects.rs
+++ b/forcebot_core/src/botcore/bot_objects.rs
@@ -105,8 +105,16 @@ pub mod built_in_objects {
     use std::{sync::Arc, time::{Duration, Instant}};
 
     use twitch_irc::message::ServerMessage;
+    
+    use super::{execution_async,command::Command,Bot,Badge,super::modules::Status};
+    
+    // use super::execution_async;
+    // use super::command::Command;
+    // use super::Bot;
+    // use super::Badge;
+    // use super::super::modules::Status;
+    // ::{execution_async, modules::Status, Badge, Bot, Command};
 
-    use crate::{execution_async, modules::Status, Badge, Bot, Command};
 
 
     /// create a vector of command build in objects
@@ -146,14 +154,14 @@ pub mod built_in_objects {
             Result::Err("Not Valid message type".to_string()) 
         }
 
-        /* 3. Set and Store the execution body using `async_box()`  */
+        /* 3. Set and Store the execution body using `execution_async()`  */
         cmd.set_exec_fn(execution_async(execbody));
 
         /* 4. optionally, remove admin only default flag */
         cmd.set_admin_only(false);
 
         /* 5. optionally, set min badge*/
-        cmd.set_min_badge(Badge::Moderator);
+        cmd.set_min_badge(Badge::Moderator /* ::Moderator */);
         cmd
         
     } 
@@ -201,7 +209,7 @@ pub mod built_in_objects {
             Result::Err("Not Valid message type".to_string()) 
         }
 
-        /* 3. Set and Store the execution body using `async_box()`  */
+        /* 3. Set and Store the execution body using `execution_async()`  */
         cmd.set_exec_fn(execution_async(execbody));
 
         /* 4. optionally, remove admin only default flag */
@@ -304,7 +312,7 @@ pub mod built_in_objects {
             Result::Err("Not Valid message type".to_string()) 
         }
 
-        /* 3. Set and Store the execution body using `async_box()`  */
+        /* 3. Set and Store the execution body using `execution_async()`  */
         cmd.set_exec_fn(execution_async(execbody));
 
         /* 4. optionally, remove admin only default flag */
diff --git a/src/botcore/bot_objects/command.rs b/forcebot_core/src/botcore/bot_objects/command.rs
similarity index 97%
rename from src/botcore/bot_objects/command.rs
rename to forcebot_core/src/botcore/bot_objects/command.rs
index 001ac31..54c4b85 100644
--- a/src/botcore/bot_objects/command.rs
+++ b/forcebot_core/src/botcore/bot_objects/command.rs
@@ -2,7 +2,9 @@ use std::sync::Arc;
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
+// use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
+use super::{execution_async,Bot,Badge,command_condition_async};
+    
 
 use super::{CommandTrigger, ExecBody};
 
@@ -80,7 +82,7 @@ impl Command
     /// /* 2. define an async function */
     /// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
     /// 
-    /// /* 3. Set and Store the execution body using `async_box()`  */
+    /// /* 3. Set and Store the execution body using `execution_async()`  */
     /// cmd.set_custom_cond_async(condition_async(condition01));
     /// ```
     /// 
diff --git a/src/botcore/bot_objects/listener.rs b/forcebot_core/src/botcore/bot_objects/listener.rs
similarity index 63%
rename from src/botcore/bot_objects/listener.rs
rename to forcebot_core/src/botcore/bot_objects/listener.rs
index af96cf7..84bd468 100644
--- a/src/botcore/bot_objects/listener.rs
+++ b/forcebot_core/src/botcore/bot_objects/listener.rs
@@ -2,13 +2,22 @@ use std::sync::Arc;
 
 use twitch_irc::message::ServerMessage;
 
-use crate::{execution_async, listener_condition_async, Bot};
-
+use super::{execution_async,Bot,listener_condition_async};
+    
 use super::{ExecBody, ListenerTrigger};
 
-/// Bot `Listener`` that stores trigger condition callback and a execution functon
+/// Bot `Listener` that stores trigger condition callback and a execution functon
 /// 
-/// Use `asyncfn_box()` on custom async execution bodies
+/// Use `Listener` functions to define the Trigger Condition & Execution callbacks. 
+/// When the Trigger callback is `true`, the Execution callback runs in the bot loop 
+/// 
+/// Create a new empty `Listener` with `new()`
+/// 
+/// Use the following on the empty listener before loading it into the bot to set the callbacks
+/// 
+/// - `set_trigger_cond_fn()` - to define the Trigger condition callback 
+/// 
+/// - `set_exec_fn()` - to define the Execution Callback
 #[derive(Clone)]
 pub struct Listener
 {
@@ -23,14 +32,22 @@ pub struct Listener
 impl Listener
 {
 
-    /// Creates a new empty `Listener` . 
-    /// Call `set_trigger_cond_fn()` and `set_exec_fn()` to trigger & execution function callbacks
+    /// Creates a new empty `Listener` 
+    /// 
+    /// Use `Listener` functions to define the Trigger Condition & Execution callbacks. 
+    /// When the Trigger callback is `true`, the Execution callback runs in the bot loop 
+    /// 
+    /// Use the following on the empty listener before loading it into the bot to set the callbacks
+    /// 
+    /// - `set_trigger_cond_fn()` - to define the Trigger condition callback 
+    /// 
+    /// - `set_exec_fn()` - to define the Execution Callback
     pub fn new() -> Listener {
 
         async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
         async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
         Listener {
-            trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| false,
+            trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| true,
             trigger_cond_async : Arc::new(listener_condition_async(condition01)),
             exec_fn : Arc::new(execution_async(execbody)),
         }
@@ -45,7 +62,7 @@ impl Listener
     /// 
     /// Same as `set_trigger_cond_fn()` , but async define
     /// 
-    /// Use`asyncfn_box()` on the async fn when storing
+    /// Use`condition_async()` on the async fn when storing
     /// 
     /// Example - 
     /// ```rust
@@ -55,7 +72,7 @@ impl Listener
     /// /* 2. define an async function */
     /// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
     /// 
-    /// /* 3. Set and Store the execution body using `async_box()`  */
+    /// /* 3. Set and Store the execution body using `execution_async()`  */
     /// listener.set_trigger_cond_async(condition_async(condition01));
     /// ```
     /// 
@@ -66,7 +83,7 @@ impl Listener
 
     /// sets the execution body of the listener for when it triggers
     /// 
-    /// Use`asyncfn_box()` on the async fn when storing
+    /// Use`execution_async()` on the async fn when storing
     /// 
     /// Example - 
     /// ```rust
@@ -76,8 +93,8 @@ impl Listener
     /// /* 2. define an async function */
     /// async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
     /// 
-    /// /* 3. Set and Store the execution body using `async_box()`  */
-    /// listener.set_exec_fn(asyncfn_box(execbody));
+    /// /* 3. Set and Store the execution body using `execution_async()`  */
+    /// listener.set_exec_fn(execution_async(execbody));
     /// ```
     /// 
     pub fn set_exec_fn(&mut self,exec_fn:ExecBody ) {
diff --git a/src/botcore/modules.rs b/forcebot_core/src/botcore/modules.rs
similarity index 91%
rename from src/botcore/modules.rs
rename to forcebot_core/src/botcore/modules.rs
index 2ed44dc..9a30e41 100644
--- a/src/botcore/modules.rs
+++ b/forcebot_core/src/botcore/modules.rs
@@ -1,6 +1,7 @@
 
+use super::bot_objects::command::Command;
+use super::bot_objects::listener::Listener;
 
-use crate::{Command, Listener};
 
 #[derive(PartialEq, Eq,Debug,Clone)]
 pub enum Status {
@@ -33,7 +34,7 @@ impl Module
             bot_read_description,
             listeners: vec![],
             commands: vec![],
-            default_status_per_channel: Status::Disabled,
+            default_status_per_channel: Status::Enabled,
         }
     }
 
diff --git a/src/custom_mods.rs b/forcebot_core/src/custom_mods.rs
similarity index 100%
rename from src/custom_mods.rs
rename to forcebot_core/src/custom_mods.rs
diff --git a/src/custom_mods/guest_badge.rs b/forcebot_core/src/custom_mods/guest_badge.rs
similarity index 100%
rename from src/custom_mods/guest_badge.rs
rename to forcebot_core/src/custom_mods/guest_badge.rs
diff --git a/src/custom_mods/pyramid.rs b/forcebot_core/src/custom_mods/pyramid.rs
similarity index 73%
rename from src/custom_mods/pyramid.rs
rename to forcebot_core/src/custom_mods/pyramid.rs
index fcc9da2..9b00db2 100644
--- a/src/custom_mods/pyramid.rs
+++ b/forcebot_core/src/custom_mods/pyramid.rs
@@ -4,7 +4,14 @@ use std::sync::{Arc, Mutex};
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-use crate::{listener_condition_async, Badge, Bot, Command, Listener, Module};
+// use crate::{execution_async, listener_condition_async, Badge, Bot, Command, Listener, Module};
+use super::super::botcore::bot_objects::execution_async;
+use super::super::botcore::bot_objects::listener_condition_async;
+use super::super::botcore::bot_objects::listener::Listener;
+use super::super::botcore::modules::Module;
+use super::super::botcore::bot::Bot;
+use super::super::botcore::bot_objects::command::Command;
+use super::super::botcore::bot_objects::Badge;
 
 /// pyramid module
 /// 
@@ -58,15 +65,15 @@ fn create_pyramid_detector() -> Listener {
         /* 4. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
+                // if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
                     let _ = bot.client.say_in_reply_to(&msg, "Clap".to_string()).await ;
                     return Result::Ok("Success".to_string()) ;
-                }
+                // }
             }
             Result::Err("Not Valid message type".to_string()) 
         }
     
-        /* 5. Set and Store the execution body using `async_box()`  */
+        /* 5. Set and Store the execution body using `execution_async()`  */
         listener.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
 
         listener
@@ -78,16 +85,17 @@ async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
 
     let msgtext = msg.message_text.replace("\u{e0000}","").trim().to_string();
     let msgchannel = msg.channel_login;
+    let msgchatter = msg.sender.login;
 
     // 1. Check if Pyramid started in chat > and recognize pyramid started
     if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
         set_pyramid_started(msgchannel.clone(),true);
-        push_to_compare(msgchannel.clone(),get_start_pattern(msgchannel.clone()));
+        push_to_compare(msgchannel.clone(),msgchatter.clone(),get_start_pattern(msgchannel.clone()));
                 
     }
 
     if is_pyramid_started(msgchannel.clone()) {
-        push_to_compare(msgchannel.clone(),msgtext.clone());
+        push_to_compare(msgchannel.clone(),msgchatter.clone(),msgtext.clone());
     }
 
     // 2a. If Pyramid Not Started, Assume message is a potential start pattern
@@ -111,8 +119,8 @@ async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
 
 
 lazy_static!{
-    /// Message Compare stack per channel (channel:String,msgstack:Vec<String>)
-    pub static ref COMPARE_MSG_STACK_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<String>>)>>  = Mutex::new(vec![]);
+    /// Message Compare stack per channel (channel:String,msgstack:Vec<(chatter:String,message:String)>)
+    pub static ref COMPARE_MSG_STACK_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<(String,String)>>)>>  = Mutex::new(vec![]);
     #[derive(Debug)]
     /// Pyramid Started per channel (channel:String,started:bool)
     pub static ref PYRAMID_STARTED_PER_CHNL:  Mutex<Vec<(String,Mutex<bool>)>> = Mutex::new(vec![]);
@@ -126,7 +134,7 @@ lazy_static!{
 
 }
 
-fn read_top_of_compare(channel:String) -> Option<String> {
+fn read_top_of_compare(channel:String) -> Option<(String,String)> {
 
     let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
 
@@ -142,7 +150,7 @@ fn read_top_of_compare(channel:String) -> Option<String> {
 
 }
 
-fn pop_top_of_compare(channel:String) -> Option<String> {
+fn pop_top_of_compare(channel:String) -> Option<(String,String)> {
     let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
 
     for rec in comp_perchnl.iter() {
@@ -218,7 +226,7 @@ fn get_start_pattern(channel:String) -> String {
 
 
 /// pushes message to compare stack
-fn push_to_compare(channel:String,message:String) {
+fn push_to_compare(channel:String,chatter:String,message:String) {
     let mut comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
 
     let mut found = false;
@@ -226,12 +234,12 @@ fn push_to_compare(channel:String,message:String) {
         if rec.0 == channel {
             found = true;
             let mut msg_stack = rec.1.lock().unwrap();
-            msg_stack.push(message.clone());
+            msg_stack.push((chatter.clone(),message.clone()));
             // dbg!("Push message to cmp stack ; result last cmp_pchnl - ",comp_perchnl.last());
         }
     }
     if !found {
-        comp_perchnl.push((channel,Mutex::new(vec![message])));
+        comp_perchnl.push((channel,Mutex::new(vec![(chatter,message)])));
     }
 
 }
@@ -247,21 +255,21 @@ fn check_start_pyramid(channel:String,msgtext: String) -> bool {
 fn symmetry_ok(channel:String) -> bool {
     let mut temp_stack = TEMP_MSG_STACK.lock().unwrap();
     let mut checking_started = false;
-    if !(read_top_of_compare(channel.clone()).unwrap_or("".to_string()) == get_start_pattern(channel.clone())) {
+    if !(read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone())) {
         return false;
     }
     loop {
         
-        if !checking_started && read_top_of_compare(channel.clone()).unwrap_or("".to_string()) == get_start_pattern(channel.clone()) {  
+        if !checking_started && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone()) {  
             checking_started = true;
         } 
-        if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or("".to_string()).len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
-            temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or("".to_string()));
+        if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
+            temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1);
             
-        } else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or("".to_string()).len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
+        } else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
     
             temp_stack.pop();
-            if temp_stack.last().unwrap_or(&"".to_string()).clone() == read_top_of_compare(channel.clone()).unwrap_or("".to_string()) {
+            if temp_stack.last().unwrap_or(&"".to_string()).clone() == read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 {
                 temp_stack.pop();
                 
                 continue;
@@ -272,7 +280,7 @@ fn symmetry_ok(channel:String) -> bool {
             }
 
         } else { /* dbg!("failed catchall"); */ return false; }
-        if checking_started && read_top_of_compare(channel.clone()).unwrap() == get_start_pattern(channel.clone()) {  
+        if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {  
             
             temp_stack.clear();
             return true;
@@ -284,7 +292,9 @@ fn symmetry_ok(channel:String) -> bool {
 }
 
 
-/// pyramid interruptor #todo
+/// #todo
+/// 
+/// pyramid interruptor 
 /// 
 /// pick chatters that will be interrupted if they solo build
 /// 
@@ -311,7 +321,7 @@ fn create_interruptor_cmd() -> Command {
             Result::Err("Not Valid message type".to_string()) 
         }
 
-        /* 3. Set and Store the execution body using `async_box()`  */
+        /* 3. Set and Store the execution body using `execution_async()`  */
         cmd.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
 
         /* 4. optionally, remove admin only default flag */
@@ -324,5 +334,47 @@ fn create_interruptor_cmd() -> Command {
     cmd
 }
 
+/// #todo
+fn create_interruptor_module(channel:String) -> Module  {
+    /* 1. Create a new module */
+    let modname = format!("interruptor {}",channel);
+    let mut custom_mod = Module::new(
+        vec![modname], 
+        "".to_string());
 
+    /* 2. Load the cmd into a new module */
+    custom_mod.load_listener(create_interruptor_listener());
 
+    custom_mod
+
+}
+
+/// #todo
+fn create_interruptor_listener() -> Listener  {
+    /* 2a. Create a new blank Listener */
+    let mut listener = Listener::new();
+
+    /* 2b. Set a trigger condition function for listener */
+    listener.set_trigger_cond_fn(
+        |_:Arc<Bot>,_:ServerMessage| true
+    );
+
+    /* 2c. Define an async fn callback execution */
+    async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        dbg!(message);
+        Result::Ok("Success".to_string()) 
+    }
+
+    /* 2d. Set and Store the execution body using `execution_async()`  */
+    listener.set_exec_fn(execution_async(execbody));
+
+    listener
+    
+}
+
+/// #todo
+/// 
+/// Returns Some(chatter) if the pyramid in progress is being built by a solo
+fn solo_building(channel:String) -> Option<String> {
+    None
+}
diff --git a/src/lib.rs b/forcebot_core/src/lib.rs
similarity index 85%
rename from src/lib.rs
rename to forcebot_core/src/lib.rs
index 817caac..8ccc272 100644
--- a/src/lib.rs
+++ b/forcebot_core/src/lib.rs
@@ -1,4 +1,4 @@
-//! `forcebot-rs-v2` : Twitch chat bot written in rust.
+//! `forcebot_core` library for `forcebot-rs-v2` Twitch chat bot
 //! 
 //! Customize by adding additional bot objects
 //! 
@@ -7,7 +7,7 @@
 //! Uses Env defined variables to create and run the bot
 //! 
 //! ```rust
-//! use forcebot_rs_v2::Bot;
+//! use forcebot_core::Bot;
 //! 
 //! #[tokio::main]
 //! pub async fn main() {
@@ -27,11 +27,10 @@
 //! 
 //! A `Module` is a group of bot objects (eg `Command`) that elevated users can manage through built in `disable` and `enable` commands
 //! 
-//! 
 //! Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
 //!    
 //! ```rust
-//! use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
+//! use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
 //!    
 //! #[tokio::main]
 //! pub async fn main() {
@@ -60,7 +59,7 @@
 //! 
 //! 
 //! ```rust
-//! use forcebot_rs_v2::Bot;
+//! use forcebot_core::Bot;
 //!    
 //!    #[tokio::main]
 //!    pub async fn main() {
@@ -79,7 +78,7 @@
 //!    pub mod custom_mod {
 //!        use std::sync::Arc;
 //!    
-//!        use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
+//!        use forcebot_core::{execution_async, Badge, Bot, Command, Module};
 //!        use twitch_irc::message::ServerMessage;
 //!    
 //!    
@@ -129,7 +128,7 @@
 //! ```rust
 //! use std::sync::Arc;
 //! 
-//! use forcebot_rs_v2::{execution_async, Bot, Listener};
+//! use forcebot_core::{execution_async, Bot, Listener};
 //! use twitch_irc::message::ServerMessage;
 //! 
 //! #[tokio::main]
@@ -152,7 +151,7 @@
 //!         Result::Ok("Success".to_string()) 
 //!     }
 //! 
-//!     /* 2d. Set and Store the execution body using `async_box()`  */
+//!     /* 2d. Set and Store the execution body using `execution_async()`  */
 //!     listener.set_exec_fn(execution_async(execbody));
 //! 
 //!     /* 3. Load the listener into the bot */
@@ -171,9 +170,9 @@
 //! 
 //! use std::sync::Arc;
 //! 
-//! use forcebot_rs_v2::Bot;
-//! use forcebot_rs_v2::execution_async;
-//! use forcebot_rs_v2::Listener;
+//! use forcebot_core::Bot;
+//! use forcebot_core::execution_async;
+//! use forcebot_core::Listener;
 //! use twitch_irc::message::ServerMessage;
 //! 
 //! 
@@ -211,7 +210,7 @@
 //!         Result::Err("Not Valid message type".to_string()) 
 //!     }
 //! 
-//!     /* 4. Set and Store the execution body using `async_box()`  */
+//!     /* 4. Set and Store the execution body using `execution_async()`  */
 //!     listener.set_exec_fn(execution_async(execbody));
 //! 
 //!     /* 5. Load the listener into the bot */
@@ -226,12 +225,13 @@
 
 pub mod botcore;
 pub mod custom_mods;
-pub use crate::botcore::bot::Bot;
-pub use crate::botcore::bot_objects::execution_async;
-pub use crate::botcore::bot_objects::command_condition_async;
-pub use crate::botcore::bot_objects::listener_condition_async;
-pub use crate::botcore::bot_objects::listener::Listener;
-pub use crate::botcore::bot_objects::command::Command;
-pub use crate::botcore::modules::Module;
-pub use crate::botcore::bot_objects::Badge;
-pub use crate::botcore::modules;
+pub use botcore::bot::Bot;
+pub use botcore::bot_objects::execution_async;
+pub use botcore::bot_objects::command_condition_async;
+pub use botcore::bot_objects::listener_condition_async;
+pub use botcore::bot_objects::listener::Listener;
+// pub use crate::botcore::bot_objects::command::Command;
+pub use botcore::bot_objects::command::Command;
+pub use botcore::modules::Module;
+pub use botcore::bot_objects::Badge;
+pub use botcore::modules;
diff --git a/moderator_reactor/Cargo.toml b/moderator_reactor/Cargo.toml
new file mode 100644
index 0000000..f3431e6
--- /dev/null
+++ b/moderator_reactor/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "moderator_reactor"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+forcebot_core = {path = "../forcebot_core"}
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
diff --git a/src/bin/moderator_reactor.rs b/moderator_reactor/src/main.rs
similarity index 91%
rename from src/bin/moderator_reactor.rs
rename to moderator_reactor/src/main.rs
index 279d204..58c0c9f 100644
--- a/src/bin/moderator_reactor.rs
+++ b/moderator_reactor/src/main.rs
@@ -13,9 +13,9 @@
 
 use std::sync::Arc;
 
-use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::execution_async;
-use forcebot_rs_v2::Listener;
+use forcebot_core::Bot;
+use forcebot_core::execution_async;
+use forcebot_core::Listener;
 use twitch_irc::message::ServerMessage;
 
 
@@ -53,7 +53,7 @@ pub async fn main() {
         Result::Err("Not Valid message type".to_string()) 
     }
 
-    /* 4. Set and Store the execution body using `async_box()`  */
+    /* 4. Set and Store the execution body using `execution_async()`  */
     listener.set_exec_fn(execution_async(execbody));
 
     /* 5. Load the listener into the bot */
diff --git a/new_empty_bot/Cargo.toml b/new_empty_bot/Cargo.toml
new file mode 100644
index 0000000..96d5d3f
--- /dev/null
+++ b/new_empty_bot/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "new_empty_bot"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+forcebot_core = {path = "../forcebot_core"}
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
diff --git a/src/main.rs b/new_empty_bot/src/main.rs
similarity index 93%
rename from src/main.rs
rename to new_empty_bot/src/main.rs
index ebbe171..4abdbc7 100644
--- a/src/main.rs
+++ b/new_empty_bot/src/main.rs
@@ -10,8 +10,7 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_rs_v2::botcore::bot::Bot;
-
+use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
diff --git a/readme.md b/readme.md
index 6af83e2..d94791a 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-Twitch chat bot written in rust
+Customizable Twitch chat bot written in rust
 
 # Quick Start 
 
@@ -22,28 +22,47 @@ bot_admins=ADMIN
 3. Build & run
 
 ```
-cargo run
+cargo run -p forcebot_core 
 ```
 
+# Features
+
+- Quick Start to use full feature set bot
+- Moderators & Broadcasters can `disable` or `enable` `Modules` of bot functionality through chat `Commands`
+- Full Feature Set `forcebot_core` bot has the following modules loaded
+    - `guest_badge` - Temporary badges can be issued to chatters
+    - `besty` - Tomfoolery
+    - `pyramid` - for detecting & handling pyramids
+- `forcebot_core` library API provides Custom package developers a way to add functionality by adding `Modules` that contain Bot Objects like `Commands` and `Listeners`
+- `Listeners` and `Commands` listen for a defined callback trigger condition and run an defined execution callback 
+- `Commands` are similar to `Listeners` with refined trigger conditions including using bot `prefix` with the `Command` , triggers based on `Badge` , and more
+- Workspace for package developers to independently code their own `Modules`
+- Workspace comes with binary crates with working or example bots that use `forcebot_core` library 
+    - `moderator_reactor` - bot kneels to all moderator messages
+    - `simple_module_example` - bot has a `test` `Module` with a `test` `Command` .Moderators & Broadcasters can manage the `Module` in chat with `enable` / `disable` `Commands`
+    - `new_empty_bot` - while empty, has `disable` and `enable` chat `Commands` . This is an example of the bot without any loaded modules
+    - `simple_command_bot` - bot responds to a `test` `Command`. As the command was not loaded through a `Module`, `disable` & `enable` commands don't work on the `test` command. This could be a Global `Command`  
+    - `simple_debug_listener` - bot outputs all twitch `ServerMessages` received to terminal
+    
+
+
 # Example Bots
 
-Use the following commands to build and run built-in bots. No coding required!
+Use the following to build and run built-in bots. No coding required!
 
-## New Bot
+## New Empty Bot
 Run an empty simple bot that logs into chat and has minimum built in functions 
 
 ```
-cargo run --bin new_bot
+cargo run -p new_empty_bot
 ```
 
-## WIP Customized Fun Bot
+## Full Featured Forcebot
 
 Run a forcebot with fun catered customizations
 
-*ongoing work in progress*
-
 ```
-cargo run --bin fun_bot
+cargo run -p forcebot_core 
 ```
 
 
@@ -51,28 +70,69 @@ cargo run --bin fun_bot
 Run a bot that listens to all messages and output to console
 
 ```
-cargo run --bin simple_debug_listener
+cargo run -p simple_debug_listener
 ```
 
 ## Simple Command Bot
 Run a bot that uses the `test` chat `Command` . `Commands` are prefixed and must be ran by a chatter with a `vip` badge or above 
 
 ```
-cargo run --bin simple_command_bot
+cargo run -p simple_command_bot
 ```
 
 ## Moderator Reactor
 Run a bot that listens for messages with the `moderator` badge, and replies to that mod with an emote
 
 ```
-cargo run --bin moderator_reactor
+cargo run -p moderator_reactor
 ```
 
 ## Module loaded Bot
 Run a bot that has a `test` chat `Command`. As the command was loaded through a module, moderators or broadcastors can `enable` or `disable` the module through chat commands
 
 ```
-cargo run --bin simple_module
+cargo run -p simple_module_example
+```
+
+# Workspace packages 
+
+Source is a workspace of packages . In particular, `forcebot_core` is the main library crate to use
+
+*TIP* : if you want to start customizing you own bot, create a binary package in the workspace for your bot's binary crate
+
+More info about workspaces - https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
+
+
+## Creating a new package
+
+To create a new package 
+
+1. Create a new package
+
+For example, to create a new binary crate in the workspace
+
+```
+cargo new my_new_bot
+```
+
+2. In the newly created directory for your package, adjust the `Cargo.toml` to the following 
+
+```
+[dependencies]
+forcebot_core = {path = "../forcebot_core"}
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
+```
+
+3. Copy `main.rs` from the `new_empty_bot` package into your package
+
+4. Optionally, customize your `main()` to load modules before starting the bot
+
+5. Build and run your package
+```
+cargo run -p my_new_bot
 ```
 
 
@@ -83,7 +143,7 @@ cargo run --bin simple_module
 Uses Env defined variables to create and run the bot
 
 ```rust
-use forcebot_rs_v2::Bot;
+use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
@@ -95,7 +155,6 @@ pub async fn main() {
     bot.run().await;
 
 }
-
 ```
 
 ## Customize by Loading Custom Modules 
@@ -105,7 +164,8 @@ A `Module` is a group of bot objects (eg `Command`) that elevated users can mana
 Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
 
 ```rust
-use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
+use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
+
 
 #[tokio::main]
 pub async fn main() {
@@ -134,7 +194,7 @@ Create a custom `Module` by :
 
 
 ```rust
-use forcebot_rs_v2::Bot;
+use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
@@ -153,7 +213,7 @@ pub async fn main() {
 pub mod custom_mod {
     use std::sync::Arc;
 
-    use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
+    use forcebot_core::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
 
@@ -193,7 +253,6 @@ pub mod custom_mod {
         cmd
     }
 }
-
 ```
 
 ## Simple Debug Listener
@@ -202,7 +261,7 @@ Bot with a simple listener that listens for all messages and prints in output
 ```rust
 use std::sync::Arc;
 
-use forcebot_rs_v2::{execution_async, Bot, Listener};
+use forcebot_core::{execution_async, Bot, Listener};
 use twitch_irc::message::ServerMessage;
 
 #[tokio::main]
@@ -225,7 +284,7 @@ pub async fn main() {
         Result::Ok("Success".to_string()) 
     }
 
-    /* 2d. Set and Store the execution body using `async_box()`  */
+    /* 2d. Set and Store the execution body using `execution_async()`  */
     listener.set_exec_fn(execution_async(execbody));
 
     /* 3. Load the listener into the bot */
@@ -235,7 +294,6 @@ pub async fn main() {
     bot.run().await;
 
 }
-
 ```
 
 ## Moderator Reactor
@@ -243,12 +301,11 @@ pub async fn main() {
 Example listener listens for a moderator badge and reply in chat 
 
 ```rust
-
 use std::sync::Arc;
 
-use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::execution_async;
-use forcebot_rs_v2::Listener;
+use forcebot_core::Bot;
+use forcebot_core::execution_async;
+use forcebot_core::Listener;
 use twitch_irc::message::ServerMessage;
 
 
@@ -286,7 +343,7 @@ pub async fn main() {
         Result::Err("Not Valid message type".to_string()) 
     }
 
-    /* 4. Set and Store the execution body using `async_box()`  */
+    /* 4. Set and Store the execution body using `execution_async()`  */
     listener.set_exec_fn(execution_async(execbody));
 
     /* 5. Load the listener into the bot */
@@ -296,9 +353,6 @@ pub async fn main() {
     bot.run().await;
 
 }
-
-
-
 ```
 
 ## Simple Test Command
@@ -306,13 +360,12 @@ pub async fn main() {
 ```rust
 use std::sync::Arc;
 
-use forcebot_rs_v2::Badge;
-use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::execution_async;
-use forcebot_rs_v2::Command;
+use forcebot_core::Badge;
+use forcebot_core::Bot;
+use forcebot_core::execution_async;
+use forcebot_core::Command;
 use twitch_irc::message::ServerMessage;
 
-
 #[tokio::main]
 pub async fn main() {
 
@@ -331,7 +384,7 @@ pub async fn main() {
         Result::Err("Not Valid message type".to_string()) 
     }
 
-    /* 3. Set and Store the execution body using `async_box()`  */
+    /* 3. Set and Store the execution body using `execution_async()`  */
     cmd.set_exec_fn(execution_async(execbody));
 
     /* 4. optionally, remove admin only default flag */
@@ -347,11 +400,9 @@ pub async fn main() {
     bot.run().await;
 
 }
-
-
 ```
 
-# Crate Rust Documentation 
+# Crate Rust API Documentation 
 
 Create `forcebot_rs_v2` Rust Crate documentation
 
diff --git a/simple_command_bot/Cargo.toml b/simple_command_bot/Cargo.toml
new file mode 100644
index 0000000..25f2f0f
--- /dev/null
+++ b/simple_command_bot/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "simple_command_bot"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+forcebot_core = {path = "../forcebot_core"}
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
diff --git a/src/bin/simple_command_bot.rs b/simple_command_bot/src/main.rs
similarity index 88%
rename from src/bin/simple_command_bot.rs
rename to simple_command_bot/src/main.rs
index 9c2f2ff..2f29e09 100644
--- a/src/bin/simple_command_bot.rs
+++ b/simple_command_bot/src/main.rs
@@ -15,10 +15,10 @@
 
 use std::sync::Arc;
 
-use forcebot_rs_v2::Badge;
-use forcebot_rs_v2::Bot;
-use forcebot_rs_v2::execution_async;
-use forcebot_rs_v2::Command;
+use forcebot_core::Badge;
+use forcebot_core::Bot;
+use forcebot_core::execution_async;
+use forcebot_core::Command;
 use twitch_irc::message::ServerMessage;
 
 
@@ -40,7 +40,7 @@ pub async fn main() {
         Result::Err("Not Valid message type".to_string()) 
     }
 
-    /* 3. Set and Store the execution body using `async_box()`  */
+    /* 3. Set and Store the execution body using `execution_async()`  */
     cmd.set_exec_fn(execution_async(execbody));
 
     /* 4. optionally, remove admin only default flag */
diff --git a/simple_debug_listener/Cargo.toml b/simple_debug_listener/Cargo.toml
new file mode 100644
index 0000000..fceb717
--- /dev/null
+++ b/simple_debug_listener/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "simple_debug_listener"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+forcebot_core = {path = "../forcebot_core"}
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
diff --git a/src/bin/simple_debug_listener.rs b/simple_debug_listener/src/main.rs
similarity index 90%
rename from src/bin/simple_debug_listener.rs
rename to simple_debug_listener/src/main.rs
index 791c5d0..3ce82fc 100644
--- a/src/bin/simple_debug_listener.rs
+++ b/simple_debug_listener/src/main.rs
@@ -12,7 +12,7 @@
 
 use std::sync::Arc;
 
-use forcebot_rs_v2::{execution_async, Bot, Listener};
+use forcebot_core::{execution_async, Bot, Listener};
 use twitch_irc::message::ServerMessage;
 
 #[tokio::main]
@@ -35,7 +35,7 @@ pub async fn main() {
         Result::Ok("Success".to_string()) 
     }
 
-    /* 2d. Set and Store the execution body using `async_box()`  */
+    /* 2d. Set and Store the execution body using `execution_async()`  */
     listener.set_exec_fn(execution_async(execbody));
 
     /* 3. Load the listener into the bot */
diff --git a/simple_module_example/Cargo.toml b/simple_module_example/Cargo.toml
new file mode 100644
index 0000000..b00ba0b
--- /dev/null
+++ b/simple_module_example/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "simple_module_example"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+forcebot_core = {path = "../forcebot_core"}
+dotenv = "0.15.0"
+lazy_static = "1.5.0"
+tokio = { version = "1.33.0", features = ["full"] }
+twitch-irc = "5.0.1"
diff --git a/simple_module_example/src/main.rs b/simple_module_example/src/main.rs
new file mode 100644
index 0000000..4793ed4
--- /dev/null
+++ b/simple_module_example/src/main.rs
@@ -0,0 +1,79 @@
+//! Simple Module with a Command
+//! 
+//! Adding objects through packages provides controls , 
+//! such as moderators, and brodcasters can disable or enable mods
+//! 
+//! Here, moderators or above can enable or disable the `test` 
+//! module with the command `<prefix> disable test`
+//! 
+//! Be sure the followig is defined in `.env` 
+//! - login_name
+//! - access_token
+//! - bot_channels
+//! - prefix
+//! - bot_admins
+//! 
+//! Bot access tokens be generated here - 
+//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
+//! - More Info - <https://dev.twitch.tv/docs/authentication>
+
+use forcebot_core::Bot;
+
+#[tokio::main]
+pub async fn main() {
+
+    /* Create the bot using env */
+    let mut bot = Bot::new();
+
+    /* load the Module */
+    bot.load_module(custom_mod::new()).await;
+
+    /* Run the bot */
+    bot.run().await;
+
+}
+
+
+pub mod custom_mod {
+    use std::sync::Arc;
+
+    use forcebot_core::{execution_async, Badge, Bot, Command, Module};
+    use twitch_irc::message::ServerMessage;
+
+
+    /// Module definition with a loaded command
+    pub fn new() -> Module {
+        /* 1. Create a new module */
+        let mut custom_mod = Module::new(
+            vec!["test".to_string()], 
+            "".to_string());
+
+        /* 2. Load the cmd into a new module */
+        custom_mod.load_command(cmd_test());
+
+        custom_mod
+
+    }
+
+    /// Command definition 
+    pub fn cmd_test() -> Command {
+        /* 1. Create a new cmd */
+        let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
+
+        /* 2. Define exec callback  */
+        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+            if let ServerMessage::Privmsg(msg) = message {
+                let _= bot.client.say_in_reply_to(
+                    &msg, "test return".to_string()).await;
+            }
+            Result::Err("Not Valid message type".to_string()) 
+        }
+
+        /* 3. Set Command flags */
+        cmd.set_exec_fn(execution_async(execbody));
+        cmd.set_admin_only(false);
+        cmd.set_min_badge(Badge::Vip);
+
+        cmd
+    }
+}
\ No newline at end of file
-- 
2.49.0


From 5a02b090c6456ad2e0e946c5b0ccf06be1db3f6f Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Sun, 2 Feb 2025 17:06:42 -0500
Subject: [PATCH 10/14] fix - pyramid is recognized finished as expected

---
 forcebot_core/src/custom_mods/pyramid.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/forcebot_core/src/custom_mods/pyramid.rs b/forcebot_core/src/custom_mods/pyramid.rs
index 9b00db2..3a3de01 100644
--- a/forcebot_core/src/custom_mods/pyramid.rs
+++ b/forcebot_core/src/custom_mods/pyramid.rs
@@ -66,7 +66,11 @@ fn create_pyramid_detector() -> Listener {
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
                 // if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
+
                     let _ = bot.client.say_in_reply_to(&msg, "Clap".to_string()).await ;
+
+                    set_pyramid_started(msg.channel_login,false);
+
                     return Result::Ok("Success".to_string()) ;
                 // }
             }
-- 
2.49.0


From 1e522940c91ae3d10ce220abe7f2de6c397441e4 Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Mon, 3 Feb 2025 21:33:44 -0500
Subject: [PATCH 11/14] debug module and fix issue with modules

---
 forcebot_core/src/bin/fun_bot.rs              |   5 +-
 forcebot_core/src/bin/new_bot.rs              |   2 +-
 .../src/bin/simple_debug_listener.rs          |  47 +++++
 forcebot_core/src/bin/simple_module.rs        |   2 +-
 forcebot_core/src/botcore/bot.rs              | 199 ++++++++++++------
 forcebot_core/src/botcore/bot_objects.rs      |   2 +-
 forcebot_core/src/botcore/modules.rs          |   3 +-
 forcebot_core/src/custom_mods.rs              |   3 +-
 forcebot_core/src/custom_mods/debug.rs        | 146 +++++++++++++
 forcebot_core/src/custom_mods/guest_badge.rs  |   2 +-
 forcebot_core/src/custom_mods/pyramid.rs      |  17 +-
 forcebot_core/src/lib.rs                      |   3 +-
 moderator_reactor/src/main.rs                 |   5 +-
 new_empty_bot/src/main.rs                     |   2 +-
 readme.md                                     |   5 +-
 simple_command_bot/src/main.rs                |   2 +-
 simple_debug_listener/src/main.rs             |   6 +-
 simple_module_example/src/main.rs             |   2 +-
 18 files changed, 360 insertions(+), 93 deletions(-)
 create mode 100644 forcebot_core/src/bin/simple_debug_listener.rs
 create mode 100644 forcebot_core/src/custom_mods/debug.rs

diff --git a/forcebot_core/src/bin/fun_bot.rs b/forcebot_core/src/bin/fun_bot.rs
index d9d8d45..872aff9 100644
--- a/forcebot_core/src/bin/fun_bot.rs
+++ b/forcebot_core/src/bin/fun_bot.rs
@@ -18,7 +18,7 @@
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
 // use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
-use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
+use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
 
 
 
@@ -26,7 +26,7 @@ use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 1. Load the module into the bot */
     bot.load_module(funbot_objs::create_module()).await;
@@ -34,6 +34,7 @@ pub async fn main() {
     /* 2. Load Custom Modules */
     bot.load_module(guest_badge::create_module()).await;
     bot.load_module(pyramid::create_module()).await;
+    bot.load_module(debug::create_module()).await;
     
     /* 3. Run the bot */
     bot.run().await;
diff --git a/forcebot_core/src/bin/new_bot.rs b/forcebot_core/src/bin/new_bot.rs
index 4abdbc7..a81462e 100644
--- a/forcebot_core/src/bin/new_bot.rs
+++ b/forcebot_core/src/bin/new_bot.rs
@@ -16,7 +16,7 @@ use forcebot_core::Bot;
 pub async fn main() {
 
     /* 1. Create the bot using env */
-    let bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 2. Run the bot */
     bot.run().await;
diff --git a/forcebot_core/src/bin/simple_debug_listener.rs b/forcebot_core/src/bin/simple_debug_listener.rs
new file mode 100644
index 0000000..bac75ed
--- /dev/null
+++ b/forcebot_core/src/bin/simple_debug_listener.rs
@@ -0,0 +1,47 @@
+//! Example simple Binary crate that creates & runs bot based on `.env`
+//! Be sure the followig is defined in `.env` 
+//! - login_name
+//! - access_token
+//! - bot_channels
+//! - prefix
+//! - bot_admins
+//! 
+//! Bot access tokens be generated here - 
+//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
+//! - More Info - <https://dev.twitch.tv/docs/authentication>
+
+use std::sync::Arc;
+
+use forcebot_core::{execution_async, Bot, Listener};
+use twitch_irc::message::ServerMessage;
+
+#[tokio::main]
+pub async fn main() {
+
+    /* 1. Create the bot using env */
+    let bot = Bot::new().await;
+
+    /* 2a. Create a new blank Listener */
+    let mut listener = Listener::new();
+
+    /* 2b. Set a trigger condition function for listener */
+    listener.set_trigger_cond_fn(
+        |_:Arc<Bot>,_:ServerMessage| true
+    );
+
+    /* 2c. Define an async fn callback execution */
+    async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        dbg!(message); /* outputs message to debug */
+        Result::Ok("Success".to_string()) 
+    }
+
+    /* 2d. Set and Store the execution body using `async_box()`  */
+    listener.set_exec_fn(execution_async(execbody));
+
+    /* 3. Load the listener into the bot */
+    bot.load_listener(listener).await;
+
+    /* 4. Run the bot */
+    bot.run().await;
+
+}
diff --git a/forcebot_core/src/bin/simple_module.rs b/forcebot_core/src/bin/simple_module.rs
index 02756d7..45bf06d 100644
--- a/forcebot_core/src/bin/simple_module.rs
+++ b/forcebot_core/src/bin/simple_module.rs
@@ -23,7 +23,7 @@ use forcebot_core::Bot;
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* load the Module */
     bot.load_module(custom_mod::new()).await;
diff --git a/forcebot_core/src/botcore/bot.rs b/forcebot_core/src/botcore/bot.rs
index 96a66b6..9e3070f 100644
--- a/forcebot_core/src/botcore/bot.rs
+++ b/forcebot_core/src/botcore/bot.rs
@@ -3,7 +3,7 @@
 use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
 use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, ServerMessage}, SecureTCPTransport, TwitchIRCClient};
 use dotenv::dotenv;
-use std::{env, sync::Arc, time::{Duration, Instant}};
+use std::{env, sync::{Arc, RwLock}, time::{Duration, Instant}};
 
 // use crate::{Badge, Command, Listener, Module};
 use super::bot_objects::command::Command;
@@ -30,13 +30,13 @@ pub struct Bot
     /// admin chatters
     admins : Vec<String>,
     /// listeners
-    listeners: Vec<Listener>,
+    listeners: Mutex<Vec<Listener>>,
     /// commands
-    commands: Vec<Command>,
+    commands: Mutex<Vec<Command>>,
     /// modules
-    modules: Vec<Module>,
+    modules: RwLock<Vec<Module>>,
     /// channel module status
-    channel_module_status: Mutex<Vec<(String,String,modules::Status)>>,
+    channel_module_status: RwLock<Vec<(String,String,modules::Status)>>,
     /// chatter guest badges - chatter,channel,Badge,start_time,duration
     chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
     /// Message cache
@@ -55,7 +55,7 @@ impl Bot
     /// - bot_channels
     /// - prefix
     /// - bot_admins
-    pub fn new() -> Bot {
+    pub async fn new() -> Bot {
 
 
         
@@ -72,7 +72,7 @@ impl Bot
                 botchannels.push(chnl.to_owned());
         }
 
-        Bot::new_from(bot_login_name, oauth_token, prefix, botchannels)
+        Bot::new_from(bot_login_name, oauth_token, prefix, botchannels).await
        
 
     }
@@ -80,7 +80,7 @@ impl Bot
     /// Creates a new `Bot` using bot information
     /// 
     /// Bot will join `botchannels` argument
-    pub fn new_from(bot_login_name:String,oauth_token:String,prefix:String,botchannels:Vec<String>) -> Bot {
+    pub async fn new_from(bot_login_name:String,oauth_token:String,prefix:String,botchannels:Vec<String>) -> Bot {
 
         dotenv().ok();
         let bot_login_name = bot_login_name;
@@ -106,22 +106,22 @@ impl Bot
         }
 
 
-        let mut bot = Bot {
+        let bot = Bot {
              prefix,
              incoming_msgs : Mutex::new(incoming_messages),
              client,
              botchannels : botchannels_all,
-             listeners : vec![],
-             commands : vec![],
+             listeners : Mutex::new(vec![]),
+             commands : Mutex::new(vec![]),
              admins,
-             modules:   vec![],
-             channel_module_status: Mutex::new(vec![]),
+             modules:  RwLock::new(vec![]),
+             channel_module_status: RwLock::new(vec![]),
              chatter_guest_badges: Mutex::new(vec![]),
              message_cache : Mutex::new(vec![]),
         };
 
         for cmd in built_in_objects::create_commands() {
-            bot.load_command(cmd);
+            bot.load_command(cmd).await;
         }
 
         bot
@@ -143,8 +143,8 @@ impl Bot
             
             while let Some(message) = in_msgs_lock.recv().await {   
 
-        
-                for listener in &(*bot).listeners {
+                let bot_listener_lock = bot.listeners.lock().await;
+                for listener in bot_listener_lock.iter() {
                     
                     let a = listener.clone();
                     if a.cond_triggered(bot.clone(),message.clone()).await {
@@ -160,8 +160,8 @@ impl Bot
                     // dbg!(cache_lock.clone());
                     drop(cache_lock);
 
-
-                    for cmd in &(*bot).commands {
+                    let cmd_lock = bot.commands.lock().await;
+                    for cmd in cmd_lock.iter() {
                     
                         let a = cmd.clone();
                         if a.command_triggered(bot.clone(),msg.clone()).await {
@@ -171,40 +171,61 @@ impl Bot
                     }
 
 
-                    'module_loop: for module in &(*bot).modules {
-                        
-                        // skip modules that are disable 
-                        let cms_lock = bot.channel_module_status.lock().await;
+                    fn get_enabled_channel_modules(bot: Arc<Bot>,channel:String) -> Vec<Module>  {
 
+                    let botmodules_lock = bot.modules.read().unwrap();
+                    let botmodules_cpy = botmodules_lock.clone();
+                    drop(botmodules_lock);
+
+                    let mut enabled_mods = Vec::new();
+
+                    'module_loop: for module in &*botmodules_cpy {
+                    
+                        // dbg!("try cms read");
+                        let cms_lock = bot.channel_module_status.read().unwrap();
+                        // dbg!("cms read lock");
+                        // dbg!(module.get_names());
                         for channel_flags in cms_lock.iter() {
-                            if channel_flags.0 == msg.channel_login.clone() {
-                                
+                            if channel_flags.0 == channel {
+
                                 if module.get_names().contains(&channel_flags.1) && channel_flags.2 == Status::Disabled {
                                     continue 'module_loop;
+                                    // disabled_mods.push(module);
                                 }
                             }
                         }
-                    
-
-
-                        for listener in module.get_listeners() {
-                        
-                            let a = listener.clone();
-                            if a.cond_triggered(bot.clone(),message.clone()).await {
-                                
-                                let _ = listener.execute_fn(bot.clone(),message.clone()).await;
-                            }
-                        }
-                        for cmd in module.get_commands() {
-                            
-                            let a = cmd.clone();
-                            if a.command_triggered(bot.clone(),msg.clone()).await {
-                                
-                                let _ = cmd.execute_fn(bot.clone(),message.clone()).await;
-                            }
-                        }  
-                        
+                        enabled_mods.push(module.clone());
                     }
+
+                    enabled_mods
+
+                    
+                }
+
+                         
+                    
+                for module in get_enabled_channel_modules(bot.clone(), msg.clone().channel_login) {
+
+                    for listener in module.get_listeners() {
+                    
+                        let a = listener.clone();
+                        if a.cond_triggered(bot.clone(),message.clone()).await {
+                            
+                            let _ = listener.execute_fn(bot.clone(),message.clone()).await;
+                        }
+                    }
+                    for cmd in module.get_commands() {
+                        
+                        let a = cmd.clone();
+                        if a.command_triggered(bot.clone(),msg.clone()).await {
+                            
+                            let _ = cmd.execute_fn(bot.clone(),message.clone()).await;
+                        }
+                    }  
+                    
+                }
+
+                    
                     
                 } else {} ;
                 
@@ -217,20 +238,25 @@ impl Bot
     }
 
     /// Loads a `Listener` into the bot
-    pub fn load_listener(&mut self,l : Listener) {
-        self.listeners.push(l);
+    pub async fn load_listener(&self,l : Listener) {
+        let a = Arc::new(self);
+        let mut listlock = a.listeners.lock().await;
+        listlock.push(l);
     }
 
     /// Loads a `Command` into the bot
-    pub fn load_command(&mut self,c : Command) {
-        self.commands.push(c);
+    pub async fn load_command(&self,c : Command) {
+        let a = Arc::new(self);
+        let mut cmdlock = a.commands.lock().await;
+        cmdlock.push(c);
     }
 
     
-    pub fn get_module(&self,module:String) -> Option<Module> {
-        for modl in self.modules.clone() {
+    pub async fn get_module(&self,module:String) -> Option<Module> {
+        let modlock = self.modules.read().unwrap();
+        for modl in modlock.iter() {
             if modl.get_names().contains(&module) {
-                return Some(modl);
+                return Some(modl.clone());
             }
         }
         None
@@ -247,20 +273,35 @@ impl Bot
     }
     
     /// loads a `Module` and its bot objects
-    pub async fn load_module(&mut self,m: Module) {
+    pub async fn load_module(&self,m: Module) {
+        // dbg!("load module - start",m.get_names().first().unwrap());
+        let bot = Arc::new(self);
+        // let bot_lock = bot.lock().await;
+        // dbg!("bot arc");
         if m.get_status_by_default() == Status::Disabled {
-            for chnl in &self.botchannels {
-                self.disable_module(chnl.clone(),  
+            // dbg!("module fund disabled by default");
+            // dbg!("inner if");
+            for (_index,chnl) in bot.botchannels.iter().enumerate() {
+                // dbg!("iter - ",index);
+                bot.disable_module(chnl.clone(),  
                 m.get_names().first().unwrap().clone()).await
             }
         }
-        self.modules.push(m)
+        // dbg!("aftee disable check");
+        // dbg!(bot.modules);
+        let mut botmods = bot.modules.write().unwrap();
+        // dbg!(m);
+        // dbg!("loading module ",m.get_names());
+        botmods.push(m);
     }  
 
     pub async fn get_channel_module_status(&self,channel:String,module:String) -> Status {
+        // dbg!("get channel module status");
         let found_disabled:bool = {
-            let cms_lock = self.channel_module_status.lock().await;
-
+            // dbg!("try cms read");
+            let cms_lock = self.channel_module_status.read().unwrap();
+            // dbg!("cms read lock");
+            // dbg!(module.clone());
             let mut found = false;
 
             for channel_flags in cms_lock.iter() {
@@ -272,15 +313,37 @@ impl Bot
             }
             found
         };
+
+        let module_loaded:bool = {
+
+            let mut loaded_yn = false;
+
+            for loaded_m in self.modules.read().unwrap().iter() {
+                if loaded_m.get_names().contains(&module) { 
+                    loaded_yn = true;
+                }
+            }
+
+            loaded_yn
+
+        };
+        
         if found_disabled { return Status::Disabled;}
+    
+        else if !module_loaded { return Status::NotLoaded ;}
         else { return Status::Enabled; };
 
     }
 
     pub async fn disable_module(&self,channel:String,module:String){
+        // dbg!("disable module called",channel.clone(),module.clone());
 
         let found_disabled:bool = {
-            let cms_lock = self.channel_module_status.lock().await;
+            // dbg!("finding disabled mod");
+            // dbg!("try cms read");
+            let cms_lock = self.channel_module_status.read().unwrap();
+            // dbg!("cms read lock");
+            // dbg!(module.clone());
 
             let mut found = false;
 
@@ -291,13 +354,20 @@ impl Bot
                     }
                 }
             }
+            drop(cms_lock);
             found
         };
 
         if !found_disabled {
 
-            let mut cms_lock = self.channel_module_status.lock().await;
-            cms_lock.push((channel,module,Status::Disabled));
+            // self.channel_module_status.
+            // dbg!("try cms write"); /*,self.channel_module_status */
+            let mut cms_lock = self.channel_module_status.write().unwrap();
+            // cms_lock.
+            // dbg!("cms write lock");
+            cms_lock.push((channel,module.clone(),Status::Disabled));
+            // dbg!(module.clone());
+            drop(cms_lock);
             
 
         } 
@@ -306,8 +376,12 @@ impl Bot
     }
 
     pub async fn enable_module(&self,channel:String,module:String){
-        let mut lock = self.channel_module_status.lock().await;
-        if lock.contains(&(channel.clone(),module.clone(),Status::Disabled)) {
+        // dbg!("enable module called",channel.clone(),module.clone());
+        // dbg!("try cms write");
+        let mut lock = self.channel_module_status.write().unwrap();
+        // dbg!("cms write lock");
+        // dbg!(module.clone());
+        while lock.contains(&(channel.clone(),module.clone(),Status::Disabled)) {
             
             let index = lock
                 .iter()
@@ -316,6 +390,7 @@ impl Bot
                 .unwrap();
             lock.remove(index);
         }
+        drop(lock);
     }
 
     pub async fn get_channel_guest_badges(&self,chatter:String,channel:String) ->  Vec<(Badge,Instant,Duration)> {
diff --git a/forcebot_core/src/botcore/bot_objects.rs b/forcebot_core/src/botcore/bot_objects.rs
index 397d7fc..b0d334d 100644
--- a/forcebot_core/src/botcore/bot_objects.rs
+++ b/forcebot_core/src/botcore/bot_objects.rs
@@ -184,7 +184,7 @@ pub mod built_in_objects {
                             bot.enable_module(msg.channel_login.clone(), arg.to_string()).await;
                                 
                             //bot.get_modules()
-                            if let Some(found_mod) = bot.get_module(arg.to_string()) {
+                            if let Some(found_mod) = bot.get_module(arg.to_string()).await {
                                 bot_message = bot_message.to_string() + found_mod.get_bot_read_description().as_str();
                             }
                             re_enabled = true;
diff --git a/forcebot_core/src/botcore/modules.rs b/forcebot_core/src/botcore/modules.rs
index 9a30e41..889cf9e 100644
--- a/forcebot_core/src/botcore/modules.rs
+++ b/forcebot_core/src/botcore/modules.rs
@@ -6,7 +6,8 @@ use super::bot_objects::listener::Listener;
 #[derive(PartialEq, Eq,Debug,Clone)]
 pub enum Status {
     Disabled,
-    Enabled
+    Enabled,
+    NotLoaded,
 }
 
 /// Bot `Module` that groups a set of `bot_objects`
diff --git a/forcebot_core/src/custom_mods.rs b/forcebot_core/src/custom_mods.rs
index 099cee1..f295b1e 100644
--- a/forcebot_core/src/custom_mods.rs
+++ b/forcebot_core/src/custom_mods.rs
@@ -1,2 +1,3 @@
 pub mod guest_badge;
-pub mod pyramid;
\ No newline at end of file
+pub mod pyramid;
+pub mod debug;
\ No newline at end of file
diff --git a/forcebot_core/src/custom_mods/debug.rs b/forcebot_core/src/custom_mods/debug.rs
new file mode 100644
index 0000000..df5b321
--- /dev/null
+++ b/forcebot_core/src/custom_mods/debug.rs
@@ -0,0 +1,146 @@
+
+
+use std::sync::{Arc};
+
+use crate::{execution_async, modules::Status, Bot, Command, Listener, Module};
+use twitch_irc::message::ServerMessage;
+// use lazy_static::lazy_static;
+
+// lazy_static!{
+//     /// Listener per channel (channel:String,Listener)
+//     pub static ref LISTENER_PER_CHNL:  Mutex<Vec<(String,Mutex<Listener>)>> = Mutex::new(vec![]); 
+// }
+
+/// debug module
+/// 
+/// Commands to enable debugging messages in chat
+/// 
+/// `debug on` to start
+/// 
+/// `debug off` to stop
+/// 
+/// 
+
+
+/// Use this function when loading modules into the bot
+/// 
+/// For example
+/// ```rust
+/// bot.load_module(debug::create_module());
+/// ```
+///
+pub fn create_module() -> Module {
+    /* 1. Create a new module */
+    let mut custom_mod = Module::new(
+        vec!["debug".to_string()], 
+        "".to_string());
+
+    /* 2. Load the cmd into a new module */
+    custom_mod.load_command(cmd_debug_on());
+    custom_mod.load_command(cmd_debug_off());
+    custom_mod
+
+}
+
+/// Command definition for debug command
+fn cmd_debug_on() -> Command {
+    /* 1. Create a new cmd */
+    let mut cmd = Command::new(vec!["debug on".to_string()],"".to_string());
+
+    /* 2. Define exec callback  */
+    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            // dbg!("debug cmd on executed");
+
+            let modulename="debug listener".to_string();
+
+            if let Status::NotLoaded = bot.get_channel_module_status(msg.channel_login.clone(), modulename.clone()).await  {
+                let module = create_listener_module(modulename.clone());
+                bot.load_module(module.clone()).await;
+            }
+            let modl = bot.get_module(modulename).await.unwrap();
+            bot.enable_module(msg.channel_login.clone(), modl.get_names().first().unwrap().clone()).await;
+            println!("Debug enabled for channel {}",msg.channel_login);
+        }
+        Result::Err("Not Valid message type".to_string()) 
+    }
+
+    /* 3. Set Command flags */
+    cmd.set_exec_fn(execution_async(execbody));
+    cmd.set_admin_only(true);
+    // cmd.set_min_badge(Badge::Moderator);
+    cmd.set_admin_override(true);
+
+    cmd
+}
+
+/// Command definition for debug off command
+fn cmd_debug_off() -> Command {
+    /* 1. Create a new cmd */
+    let mut cmd = Command::new(vec!["debug off".to_string()],"".to_string());
+
+    /* 2. Define exec callback  */
+    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            // dbg!("debug cmd on executed");
+
+            let modulename="debug listener".to_string();
+
+            // if let Status::NotLoaded = bot.get_channel_module_status(msg.channel_login.clone(), modulename.clone()).await  {
+            //     let module = create_listener_module(modulename.clone());
+            //     bot.load_module(module.clone()).await;
+            // }
+            let modl = bot.get_module(modulename).await.unwrap();
+            bot.disable_module(msg.channel_login.clone(), modl.get_names().first().unwrap().clone()).await;
+            println!("Debug disabled for channel {}",msg.channel_login);
+        }
+        Result::Err("Not Valid message type".to_string()) 
+    }
+
+    /* 3. Set Command flags */
+    cmd.set_exec_fn(execution_async(execbody));
+    cmd.set_admin_only(true);
+    // cmd.set_min_badge(Badge::Moderator);
+    cmd.set_admin_override(true);
+
+    cmd
+}
+
+fn create_listener_module(name:String) -> Module {
+
+    let mut custom_mod = Module::new(
+        vec![name], 
+        "".to_string());
+        // dbg!("debug listener module created");
+    custom_mod.load_listener(cmd_debug_listener());
+    custom_mod.set_status_by_default(Status::Disabled);
+
+    custom_mod
+
+}
+
+/// Listener for debug
+fn cmd_debug_listener() -> Listener {
+        // dbg!("Creating debug listener");
+       /* 2a. Create a new blank Listener */
+       let mut listener = Listener::new();
+
+       /* 2b. Set a trigger condition function for listener */
+       listener.set_trigger_cond_fn(
+           |_:Arc<Bot>,_:ServerMessage| true
+       );
+   
+       /* 2c. Define an async fn callback execution */
+       async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+            if let ServerMessage::Privmsg(msg) = message {
+                dbg!(msg); /* outputs message to debug */
+            }
+            Result::Err("Not Valid message type".to_string()) 
+       }
+   
+       /* 2d. Set and Store the execution body using `execution_async()`  */
+       listener.set_exec_fn(execution_async(execbody));
+
+       listener 
+}
+
diff --git a/forcebot_core/src/custom_mods/guest_badge.rs b/forcebot_core/src/custom_mods/guest_badge.rs
index ee1eb16..8577bf9 100644
--- a/forcebot_core/src/custom_mods/guest_badge.rs
+++ b/forcebot_core/src/custom_mods/guest_badge.rs
@@ -7,7 +7,7 @@ use crate::{execution_async, Badge, Bot, Command, Module};
 /// guest_badge / guest module
 /// 
 /// Temporary badges can be issued to chatters. The bot then opens functionality
-/// to that chatter baded on the recognized role
+/// to that chatter based on the recognized role
 ///  
 /// Chatters with real badge roles will be able to share guest 
 /// badges based on their role
diff --git a/forcebot_core/src/custom_mods/pyramid.rs b/forcebot_core/src/custom_mods/pyramid.rs
index 3a3de01..0ea950b 100644
--- a/forcebot_core/src/custom_mods/pyramid.rs
+++ b/forcebot_core/src/custom_mods/pyramid.rs
@@ -149,9 +149,7 @@ fn read_top_of_compare(channel:String) -> Option<(String,String)> {
             return msg_stack.last().cloned();
         }
     }
-
     None
-
 }
 
 fn pop_top_of_compare(channel:String) -> Option<(String,String)> {
@@ -165,7 +163,6 @@ fn pop_top_of_compare(channel:String) -> Option<(String,String)> {
             return popped;
         }
     }
-    
     None
 }
 
@@ -283,7 +280,7 @@ fn symmetry_ok(channel:String) -> bool {
                 return false;
             }
 
-        } else { /* dbg!("failed catchall"); */ return false; }
+        } else { return false; }
         if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {  
             
             temp_stack.clear();
@@ -309,7 +306,7 @@ fn symmetry_ok(channel:String) -> bool {
 /// if a duration is given, take that duration eg 15m , 25m 
 /// 
 /// 
-fn create_interruptor_cmd() -> Command {
+fn _create_interruptor_cmd() -> Command {
     let mut cmd = Command::new(vec![
         "no pyramid".to_string(),
         "no pyramids".to_string()
@@ -339,7 +336,7 @@ fn create_interruptor_cmd() -> Command {
 }
 
 /// #todo
-fn create_interruptor_module(channel:String) -> Module  {
+fn _create_interruptor_module(channel:String) -> Module  {
     /* 1. Create a new module */
     let modname = format!("interruptor {}",channel);
     let mut custom_mod = Module::new(
@@ -347,14 +344,14 @@ fn create_interruptor_module(channel:String) -> Module  {
         "".to_string());
 
     /* 2. Load the cmd into a new module */
-    custom_mod.load_listener(create_interruptor_listener());
+    custom_mod.load_listener(_create_interruptor_listener());
 
     custom_mod
 
 }
 
 /// #todo
-fn create_interruptor_listener() -> Listener  {
+fn _create_interruptor_listener() -> Listener  {
     /* 2a. Create a new blank Listener */
     let mut listener = Listener::new();
 
@@ -365,7 +362,7 @@ fn create_interruptor_listener() -> Listener  {
 
     /* 2c. Define an async fn callback execution */
     async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-        dbg!(message);
+        dbg!(message); /* outputs message to debug */
         Result::Ok("Success".to_string()) 
     }
 
@@ -379,6 +376,6 @@ fn create_interruptor_listener() -> Listener  {
 /// #todo
 /// 
 /// Returns Some(chatter) if the pyramid in progress is being built by a solo
-fn solo_building(channel:String) -> Option<String> {
+fn _solo_building(_channel:String) -> Option<String> {
     None
 }
diff --git a/forcebot_core/src/lib.rs b/forcebot_core/src/lib.rs
index 8ccc272..f33057e 100644
--- a/forcebot_core/src/lib.rs
+++ b/forcebot_core/src/lib.rs
@@ -147,7 +147,7 @@
 //! 
 //!     /* 2c. Define an async fn callback execution */
 //!     async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-//!         dbg!(message);
+//!         dbg!(message); /* outputs message to debug */
 //!         Result::Ok("Success".to_string()) 
 //!     }
 //! 
@@ -190,7 +190,6 @@
 //!     listener.set_trigger_cond_fn(
 //!         |_:Arc<Bot>,message:ServerMessage| 
 //!            if let ServerMessage::Privmsg(msg) = message {
-//!                // dbg!(msg.clone());
 //!                 for badge in msg.badges {
 //!                     if matches!(badge, x if x.name == "moderator") {
 //!                         // dbg!("moderator found");
diff --git a/moderator_reactor/src/main.rs b/moderator_reactor/src/main.rs
index 58c0c9f..528b62e 100644
--- a/moderator_reactor/src/main.rs
+++ b/moderator_reactor/src/main.rs
@@ -23,7 +23,7 @@ use twitch_irc::message::ServerMessage;
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 1. Create a new blank Listener */
     let mut listener = Listener::new();
@@ -33,7 +33,6 @@ pub async fn main() {
     listener.set_trigger_cond_fn(
         |_:Arc<Bot>,message:ServerMessage| 
             if let ServerMessage::Privmsg(msg) = message {
-                // dbg!(msg.clone());
                 for badge in msg.badges {
                     if matches!(badge, x if x.name == "moderator") {
                         // dbg!("moderator found");
@@ -57,7 +56,7 @@ pub async fn main() {
     listener.set_exec_fn(execution_async(execbody));
 
     /* 5. Load the listener into the bot */
-    bot.load_listener(listener);
+    bot.load_listener(listener).await;
 
     /* Run the bot */
     bot.run().await;
diff --git a/new_empty_bot/src/main.rs b/new_empty_bot/src/main.rs
index 4abdbc7..a81462e 100644
--- a/new_empty_bot/src/main.rs
+++ b/new_empty_bot/src/main.rs
@@ -16,7 +16,7 @@ use forcebot_core::Bot;
 pub async fn main() {
 
     /* 1. Create the bot using env */
-    let bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 2. Run the bot */
     bot.run().await;
diff --git a/readme.md b/readme.md
index d94791a..c4e81d0 100644
--- a/readme.md
+++ b/readme.md
@@ -30,6 +30,7 @@ cargo run -p forcebot_core
 - Quick Start to use full feature set bot
 - Moderators & Broadcasters can `disable` or `enable` `Modules` of bot functionality through chat `Commands`
 - Full Feature Set `forcebot_core` bot has the following modules loaded
+    - `debug` - outputs to console messages from the channel where it was enabled. Toggle debug with the Commands `debug on` or `debug off`
     - `guest_badge` - Temporary badges can be issued to chatters
     - `besty` - Tomfoolery
     - `pyramid` - for detecting & handling pyramids
@@ -280,7 +281,7 @@ pub async fn main() {
 
     /* 2c. Define an async fn callback execution */
     async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-        dbg!(message);
+        dbg!(message); /* outputs message to debug */
         Result::Ok("Success".to_string()) 
     }
 
@@ -323,7 +324,7 @@ pub async fn main() {
     listener.set_trigger_cond_fn(
         |_:Arc<Bot>,message:ServerMessage| 
             if let ServerMessage::Privmsg(msg) = message {
-                // dbg!(msg.clone());
+                
                 for badge in msg.badges {
                     if matches!(badge, x if x.name == "moderator") {
                         // dbg!("moderator found");
diff --git a/simple_command_bot/src/main.rs b/simple_command_bot/src/main.rs
index 2f29e09..3806e7f 100644
--- a/simple_command_bot/src/main.rs
+++ b/simple_command_bot/src/main.rs
@@ -26,7 +26,7 @@ use twitch_irc::message::ServerMessage;
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let mut bot = Bot::new().await;
 
     /* 1. Create a new blank cmd */
     let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
diff --git a/simple_debug_listener/src/main.rs b/simple_debug_listener/src/main.rs
index 3ce82fc..27ab37e 100644
--- a/simple_debug_listener/src/main.rs
+++ b/simple_debug_listener/src/main.rs
@@ -19,7 +19,7 @@ use twitch_irc::message::ServerMessage;
 pub async fn main() {
 
     /* 1. Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 2a. Create a new blank Listener */
     let mut listener = Listener::new();
@@ -31,7 +31,7 @@ pub async fn main() {
 
     /* 2c. Define an async fn callback execution */
     async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-        dbg!(message);
+        dbg!(message); /* outputs message to debug */
         Result::Ok("Success".to_string()) 
     }
 
@@ -39,7 +39,7 @@ pub async fn main() {
     listener.set_exec_fn(execution_async(execbody));
 
     /* 3. Load the listener into the bot */
-    bot.load_listener(listener);
+    bot.load_listener(listener).await;
 
     /* 4. Run the bot */
     bot.run().await;
diff --git a/simple_module_example/src/main.rs b/simple_module_example/src/main.rs
index 4793ed4..3851d34 100644
--- a/simple_module_example/src/main.rs
+++ b/simple_module_example/src/main.rs
@@ -23,7 +23,7 @@ use forcebot_core::Bot;
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* load the Module */
     bot.load_module(custom_mod::new()).await;
-- 
2.49.0


From 5faf982485a5770fba143fd0bb747b52a7cdca3e Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Tue, 4 Feb 2025 00:20:36 -0500
Subject: [PATCH 12/14] quiet module

---
 forcebot_core/src/bin/fun_bot.rs              | 10 ++-
 forcebot_core/src/botcore/bot.rs              | 56 +++++++++----
 .../src/botcore/bot_objects/command.rs        | 13 ++-
 .../src/botcore/bot_objects/listener.rs       | 38 ++++++++-
 forcebot_core/src/botcore/modules.rs          |  7 +-
 forcebot_core/src/custom_mods.rs              |  3 +-
 forcebot_core/src/custom_mods/debug.rs        |  8 +-
 forcebot_core/src/custom_mods/quiet.rs        | 84 +++++++++++++++++++
 simple_command_bot/src/main.rs                |  4 +-
 9 files changed, 183 insertions(+), 40 deletions(-)
 create mode 100644 forcebot_core/src/custom_mods/quiet.rs

diff --git a/forcebot_core/src/bin/fun_bot.rs b/forcebot_core/src/bin/fun_bot.rs
index 872aff9..483177c 100644
--- a/forcebot_core/src/bin/fun_bot.rs
+++ b/forcebot_core/src/bin/fun_bot.rs
@@ -1,9 +1,10 @@
 //! WIP Fun forcebot with catered customizations #todo
 //! 
 //! Custom modules that can be managed in chat through `disable` and `enable` commands
-//! - `besty`
+//! - `besty` - uses a custom prefix tp trigger
 //! - `guests`
 //! - `pyramid`
+//! - `quiet`
 //! 
 //! 
 //! Be sure the followig is defined in `.env` 
@@ -18,7 +19,7 @@
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
 // use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
-use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
+use forcebot_core::{custom_mods::{debug, guest_badge, pyramid, quiet}, Bot};
 
 
 
@@ -35,6 +36,7 @@ pub async fn main() {
     bot.load_module(guest_badge::create_module()).await;
     bot.load_module(pyramid::create_module()).await;
     bot.load_module(debug::create_module()).await;
+    bot.load_module(quiet::create_module()).await;
     
     /* 3. Run the bot */
     bot.run().await;
@@ -45,7 +47,7 @@ pub async fn main() {
 pub mod funbot_objs {
     use std::sync::Arc;
 
-    use forcebot_core::{execution_async, modules::Status, Badge, Bot, Command, Module};
+    use forcebot_core::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
     /// Create a Module with a loaded Command object
@@ -55,7 +57,7 @@ pub mod funbot_objs {
             "Now Aware of besty xdd666 ".to_string());
 
         custom_mod.load_command(create_cmd_test());
-        custom_mod.set_status_by_default(Status::Disabled);
+        // custom_mod.set_status_by_default(Status::Disabled);
 
         custom_mod
     }
diff --git a/forcebot_core/src/botcore/bot.rs b/forcebot_core/src/botcore/bot.rs
index 9e3070f..e8a4d04 100644
--- a/forcebot_core/src/botcore/bot.rs
+++ b/forcebot_core/src/botcore/bot.rs
@@ -41,7 +41,9 @@ pub struct Bot
     chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
     /// Message cache
     message_cache: Mutex<Vec<PrivmsgMessage>>,
-    // message_cache: Vec<PrivmsgMessage>
+    /// channel_quiet
+    channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
+    
 }
 
 
@@ -118,6 +120,7 @@ impl Bot
              channel_module_status: RwLock::new(vec![]),
              chatter_guest_badges: Mutex::new(vec![]),
              message_cache : Mutex::new(vec![]),
+             channel_quiet_yn : RwLock::new(vec![]),
         };
 
         for cmd in built_in_objects::create_commands() {
@@ -183,14 +186,13 @@ impl Bot
                     
                         // dbg!("try cms read");
                         let cms_lock = bot.channel_module_status.read().unwrap();
-                        // dbg!("cms read lock");
-                        // dbg!(module.get_names());
+                        
                         for channel_flags in cms_lock.iter() {
                             if channel_flags.0 == channel {
 
                                 if module.get_names().contains(&channel_flags.1) && channel_flags.2 == Status::Disabled {
                                     continue 'module_loop;
-                                    // disabled_mods.push(module);
+                                    
                                 }
                             }
                         }
@@ -202,7 +204,6 @@ impl Bot
                     
                 }
 
-                         
                     
                 for module in get_enabled_channel_modules(bot.clone(), msg.clone().channel_login) {
 
@@ -224,12 +225,8 @@ impl Bot
                     }  
                     
                 }
-
-                    
-                    
                 } else {} ;
                 
-
             }
             drop(in_msgs_lock);
         });
@@ -360,13 +357,11 @@ impl Bot
 
         if !found_disabled {
 
-            // self.channel_module_status.
-            // dbg!("try cms write"); /*,self.channel_module_status */
+            
             let mut cms_lock = self.channel_module_status.write().unwrap();
-            // cms_lock.
-            // dbg!("cms write lock");
+            
             cms_lock.push((channel,module.clone(),Status::Disabled));
-            // dbg!(module.clone());
+            
             drop(cms_lock);
             
 
@@ -434,6 +429,39 @@ impl Bot
         rslt
     }
 
+    /// Get the quiet status of a channel
+    pub fn get_channel_quiet(&self,channel:String) -> bool {
+        for a in self.channel_quiet_yn.read().unwrap().iter() {
+            if a.0 == channel {
+                return a.1.read().unwrap().clone();
+            }
+        }
+        return false;
+    }
+    
+    /// Get the quiet status of a channel
+    pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
+        let mut found = false;
+
+        let chnlquiet = self.channel_quiet_yn.read().unwrap();
+        for rec in chnlquiet.iter() {
+            if rec.0 == channel {
+                found = true;
+                let mut status = rec.1.write().unwrap();
+                *status = quiet_on;
+                drop(status);
+            }
+        }
+        drop(chnlquiet);
+        
+        if !found {
+            // dbg!("set chn quiet > !found channel quiet status");
+            let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
+            chnlquiet.push((channel,RwLock::new(quiet_on)));
+            drop(chnlquiet);
+        }
+        
+    }
 
 }
 
diff --git a/forcebot_core/src/botcore/bot_objects/command.rs b/forcebot_core/src/botcore/bot_objects/command.rs
index 54c4b85..7095ca5 100644
--- a/forcebot_core/src/botcore/bot_objects/command.rs
+++ b/forcebot_core/src/botcore/bot_objects/command.rs
@@ -188,18 +188,17 @@ impl Command
             (cmd.custom_cond_fn)(bot.clone(),message.clone()) && (cmd.custom_cond_async)(bot,message).await
         }
 
-        // dbg!(msg.clone());
+        fn quiet_off_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
+            !bot.get_channel_quiet(message.channel_login.clone()) 
+            || bot.get_channel_quiet(message.channel_login.clone()) && cmd.commands.contains(&("quiet off".to_string()))
+        }
 
-        // dbg!(caller_badge_ok(self, bot.clone(), msg.clone()));
-        // dbg!(cmd_called(self, bot.clone(), msg.clone()) && 
-        // caller_badge_ok(self, bot.clone(), msg.clone()) &&
-        // admin_only_ok(self, bot.clone(), msg.clone()) &&
-        // custom_cond_ok(self, bot.clone(), msg.clone()));
 
         cmd_called(self, bot.clone(), msg.clone()) && 
         caller_badge_ok(self, bot.clone(), msg.clone()).await &&
         admin_only_ok(self, bot.clone(), msg.clone()) &&
-        custom_cond_ok(self, bot, msg).await
+        custom_cond_ok(self, bot.clone(), msg.clone()).await &&
+        quiet_off_ok(self, bot, msg)
 
     }
 
diff --git a/forcebot_core/src/botcore/bot_objects/listener.rs b/forcebot_core/src/botcore/bot_objects/listener.rs
index 84bd468..2895acb 100644
--- a/forcebot_core/src/botcore/bot_objects/listener.rs
+++ b/forcebot_core/src/botcore/bot_objects/listener.rs
@@ -1,7 +1,10 @@
 use std::sync::Arc;
 
+// use tokio::sync::Mutex;
 use twitch_irc::message::ServerMessage;
 
+use crate::Module;
+
 use super::{execution_async,Bot,listener_condition_async};
     
 use super::{ExecBody, ListenerTrigger};
@@ -27,6 +30,7 @@ pub struct Listener
     trigger_cond_async : Arc<ListenerTrigger> ,
     /// execution body
     exec_fn : Arc<ExecBody>,
+    parent_module : Arc<Option<Module>>,
 }
 
 impl Listener
@@ -50,6 +54,7 @@ impl Listener
             trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| true,
             trigger_cond_async : Arc::new(listener_condition_async(condition01)),
             exec_fn : Arc::new(execution_async(execbody)),
+            parent_module : Arc::new(None),
         }
     }
 
@@ -103,14 +108,41 @@ impl Listener
 
     /// checks if the trigger condition is met
     pub async fn cond_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
-        let list = Arc::new(self);
-        (list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
+
+        let list = Arc::new(self.clone());
+
+        async fn defined_conditions_ok(list:Arc<Listener>,bot:Arc<Bot>,msg:ServerMessage) -> bool {
+            // let list = Arc::new(self);
+            (list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
+        }
+
+        fn quiet_off_ok(list:Arc<Listener>,bot:Arc<Bot>,message:ServerMessage) -> bool {
+            if let ServerMessage::Privmsg(msg) = message {
+                
+                if let Some(parent_mod) = &*list.parent_module {
+                    return !bot.get_channel_quiet(msg.channel_login) || parent_mod.get_names().contains(&"debug".to_string());
+                }
+
+                return !bot.get_channel_quiet(msg.channel_login) ;
+            }
+            return true; /* quiet is off for non chat msgs */
+        }
+
+        defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await &&
+        quiet_off_ok(list, bot, msg)
     }
 
     /// executes the listeners executon body
     pub async fn execute_fn(&self,bot:Arc<Bot>,msg:ServerMessage) -> Result<String, String> {
-        // (self.exec_fn)(bot,msg)
+        
         (self.exec_fn)(bot,msg).await
     }
+
+    /// sets parent module
+    pub fn set_parent_module(&mut self,module:Module) {
+        self.parent_module = Arc::new(Some(module));
+    }
+
+
 }
 
diff --git a/forcebot_core/src/botcore/modules.rs b/forcebot_core/src/botcore/modules.rs
index 889cf9e..d3802b5 100644
--- a/forcebot_core/src/botcore/modules.rs
+++ b/forcebot_core/src/botcore/modules.rs
@@ -1,4 +1,6 @@
 
+// use std::sync::{Arc, Mutex};
+
 use super::bot_objects::command::Command;
 use super::bot_objects::listener::Listener;
 
@@ -40,13 +42,14 @@ impl Module
     }
 
     /// Loads a `Listener` into the module
-    pub fn load_listener(&mut self,l : Listener) {
+    pub fn load_listener(&mut self,mut l : Listener) {
+        l.set_parent_module(self.clone());
         self.listeners.push(l);
     }
 
     /// Loads a `Command` into the module
     pub fn load_command(&mut self,c : Command) {
-        self.commands.push(c);
+        self.commands.push(c); 
     }
 
     pub fn get_listeners(&self) -> Vec<Listener> {
diff --git a/forcebot_core/src/custom_mods.rs b/forcebot_core/src/custom_mods.rs
index f295b1e..49848a1 100644
--- a/forcebot_core/src/custom_mods.rs
+++ b/forcebot_core/src/custom_mods.rs
@@ -1,3 +1,4 @@
 pub mod guest_badge;
 pub mod pyramid;
-pub mod debug;
\ No newline at end of file
+pub mod debug;
+pub mod quiet;
\ No newline at end of file
diff --git a/forcebot_core/src/custom_mods/debug.rs b/forcebot_core/src/custom_mods/debug.rs
index df5b321..4bbfc73 100644
--- a/forcebot_core/src/custom_mods/debug.rs
+++ b/forcebot_core/src/custom_mods/debug.rs
@@ -1,15 +1,9 @@
 
 
-use std::sync::{Arc};
+use std::sync::Arc;
 
 use crate::{execution_async, modules::Status, Bot, Command, Listener, Module};
 use twitch_irc::message::ServerMessage;
-// use lazy_static::lazy_static;
-
-// lazy_static!{
-//     /// Listener per channel (channel:String,Listener)
-//     pub static ref LISTENER_PER_CHNL:  Mutex<Vec<(String,Mutex<Listener>)>> = Mutex::new(vec![]); 
-// }
 
 /// debug module
 /// 
diff --git a/forcebot_core/src/custom_mods/quiet.rs b/forcebot_core/src/custom_mods/quiet.rs
new file mode 100644
index 0000000..a9827f6
--- /dev/null
+++ b/forcebot_core/src/custom_mods/quiet.rs
@@ -0,0 +1,84 @@
+use std::sync::Arc;
+
+use twitch_irc::message::ServerMessage;
+
+use crate::{execution_async, Badge, Bot, Command, Module};
+
+
+/// quiet the bot in a channel
+///
+/// use 
+/// `quiet on`
+/// `quiet off`
+/// 
+/// 
+/// 
+
+/// Use this function when loading modules into the bot
+/// 
+/// For example
+/// ```rust
+/// bot.load_module(quiet::create_module());
+/// ```
+///
+pub fn create_module() -> Module {
+    /* 1. Create a new module */
+    let mut custom_mod = Module::new(
+        vec!["quiet".to_string()], 
+        "".to_string());
+
+    /* 2. Load the cmd into a new module */
+    custom_mod.load_command(cmd_quiet_on());
+    custom_mod.load_command(cmd_quiet_off());
+    custom_mod
+
+}
+
+/// Command definition for quiet command
+fn cmd_quiet_on() -> Command {
+    /* 1. Create a new cmd */
+    let mut cmd = Command::new(vec!["quiet on".to_string()],"".to_string());
+
+    /* 2. Define exec callback  */
+    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            
+            // dbg!("quiet on called");
+            bot.set_channel_quiet(msg.channel_login.clone(), true);
+            println!("channel {} set quiet true",msg.channel_login);
+        }
+        Result::Err("Not Valid message type".to_string()) 
+    }
+
+    /* 3. Set Command flags */
+    cmd.set_exec_fn(execution_async(execbody));
+    cmd.set_admin_only(false);
+    cmd.set_min_badge(Badge::Moderator);
+    cmd.set_admin_override(true);
+
+    cmd
+}
+
+/// Command definition for quiet command
+fn cmd_quiet_off() -> Command {
+    /* 1. Create a new cmd */
+    let mut cmd = Command::new(vec!["quiet off".to_string()],"".to_string());
+
+    /* 2. Define exec callback  */
+    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            
+            bot.set_channel_quiet(msg.channel_login.clone(), false);
+            println!("channel {} set quiet false",msg.channel_login);
+        }
+        Result::Err("Not Valid message type".to_string()) 
+    }
+
+    /* 3. Set Command flags */
+    cmd.set_exec_fn(execution_async(execbody));
+    cmd.set_admin_only(false);
+    cmd.set_min_badge(Badge::Moderator);
+    cmd.set_admin_override(true);
+
+    cmd
+}
diff --git a/simple_command_bot/src/main.rs b/simple_command_bot/src/main.rs
index 3806e7f..f231f0c 100644
--- a/simple_command_bot/src/main.rs
+++ b/simple_command_bot/src/main.rs
@@ -26,7 +26,7 @@ use twitch_irc::message::ServerMessage;
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new().await;
+    let bot = Bot::new().await;
 
     /* 1. Create a new blank cmd */
     let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
@@ -50,7 +50,7 @@ pub async fn main() {
     cmd.set_min_badge(Badge::Moderator);
 
     /* 6. Load the cmd into the bot */
-    bot.load_command(cmd);
+    bot.load_command(cmd).await;
 
     /* Run the bot */
     bot.run().await;
-- 
2.49.0


From 8eaa56dd0c04da7d16d2adbf32d336068601568a Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Thu, 6 Feb 2025 09:35:39 -0500
Subject: [PATCH 13/14] enh pyramid + chat module

---
 forcebot_core/Cargo.toml                      |   1 +
 forcebot_core/src/bin/fun_bot.rs              |   9 +-
 .../src/bin/simple_debug_listener.rs          |  47 ------
 forcebot_core/src/bin/simple_module.rs        |   2 +-
 forcebot_core/src/botcore.rs                  |   4 +-
 forcebot_core/src/botcore/bot.rs              | 120 +++++++++-----
 forcebot_core/src/botcore/bot_objects.rs      |  25 ++-
 .../src/botcore/bot_objects/command.rs        |  15 +-
 .../src/botcore/bot_objects/listener.rs       |  25 +--
 forcebot_core/src/botcore/built_in_mods.rs    |  14 ++
 .../built_in_mods}/quiet.rs                   |  16 +-
 forcebot_core/src/botcore/chat.rs             | 151 ++++++++++++++++++
 forcebot_core/src/custom_mods.rs              |   2 +-
 forcebot_core/src/custom_mods/guest_badge.rs  |   4 +-
 forcebot_core/src/custom_mods/pyramid.rs      | 101 +++++++++++-
 moderator_reactor/src/main.rs                 |   2 +-
 readme.md                                     |  89 +++++++----
 simple_command_bot/src/main.rs                |   2 +-
 simple_module_example/src/main.rs             |   2 +-
 19 files changed, 455 insertions(+), 176 deletions(-)
 delete mode 100644 forcebot_core/src/bin/simple_debug_listener.rs
 create mode 100644 forcebot_core/src/botcore/built_in_mods.rs
 rename forcebot_core/src/{custom_mods => botcore/built_in_mods}/quiet.rs (79%)
 create mode 100644 forcebot_core/src/botcore/chat.rs

diff --git a/forcebot_core/Cargo.toml b/forcebot_core/Cargo.toml
index 082c28b..70ac147 100644
--- a/forcebot_core/Cargo.toml
+++ b/forcebot_core/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
 default-run = "fun_bot"
 
 [dependencies]
+# async-recursion = "1.1.1" /* has issues when used */
 dotenv = "0.15.0"
 lazy_static = "1.5.0"
 tokio = { version = "1.33.0", features = ["full"] }
diff --git a/forcebot_core/src/bin/fun_bot.rs b/forcebot_core/src/bin/fun_bot.rs
index 483177c..abbc426 100644
--- a/forcebot_core/src/bin/fun_bot.rs
+++ b/forcebot_core/src/bin/fun_bot.rs
@@ -18,8 +18,7 @@
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-// use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
-use forcebot_core::{custom_mods::{debug, guest_badge, pyramid, quiet}, Bot};
+use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
 
 
 
@@ -31,12 +30,10 @@ pub async fn main() {
 
     /* 1. Load the module into the bot */
     bot.load_module(funbot_objs::create_module()).await;
-
-    /* 2. Load Custom Modules */
     bot.load_module(guest_badge::create_module()).await;
     bot.load_module(pyramid::create_module()).await;
     bot.load_module(debug::create_module()).await;
-    bot.load_module(quiet::create_module()).await;
+    
     
     /* 3. Run the bot */
     bot.run().await;
@@ -69,7 +66,7 @@ pub mod funbot_objs {
 
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.client.say_in_reply_to(
+                let _= bot.chat.lock().await.say_in_reply_to(
                     &msg, "annytfYandere he's mine".to_string()).await;
                     return Result::Ok("Success".to_string());
             }
diff --git a/forcebot_core/src/bin/simple_debug_listener.rs b/forcebot_core/src/bin/simple_debug_listener.rs
deleted file mode 100644
index bac75ed..0000000
--- a/forcebot_core/src/bin/simple_debug_listener.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-//! Example simple Binary crate that creates & runs bot based on `.env`
-//! Be sure the followig is defined in `.env` 
-//! - login_name
-//! - access_token
-//! - bot_channels
-//! - prefix
-//! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
-//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
-//! - More Info - <https://dev.twitch.tv/docs/authentication>
-
-use std::sync::Arc;
-
-use forcebot_core::{execution_async, Bot, Listener};
-use twitch_irc::message::ServerMessage;
-
-#[tokio::main]
-pub async fn main() {
-
-    /* 1. Create the bot using env */
-    let bot = Bot::new().await;
-
-    /* 2a. Create a new blank Listener */
-    let mut listener = Listener::new();
-
-    /* 2b. Set a trigger condition function for listener */
-    listener.set_trigger_cond_fn(
-        |_:Arc<Bot>,_:ServerMessage| true
-    );
-
-    /* 2c. Define an async fn callback execution */
-    async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-        dbg!(message); /* outputs message to debug */
-        Result::Ok("Success".to_string()) 
-    }
-
-    /* 2d. Set and Store the execution body using `async_box()`  */
-    listener.set_exec_fn(execution_async(execbody));
-
-    /* 3. Load the listener into the bot */
-    bot.load_listener(listener).await;
-
-    /* 4. Run the bot */
-    bot.run().await;
-
-}
diff --git a/forcebot_core/src/bin/simple_module.rs b/forcebot_core/src/bin/simple_module.rs
index 45bf06d..3e36d90 100644
--- a/forcebot_core/src/bin/simple_module.rs
+++ b/forcebot_core/src/bin/simple_module.rs
@@ -63,7 +63,7 @@ pub mod custom_mod {
         /* 2. Define exec callback  */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.client.say_in_reply_to(
+                let _= bot.chat.lock().await.say_in_reply_to(
                     &msg, "test return".to_string()).await;
             }
             Result::Err("Not Valid message type".to_string()) 
diff --git a/forcebot_core/src/botcore.rs b/forcebot_core/src/botcore.rs
index 678d9a9..99449f0 100644
--- a/forcebot_core/src/botcore.rs
+++ b/forcebot_core/src/botcore.rs
@@ -1,3 +1,5 @@
 pub mod bot;
 pub mod bot_objects;
-pub mod modules;
\ No newline at end of file
+pub mod modules;
+pub mod built_in_mods;
+pub mod chat;
\ No newline at end of file
diff --git a/forcebot_core/src/botcore/bot.rs b/forcebot_core/src/botcore/bot.rs
index e8a4d04..cccc61f 100644
--- a/forcebot_core/src/botcore/bot.rs
+++ b/forcebot_core/src/botcore/bot.rs
@@ -1,14 +1,15 @@
 
 
+// use async_recursion::async_recursion;
 use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
 use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, ServerMessage}, SecureTCPTransport, TwitchIRCClient};
 use dotenv::dotenv;
 use std::{env, sync::{Arc, RwLock}, time::{Duration, Instant}};
 
 // use crate::{Badge, Command, Listener, Module};
-use super::bot_objects::command::Command;
+use super::{bot_objects::command::Command, built_in_mods, chat::Chat};
 
-use crate::botcore::bot_objects::Badge;
+use crate::botcore::{bot_objects::Badge, chat};
 use crate::botcore::bot_objects::listener::Listener;
 use super::super::botcore::modules::Module;
 // use super::
@@ -25,6 +26,8 @@ pub struct Bot
     incoming_msgs: Mutex<UnboundedReceiver<ServerMessage>>,
     /// outbound chat client msg stream
     pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
+    /// *preferred* bot enforced outbound chat client msg stream
+    pub chat : Mutex<Chat>,
     /// joined channels
     botchannels: Vec<String>,
     /// admin chatters
@@ -41,8 +44,8 @@ pub struct Bot
     chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
     /// Message cache
     message_cache: Mutex<Vec<PrivmsgMessage>>,
-    /// channel_quiet
-    channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
+    // /// channel_quiet
+    // channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
     
 }
 
@@ -57,7 +60,8 @@ impl Bot
     /// - bot_channels
     /// - prefix
     /// - bot_admins
-    pub async fn new() -> Bot {
+    // #[async_recursion]
+    pub async fn new() -> Arc<Bot> {
 
 
         
@@ -82,7 +86,10 @@ impl Bot
     /// Creates a new `Bot` using bot information
     /// 
     /// Bot will join `botchannels` argument
-    pub async fn new_from(bot_login_name:String,oauth_token:String,prefix:String,botchannels:Vec<String>) -> Bot {
+    pub async fn new_from(bot_login_name:String,
+        oauth_token:String,
+        prefix:String,
+        botchannels:Vec<String>) -> Arc<Bot> {
 
         dotenv().ok();
         let bot_login_name = bot_login_name;
@@ -111,7 +118,8 @@ impl Bot
         let bot = Bot {
              prefix,
              incoming_msgs : Mutex::new(incoming_messages),
-             client,
+             client : client.clone(),
+             chat : Mutex::new(Chat::new(client).await),
              botchannels : botchannels_all,
              listeners : Mutex::new(vec![]),
              commands : Mutex::new(vec![]),
@@ -120,31 +128,61 @@ impl Bot
              channel_module_status: RwLock::new(vec![]),
              chatter_guest_badges: Mutex::new(vec![]),
              message_cache : Mutex::new(vec![]),
-             channel_quiet_yn : RwLock::new(vec![]),
+            //  channel_quiet_yn : RwLock::new(vec![]),
         };
 
+        async fn load_modules(bot:Bot) -> Bot {
+            // let mut bot1 = bot;
+
+        // bot1.chat = Some(Chat::new(client, bot1));
+
         for cmd in built_in_objects::create_commands() {
-            bot.load_command(cmd).await;
+            bot.load_command(cmd).await;        
         }
+        built_in_mods::load_built_in_mods(&bot).await;
 
         bot
+
+        }
+
+        let bot = load_modules(bot).await;
+
+
+        let bot = Arc::new(bot);
+
+        // let lock = bot.chat.lock().await;
+
+        // *lock = Some(Chat::new(chat, bot.clone()));
+
+        // let cht = Chat::new(chat).await;
+
+        // bot.chat.lock()
+
+        // lock.set_parent_bot(bot.clone());
+
+        println!("Joined - {:?}",bot.botchannels);
+        
+
+        bot.clone()
     }
 
     /// Runs the bot 
-    pub async fn run(self) {
+    pub async fn run(self:Arc<Self>) {
 
         for chnl in &self.botchannels {
             self.client.join(chnl.to_owned()).unwrap();
         }
 
-        let bot = Arc::new(self);
+        // let bot = Arc::new(self);
+        let bot = self;
 
         let join_handle = tokio::spawn(async move {
             
             let a = bot.clone();
             let mut in_msgs_lock = a.incoming_msgs.lock().await;
             
-            while let Some(message) = in_msgs_lock.recv().await {   
+            while let Some(message) = in_msgs_lock.recv().await { 
+                // dbg!(message.clone())  ;
 
                 let bot_listener_lock = bot.listeners.lock().await;
                 for listener in bot_listener_lock.iter() {
@@ -429,39 +467,39 @@ impl Bot
         rslt
     }
 
-    /// Get the quiet status of a channel
-    pub fn get_channel_quiet(&self,channel:String) -> bool {
-        for a in self.channel_quiet_yn.read().unwrap().iter() {
-            if a.0 == channel {
-                return a.1.read().unwrap().clone();
-            }
-        }
-        return false;
-    }
+    // /// Get the quiet status of a channel
+    // pub fn get_channel_quiet(&self,channel:String) -> bool {
+    //     for a in self.channel_quiet_yn.read().unwrap().iter() {
+    //         if a.0 == channel {
+    //             return a.1.read().unwrap().clone();
+    //         }
+    //     }
+    //     return false;
+    // }
     
-    /// Get the quiet status of a channel
-    pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
-        let mut found = false;
+    // /// Get the quiet status of a channel
+    // pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
+    //     let mut found = false;
 
-        let chnlquiet = self.channel_quiet_yn.read().unwrap();
-        for rec in chnlquiet.iter() {
-            if rec.0 == channel {
-                found = true;
-                let mut status = rec.1.write().unwrap();
-                *status = quiet_on;
-                drop(status);
-            }
-        }
-        drop(chnlquiet);
+    //     let chnlquiet = self.channel_quiet_yn.read().unwrap();
+    //     for rec in chnlquiet.iter() {
+    //         if rec.0 == channel {
+    //             found = true;
+    //             let mut status = rec.1.write().unwrap();
+    //             *status = quiet_on;
+    //             drop(status);
+    //         }
+    //     }
+    //     drop(chnlquiet);
         
-        if !found {
-            // dbg!("set chn quiet > !found channel quiet status");
-            let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
-            chnlquiet.push((channel,RwLock::new(quiet_on)));
-            drop(chnlquiet);
-        }
+    //     if !found {
+    //         // dbg!("set chn quiet > !found channel quiet status");
+    //         let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
+    //         chnlquiet.push((channel,RwLock::new(quiet_on)));
+    //         drop(chnlquiet);
+    //     }
         
-    }
+    // }
 
 }
 
diff --git a/forcebot_core/src/botcore/bot_objects.rs b/forcebot_core/src/botcore/bot_objects.rs
index b0d334d..dd4e6db 100644
--- a/forcebot_core/src/botcore/bot_objects.rs
+++ b/forcebot_core/src/botcore/bot_objects.rs
@@ -21,6 +21,7 @@ pub enum Badge {
 }
 
 
+
 pub type ExecBody = Box<
     dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = Result<String,String>> + Send>> + Send + Sync,
 >;
@@ -107,14 +108,6 @@ pub mod built_in_objects {
     use twitch_irc::message::ServerMessage;
     
     use super::{execution_async,command::Command,Bot,Badge,super::modules::Status};
-    
-    // use super::execution_async;
-    // use super::command::Command;
-    // use super::Bot;
-    // use super::Badge;
-    // use super::super::modules::Status;
-    // ::{execution_async, modules::Status, Badge, Bot, Command};
-
 
 
     /// create a vector of command build in objects
@@ -148,7 +141,7 @@ pub mod built_in_objects {
                     }
                 }
                 if action_taken {
-                    let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+                    let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("Disabled!")).await ;
                 }
             }
             Result::Err("Not Valid message type".to_string()) 
@@ -199,7 +192,7 @@ pub mod built_in_objects {
                         bot_message = bot_message[..250].to_string();
                     }
 
-                    let _ = bot.client.say_in_reply_to(&msg, 
+                    let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
                         format!("Enabled! {}", bot_message)
                     ).await ;
 
@@ -257,7 +250,7 @@ pub mod built_in_objects {
                                 msg.channel_login.clone(), 
                                 Badge::Moderator, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
 
-                                let _ = bot.client.say_in_reply_to(&msg, 
+                                let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
                                     format!("Temp {:?} issued for {:?} minutes",Badge::Moderator,TEMP_BADGE_DUR_MIN)
                                     ).await ;
                             }
@@ -279,8 +272,8 @@ pub mod built_in_objects {
                                 msg.channel_login.clone(), 
                                 Badge::Vip, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
 
-                                let _ = bot.client.say_in_reply_to(&msg, 
-                                    format!("Temp {:?} issued for {:?} minutes",Badge::Vip,TEMP_BADGE_DUR_MIN)
+                                let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
+                                    format!("Temp {:?} issued for {:?} minutes for the bot admin",Badge::Vip,TEMP_BADGE_DUR_MIN)
                                     ).await ;
                             }
                         }
@@ -300,14 +293,14 @@ pub mod built_in_objects {
                                 msg.channel_login.clone(), 
                                 Badge::Broadcaster, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
                                 
-                                let _ = bot.client.say_in_reply_to(&msg, 
-                                format!("Temp {:?} issued for {:?} minutes",Badge::Broadcaster,TEMP_BADGE_DUR_MIN)
+                                let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
+                                format!("Temp {:?} issued for {:?} minutes for the bot admin",Badge::Broadcaster,TEMP_BADGE_DUR_MIN)
                                 ).await ;
                             }
                         }
                     }
                 }
-                // let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+                // let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("Disabled!")).await ;
             }
             Result::Err("Not Valid message type".to_string()) 
         }
diff --git a/forcebot_core/src/botcore/bot_objects/command.rs b/forcebot_core/src/botcore/bot_objects/command.rs
index 7095ca5..b72cc8a 100644
--- a/forcebot_core/src/botcore/bot_objects/command.rs
+++ b/forcebot_core/src/botcore/bot_objects/command.rs
@@ -2,7 +2,7 @@ use std::sync::Arc;
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-// use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
+
 use super::{execution_async,Bot,Badge,command_condition_async};
     
 
@@ -188,17 +188,18 @@ impl Command
             (cmd.custom_cond_fn)(bot.clone(),message.clone()) && (cmd.custom_cond_async)(bot,message).await
         }
 
-        fn quiet_off_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-            !bot.get_channel_quiet(message.channel_login.clone()) 
-            || bot.get_channel_quiet(message.channel_login.clone()) && cmd.commands.contains(&("quiet off".to_string()))
-        }
+        // async fn quiet_off_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
+        //     !bot.chat.lock().await.get_channel_quiet(message.channel_login.clone()) 
+        //     || bot.chat.lock().await.get_channel_quiet(message.channel_login.clone()) && cmd.commands.contains(&("quiet off".to_string()))
+        // }
 
 
         cmd_called(self, bot.clone(), msg.clone()) && 
         caller_badge_ok(self, bot.clone(), msg.clone()).await &&
         admin_only_ok(self, bot.clone(), msg.clone()) &&
-        custom_cond_ok(self, bot.clone(), msg.clone()).await &&
-        quiet_off_ok(self, bot, msg)
+        custom_cond_ok(self, bot.clone(), msg.clone()).await 
+        //  &&
+        // quiet_off_ok(self, bot, msg).await
 
     }
 
diff --git a/forcebot_core/src/botcore/bot_objects/listener.rs b/forcebot_core/src/botcore/bot_objects/listener.rs
index 2895acb..24f7ce3 100644
--- a/forcebot_core/src/botcore/bot_objects/listener.rs
+++ b/forcebot_core/src/botcore/bot_objects/listener.rs
@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-// use tokio::sync::Mutex;
+
 use twitch_irc::message::ServerMessage;
 
 use crate::Module;
@@ -116,20 +116,21 @@ impl Listener
             (list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
         }
 
-        fn quiet_off_ok(list:Arc<Listener>,bot:Arc<Bot>,message:ServerMessage) -> bool {
-            if let ServerMessage::Privmsg(msg) = message {
+        // async fn quiet_off_ok(list:Arc<Listener>,bot:Arc<Bot>,message:ServerMessage) -> bool {
+        //     if let ServerMessage::Privmsg(msg) = message {
                 
-                if let Some(parent_mod) = &*list.parent_module {
-                    return !bot.get_channel_quiet(msg.channel_login) || parent_mod.get_names().contains(&"debug".to_string());
-                }
+        //         if let Some(parent_mod) = &*list.parent_module {
+        //             return !bot.chat.lock().await.get_channel_quiet(msg.channel_login) || parent_mod.get_names().contains(&"debug".to_string());
+        //         }
 
-                return !bot.get_channel_quiet(msg.channel_login) ;
-            }
-            return true; /* quiet is off for non chat msgs */
-        }
+        //         return !bot.chat.lock().await.get_channel_quiet(msg.channel_login) ;
+        //     }
+        //     return true; /* quiet is off for non chat msgs */
+        // }
 
-        defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await &&
-        quiet_off_ok(list, bot, msg)
+        defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await 
+        // &&
+        // quiet_off_ok(list, bot, msg).await
     }
 
     /// executes the listeners executon body
diff --git a/forcebot_core/src/botcore/built_in_mods.rs b/forcebot_core/src/botcore/built_in_mods.rs
new file mode 100644
index 0000000..53e04a3
--- /dev/null
+++ b/forcebot_core/src/botcore/built_in_mods.rs
@@ -0,0 +1,14 @@
+// use std::sync::Arc;
+
+use crate::Bot;
+
+pub mod quiet;
+
+
+/// used to internally load internal modules
+pub async fn load_built_in_mods(bot:&Bot){
+
+    bot.load_module(quiet::create_module()).await;
+
+}
+
diff --git a/forcebot_core/src/custom_mods/quiet.rs b/forcebot_core/src/botcore/built_in_mods/quiet.rs
similarity index 79%
rename from forcebot_core/src/custom_mods/quiet.rs
rename to forcebot_core/src/botcore/built_in_mods/quiet.rs
index a9827f6..865c81b 100644
--- a/forcebot_core/src/custom_mods/quiet.rs
+++ b/forcebot_core/src/botcore/built_in_mods/quiet.rs
@@ -44,8 +44,15 @@ fn cmd_quiet_on() -> Command {
         if let ServerMessage::Privmsg(msg) = message {
             
             // dbg!("quiet on called");
-            bot.set_channel_quiet(msg.channel_login.clone(), true);
+
+            let chatlock = bot.chat.lock().await;
+            let _=chatlock.say_in_reply_to(&msg, "Shush ".to_string()).await;
+                
+            chatlock.set_channel_quiet(msg.channel_login.clone(), true);
             println!("channel {} set quiet true",msg.channel_login);
+           
+
+            return Result::Ok("Success".to_string());
         }
         Result::Err("Not Valid message type".to_string()) 
     }
@@ -68,7 +75,12 @@ fn cmd_quiet_off() -> Command {
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
         if let ServerMessage::Privmsg(msg) = message {
             
-            bot.set_channel_quiet(msg.channel_login.clone(), false);
+            let chatlock = bot.chat.lock().await;
+           
+            chatlock.set_channel_quiet(msg.channel_login.clone(), false);
+            let _=chatlock.say_in_reply_to(&msg, "GoodGirl I'll be good for u chat rar ".to_string()).await;
+          
+            
             println!("channel {} set quiet false",msg.channel_login);
         }
         Result::Err("Not Valid message type".to_string()) 
diff --git a/forcebot_core/src/botcore/chat.rs b/forcebot_core/src/botcore/chat.rs
new file mode 100644
index 0000000..d548a4f
--- /dev/null
+++ b/forcebot_core/src/botcore/chat.rs
@@ -0,0 +1,151 @@
+use std::{fmt::Error, ops::Mul, rc::Rc, sync::{Arc, Mutex, RwLock}};
+
+use twitch_irc::{login::StaticLoginCredentials, message::ReplyToMessage, SecureTCPTransport, TwitchIRCClient};
+
+use crate::Bot;
+
+/// Bot API to send messages to send messages to chat
+/// 
+/// Uses TwitchIRCClient say_in_reply_to() but enforces controls like quiet
+/// 
+/// 
+
+pub struct Chat 
+{
+    /// outbound chat client msg stream
+    pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
+    /// channel_quiet
+    channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
+    
+}
+
+impl Chat {
+
+
+    pub async fn new(client:TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>)
+     -> Chat {
+        Chat { 
+            client,
+            // parent_bot : Mutex::new(Bot::new().await) ,
+            channel_quiet_yn : RwLock::new(vec![]),
+         }
+    }
+
+    // pub fn set_parent_bot(&self,parent_bot_in:Arc<Bot>)
+    // {
+    //     let mut lock = self.parent_bot.lock().unwrap();
+    //     *lock = parent_bot_in;
+    // }
+
+    /// helper
+    fn ok_to_send(&self,channel_login: String) -> bool {
+
+        fn not_quiet_ok(chat:&Chat,channel_login:String) -> bool {
+            // let lock = chat.parent_bot.lock().unwrap();
+            // let a = lock.as_ref();
+            // if let Some(bot) = &*lock {
+            return !chat.get_channel_quiet(channel_login);
+            // }
+            // true            
+        }
+        not_quiet_ok(self, channel_login)
+
+    }
+
+    
+    /// Get the quiet status of a channel
+    pub fn get_channel_quiet(&self,channel:String) -> bool {
+        for a in self.channel_quiet_yn.read().unwrap().iter() {
+            if a.0 == channel {
+                return a.1.read().unwrap().clone();
+            }
+        }
+        return false;
+    }
+    
+    /// Get the quiet status of a channel
+    pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
+        let mut found = false;
+
+        let chnlquiet = self.channel_quiet_yn.read().unwrap();
+        for rec in chnlquiet.iter() {
+            if rec.0 == channel {
+                found = true;
+                let mut status = rec.1.write().unwrap();
+                *status = quiet_on;
+                drop(status);
+            }
+        }
+        drop(chnlquiet);
+        
+        if !found {
+            // dbg!("set chn quiet > !found channel quiet status");
+            let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
+            chnlquiet.push((channel,RwLock::new(quiet_on)));
+            drop(chnlquiet);
+        }
+        
+    }
+
+
+
+    pub async fn say_in_reply_to(&self,
+            reply_to: &impl ReplyToMessage,
+            message: String
+    ) -> Result<(),()> {
+        // reply_to.channel_login()
+        if self.ok_to_send(reply_to.channel_login().to_string()) {
+            match self.client.say_in_reply_to(reply_to, message).await {
+                Ok(_) => return Ok(()),
+                Err(_) => return Err(()),
+            }
+        } else {
+            return Err(());
+        }
+        
+    }
+
+    pub async fn say(&self,
+        channel_login: String,
+        message: String,
+    ) -> Result<(),()> {
+        if self.ok_to_send(channel_login.to_string()) {
+match self.client.say(channel_login, message).await {
+    Ok(_) => return Ok(()),
+    Err(_) => return Err(()),
+        }
+        } else {
+            return Err(());
+        }
+    }
+
+    pub async fn me(&self,
+        channel_login: String,
+        message: String,
+    ) -> Result<(),()> {
+        if self.ok_to_send(channel_login.to_string()) {
+match self.client.me(channel_login, message).await {
+    Ok(_) => return Ok(()),
+    Err(_) => return Err(()),
+        }
+        } else {
+            return Err(());
+        }
+
+    }
+
+    pub async fn me_in_reply_to(&self,
+        reply_to: &impl ReplyToMessage,
+        message: String
+) -> Result<(),()> {
+    if self.ok_to_send(reply_to.channel_login().to_string()) {
+    match self.client.me_in_reply_to(reply_to, message).await {
+        Ok(_) => return Ok(()),
+        Err(_) => return Err(()),
+    }
+            } else {
+                return Err(());
+            }
+
+            }
+}
\ No newline at end of file
diff --git a/forcebot_core/src/custom_mods.rs b/forcebot_core/src/custom_mods.rs
index 49848a1..754b7b9 100644
--- a/forcebot_core/src/custom_mods.rs
+++ b/forcebot_core/src/custom_mods.rs
@@ -1,4 +1,4 @@
 pub mod guest_badge;
 pub mod pyramid;
 pub mod debug;
-pub mod quiet;
\ No newline at end of file
+// pub mod quiet;
\ No newline at end of file
diff --git a/forcebot_core/src/custom_mods/guest_badge.rs b/forcebot_core/src/custom_mods/guest_badge.rs
index 8577bf9..39c121d 100644
--- a/forcebot_core/src/custom_mods/guest_badge.rs
+++ b/forcebot_core/src/custom_mods/guest_badge.rs
@@ -79,7 +79,7 @@ fn create_cmd_vip() -> Command {
             if badges_issued { 
            
                 
-            let _= bot.client.say_in_reply_to(
+            let _= bot.chat.lock().await.say_in_reply_to(
                 &msg.clone(), format!("Guest badges issued for {} min",guest_dur_min)).await;
                 return Result::Ok("Success".to_string());
             }
@@ -123,7 +123,7 @@ fn create_cmd_mod() -> Command {
                 }
             } 
             if badges_issued {
-            let _= bot.client.say_in_reply_to(
+            let _= bot.chat.lock().await.say_in_reply_to(
                 &msg, format!("Guest badges issued for {} min",MOD_GIVEN_DUR_MIN)).await;
                 return Result::Ok("Success".to_string());
             }
diff --git a/forcebot_core/src/custom_mods/pyramid.rs b/forcebot_core/src/custom_mods/pyramid.rs
index 0ea950b..8b36395 100644
--- a/forcebot_core/src/custom_mods/pyramid.rs
+++ b/forcebot_core/src/custom_mods/pyramid.rs
@@ -51,7 +51,7 @@ fn create_pyramid_detector() -> Listener {
         /* 2. Define an async trigger condition callback */
         async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
             if let ServerMessage::Privmsg(msg) = message {
-                if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
+                if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await && get_pyramid_size(msg.channel_login) > 3 {
                     return true;
                 }
             }
@@ -65,11 +65,33 @@ fn create_pyramid_detector() -> Listener {
         /* 4. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
+                dbg!("enter pyramid listener execution - after pyramid complete");
                 // if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
 
-                    let _ = bot.client.say_in_reply_to(&msg, "Clap".to_string()).await ;
+                    // let _ = bot.chat.lock().await.
+                    dbg!("> get start pattern");
+                    let pattern = get_start_pattern(msg.channel_login.clone());
+                    let mut outmsg ;
 
-                    set_pyramid_started(msg.channel_login,false);
+                    /* Prefer emote before pattern in case pattern is command */
+                    if pattern.len() < 50 {
+                        outmsg = format!("Clap {}",pattern);
+                    } else {
+                        outmsg = "Clap".to_string();
+                    }
+
+                    dbg!(get_pyramid_size(msg.channel_login.clone()));
+                    if get_pyramid_size(msg.channel_login.clone()) < 4 {
+                        outmsg = format!("{} annytfMagniGlass",outmsg);
+                    }
+                    
+                    dbg!("> start pattern :",pattern);
+                    
+                    dbg!("> say_in_reply_to completed :",outmsg.clone());
+                    let _ = bot.chat.lock().await.say_in_reply_to(&msg, outmsg).await ;
+
+                    dbg!("> set pyramid started - false");
+                    set_pyramid_started(msg.channel_login.clone(),false);
 
                     return Result::Ok("Success".to_string()) ;
                 // }
@@ -84,17 +106,22 @@ fn create_pyramid_detector() -> Listener {
 
 }
 
-
+/// detect pyramid based on latest message and channel
+/// 
+/// 
 async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
+    dbg!("enter detect_pyramid_complete()");
 
-    let msgtext = msg.message_text.replace("\u{e0000}","").trim().to_string();
+    let msgtext = msg.message_text.replace("󠀀","").replace("\u{e0000}","").trim().to_string();
     let msgchannel = msg.channel_login;
     let msgchatter = msg.sender.login;
 
     // 1. Check if Pyramid started in chat > and recognize pyramid started
     if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
+        dbg!("> set pyramid started - true");
         set_pyramid_started(msgchannel.clone(),true);
         push_to_compare(msgchannel.clone(),msgchatter.clone(),get_start_pattern(msgchannel.clone()));
+        
                 
     }
 
@@ -109,15 +136,29 @@ async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
 
     // 2b. If Pyramid is Started, and the latest message is the pattern, check for
     // symmetry to determine pyramid 
+
     if is_pyramid_started(msgchannel.clone()) && msgtext.clone() == get_start_pattern(msgchannel.clone()) {
         if symmetry_ok(msgchannel.clone()) {
             return true;
         } else {
+            dbg!("> set pyramid started - false");
+            set_pyramid_started(msgchannel,false);
+
             return false ; 
         }
+    }
+
+    // 2c. if Pyramid is strted but latest message does not ontain pattern
+    if is_pyramid_started(msgchannel.clone()) && !msgtext.clone().contains( get_start_pattern(msgchannel.clone()).as_str()) {
+    
+        dbg!("> set pyramid started - false");
+        set_pyramid_started(msgchannel,false);
+
+        return false ; 
     } else {
         return false;
-    }
+
+    };
 
 }
 
@@ -130,6 +171,8 @@ lazy_static!{
     pub static ref PYRAMID_STARTED_PER_CHNL:  Mutex<Vec<(String,Mutex<bool>)>> = Mutex::new(vec![]);
     /// Start patterns per channel (channel:String,pattern:String)
     pub static ref START_PATTERNS_PER_CHNL: Mutex<Vec<(String,Mutex<String>)>> = Mutex::new(vec![]);
+    /// Pyramid sze per channel (channel:String,started:bool)
+    pub static ref PYRAMID_SIZE_PER_CHNL:  Mutex<Vec<(String,Mutex<i32>)>> = Mutex::new(vec![]);
     /// temp message stack checker 
     pub static ref TEMP_MSG_STACK: Mutex<Vec<String>> = Mutex::new(vec![]);
     
@@ -249,6 +292,11 @@ fn push_to_compare(channel:String,chatter:String,message:String) {
 /// checks latest and next latest messages for potential start
 fn check_start_pyramid(channel:String,msgtext: String) -> bool {
     msgtext == format!("{} {}",get_start_pattern(channel.clone()),get_start_pattern(channel.clone()))
+    // msgtext == format!("{} {} {}",
+    //     get_start_pattern(channel.clone()),
+    //     get_start_pattern(channel.clone()),
+    //     get_start_pattern(channel.clone())
+    // )
 }
 
 
@@ -259,13 +307,17 @@ fn symmetry_ok(channel:String) -> bool {
     if !(read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone())) {
         return false;
     }
+
+    let mut pyramid_size = 0;
     loop {
         
         if !checking_started && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone()) {  
             checking_started = true;
         } 
+
         if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
             temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1);
+            pyramid_size += 1;
             
         } else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
     
@@ -276,13 +328,20 @@ fn symmetry_ok(channel:String) -> bool {
                 continue;
             } else {
                 
+                set_pyramid_size(channel.clone(), 0);
                 temp_stack.clear();
                 return false;
             }
 
-        } else { return false; }
+        } else { 
+            set_pyramid_size(channel.clone(), 0);
+            return false; 
+        }
+
         if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {  
             
+            /* leave pyramid size set for exection */
+            set_pyramid_size(channel.clone(), pyramid_size*2-1);
             temp_stack.clear();
             return true;
         } 
@@ -293,6 +352,32 @@ fn symmetry_ok(channel:String) -> bool {
 }
 
 
+fn set_pyramid_size(channel:String,size:i32) {
+    let mut size_perchnl = PYRAMID_SIZE_PER_CHNL.lock().unwrap();
+    let mut found = false;
+    for rec in size_perchnl.iter() {
+        if rec.0 == channel {
+            found = true;
+            let mut rec_started = rec.1.lock().unwrap();
+            *rec_started = size;
+        } 
+    }
+    if !found {
+        size_perchnl.push((channel,Mutex::new(size)));
+    }
+}
+
+fn get_pyramid_size(channel:String) -> i32 {
+    let size_perchnl = PYRAMID_SIZE_PER_CHNL.lock().unwrap();
+    for rec in size_perchnl.iter() {
+        if rec.0 == channel {
+            let rec_started = rec.1.lock().unwrap();
+            return *rec_started;
+        }
+    }
+    0
+}
+
 /// #todo
 /// 
 /// pyramid interruptor 
@@ -316,7 +401,7 @@ fn _create_interruptor_cmd() -> Command {
         /* 2. Define an async fn callback execution */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
+                let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
                 return Result::Ok("Success".to_string()) ;
             }
             Result::Err("Not Valid message type".to_string()) 
diff --git a/moderator_reactor/src/main.rs b/moderator_reactor/src/main.rs
index 528b62e..5705fbe 100644
--- a/moderator_reactor/src/main.rs
+++ b/moderator_reactor/src/main.rs
@@ -46,7 +46,7 @@ pub async fn main() {
     /* 3. Define an async fn callback execution */
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
         if let ServerMessage::Privmsg(msg) = message {
-            let _ = bot.client.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
+            let _ = bot.chat.lock().await.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
             return Result::Ok("Success".to_string()) ;
         }
         Result::Err("Not Valid message type".to_string()) 
diff --git a/readme.md b/readme.md
index c4e81d0..aa9355c 100644
--- a/readme.md
+++ b/readme.md
@@ -27,23 +27,41 @@ cargo run -p forcebot_core
 
 # Features
 
-- Quick Start to use full feature set bot
-- Moderators & Broadcasters can `disable` or `enable` `Modules` of bot functionality through chat `Commands`
-- Full Feature Set `forcebot_core` bot has the following modules loaded
-    - `debug` - outputs to console messages from the channel where it was enabled. Toggle debug with the Commands `debug on` or `debug off`
-    - `guest_badge` - Temporary badges can be issued to chatters
-    - `besty` - Tomfoolery
-    - `pyramid` - for detecting & handling pyramids
+## Built In Chat Commands
+
+- `quiet on` / `quiet off` - Moderators & Broadcasters can quiet the bot
+
+- `enable $module$` / `disable $module$` - Moderators & Broadcasters can enable or disable `Modules` of bot functionality through chat `Commands`
+
+
+## Custom Modules can be coded to load additional functionality 
+
+Developers an create Modules that add more bot functionality
+
+The main `forcebot_core` Binary crate includes the following Custom `Modules`  
+   
+- `debug` - outputs to console messages from the channel where it was enabled. Toggle debug with the Commands `debug on` or `debug off`
+- `guest_badge` - Temporary badges can be issued to chatters
+- `besty` - Tomfoolery
+- `pyramid` - for detecting & handling pyramids
+
+
+## `forcebot_core` Bot Library
+
 - `forcebot_core` library API provides Custom package developers a way to add functionality by adding `Modules` that contain Bot Objects like `Commands` and `Listeners`
 - `Listeners` and `Commands` listen for a defined callback trigger condition and run an defined execution callback 
 - `Commands` are similar to `Listeners` with refined trigger conditions including using bot `prefix` with the `Command` , triggers based on `Badge` , and more
 - Workspace for package developers to independently code their own `Modules`
-- Workspace comes with binary crates with working or example bots that use `forcebot_core` library 
-    - `moderator_reactor` - bot kneels to all moderator messages
-    - `simple_module_example` - bot has a `test` `Module` with a `test` `Command` .Moderators & Broadcasters can manage the `Module` in chat with `enable` / `disable` `Commands`
-    - `new_empty_bot` - while empty, has `disable` and `enable` chat `Commands` . This is an example of the bot without any loaded modules
-    - `simple_command_bot` - bot responds to a `test` `Command`. As the command was not loaded through a `Module`, `disable` & `enable` commands don't work on the `test` command. This could be a Global `Command`  
-    - `simple_debug_listener` - bot outputs all twitch `ServerMessages` received to terminal
+
+## Workspaces
+
+
+Workspace comes with binary crates with working or example bots that use `forcebot_core` library 
+- `moderator_reactor` - bot kneels to all moderator messages
+- `simple_module_example` - bot has a `test` `Module` with a `test` `Command` . Moderators & Broadcasters can manage the `Module` in chat with `enable` / `disable` `Commands`
+- `new_empty_bot` - while empty, has `disable` and `enable` chat `Commands` . This is an example of the bot without any loaded modules
+- `simple_command_bot` - bot responds to a `test` `Command`. As the command was not loaded through a `Module`, `disable` & `enable` commands don't work on the `test` command. This could be a Global `Command`  
+- `simple_debug_listener` - bot outputs all twitch `ServerMessages` received to terminal
     
 
 
@@ -150,7 +168,7 @@ use forcebot_core::Bot;
 pub async fn main() {
 
     /* 1. Create the bot using env */
-    let bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 2. Run the bot */
     bot.run().await;
@@ -165,18 +183,24 @@ A `Module` is a group of bot objects (eg `Command`) that elevated users can mana
 Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
 
 ```rust
-use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
+use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
+
 
 
 #[tokio::main]
 pub async fn main() {
 
-    /* 1. Create the bot using env */
-    let mut bot = Bot::new();
+    /* Create the bot using env */
+    let bot = Bot::new().await;
+
+    /* 1. Load the module into the bot */
+    bot.load_module(funbot_objs::create_module()).await;
 
     /* 2. Load Custom Modules */
     bot.load_module(guest_badge::create_module()).await;
     bot.load_module(pyramid::create_module()).await;
+    bot.load_module(debug::create_module()).await;
+    
     
     /* 3. Run the bot */
     bot.run().await;
@@ -195,13 +219,14 @@ Create a custom `Module` by :
 
 
 ```rust
+
 use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* load the Module */
     bot.load_module(custom_mod::new()).await;
@@ -211,6 +236,7 @@ pub async fn main() {
 
 }
 
+
 pub mod custom_mod {
     use std::sync::Arc;
 
@@ -240,7 +266,7 @@ pub mod custom_mod {
         /* 2. Define exec callback  */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.client.say_in_reply_to(
+                let _= bot.chat.lock().await.say_in_reply_to(
                     &msg, "test return".to_string()).await;
             }
             Result::Err("Not Valid message type".to_string()) 
@@ -249,7 +275,7 @@ pub mod custom_mod {
         /* 3. Set Command flags */
         cmd.set_exec_fn(execution_async(execbody));
         cmd.set_admin_only(false);
-        cmd.set_min_badge(Badge::Moderator);
+        cmd.set_min_badge(Badge::Vip);
 
         cmd
     }
@@ -260,6 +286,7 @@ pub mod custom_mod {
 Bot with a simple listener that listens for all messages and prints in output
 
 ```rust
+
 use std::sync::Arc;
 
 use forcebot_core::{execution_async, Bot, Listener};
@@ -269,7 +296,7 @@ use twitch_irc::message::ServerMessage;
 pub async fn main() {
 
     /* 1. Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 2a. Create a new blank Listener */
     let mut listener = Listener::new();
@@ -289,12 +316,13 @@ pub async fn main() {
     listener.set_exec_fn(execution_async(execbody));
 
     /* 3. Load the listener into the bot */
-    bot.load_listener(listener);
+    bot.load_listener(listener).await;
 
     /* 4. Run the bot */
     bot.run().await;
 
 }
+
 ```
 
 ## Moderator Reactor
@@ -302,6 +330,7 @@ pub async fn main() {
 Example listener listens for a moderator badge and reply in chat 
 
 ```rust
+
 use std::sync::Arc;
 
 use forcebot_core::Bot;
@@ -314,7 +343,7 @@ use twitch_irc::message::ServerMessage;
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 1. Create a new blank Listener */
     let mut listener = Listener::new();
@@ -324,7 +353,6 @@ pub async fn main() {
     listener.set_trigger_cond_fn(
         |_:Arc<Bot>,message:ServerMessage| 
             if let ServerMessage::Privmsg(msg) = message {
-                
                 for badge in msg.badges {
                     if matches!(badge, x if x.name == "moderator") {
                         // dbg!("moderator found");
@@ -338,7 +366,7 @@ pub async fn main() {
     /* 3. Define an async fn callback execution */
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
         if let ServerMessage::Privmsg(msg) = message {
-            let _ = bot.client.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
+            let _ = bot.chat.lock().await.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
             return Result::Ok("Success".to_string()) ;
         }
         Result::Err("Not Valid message type".to_string()) 
@@ -348,12 +376,13 @@ pub async fn main() {
     listener.set_exec_fn(execution_async(execbody));
 
     /* 5. Load the listener into the bot */
-    bot.load_listener(listener);
+    bot.load_listener(listener).await;
 
     /* Run the bot */
     bot.run().await;
 
 }
+
 ```
 
 ## Simple Test Command
@@ -367,11 +396,12 @@ use forcebot_core::execution_async;
 use forcebot_core::Command;
 use twitch_irc::message::ServerMessage;
 
+
 #[tokio::main]
 pub async fn main() {
 
     /* Create the bot using env */
-    let mut bot = Bot::new();
+    let bot = Bot::new().await;
 
     /* 1. Create a new blank cmd */
     let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
@@ -379,7 +409,7 @@ pub async fn main() {
     /* 2. Define an async fn callback execution */
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
         if let ServerMessage::Privmsg(msg) = message {
-            let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
+            let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
             return Result::Ok("Success".to_string()) ;
         }
         Result::Err("Not Valid message type".to_string()) 
@@ -395,12 +425,13 @@ pub async fn main() {
     cmd.set_min_badge(Badge::Moderator);
 
     /* 6. Load the cmd into the bot */
-    bot.load_command(cmd);
+    bot.load_command(cmd).await;
 
     /* Run the bot */
     bot.run().await;
 
 }
+
 ```
 
 # Crate Rust API Documentation 
diff --git a/simple_command_bot/src/main.rs b/simple_command_bot/src/main.rs
index f231f0c..418e1eb 100644
--- a/simple_command_bot/src/main.rs
+++ b/simple_command_bot/src/main.rs
@@ -34,7 +34,7 @@ pub async fn main() {
     /* 2. Define an async fn callback execution */
     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
         if let ServerMessage::Privmsg(msg) = message {
-            let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
+            let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
             return Result::Ok("Success".to_string()) ;
         }
         Result::Err("Not Valid message type".to_string()) 
diff --git a/simple_module_example/src/main.rs b/simple_module_example/src/main.rs
index 3851d34..e588b22 100644
--- a/simple_module_example/src/main.rs
+++ b/simple_module_example/src/main.rs
@@ -63,7 +63,7 @@ pub mod custom_mod {
         /* 2. Define exec callback  */
         async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.client.say_in_reply_to(
+                let _= bot.chat.lock().await.say_in_reply_to(
                     &msg, "test return".to_string()).await;
             }
             Result::Err("Not Valid message type".to_string()) 
-- 
2.49.0


From cd69a35ec1b3e65ee72213a26060bcccd8b73f1c Mon Sep 17 00:00:00 2001
From: modulatingforce <modulatingforce@gmail.com>
Date: Thu, 6 Feb 2025 09:41:54 -0500
Subject: [PATCH 14/14] cargo-fmt

---
 forcebot_core/src/bin/fun_bot.rs              |  53 +--
 forcebot_core/src/bin/new_bot.rs              |   8 +-
 forcebot_core/src/bin/simple_module.rs        |  43 +-
 forcebot_core/src/botcore.rs                  |   4 +-
 forcebot_core/src/botcore/bot.rs              | 334 +++++++--------
 forcebot_core/src/botcore/bot_objects.rs      | 288 ++++++++-----
 .../src/botcore/bot_objects/command.rs        | 218 +++++-----
 .../src/botcore/bot_objects/listener.rs       | 124 +++---
 forcebot_core/src/botcore/built_in_mods.rs    |   6 +-
 .../src/botcore/built_in_mods/quiet.rs        |  48 +--
 forcebot_core/src/botcore/chat.rs             | 121 +++---
 forcebot_core/src/botcore/modules.rs          |  30 +-
 forcebot_core/src/custom_mods.rs              |   4 +-
 forcebot_core/src/custom_mods/debug.rs        | 104 +++--
 forcebot_core/src/custom_mods/guest_badge.rs  | 165 ++++---
 forcebot_core/src/custom_mods/pyramid.rs      | 403 +++++++++---------
 forcebot_core/src/lib.rs                      | 114 +++--
 moderator_reactor/src/main.rs                 |  51 +--
 new_empty_bot/src/main.rs                     |   8 +-
 simple_command_bot/src/main.rs                |  30 +-
 simple_debug_listener/src/main.rs             |  16 +-
 simple_module_example/src/main.rs             |  43 +-
 22 files changed, 1140 insertions(+), 1075 deletions(-)

diff --git a/forcebot_core/src/bin/fun_bot.rs b/forcebot_core/src/bin/fun_bot.rs
index abbc426..b506faf 100644
--- a/forcebot_core/src/bin/fun_bot.rs
+++ b/forcebot_core/src/bin/fun_bot.rs
@@ -1,30 +1,30 @@
 //! WIP Fun forcebot with catered customizations #todo
-//! 
+//!
 //! Custom modules that can be managed in chat through `disable` and `enable` commands
 //! - `besty` - uses a custom prefix tp trigger
 //! - `guests`
 //! - `pyramid`
 //! - `quiet`
-//! 
-//! 
-//! Be sure the followig is defined in `.env` 
+//!
+//!
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
-use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
-
-
+use forcebot_core::{
+    custom_mods::{debug, guest_badge, pyramid},
+    Bot,
+};
 
 #[tokio::main]
 pub async fn main() {
-
     /* Create the bot using env */
     let bot = Bot::new().await;
 
@@ -33,14 +33,11 @@ pub async fn main() {
     bot.load_module(guest_badge::create_module()).await;
     bot.load_module(pyramid::create_module()).await;
     bot.load_module(debug::create_module()).await;
-    
-    
+
     /* 3. Run the bot */
     bot.run().await;
-
 }
 
-
 pub mod funbot_objs {
     use std::sync::Arc;
 
@@ -50,8 +47,9 @@ pub mod funbot_objs {
     /// Create a Module with a loaded Command object
     pub fn create_module() -> Module {
         let mut custom_mod = Module::new(
-            vec!["besty".to_string()], 
-            "Now Aware of besty xdd666 ".to_string());
+            vec!["besty".to_string()],
+            "Now Aware of besty xdd666 ".to_string(),
+        );
 
         custom_mod.load_command(create_cmd_test());
         // custom_mod.set_status_by_default(Status::Disabled);
@@ -61,16 +59,22 @@ pub mod funbot_objs {
 
     /// Create a Command Object
     fn create_cmd_test() -> Command {
-    
-        let mut cmd = Command::new(vec!["remind besty".to_string()],"annytfYandere ".to_string());
+        let mut cmd = Command::new(
+            vec!["remind besty".to_string()],
+            "annytfYandere ".to_string(),
+        );
 
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.chat.lock().await.say_in_reply_to(
-                    &msg, "annytfYandere he's mine".to_string()).await;
-                    return Result::Ok("Success".to_string());
+                let _ = bot
+                    .chat
+                    .lock()
+                    .await
+                    .say_in_reply_to(&msg, "annytfYandere he's mine".to_string())
+                    .await;
+                return Result::Ok("Success".to_string());
             }
-            Result::Err("Not Valid message type".to_string()) 
+            Result::Err("Not Valid message type".to_string())
         }
 
         cmd.set_exec_fn(execution_async(execbody));
@@ -79,6 +83,5 @@ pub mod funbot_objs {
         cmd.set_min_badge(Badge::Vip);
 
         cmd
-
     }
-}
\ No newline at end of file
+}
diff --git a/forcebot_core/src/bin/new_bot.rs b/forcebot_core/src/bin/new_bot.rs
index a81462e..856f6b1 100644
--- a/forcebot_core/src/bin/new_bot.rs
+++ b/forcebot_core/src/bin/new_bot.rs
@@ -1,12 +1,12 @@
 //! Example simple Binary crate that creates & runs bot based on `.env`
-//! Be sure the followig is defined in `.env` 
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
@@ -14,11 +14,9 @@ use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
-
     /* 1. Create the bot using env */
     let bot = Bot::new().await;
 
     /* 2. Run the bot */
     bot.run().await;
-
 }
diff --git a/forcebot_core/src/bin/simple_module.rs b/forcebot_core/src/bin/simple_module.rs
index 3e36d90..477da78 100644
--- a/forcebot_core/src/bin/simple_module.rs
+++ b/forcebot_core/src/bin/simple_module.rs
@@ -1,19 +1,19 @@
 //! Simple Module with a Command
-//! 
-//! Adding objects through packages provides controls , 
+//!
+//! Adding objects through packages provides controls ,
 //! such as moderators, and brodcasters can disable or enable mods
-//! 
-//! Here, moderators or above can enable or disable the `test` 
+//!
+//! Here, moderators or above can enable or disable the `test`
 //! module with the command `<prefix> disable test`
-//! 
-//! Be sure the followig is defined in `.env` 
+//!
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
@@ -21,7 +21,6 @@ use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
-
     /* Create the bot using env */
     let bot = Bot::new().await;
 
@@ -30,43 +29,41 @@ pub async fn main() {
 
     /* Run the bot */
     bot.run().await;
-
 }
 
-
 pub mod custom_mod {
     use std::sync::Arc;
 
     use forcebot_core::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
-
     /// Module definition with a loaded command
     pub fn new() -> Module {
         /* 1. Create a new module */
-        let mut custom_mod = Module::new(
-            vec!["test".to_string()], 
-            "".to_string());
+        let mut custom_mod = Module::new(vec!["test".to_string()], "".to_string());
 
         /* 2. Load the cmd into a new module */
         custom_mod.load_command(cmd_test());
 
         custom_mod
-
     }
 
-    /// Command definition 
+    /// Command definition
     pub fn cmd_test() -> Command {
         /* 1. Create a new cmd */
-        let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
+        let mut cmd = Command::new(vec!["test".to_string()], "".to_string());
 
         /* 2. Define exec callback  */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.chat.lock().await.say_in_reply_to(
-                    &msg, "test return".to_string()).await;
+                let _ = bot
+                    .chat
+                    .lock()
+                    .await
+                    .say_in_reply_to(&msg, "test return".to_string())
+                    .await;
             }
-            Result::Err("Not Valid message type".to_string()) 
+            Result::Err("Not Valid message type".to_string())
         }
 
         /* 3. Set Command flags */
@@ -76,4 +73,4 @@ pub mod custom_mod {
 
         cmd
     }
-}
\ No newline at end of file
+}
diff --git a/forcebot_core/src/botcore.rs b/forcebot_core/src/botcore.rs
index 99449f0..a750843 100644
--- a/forcebot_core/src/botcore.rs
+++ b/forcebot_core/src/botcore.rs
@@ -1,5 +1,5 @@
 pub mod bot;
 pub mod bot_objects;
-pub mod modules;
 pub mod built_in_mods;
-pub mod chat;
\ No newline at end of file
+pub mod chat;
+pub mod modules;
diff --git a/forcebot_core/src/botcore/bot.rs b/forcebot_core/src/botcore/bot.rs
index cccc61f..f3774a1 100644
--- a/forcebot_core/src/botcore/bot.rs
+++ b/forcebot_core/src/botcore/bot.rs
@@ -1,37 +1,44 @@
-
-
 // use async_recursion::async_recursion;
-use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
-use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, ServerMessage}, SecureTCPTransport, TwitchIRCClient};
 use dotenv::dotenv;
-use std::{env, sync::{Arc, RwLock}, time::{Duration, Instant}};
+use std::{
+    env,
+    sync::{Arc, RwLock},
+    time::{Duration, Instant},
+};
+use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
+use twitch_irc::{
+    login::StaticLoginCredentials,
+    message::{PrivmsgMessage, ServerMessage},
+    SecureTCPTransport, TwitchIRCClient,
+};
 
 // use crate::{Badge, Command, Listener, Module};
 use super::{bot_objects::command::Command, built_in_mods, chat::Chat};
 
-use crate::botcore::{bot_objects::Badge, chat};
-use crate::botcore::bot_objects::listener::Listener;
 use super::super::botcore::modules::Module;
+use crate::botcore::bot_objects::listener::Listener;
+use crate::botcore::{bot_objects::Badge, chat};
 // use super::
 
-use super:: {bot_objects::built_in_objects, modules::{self, Status}};
- 
+use super::{
+    bot_objects::built_in_objects,
+    modules::{self, Status},
+};
 
 /// Twitch chat bot
-pub struct Bot 
-{
+pub struct Bot {
     /// Prefix for commands
     prefix: String,
     /// inbound chat msg stream
     incoming_msgs: Mutex<UnboundedReceiver<ServerMessage>>,
     /// outbound chat client msg stream
-    pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
+    pub client: TwitchIRCClient<SecureTCPTransport, StaticLoginCredentials>,
     /// *preferred* bot enforced outbound chat client msg stream
-    pub chat : Mutex<Chat>,
+    pub chat: Mutex<Chat>,
     /// joined channels
     botchannels: Vec<String>,
     /// admin chatters
-    admins : Vec<String>,
+    admins: Vec<String>,
     /// listeners
     listeners: Mutex<Vec<Listener>>,
     /// commands
@@ -39,21 +46,18 @@ pub struct Bot
     /// modules
     modules: RwLock<Vec<Module>>,
     /// channel module status
-    channel_module_status: RwLock<Vec<(String,String,modules::Status)>>,
+    channel_module_status: RwLock<Vec<(String, String, modules::Status)>>,
     /// chatter guest badges - chatter,channel,Badge,start_time,duration
-    chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
+    chatter_guest_badges: Mutex<Vec<(String, String, Badge, Instant, Duration)>>,
     /// Message cache
     message_cache: Mutex<Vec<PrivmsgMessage>>,
     // /// channel_quiet
     // channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
-    
 }
 
-
-impl Bot
-{
+impl Bot {
     /// Creates a new `Bot` using env variables
-    /// 
+    ///
     /// Be sure the following is defined in an `.env` file
     /// - login_name
     /// - access_token
@@ -62,35 +66,29 @@ impl Bot
     /// - bot_admins
     // #[async_recursion]
     pub async fn new() -> Arc<Bot> {
-
-
-        
         dotenv().ok();
         let bot_login_name = env::var("login_name").unwrap().to_owned();
         let oauth_token = env::var("access_token").unwrap().to_owned();
-        let prefix = env::var("prefix")
-            .unwrap()
-            .to_owned();
+        let prefix = env::var("prefix").unwrap().to_owned();
 
         let mut botchannels = Vec::new();
 
         for chnl in env::var("bot_channels").unwrap().split(',') {
-                botchannels.push(chnl.to_owned());
+            botchannels.push(chnl.to_owned());
         }
 
         Bot::new_from(bot_login_name, oauth_token, prefix, botchannels).await
-       
-
     }
 
     /// Creates a new `Bot` using bot information
-    /// 
+    ///
     /// Bot will join `botchannels` argument
-    pub async fn new_from(bot_login_name:String,
-        oauth_token:String,
-        prefix:String,
-        botchannels:Vec<String>) -> Arc<Bot> {
-
+    pub async fn new_from(
+        bot_login_name: String,
+        oauth_token: String,
+        prefix: String,
+        botchannels: Vec<String>,
+    ) -> Arc<Bot> {
         dotenv().ok();
         let bot_login_name = bot_login_name;
 
@@ -105,49 +103,45 @@ impl Bot
         let mut botchannels_all = Vec::new();
         botchannels_all.extend(botchannels);
 
-
         let mut admins = Vec::new();
 
         if let Ok(value) = env::var("bot_admins") {
             for admin in value.split(',') {
-                admins.push(String::from(admin))        
+                admins.push(String::from(admin))
             }
         }
 
-
         let bot = Bot {
-             prefix,
-             incoming_msgs : Mutex::new(incoming_messages),
-             client : client.clone(),
-             chat : Mutex::new(Chat::new(client).await),
-             botchannels : botchannels_all,
-             listeners : Mutex::new(vec![]),
-             commands : Mutex::new(vec![]),
-             admins,
-             modules:  RwLock::new(vec![]),
-             channel_module_status: RwLock::new(vec![]),
-             chatter_guest_badges: Mutex::new(vec![]),
-             message_cache : Mutex::new(vec![]),
+            prefix,
+            incoming_msgs: Mutex::new(incoming_messages),
+            client: client.clone(),
+            chat: Mutex::new(Chat::new(client).await),
+            botchannels: botchannels_all,
+            listeners: Mutex::new(vec![]),
+            commands: Mutex::new(vec![]),
+            admins,
+            modules: RwLock::new(vec![]),
+            channel_module_status: RwLock::new(vec![]),
+            chatter_guest_badges: Mutex::new(vec![]),
+            message_cache: Mutex::new(vec![]),
             //  channel_quiet_yn : RwLock::new(vec![]),
         };
 
-        async fn load_modules(bot:Bot) -> Bot {
+        async fn load_modules(bot: Bot) -> Bot {
             // let mut bot1 = bot;
 
-        // bot1.chat = Some(Chat::new(client, bot1));
+            // bot1.chat = Some(Chat::new(client, bot1));
 
-        for cmd in built_in_objects::create_commands() {
-            bot.load_command(cmd).await;        
-        }
-        built_in_mods::load_built_in_mods(&bot).await;
-
-        bot
+            for cmd in built_in_objects::create_commands() {
+                bot.load_command(cmd).await;
+            }
+            built_in_mods::load_built_in_mods(&bot).await;
 
+            bot
         }
 
         let bot = load_modules(bot).await;
 
-
         let bot = Arc::new(bot);
 
         // let lock = bot.chat.lock().await;
@@ -160,15 +154,13 @@ impl Bot
 
         // lock.set_parent_bot(bot.clone());
 
-        println!("Joined - {:?}",bot.botchannels);
-        
+        println!("Joined - {:?}", bot.botchannels);
 
         bot.clone()
     }
 
-    /// Runs the bot 
-    pub async fn run(self:Arc<Self>) {
-
+    /// Runs the bot
+    pub async fn run(self: Arc<Self>) {
         for chnl in &self.botchannels {
             self.client.join(chnl.to_owned()).unwrap();
         }
@@ -177,24 +169,21 @@ impl Bot
         let bot = self;
 
         let join_handle = tokio::spawn(async move {
-            
             let a = bot.clone();
             let mut in_msgs_lock = a.incoming_msgs.lock().await;
-            
-            while let Some(message) = in_msgs_lock.recv().await { 
+
+            while let Some(message) = in_msgs_lock.recv().await {
                 // dbg!(message.clone())  ;
 
                 let bot_listener_lock = bot.listeners.lock().await;
                 for listener in bot_listener_lock.iter() {
-                    
                     let a = listener.clone();
-                    if a.cond_triggered(bot.clone(),message.clone()).await {
-                        let _ = listener.execute_fn(bot.clone(),message.clone()).await;
+                    if a.cond_triggered(bot.clone(), message.clone()).await {
+                        let _ = listener.execute_fn(bot.clone(), message.clone()).await;
                     }
                 }
 
                 if let ServerMessage::Privmsg(msg) = message.clone() {
-
                     // let mut cache_lock = bot.message_cache.lock().await;
                     let mut cache_lock = bot.message_cache.lock().await;
                     cache_lock.push(msg.clone());
@@ -203,91 +192,78 @@ impl Bot
 
                     let cmd_lock = bot.commands.lock().await;
                     for cmd in cmd_lock.iter() {
-                    
                         let a = cmd.clone();
-                        if a.command_triggered(bot.clone(),msg.clone()).await {
-                             
-                            let _ = cmd.execute_fn(bot.clone(),message.clone()).await;
+                        if a.command_triggered(bot.clone(), msg.clone()).await {
+                            let _ = cmd.execute_fn(bot.clone(), message.clone()).await;
                         }
                     }
 
+                    fn get_enabled_channel_modules(bot: Arc<Bot>, channel: String) -> Vec<Module> {
+                        let botmodules_lock = bot.modules.read().unwrap();
+                        let botmodules_cpy = botmodules_lock.clone();
+                        drop(botmodules_lock);
 
-                    fn get_enabled_channel_modules(bot: Arc<Bot>,channel:String) -> Vec<Module>  {
+                        let mut enabled_mods = Vec::new();
 
-                    let botmodules_lock = bot.modules.read().unwrap();
-                    let botmodules_cpy = botmodules_lock.clone();
-                    drop(botmodules_lock);
+                        'module_loop: for module in &*botmodules_cpy {
+                            // dbg!("try cms read");
+                            let cms_lock = bot.channel_module_status.read().unwrap();
 
-                    let mut enabled_mods = Vec::new();
-
-                    'module_loop: for module in &*botmodules_cpy {
-                    
-                        // dbg!("try cms read");
-                        let cms_lock = bot.channel_module_status.read().unwrap();
-                        
-                        for channel_flags in cms_lock.iter() {
-                            if channel_flags.0 == channel {
-
-                                if module.get_names().contains(&channel_flags.1) && channel_flags.2 == Status::Disabled {
-                                    continue 'module_loop;
-                                    
+                            for channel_flags in cms_lock.iter() {
+                                if channel_flags.0 == channel {
+                                    if module.get_names().contains(&channel_flags.1)
+                                        && channel_flags.2 == Status::Disabled
+                                    {
+                                        continue 'module_loop;
+                                    }
                                 }
                             }
+                            enabled_mods.push(module.clone());
                         }
-                        enabled_mods.push(module.clone());
+
+                        enabled_mods
                     }
 
-                    enabled_mods
-
-                    
-                }
-
-                    
-                for module in get_enabled_channel_modules(bot.clone(), msg.clone().channel_login) {
-
-                    for listener in module.get_listeners() {
-                    
-                        let a = listener.clone();
-                        if a.cond_triggered(bot.clone(),message.clone()).await {
-                            
-                            let _ = listener.execute_fn(bot.clone(),message.clone()).await;
+                    for module in
+                        get_enabled_channel_modules(bot.clone(), msg.clone().channel_login)
+                    {
+                        for listener in module.get_listeners() {
+                            let a = listener.clone();
+                            if a.cond_triggered(bot.clone(), message.clone()).await {
+                                let _ = listener.execute_fn(bot.clone(), message.clone()).await;
+                            }
+                        }
+                        for cmd in module.get_commands() {
+                            let a = cmd.clone();
+                            if a.command_triggered(bot.clone(), msg.clone()).await {
+                                let _ = cmd.execute_fn(bot.clone(), message.clone()).await;
+                            }
                         }
                     }
-                    for cmd in module.get_commands() {
-                        
-                        let a = cmd.clone();
-                        if a.command_triggered(bot.clone(),msg.clone()).await {
-                            
-                            let _ = cmd.execute_fn(bot.clone(),message.clone()).await;
-                        }
-                    }  
-                    
-                }
-                } else {} ;
-                
+                } else {
+                };
             }
             drop(in_msgs_lock);
         });
-        
+
         join_handle.await.unwrap();
     }
 
     /// Loads a `Listener` into the bot
-    pub async fn load_listener(&self,l : Listener) {
+    pub async fn load_listener(&self, l: Listener) {
         let a = Arc::new(self);
         let mut listlock = a.listeners.lock().await;
         listlock.push(l);
     }
 
     /// Loads a `Command` into the bot
-    pub async fn load_command(&self,c : Command) {
+    pub async fn load_command(&self, c: Command) {
         let a = Arc::new(self);
         let mut cmdlock = a.commands.lock().await;
         cmdlock.push(c);
     }
 
-    
-    pub async fn get_module(&self,module:String) -> Option<Module> {
+    pub async fn get_module(&self, module: String) -> Option<Module> {
         let modlock = self.modules.read().unwrap();
         for modl in modlock.iter() {
             if modl.get_names().contains(&module) {
@@ -296,8 +272,6 @@ impl Bot
         }
         None
     }
-    
-
 
     pub fn get_prefix(&self) -> String {
         self.prefix.clone()
@@ -306,9 +280,9 @@ impl Bot
     pub fn get_admins(&self) -> Vec<String> {
         self.admins.clone()
     }
-    
+
     /// loads a `Module` and its bot objects
-    pub async fn load_module(&self,m: Module) {
+    pub async fn load_module(&self, m: Module) {
         // dbg!("load module - start",m.get_names().first().unwrap());
         let bot = Arc::new(self);
         // let bot_lock = bot.lock().await;
@@ -316,10 +290,10 @@ impl Bot
         if m.get_status_by_default() == Status::Disabled {
             // dbg!("module fund disabled by default");
             // dbg!("inner if");
-            for (_index,chnl) in bot.botchannels.iter().enumerate() {
+            for (_index, chnl) in bot.botchannels.iter().enumerate() {
                 // dbg!("iter - ",index);
-                bot.disable_module(chnl.clone(),  
-                m.get_names().first().unwrap().clone()).await
+                bot.disable_module(chnl.clone(), m.get_names().first().unwrap().clone())
+                    .await
             }
         }
         // dbg!("aftee disable check");
@@ -328,11 +302,11 @@ impl Bot
         // dbg!(m);
         // dbg!("loading module ",m.get_names());
         botmods.push(m);
-    }  
+    }
 
-    pub async fn get_channel_module_status(&self,channel:String,module:String) -> Status {
+    pub async fn get_channel_module_status(&self, channel: String, module: String) -> Status {
         // dbg!("get channel module status");
-        let found_disabled:bool = {
+        let found_disabled: bool = {
             // dbg!("try cms read");
             let cms_lock = self.channel_module_status.read().unwrap();
             // dbg!("cms read lock");
@@ -349,31 +323,31 @@ impl Bot
             found
         };
 
-        let module_loaded:bool = {
-
+        let module_loaded: bool = {
             let mut loaded_yn = false;
 
             for loaded_m in self.modules.read().unwrap().iter() {
-                if loaded_m.get_names().contains(&module) { 
+                if loaded_m.get_names().contains(&module) {
                     loaded_yn = true;
                 }
             }
 
             loaded_yn
-
         };
-        
-        if found_disabled { return Status::Disabled;}
-    
-        else if !module_loaded { return Status::NotLoaded ;}
-        else { return Status::Enabled; };
 
+        if found_disabled {
+            return Status::Disabled;
+        } else if !module_loaded {
+            return Status::NotLoaded;
+        } else {
+            return Status::Enabled;
+        };
     }
 
-    pub async fn disable_module(&self,channel:String,module:String){
+    pub async fn disable_module(&self, channel: String, module: String) {
         // dbg!("disable module called",channel.clone(),module.clone());
 
-        let found_disabled:bool = {
+        let found_disabled: bool = {
             // dbg!("finding disabled mod");
             // dbg!("try cms read");
             let cms_lock = self.channel_module_status.read().unwrap();
@@ -394,62 +368,63 @@ impl Bot
         };
 
         if !found_disabled {
-
-            
             let mut cms_lock = self.channel_module_status.write().unwrap();
-            
-            cms_lock.push((channel,module.clone(),Status::Disabled));
-            
+
+            cms_lock.push((channel, module.clone(), Status::Disabled));
+
             drop(cms_lock);
-            
-
-        } 
-
-        
+        }
     }
 
-    pub async fn enable_module(&self,channel:String,module:String){
+    pub async fn enable_module(&self, channel: String, module: String) {
         // dbg!("enable module called",channel.clone(),module.clone());
         // dbg!("try cms write");
         let mut lock = self.channel_module_status.write().unwrap();
         // dbg!("cms write lock");
         // dbg!(module.clone());
-        while lock.contains(&(channel.clone(),module.clone(),Status::Disabled)) {
-            
+        while lock.contains(&(channel.clone(), module.clone(), Status::Disabled)) {
             let index = lock
                 .iter()
-                .position(|x| *x == 
-                    (channel.clone(),module.clone(),Status::Disabled))
+                .position(|x| *x == (channel.clone(), module.clone(), Status::Disabled))
                 .unwrap();
             lock.remove(index);
         }
         drop(lock);
     }
 
-    pub async fn get_channel_guest_badges(&self,chatter:String,channel:String) ->  Vec<(Badge,Instant,Duration)> {
-        
+    pub async fn get_channel_guest_badges(
+        &self,
+        chatter: String,
+        channel: String,
+    ) -> Vec<(Badge, Instant, Duration)> {
         let bot = Arc::new(self);
         let guest_badges_lock = bot.chatter_guest_badges.lock().await;
 
         let mut badges = vec![];
         for temp_badge in guest_badges_lock.iter() {
-            if  temp_badge.0 == chatter && temp_badge.1 == channel &&
-            temp_badge.3 + temp_badge.4 > Instant::now()
+            if temp_badge.0 == chatter
+                && temp_badge.1 == channel
+                && temp_badge.3 + temp_badge.4 > Instant::now()
             {
-                badges.push((temp_badge.2.clone(),temp_badge.3,temp_badge.4));
+                badges.push((temp_badge.2.clone(), temp_badge.3, temp_badge.4));
             }
         }
 
         badges
     }
 
-
-    pub async fn issue_new_guest_badge(&self,chatter:String,channel:String,badge:Badge,start:Instant,dur:Duration) {
+    pub async fn issue_new_guest_badge(
+        &self,
+        chatter: String,
+        channel: String,
+        badge: Badge,
+        start: Instant,
+        dur: Duration,
+    ) {
         let bot = Arc::new(self);
         let mut guest_badges_lock = bot.chatter_guest_badges.lock().await;
 
-        guest_badges_lock.push((chatter,channel,badge,start,dur));
-
+        guest_badges_lock.push((chatter, channel, badge, start, dur));
     }
 
     pub fn get_message_cache(&self) -> &Mutex<Vec<PrivmsgMessage>> {
@@ -457,12 +432,16 @@ impl Bot
     }
 
     /// get message cache newest to oldest for a channel
-    pub async fn get_message_cache_per_channel(&self,channel:String) -> Vec<PrivmsgMessage> {
+    pub async fn get_message_cache_per_channel(&self, channel: String) -> Vec<PrivmsgMessage> {
         let cache = self.message_cache.lock().await;
         let mut rslt = vec![];
-        for a in cache.iter().rev().filter(|x| { x.channel_login==channel }).into_iter() {
+        for a in cache
+            .iter()
+            .rev()
+            .filter(|x| x.channel_login == channel)
+            .into_iter()
+        {
             rslt.push(a.clone());
-
         }
         rslt
     }
@@ -476,7 +455,7 @@ impl Bot
     //     }
     //     return false;
     // }
-    
+
     // /// Get the quiet status of a channel
     // pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
     //     let mut found = false;
@@ -491,16 +470,13 @@ impl Bot
     //         }
     //     }
     //     drop(chnlquiet);
-        
+
     //     if !found {
     //         // dbg!("set chn quiet > !found channel quiet status");
     //         let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
     //         chnlquiet.push((channel,RwLock::new(quiet_on)));
     //         drop(chnlquiet);
     //     }
-        
+
     // }
-
 }
-
-
diff --git a/forcebot_core/src/botcore/bot_objects.rs b/forcebot_core/src/botcore/bot_objects.rs
index dd4e6db..77de39c 100644
--- a/forcebot_core/src/botcore/bot_objects.rs
+++ b/forcebot_core/src/botcore/bot_objects.rs
@@ -1,6 +1,5 @@
-pub mod listener;
 pub mod command;
-
+pub mod listener;
 
 use std::boxed::Box;
 use std::future::Future;
@@ -11,25 +10,24 @@ use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
 use super::bot::Bot;
 
-
 /// chat badge
-#[derive(Clone,PartialEq, Eq,Debug)]
+#[derive(Clone, PartialEq, Eq, Debug)]
 pub enum Badge {
     Moderator,
     Broadcaster,
-    Vip
+    Vip,
 }
 
-
-
 pub type ExecBody = Box<
-    dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = Result<String,String>> + Send>> + Send + Sync,
+    dyn Fn(Arc<Bot>, ServerMessage) -> Pin<Box<dyn Future<Output = Result<String, String>> + Send>>
+        + Send
+        + Sync,
 >;
 
 /// used to store async execution functions. Primarily used for `Command`
-/// 
+///
 /// call this to store execution functions in `Commands`
-/// 
+///
 /// # example
 /// ```
 /// /* 2. Define exec callback  */
@@ -40,20 +38,20 @@ pub type ExecBody = Box<
 /// /* 3. Set Command flags */
 ///  cmd.set_exec_fn(execution_async(execbody));
 /// ```
-/// 
-pub fn execution_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> ExecBody
+///
+pub fn execution_async<T>(f: fn(Arc<Bot>, ServerMessage) -> T) -> ExecBody
 where
-    T: Future<Output = Result<String,String>> + Send + 'static,
+    T: Future<Output = Result<String, String>> + Send + 'static,
 {
-    Box::new(move |a,b| Box::pin(f(a,b)))
+    Box::new(move |a, b| Box::pin(f(a, b)))
 }
 
 pub type CommandTrigger = Box<
-    dyn Fn(Arc<Bot>,PrivmsgMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
+    dyn Fn(Arc<Bot>, PrivmsgMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
 >;
 
 /// used to store async trigger condition callback functions. Primarily used for `Command`
-/// 
+///
 /// # example
 /// ```
 /// /* 2. Define condition callback  */
@@ -64,21 +62,20 @@ pub type CommandTrigger = Box<
 /// /* 3. Set Command flags */
 /// cmd.set_custom_cond_async(command_condition_async(condition01));
 /// ```
-/// 
-pub fn command_condition_async<T>(f: fn(Arc<Bot>,PrivmsgMessage) -> T) -> CommandTrigger
+///
+pub fn command_condition_async<T>(f: fn(Arc<Bot>, PrivmsgMessage) -> T) -> CommandTrigger
 where
     T: Future<Output = bool> + Send + 'static,
 {
-    Box::new(move |a,b| Box::pin(f(a,b)))
+    Box::new(move |a, b| Box::pin(f(a, b)))
 }
 
-
 pub type ListenerTrigger = Box<
-    dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
+    dyn Fn(Arc<Bot>, ServerMessage) -> Pin<Box<dyn Future<Output = bool> + Send>> + Send + Sync,
 >;
 
 /// used to store async trigger condition callback functions. Primarily used for `Listener`
-/// 
+///
 /// # example
 /// ```
 /// /* 2. Define condition callback  */
@@ -89,30 +86,29 @@ pub type ListenerTrigger = Box<
 /// /* 3. Set Command flags */
 /// cmd.set_custom_cond_async(listener_condition_async(condition01));
 /// ```
-/// 
-pub fn listener_condition_async<T>(f: fn(Arc<Bot>,ServerMessage) -> T) -> ListenerTrigger
+///
+pub fn listener_condition_async<T>(f: fn(Arc<Bot>, ServerMessage) -> T) -> ListenerTrigger
 where
     T: Future<Output = bool> + Send + 'static,
 {
-    Box::new(move |a,b| Box::pin(f(a,b)))
+    Box::new(move |a, b| Box::pin(f(a, b)))
 }
 
-
-
 /// collection of functions to create built in objects
 pub mod built_in_objects {
-    const TEMP_BADGE_DUR_MIN:u64 = 30;
+    const TEMP_BADGE_DUR_MIN: u64 = 30;
 
-    use std::{sync::Arc, time::{Duration, Instant}};
+    use std::{
+        sync::Arc,
+        time::{Duration, Instant},
+    };
 
     use twitch_irc::message::ServerMessage;
-    
-    use super::{execution_async,command::Command,Bot,Badge,super::modules::Status};
 
+    use super::{super::modules::Status, command::Command, execution_async, Badge, Bot};
 
     /// create a vector of command build in objects
-    pub fn create_commands() -> Vec<Command> 
-    {
+    pub fn create_commands() -> Vec<Command> {
         let mut cmds = vec![];
 
         cmds.push(create_disable_cmd());
@@ -120,31 +116,45 @@ pub mod built_in_objects {
         cmds.push(create_iam_role_cmd());
 
         cmds
-
     }
 
     fn create_disable_cmd() -> Command {
         /* 1. Create a new blank cmd */
-        let mut cmd = Command::new(vec!["disable".to_string()],"".to_string());
+        let mut cmd = Command::new(vec!["disable".to_string()], "".to_string());
 
         /* 2. Define an async fn callback execution */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
             if let ServerMessage::Privmsg(msg) = message {
                 let mut action_taken = false;
-                for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+                for (i, arg) in msg
+                    .message_text
+                    .replace("\u{e0000}", "")
+                    .trim()
+                    .split(" ")
+                    .enumerate()
+                {
                     if i > 1 {
-                        if bot.get_channel_module_status(msg.channel_login.clone(), arg.to_string()).await == Status::Enabled {
+                        if bot
+                            .get_channel_module_status(msg.channel_login.clone(), arg.to_string())
+                            .await
+                            == Status::Enabled
+                        {
                             action_taken = true;
-                            bot.disable_module(msg.channel_login.clone(), arg.to_string()).await;
+                            bot.disable_module(msg.channel_login.clone(), arg.to_string())
+                                .await;
                         }
-                        
                     }
                 }
                 if action_taken {
-                    let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("Disabled!")).await ;
+                    let _ = bot
+                        .chat
+                        .lock()
+                        .await
+                        .say_in_reply_to(&msg, String::from("Disabled!"))
+                        .await;
                 }
             }
-            Result::Err("Not Valid message type".to_string()) 
+            Result::Err("Not Valid message type".to_string())
         }
 
         /* 3. Set and Store the execution body using `execution_async()`  */
@@ -156,50 +166,61 @@ pub mod built_in_objects {
         /* 5. optionally, set min badge*/
         cmd.set_min_badge(Badge::Moderator /* ::Moderator */);
         cmd
-        
-    } 
+    }
 
     fn create_enable_cmd() -> Command {
         /* 1. Create a new blank cmd */
-        let mut cmd = Command::new(vec!["enable".to_string()],"".to_string());
+        let mut cmd = Command::new(vec!["enable".to_string()], "".to_string());
 
         /* 2. Define an async fn callback execution */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
             if let ServerMessage::Privmsg(msg) = message {
-
-                let mut bot_message="".to_string();
+                let mut bot_message = "".to_string();
                 let mut re_enabled = false;
 
-                for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+                for (i, arg) in msg
+                    .message_text
+                    .replace("\u{e0000}", "")
+                    .trim()
+                    .split(" ")
+                    .enumerate()
+                {
                     if i > 1 {
+                        if Status::Disabled
+                            == bot
+                                .get_channel_module_status(
+                                    msg.channel_login.clone(),
+                                    arg.to_string(),
+                                )
+                                .await
+                        {
+                            bot.enable_module(msg.channel_login.clone(), arg.to_string())
+                                .await;
 
-                        if Status::Disabled == bot.get_channel_module_status(msg.channel_login.clone(), arg.to_string()).await {
-                            bot.enable_module(msg.channel_login.clone(), arg.to_string()).await;
-                                
                             //bot.get_modules()
                             if let Some(found_mod) = bot.get_module(arg.to_string()).await {
-                                bot_message = bot_message.to_string() + found_mod.get_bot_read_description().as_str();
+                                bot_message = bot_message.to_string()
+                                    + found_mod.get_bot_read_description().as_str();
                             }
                             re_enabled = true;
                         }
-
                     }
                 }
 
                 if re_enabled {
-                    
-                    if bot_message.len() > 250 { 
+                    if bot_message.len() > 250 {
                         bot_message = bot_message[..250].to_string();
                     }
 
-                    let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
-                        format!("Enabled! {}", bot_message)
-                    ).await ;
-
+                    let _ = bot
+                        .chat
+                        .lock()
+                        .await
+                        .say_in_reply_to(&msg, format!("Enabled! {}", bot_message))
+                        .await;
                 }
-                
-         }
-            Result::Err("Not Valid message type".to_string()) 
+            }
+            Result::Err("Not Valid message type".to_string())
         }
 
         /* 3. Set and Store the execution body using `execution_async()`  */
@@ -211,98 +232,158 @@ pub mod built_in_objects {
         /* 5. optionally, set min badge*/
         cmd.set_min_badge(Badge::Moderator);
         cmd
-    } 
-
+    }
 
     /// adminonly command that grants a temporary role
     fn create_iam_role_cmd() -> Command {
         /* 1. Create a new blank cmd */
-        let mut cmd = Command::new(vec![
-            "I am ".to_string(), 
-            "I'm ".to_string(),
-            "Im a ".to_string(),
-            ],"".to_string());
+        let mut cmd = Command::new(
+            vec!["I am ".to_string(), "I'm ".to_string(), "Im a ".to_string()],
+            "".to_string(),
+        );
 
         /* 2. Define an async fn callback execution */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
             if let ServerMessage::Privmsg(msg) = message {
-                for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+                for (i, arg) in msg
+                    .message_text
+                    .replace("\u{e0000}", "")
+                    .trim()
+                    .split(" ")
+                    .enumerate()
+                {
                     if i > 1 {
                         // bot.disable_module(msg.channel_login.clone(), arg.to_string()).await;
                         // #todo
-                        // if not dont have the badge or have a lower priviledge badge 
+                        // if not dont have the badge or have a lower priviledge badge
                         // and they dont have an active guest badge, ths admin can be
                         // recognzed wth that badge
 
                         if arg == "mod" || arg == "moderator" {
-                            let curr_temp_badges = bot.get_channel_guest_badges(msg.sender.login.clone(), msg.channel_login.clone()).await;
+                            let curr_temp_badges = bot
+                                .get_channel_guest_badges(
+                                    msg.sender.login.clone(),
+                                    msg.channel_login.clone(),
+                                )
+                                .await;
                             let mut found = false;
                             for temp_badge in curr_temp_badges {
                                 if temp_badge.0 == Badge::Moderator {
                                     found = true;
-                                } 
+                                }
                             }
                             if found {
                                 /* do nothing */
                             } else {
                                 bot.issue_new_guest_badge(
-                                    msg.sender.login.clone(), 
-                                msg.channel_login.clone(), 
-                                Badge::Moderator, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
+                                    msg.sender.login.clone(),
+                                    msg.channel_login.clone(),
+                                    Badge::Moderator,
+                                    Instant::now(),
+                                    Duration::from_secs(60 * TEMP_BADGE_DUR_MIN),
+                                )
+                                .await;
 
-                                let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
-                                    format!("Temp {:?} issued for {:?} minutes",Badge::Moderator,TEMP_BADGE_DUR_MIN)
-                                    ).await ;
+                                let _ = bot
+                                    .chat
+                                    .lock()
+                                    .await
+                                    .say_in_reply_to(
+                                        &msg,
+                                        format!(
+                                            "Temp {:?} issued for {:?} minutes",
+                                            Badge::Moderator,
+                                            TEMP_BADGE_DUR_MIN
+                                        ),
+                                    )
+                                    .await;
                             }
-
                         }
                         if arg == "vip" {
-                            let curr_temp_badges = bot.get_channel_guest_badges(msg.sender.login.clone(), msg.channel_login.clone()).await;
+                            let curr_temp_badges = bot
+                                .get_channel_guest_badges(
+                                    msg.sender.login.clone(),
+                                    msg.channel_login.clone(),
+                                )
+                                .await;
                             let mut found = false;
                             for temp_badge in curr_temp_badges {
                                 if temp_badge.0 == Badge::Vip {
                                     found = true;
-                                } 
+                                }
                             }
                             if found {
                                 /* do nothing */
                             } else {
                                 bot.issue_new_guest_badge(
-                                    msg.sender.login.clone(), 
-                                msg.channel_login.clone(), 
-                                Badge::Vip, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
+                                    msg.sender.login.clone(),
+                                    msg.channel_login.clone(),
+                                    Badge::Vip,
+                                    Instant::now(),
+                                    Duration::from_secs(60 * TEMP_BADGE_DUR_MIN),
+                                )
+                                .await;
 
-                                let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
-                                    format!("Temp {:?} issued for {:?} minutes for the bot admin",Badge::Vip,TEMP_BADGE_DUR_MIN)
-                                    ).await ;
+                                let _ = bot
+                                    .chat
+                                    .lock()
+                                    .await
+                                    .say_in_reply_to(
+                                        &msg,
+                                        format!(
+                                            "Temp {:?} issued for {:?} minutes for the bot admin",
+                                            Badge::Vip,
+                                            TEMP_BADGE_DUR_MIN
+                                        ),
+                                    )
+                                    .await;
                             }
                         }
                         if arg == "broadcaster" || arg == "strimmer" || arg == "streamer" {
-                            let curr_temp_badges = bot.get_channel_guest_badges(msg.sender.login.clone(), msg.channel_login.clone()).await;
+                            let curr_temp_badges = bot
+                                .get_channel_guest_badges(
+                                    msg.sender.login.clone(),
+                                    msg.channel_login.clone(),
+                                )
+                                .await;
                             let mut found = false;
                             for temp_badge in curr_temp_badges {
                                 if temp_badge.0 == Badge::Broadcaster {
                                     found = true;
-                                } 
+                                }
                             }
                             if found {
                                 /* do nothing */
                             } else {
                                 bot.issue_new_guest_badge(
-                                    msg.sender.login.clone(), 
-                                msg.channel_login.clone(), 
-                                Badge::Broadcaster, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
-                                
-                                let _ = bot.chat.lock().await.say_in_reply_to(&msg, 
-                                format!("Temp {:?} issued for {:?} minutes for the bot admin",Badge::Broadcaster,TEMP_BADGE_DUR_MIN)
-                                ).await ;
+                                    msg.sender.login.clone(),
+                                    msg.channel_login.clone(),
+                                    Badge::Broadcaster,
+                                    Instant::now(),
+                                    Duration::from_secs(60 * TEMP_BADGE_DUR_MIN),
+                                )
+                                .await;
+
+                                let _ = bot
+                                    .chat
+                                    .lock()
+                                    .await
+                                    .say_in_reply_to(
+                                        &msg,
+                                        format!(
+                                            "Temp {:?} issued for {:?} minutes for the bot admin",
+                                            Badge::Broadcaster,
+                                            TEMP_BADGE_DUR_MIN
+                                        ),
+                                    )
+                                    .await;
                             }
                         }
                     }
                 }
                 // let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("Disabled!")).await ;
             }
-            Result::Err("Not Valid message type".to_string()) 
+            Result::Err("Not Valid message type".to_string())
         }
 
         /* 3. Set and Store the execution body using `execution_async()`  */
@@ -314,8 +395,5 @@ pub mod built_in_objects {
         // /* 5. optionally, set min badge*/
         // cmd.set_min_badge(Badge::Moderator);
         cmd
-        
-    } 
-
-
-}
\ No newline at end of file
+    }
+}
diff --git a/forcebot_core/src/botcore/bot_objects/command.rs b/forcebot_core/src/botcore/bot_objects/command.rs
index b72cc8a..10a1e6b 100644
--- a/forcebot_core/src/botcore/bot_objects/command.rs
+++ b/forcebot_core/src/botcore/bot_objects/command.rs
@@ -2,131 +2,124 @@ use std::sync::Arc;
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
-
-use super::{execution_async,Bot,Badge,command_condition_async};
-    
+use super::{command_condition_async, execution_async, Badge, Bot};
 
 use super::{CommandTrigger, ExecBody};
 
 /// Bot `Command` that stores trigger condition callback and a execution functon
-/// 
+///
 /// A prefix character or phrase can be defined for the bot to evaluate a trigger condition
-/// 
+///
 /// A command or command phrase defines the phrase after the prefix phrase
-/// 
+///
 /// If no min badge role is provided, Broadcaster is defaulted. All commands require at least a vip role
-/// 
-/// AdminOnly commands can only be ran by admin 
-/// 
+///
+/// AdminOnly commands can only be ran by admin
+///
 /// Use `execution_async()` on custom async execution bodies
 #[derive(Clone)]
-pub struct Command
-{
-    commands : Vec<String>,
-    exec_fn : Arc<ExecBody>,
-    min_badge : Badge,
+pub struct Command {
+    commands: Vec<String>,
+    exec_fn: Arc<ExecBody>,
+    min_badge: Badge,
     /// only admins can run - default : `true`
-    admin_only : bool,
+    admin_only: bool,
     /// admin role overrides channel badge - default : `false`
-    admin_override : bool,
-    prefix : String,
-    custom_cond_fn : fn(Arc<Bot>,PrivmsgMessage) -> bool,
-    custom_cond_async : Arc<CommandTrigger>,
+    admin_override: bool,
+    prefix: String,
+    custom_cond_fn: fn(Arc<Bot>, PrivmsgMessage) -> bool,
+    custom_cond_async: Arc<CommandTrigger>,
 }
 
-impl Command
-{
-
+impl Command {
     /// Creates a new empty `Command` using command `String` and prefix `String`
     /// Pass an empty string prefix if the bot should use the bot default
     ///    
     /// Call `set_trigger_cond_fn()` and `set_exec_fn()` to trigger & execution function callbacks
     /// if a blank prefix is given, the bot will look for the bot prefix instead
-    /// 
+    ///
     /// By default, the new command is admin_only
-    pub fn new(commands:Vec<String>,prefix:String) -> Command {
-
-        async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> 
-        { Result::Ok("success".to_string()) }
-        async fn condition01(_:Arc<Bot>,_:PrivmsgMessage) -> bool { true }
+    pub fn new(commands: Vec<String>, prefix: String) -> Command {
+        async fn execbody(_: Arc<Bot>, _: ServerMessage) -> Result<String, String> {
+            Result::Ok("success".to_string())
+        }
+        async fn condition01(_: Arc<Bot>, _: PrivmsgMessage) -> bool {
+            true
+        }
 
         Command {
-            commands ,
-            prefix ,
-            exec_fn : Arc::new(execution_async(execbody)),
-            min_badge : Badge::Vip,
-            admin_only : true,
-            admin_override : false ,
-            custom_cond_fn : |_:Arc<Bot>,_:PrivmsgMessage| true,
-            custom_cond_async : Arc::new(command_condition_async(condition01)),
+            commands,
+            prefix,
+            exec_fn: Arc::new(execution_async(execbody)),
+            min_badge: Badge::Vip,
+            admin_only: true,
+            admin_override: false,
+            custom_cond_fn: |_: Arc<Bot>, _: PrivmsgMessage| true,
+            custom_cond_async: Arc::new(command_condition_async(condition01)),
         }
     }
 
     /// set a trigger condition callback that returns true if the command should trigger
-    pub fn set_custom_cond_fn(&mut self,cond_fn: fn(Arc<Bot>,PrivmsgMessage) -> bool) {
+    pub fn set_custom_cond_fn(&mut self, cond_fn: fn(Arc<Bot>, PrivmsgMessage) -> bool) {
         self.custom_cond_fn = cond_fn;
     }
 
-    
     /// sets the async trigger condition for listener
-    /// 
+    ///
     /// Same as `set_custom_cond_fn()` , but async define
-    /// 
+    ///
     /// Use`execution_async()` on the async fn when storing
-    /// 
-    /// Example - 
+    ///
+    /// Example -
     /// ```rust
     /// /* 1. Create a new blank Listener */
     /// let mut cmd = Command::new();
     ///
     /// /* 2. define an async function */
     /// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
-    /// 
+    ///
     /// /* 3. Set and Store the execution body using `execution_async()`  */
     /// cmd.set_custom_cond_async(condition_async(condition01));
     /// ```
-    /// 
-    pub fn set_custom_cond_async(&mut self,condition:CommandTrigger ) {
+    ///
+    pub fn set_custom_cond_async(&mut self, condition: CommandTrigger) {
         self.custom_cond_async = Arc::new(condition);
     }
 
     /// sets the execution body of the listener for when it triggers
-    /// 
+    ///
     /// Use`execution_async()` on the async fn when storing
-    /// 
-    /// 
-    pub fn set_exec_fn(&mut self,exec_fn:ExecBody ) {
+    ///
+    ///
+    pub fn set_exec_fn(&mut self, exec_fn: ExecBody) {
         self.exec_fn = Arc::new(exec_fn);
     }
 
     /// checks if the trigger condition is met
     /// specifically if the message is a valid command and min badge roles provided
-    /// 
-    pub async fn command_triggered(&self,bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
-
-        fn cmd_called(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-                let mut prefixed_cmd = "".to_string();
-                if cmd.prefix == "" {
-                    prefixed_cmd.push_str(&bot.get_prefix());
-                } else { 
-                    prefixed_cmd.push_str(&cmd.prefix); 
+    ///
+    pub async fn command_triggered(&self, bot: Arc<Bot>, msg: PrivmsgMessage) -> bool {
+        fn cmd_called(cmd: &Command, bot: Arc<Bot>, message: PrivmsgMessage) -> bool {
+            let mut prefixed_cmd = "".to_string();
+            if cmd.prefix == "" {
+                prefixed_cmd.push_str(&bot.get_prefix());
+            } else {
+                prefixed_cmd.push_str(&cmd.prefix);
+            }
+            for cmd_nm in &cmd.commands {
+                prefixed_cmd.push_str(cmd_nm);
+                if message.message_text.starts_with(prefixed_cmd.as_str()) {
+                    return true;
                 }
-                for cmd_nm in &cmd.commands {
-                    prefixed_cmd.push_str(cmd_nm);
-                    if message.message_text.starts_with(prefixed_cmd.as_str()) {
-                        return true;
-                    }
-                };
-                return false;
+            }
+            return false;
         }
 
-        
-        async fn caller_badge_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-
+        async fn caller_badge_ok(cmd: &Command, bot: Arc<Bot>, message: PrivmsgMessage) -> bool {
             // senders that are admins skip badge check if the command is adminonly
-            if cmd.admin_only && bot.get_admins().contains(&message.sender.login)  {
+            if cmd.admin_only && bot.get_admins().contains(&message.sender.login) {
                 return true;
-            } ;
+            };
 
             // adminOnly commands will can only be ran by admins
             if cmd.admin_only && bot.get_admins().contains(&message.sender.login) {
@@ -134,35 +127,41 @@ impl Command
             }
 
             // admin role overrides badge check if enabled
-            if cmd.admin_override && bot.get_admins().contains( &message.sender.login) { return true; }
+            if cmd.admin_override && bot.get_admins().contains(&message.sender.login) {
+                return true;
+            }
 
             for badge in message.badges {
-                
                 match cmd.min_badge {
                     Badge::Broadcaster => {
-                        if badge.name == "broadcaster" { return true }
-                        else { return false } 
-                    },
-                    Badge::Moderator => {
-                        match badge.name.as_str() {
-                            "moderator" | "broadcaster" => return true,
-                            _ => (),
+                        if badge.name == "broadcaster" {
+                            return true;
+                        } else {
+                            return false;
                         }
+                    }
+                    Badge::Moderator => match badge.name.as_str() {
+                        "moderator" | "broadcaster" => return true,
+                        _ => (),
                     },
-                    Badge::Vip => {
-                        match badge.name.as_str() {
-                            "vip" | "moderator" | "broadcaster" => return true,
-                            _ => (),
-                        }
+                    Badge::Vip => match badge.name.as_str() {
+                        "vip" | "moderator" | "broadcaster" => return true,
+                        _ => (),
                     },
                 }
             }
 
-            for temp_badge in bot.get_channel_guest_badges(message.sender.login, message.channel_login).await {
-                match (cmd.min_badge.clone(),temp_badge.0) {
-                    (Badge::Broadcaster,Badge::Broadcaster) => return true,
-                    (Badge::Moderator,Badge::Moderator) | (Badge::Moderator,Badge::Broadcaster) => return true,
-                    (Badge::Vip,Badge::Vip)|(Badge::Vip,Badge::Moderator)|(Badge::Vip,Badge::Broadcaster) => return true,
+            for temp_badge in bot
+                .get_channel_guest_badges(message.sender.login, message.channel_login)
+                .await
+            {
+                match (cmd.min_badge.clone(), temp_badge.0) {
+                    (Badge::Broadcaster, Badge::Broadcaster) => return true,
+                    (Badge::Moderator, Badge::Moderator)
+                    | (Badge::Moderator, Badge::Broadcaster) => return true,
+                    (Badge::Vip, Badge::Vip)
+                    | (Badge::Vip, Badge::Moderator)
+                    | (Badge::Vip, Badge::Broadcaster) => return true,
                     _ => (),
                 }
             }
@@ -170,59 +169,58 @@ impl Command
             return false;
         }
 
-        
-        /// determines if the command caller can run the command 
+        /// determines if the command caller can run the command
         /// based on admin_only flag
-        /// 
+        ///
         /// callers who are admins can run admin_only commands
         /// callers can run non-admin_only commands
-        fn admin_only_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-            if (cmd.admin_only && bot.get_admins().contains(&message.sender.login)) || !cmd.admin_only {
+        fn admin_only_ok(cmd: &Command, bot: Arc<Bot>, message: PrivmsgMessage) -> bool {
+            if (cmd.admin_only && bot.get_admins().contains(&message.sender.login))
+                || !cmd.admin_only
+            {
                 return true;
             } else {
                 return false;
             }
         }
 
-        async fn custom_cond_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-            (cmd.custom_cond_fn)(bot.clone(),message.clone()) && (cmd.custom_cond_async)(bot,message).await
+        async fn custom_cond_ok(cmd: &Command, bot: Arc<Bot>, message: PrivmsgMessage) -> bool {
+            (cmd.custom_cond_fn)(bot.clone(), message.clone())
+                && (cmd.custom_cond_async)(bot, message).await
         }
 
         // async fn quiet_off_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
-        //     !bot.chat.lock().await.get_channel_quiet(message.channel_login.clone()) 
+        //     !bot.chat.lock().await.get_channel_quiet(message.channel_login.clone())
         //     || bot.chat.lock().await.get_channel_quiet(message.channel_login.clone()) && cmd.commands.contains(&("quiet off".to_string()))
         // }
 
-
-        cmd_called(self, bot.clone(), msg.clone()) && 
-        caller_badge_ok(self, bot.clone(), msg.clone()).await &&
-        admin_only_ok(self, bot.clone(), msg.clone()) &&
-        custom_cond_ok(self, bot.clone(), msg.clone()).await 
+        cmd_called(self, bot.clone(), msg.clone())
+            && caller_badge_ok(self, bot.clone(), msg.clone()).await
+            && admin_only_ok(self, bot.clone(), msg.clone())
+            && custom_cond_ok(self, bot.clone(), msg.clone()).await
         //  &&
         // quiet_off_ok(self, bot, msg).await
-
     }
 
     /// executes the listeners executon body
-    pub async fn execute_fn(&self,bot:Arc<Bot>,msg:ServerMessage) -> Result<String, String> {
-        (self.exec_fn)(bot,msg).await
+    pub async fn execute_fn(&self, bot: Arc<Bot>, msg: ServerMessage) -> Result<String, String> {
+        (self.exec_fn)(bot, msg).await
     }
 
-
     /// sets min_badge to run the cmd
     // pub fn set_min_badge(&mut self,min_badge:String) {
-    pub fn set_min_badge(&mut self,min_badge:Badge) {
+    pub fn set_min_badge(&mut self, min_badge: Badge) {
         self.min_badge = min_badge
     }
 
     /// sets admin_only
-    pub fn set_admin_only(&mut self,admin_only:bool) {
+    pub fn set_admin_only(&mut self, admin_only: bool) {
         self.admin_only = admin_only
     }
 
-    /// sets admin_override . This lets admins bypass 
+    /// sets admin_override . This lets admins bypass
     /// badge restrictions   
-    pub fn set_admin_override(&mut self,admin_override:bool) {
+    pub fn set_admin_override(&mut self, admin_override: bool) {
         self.admin_override = admin_override
     }
 }
diff --git a/forcebot_core/src/botcore/bot_objects/listener.rs b/forcebot_core/src/botcore/bot_objects/listener.rs
index 24f7ce3..2633b3b 100644
--- a/forcebot_core/src/botcore/bot_objects/listener.rs
+++ b/forcebot_core/src/botcore/bot_objects/listener.rs
@@ -1,124 +1,126 @@
 use std::sync::Arc;
 
-
 use twitch_irc::message::ServerMessage;
 
 use crate::Module;
 
-use super::{execution_async,Bot,listener_condition_async};
-    
+use super::{execution_async, listener_condition_async, Bot};
+
 use super::{ExecBody, ListenerTrigger};
 
 /// Bot `Listener` that stores trigger condition callback and a execution functon
-/// 
-/// Use `Listener` functions to define the Trigger Condition & Execution callbacks. 
-/// When the Trigger callback is `true`, the Execution callback runs in the bot loop 
-/// 
+///
+/// Use `Listener` functions to define the Trigger Condition & Execution callbacks.
+/// When the Trigger callback is `true`, the Execution callback runs in the bot loop
+///
 /// Create a new empty `Listener` with `new()`
-/// 
+///
 /// Use the following on the empty listener before loading it into the bot to set the callbacks
-/// 
-/// - `set_trigger_cond_fn()` - to define the Trigger condition callback 
-/// 
+///
+/// - `set_trigger_cond_fn()` - to define the Trigger condition callback
+///
 /// - `set_exec_fn()` - to define the Execution Callback
 #[derive(Clone)]
-pub struct Listener
-{
+pub struct Listener {
     /// trigger condition
-    trigger_cond_fn : fn(Arc<Bot>,ServerMessage) -> bool,
+    trigger_cond_fn: fn(Arc<Bot>, ServerMessage) -> bool,
     /// trigger condition for async
-    trigger_cond_async : Arc<ListenerTrigger> ,
+    trigger_cond_async: Arc<ListenerTrigger>,
     /// execution body
-    exec_fn : Arc<ExecBody>,
-    parent_module : Arc<Option<Module>>,
+    exec_fn: Arc<ExecBody>,
+    parent_module: Arc<Option<Module>>,
 }
 
-impl Listener
-{
-
-    /// Creates a new empty `Listener` 
-    /// 
-    /// Use `Listener` functions to define the Trigger Condition & Execution callbacks. 
-    /// When the Trigger callback is `true`, the Execution callback runs in the bot loop 
-    /// 
+impl Listener {
+    /// Creates a new empty `Listener`
+    ///
+    /// Use `Listener` functions to define the Trigger Condition & Execution callbacks.
+    /// When the Trigger callback is `true`, the Execution callback runs in the bot loop
+    ///
     /// Use the following on the empty listener before loading it into the bot to set the callbacks
-    /// 
-    /// - `set_trigger_cond_fn()` - to define the Trigger condition callback 
-    /// 
+    ///
+    /// - `set_trigger_cond_fn()` - to define the Trigger condition callback
+    ///
     /// - `set_exec_fn()` - to define the Execution Callback
     pub fn new() -> Listener {
-
-        async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
-        async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
+        async fn execbody(_: Arc<Bot>, _: ServerMessage) -> Result<String, String> {
+            Result::Ok("success".to_string())
+        }
+        async fn condition01(_: Arc<Bot>, _: ServerMessage) -> bool {
+            true
+        }
         Listener {
-            trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| true,
-            trigger_cond_async : Arc::new(listener_condition_async(condition01)),
-            exec_fn : Arc::new(execution_async(execbody)),
-            parent_module : Arc::new(None),
+            trigger_cond_fn: |_: Arc<Bot>, _: ServerMessage| true,
+            trigger_cond_async: Arc::new(listener_condition_async(condition01)),
+            exec_fn: Arc::new(execution_async(execbody)),
+            parent_module: Arc::new(None),
         }
     }
 
     /// set a trigger conditin callback that returns true if the listener shoud trigger
-    pub fn set_trigger_cond_fn(&mut self,cond_fn: fn(Arc<Bot>,ServerMessage) -> bool) {
+    pub fn set_trigger_cond_fn(&mut self, cond_fn: fn(Arc<Bot>, ServerMessage) -> bool) {
         self.trigger_cond_fn = cond_fn;
     }
 
     /// sets the async trigger condition for listener
-    /// 
+    ///
     /// Same as `set_trigger_cond_fn()` , but async define
-    /// 
+    ///
     /// Use`condition_async()` on the async fn when storing
-    /// 
-    /// Example - 
+    ///
+    /// Example -
     /// ```rust
     /// /* 1. Create a new blank Listener */
     /// let mut listener = Listener::new();
     ///
     /// /* 2. define an async function */
     /// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
-    /// 
+    ///
     /// /* 3. Set and Store the execution body using `execution_async()`  */
     /// listener.set_trigger_cond_async(condition_async(condition01));
     /// ```
-    /// 
-    pub fn set_trigger_cond_async(&mut self,condition:ListenerTrigger ) {
+    ///
+    pub fn set_trigger_cond_async(&mut self, condition: ListenerTrigger) {
         self.trigger_cond_async = Arc::new(condition);
     }
-    
 
     /// sets the execution body of the listener for when it triggers
-    /// 
+    ///
     /// Use`execution_async()` on the async fn when storing
-    /// 
-    /// Example - 
+    ///
+    /// Example -
     /// ```rust
     /// /* 1. Create a new blank Listener */
     /// let mut listener = Listener::new();
     ///
     /// /* 2. define an async function */
     /// async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
-    /// 
+    ///
     /// /* 3. Set and Store the execution body using `execution_async()`  */
     /// listener.set_exec_fn(execution_async(execbody));
     /// ```
-    /// 
-    pub fn set_exec_fn(&mut self,exec_fn:ExecBody ) {
+    ///
+    pub fn set_exec_fn(&mut self, exec_fn: ExecBody) {
         self.exec_fn = Arc::new(exec_fn);
     }
 
     /// checks if the trigger condition is met
-    pub async fn cond_triggered(&self,bot:Arc<Bot>,msg:ServerMessage) -> bool {
-
+    pub async fn cond_triggered(&self, bot: Arc<Bot>, msg: ServerMessage) -> bool {
         let list = Arc::new(self.clone());
 
-        async fn defined_conditions_ok(list:Arc<Listener>,bot:Arc<Bot>,msg:ServerMessage) -> bool {
+        async fn defined_conditions_ok(
+            list: Arc<Listener>,
+            bot: Arc<Bot>,
+            msg: ServerMessage,
+        ) -> bool {
             // let list = Arc::new(self);
-            (list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
+            (list.trigger_cond_fn)(bot.clone(), msg.clone())
+                && (list.trigger_cond_async)(bot, msg).await
         }
 
         // async fn quiet_off_ok(list:Arc<Listener>,bot:Arc<Bot>,message:ServerMessage) -> bool {
         //     if let ServerMessage::Privmsg(msg) = message {
-                
+
         //         if let Some(parent_mod) = &*list.parent_module {
         //             return !bot.chat.lock().await.get_channel_quiet(msg.channel_login) || parent_mod.get_names().contains(&"debug".to_string());
         //         }
@@ -128,22 +130,18 @@ impl Listener
         //     return true; /* quiet is off for non chat msgs */
         // }
 
-        defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await 
+        defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await
         // &&
         // quiet_off_ok(list, bot, msg).await
     }
 
     /// executes the listeners executon body
-    pub async fn execute_fn(&self,bot:Arc<Bot>,msg:ServerMessage) -> Result<String, String> {
-        
-        (self.exec_fn)(bot,msg).await
+    pub async fn execute_fn(&self, bot: Arc<Bot>, msg: ServerMessage) -> Result<String, String> {
+        (self.exec_fn)(bot, msg).await
     }
 
     /// sets parent module
-    pub fn set_parent_module(&mut self,module:Module) {
+    pub fn set_parent_module(&mut self, module: Module) {
         self.parent_module = Arc::new(Some(module));
     }
-
-
 }
-
diff --git a/forcebot_core/src/botcore/built_in_mods.rs b/forcebot_core/src/botcore/built_in_mods.rs
index 53e04a3..03b8d28 100644
--- a/forcebot_core/src/botcore/built_in_mods.rs
+++ b/forcebot_core/src/botcore/built_in_mods.rs
@@ -4,11 +4,7 @@ use crate::Bot;
 
 pub mod quiet;
 
-
 /// used to internally load internal modules
-pub async fn load_built_in_mods(bot:&Bot){
-
+pub async fn load_built_in_mods(bot: &Bot) {
     bot.load_module(quiet::create_module()).await;
-
 }
-
diff --git a/forcebot_core/src/botcore/built_in_mods/quiet.rs b/forcebot_core/src/botcore/built_in_mods/quiet.rs
index 865c81b..7a12cc1 100644
--- a/forcebot_core/src/botcore/built_in_mods/quiet.rs
+++ b/forcebot_core/src/botcore/built_in_mods/quiet.rs
@@ -4,18 +4,17 @@ use twitch_irc::message::ServerMessage;
 
 use crate::{execution_async, Badge, Bot, Command, Module};
 
-
 /// quiet the bot in a channel
 ///
-/// use 
+/// use
 /// `quiet on`
 /// `quiet off`
-/// 
-/// 
-/// 
+///
+///
+///
 
 /// Use this function when loading modules into the bot
-/// 
+///
 /// For example
 /// ```rust
 /// bot.load_module(quiet::create_module());
@@ -23,38 +22,33 @@ use crate::{execution_async, Badge, Bot, Command, Module};
 ///
 pub fn create_module() -> Module {
     /* 1. Create a new module */
-    let mut custom_mod = Module::new(
-        vec!["quiet".to_string()], 
-        "".to_string());
+    let mut custom_mod = Module::new(vec!["quiet".to_string()], "".to_string());
 
     /* 2. Load the cmd into a new module */
     custom_mod.load_command(cmd_quiet_on());
     custom_mod.load_command(cmd_quiet_off());
     custom_mod
-
 }
 
 /// Command definition for quiet command
 fn cmd_quiet_on() -> Command {
     /* 1. Create a new cmd */
-    let mut cmd = Command::new(vec!["quiet on".to_string()],"".to_string());
+    let mut cmd = Command::new(vec!["quiet on".to_string()], "".to_string());
 
     /* 2. Define exec callback  */
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
-            
             // dbg!("quiet on called");
 
             let chatlock = bot.chat.lock().await;
-            let _=chatlock.say_in_reply_to(&msg, "Shush ".to_string()).await;
-                
+            let _ = chatlock.say_in_reply_to(&msg, "Shush ".to_string()).await;
+
             chatlock.set_channel_quiet(msg.channel_login.clone(), true);
-            println!("channel {} set quiet true",msg.channel_login);
-           
+            println!("channel {} set quiet true", msg.channel_login);
 
             return Result::Ok("Success".to_string());
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     /* 3. Set Command flags */
@@ -69,21 +63,21 @@ fn cmd_quiet_on() -> Command {
 /// Command definition for quiet command
 fn cmd_quiet_off() -> Command {
     /* 1. Create a new cmd */
-    let mut cmd = Command::new(vec!["quiet off".to_string()],"".to_string());
+    let mut cmd = Command::new(vec!["quiet off".to_string()], "".to_string());
 
     /* 2. Define exec callback  */
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
-            
             let chatlock = bot.chat.lock().await;
-           
+
             chatlock.set_channel_quiet(msg.channel_login.clone(), false);
-            let _=chatlock.say_in_reply_to(&msg, "GoodGirl I'll be good for u chat rar ".to_string()).await;
-          
-            
-            println!("channel {} set quiet false",msg.channel_login);
+            let _ = chatlock
+                .say_in_reply_to(&msg, "GoodGirl I'll be good for u chat rar ".to_string())
+                .await;
+
+            println!("channel {} set quiet false", msg.channel_login);
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     /* 3. Set Command flags */
diff --git a/forcebot_core/src/botcore/chat.rs b/forcebot_core/src/botcore/chat.rs
index d548a4f..42f1ec6 100644
--- a/forcebot_core/src/botcore/chat.rs
+++ b/forcebot_core/src/botcore/chat.rs
@@ -1,34 +1,36 @@
-use std::{fmt::Error, ops::Mul, rc::Rc, sync::{Arc, Mutex, RwLock}};
+use std::{
+    fmt::Error,
+    ops::Mul,
+    rc::Rc,
+    sync::{Arc, Mutex, RwLock},
+};
 
-use twitch_irc::{login::StaticLoginCredentials, message::ReplyToMessage, SecureTCPTransport, TwitchIRCClient};
+use twitch_irc::{
+    login::StaticLoginCredentials, message::ReplyToMessage, SecureTCPTransport, TwitchIRCClient,
+};
 
 use crate::Bot;
 
 /// Bot API to send messages to send messages to chat
-/// 
+///
 /// Uses TwitchIRCClient say_in_reply_to() but enforces controls like quiet
-/// 
-/// 
+///
+///
 
-pub struct Chat 
-{
+pub struct Chat {
     /// outbound chat client msg stream
-    pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
+    pub client: TwitchIRCClient<SecureTCPTransport, StaticLoginCredentials>,
     /// channel_quiet
-    channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
-    
+    channel_quiet_yn: RwLock<Vec<(String, RwLock<bool>)>>,
 }
 
 impl Chat {
-
-
-    pub async fn new(client:TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>)
-     -> Chat {
-        Chat { 
+    pub async fn new(client: TwitchIRCClient<SecureTCPTransport, StaticLoginCredentials>) -> Chat {
+        Chat {
             client,
             // parent_bot : Mutex::new(Bot::new().await) ,
-            channel_quiet_yn : RwLock::new(vec![]),
-         }
+            channel_quiet_yn: RwLock::new(vec![]),
+        }
     }
 
     // pub fn set_parent_bot(&self,parent_bot_in:Arc<Bot>)
@@ -38,23 +40,20 @@ impl Chat {
     // }
 
     /// helper
-    fn ok_to_send(&self,channel_login: String) -> bool {
-
-        fn not_quiet_ok(chat:&Chat,channel_login:String) -> bool {
+    fn ok_to_send(&self, channel_login: String) -> bool {
+        fn not_quiet_ok(chat: &Chat, channel_login: String) -> bool {
             // let lock = chat.parent_bot.lock().unwrap();
             // let a = lock.as_ref();
             // if let Some(bot) = &*lock {
             return !chat.get_channel_quiet(channel_login);
             // }
-            // true            
+            // true
         }
         not_quiet_ok(self, channel_login)
-
     }
 
-    
     /// Get the quiet status of a channel
-    pub fn get_channel_quiet(&self,channel:String) -> bool {
+    pub fn get_channel_quiet(&self, channel: String) -> bool {
         for a in self.channel_quiet_yn.read().unwrap().iter() {
             if a.0 == channel {
                 return a.1.read().unwrap().clone();
@@ -62,9 +61,9 @@ impl Chat {
         }
         return false;
     }
-    
+
     /// Get the quiet status of a channel
-    pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
+    pub fn set_channel_quiet(&self, channel: String, quiet_on: bool) {
         let mut found = false;
 
         let chnlquiet = self.channel_quiet_yn.read().unwrap();
@@ -77,22 +76,20 @@ impl Chat {
             }
         }
         drop(chnlquiet);
-        
+
         if !found {
             // dbg!("set chn quiet > !found channel quiet status");
             let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
-            chnlquiet.push((channel,RwLock::new(quiet_on)));
+            chnlquiet.push((channel, RwLock::new(quiet_on)));
             drop(chnlquiet);
         }
-        
     }
 
-
-
-    pub async fn say_in_reply_to(&self,
-            reply_to: &impl ReplyToMessage,
-            message: String
-    ) -> Result<(),()> {
+    pub async fn say_in_reply_to(
+        &self,
+        reply_to: &impl ReplyToMessage,
+        message: String,
+    ) -> Result<(), ()> {
         // reply_to.channel_login()
         if self.ok_to_send(reply_to.channel_login().to_string()) {
             match self.client.say_in_reply_to(reply_to, message).await {
@@ -102,50 +99,42 @@ impl Chat {
         } else {
             return Err(());
         }
-        
     }
 
-    pub async fn say(&self,
-        channel_login: String,
-        message: String,
-    ) -> Result<(),()> {
+    pub async fn say(&self, channel_login: String, message: String) -> Result<(), ()> {
         if self.ok_to_send(channel_login.to_string()) {
-match self.client.say(channel_login, message).await {
-    Ok(_) => return Ok(()),
-    Err(_) => return Err(()),
-        }
+            match self.client.say(channel_login, message).await {
+                Ok(_) => return Ok(()),
+                Err(_) => return Err(()),
+            }
         } else {
             return Err(());
         }
     }
 
-    pub async fn me(&self,
-        channel_login: String,
-        message: String,
-    ) -> Result<(),()> {
+    pub async fn me(&self, channel_login: String, message: String) -> Result<(), ()> {
         if self.ok_to_send(channel_login.to_string()) {
-match self.client.me(channel_login, message).await {
-    Ok(_) => return Ok(()),
-    Err(_) => return Err(()),
-        }
+            match self.client.me(channel_login, message).await {
+                Ok(_) => return Ok(()),
+                Err(_) => return Err(()),
+            }
         } else {
             return Err(());
         }
-
     }
 
-    pub async fn me_in_reply_to(&self,
+    pub async fn me_in_reply_to(
+        &self,
         reply_to: &impl ReplyToMessage,
-        message: String
-) -> Result<(),()> {
-    if self.ok_to_send(reply_to.channel_login().to_string()) {
-    match self.client.me_in_reply_to(reply_to, message).await {
-        Ok(_) => return Ok(()),
-        Err(_) => return Err(()),
+        message: String,
+    ) -> Result<(), ()> {
+        if self.ok_to_send(reply_to.channel_login().to_string()) {
+            match self.client.me_in_reply_to(reply_to, message).await {
+                Ok(_) => return Ok(()),
+                Err(_) => return Err(()),
+            }
+        } else {
+            return Err(());
+        }
     }
-            } else {
-                return Err(());
-            }
-
-            }
-}
\ No newline at end of file
+}
diff --git a/forcebot_core/src/botcore/modules.rs b/forcebot_core/src/botcore/modules.rs
index d3802b5..dd9cc29 100644
--- a/forcebot_core/src/botcore/modules.rs
+++ b/forcebot_core/src/botcore/modules.rs
@@ -1,11 +1,9 @@
-
 // use std::sync::{Arc, Mutex};
 
 use super::bot_objects::command::Command;
 use super::bot_objects::listener::Listener;
 
-
-#[derive(PartialEq, Eq,Debug,Clone)]
+#[derive(PartialEq, Eq, Debug, Clone)]
 pub enum Status {
     Disabled,
     Enabled,
@@ -13,24 +11,22 @@ pub enum Status {
 }
 
 /// Bot `Module` that groups a set of `bot_objects`
-/// 
+///
 /// Elevated chatters can disable modules by their name or chat alias
 #[derive(Clone)]
-pub struct Module
-{
+pub struct Module {
     name: Vec<String>,
     // _alias: String,
-    bot_read_description : String,
+    bot_read_description: String,
     listeners: Vec<Listener>,
     commands: Vec<Command>,
     // disable module at load for bot channels
     default_status_per_channel: Status,
 }
 
-impl Module 
-{
+impl Module {
     /// create a new module
-    pub fn new(name:Vec<String>,bot_read_description:String) -> Module {
+    pub fn new(name: Vec<String>, bot_read_description: String) -> Module {
         Module {
             name,
             // _alias: alias,
@@ -42,20 +38,20 @@ impl Module
     }
 
     /// Loads a `Listener` into the module
-    pub fn load_listener(&mut self,mut l : Listener) {
+    pub fn load_listener(&mut self, mut l: Listener) {
         l.set_parent_module(self.clone());
         self.listeners.push(l);
     }
 
     /// Loads a `Command` into the module
-    pub fn load_command(&mut self,c : Command) {
-        self.commands.push(c); 
+    pub fn load_command(&mut self, c: Command) {
+        self.commands.push(c);
     }
 
     pub fn get_listeners(&self) -> Vec<Listener> {
         self.listeners.clone()
     }
- 
+
     pub fn get_commands(&self) -> Vec<Command> {
         self.commands.clone()
     }
@@ -68,13 +64,11 @@ impl Module
         self.bot_read_description.clone()
     }
 
-    pub fn set_status_by_default(&mut self,status:Status){
+    pub fn set_status_by_default(&mut self, status: Status) {
         self.default_status_per_channel = status;
     }
 
     pub fn get_status_by_default(&self) -> Status {
         self.default_status_per_channel.clone()
     }
-
-
-}
\ No newline at end of file
+}
diff --git a/forcebot_core/src/custom_mods.rs b/forcebot_core/src/custom_mods.rs
index 754b7b9..fdbd664 100644
--- a/forcebot_core/src/custom_mods.rs
+++ b/forcebot_core/src/custom_mods.rs
@@ -1,4 +1,4 @@
+pub mod debug;
 pub mod guest_badge;
 pub mod pyramid;
-pub mod debug;
-// pub mod quiet;
\ No newline at end of file
+// pub mod quiet;
diff --git a/forcebot_core/src/custom_mods/debug.rs b/forcebot_core/src/custom_mods/debug.rs
index 4bbfc73..0457e54 100644
--- a/forcebot_core/src/custom_mods/debug.rs
+++ b/forcebot_core/src/custom_mods/debug.rs
@@ -1,23 +1,20 @@
-
-
 use std::sync::Arc;
 
 use crate::{execution_async, modules::Status, Bot, Command, Listener, Module};
 use twitch_irc::message::ServerMessage;
 
 /// debug module
-/// 
+///
 /// Commands to enable debugging messages in chat
-/// 
+///
 /// `debug on` to start
-/// 
+///
 /// `debug off` to stop
-/// 
-/// 
-
+///
+///
 
 /// Use this function when loading modules into the bot
-/// 
+///
 /// For example
 /// ```rust
 /// bot.load_module(debug::create_module());
@@ -25,38 +22,42 @@ use twitch_irc::message::ServerMessage;
 ///
 pub fn create_module() -> Module {
     /* 1. Create a new module */
-    let mut custom_mod = Module::new(
-        vec!["debug".to_string()], 
-        "".to_string());
+    let mut custom_mod = Module::new(vec!["debug".to_string()], "".to_string());
 
     /* 2. Load the cmd into a new module */
     custom_mod.load_command(cmd_debug_on());
     custom_mod.load_command(cmd_debug_off());
     custom_mod
-
 }
 
 /// Command definition for debug command
 fn cmd_debug_on() -> Command {
     /* 1. Create a new cmd */
-    let mut cmd = Command::new(vec!["debug on".to_string()],"".to_string());
+    let mut cmd = Command::new(vec!["debug on".to_string()], "".to_string());
 
     /* 2. Define exec callback  */
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
             // dbg!("debug cmd on executed");
 
-            let modulename="debug listener".to_string();
+            let modulename = "debug listener".to_string();
 
-            if let Status::NotLoaded = bot.get_channel_module_status(msg.channel_login.clone(), modulename.clone()).await  {
+            if let Status::NotLoaded = bot
+                .get_channel_module_status(msg.channel_login.clone(), modulename.clone())
+                .await
+            {
                 let module = create_listener_module(modulename.clone());
                 bot.load_module(module.clone()).await;
             }
             let modl = bot.get_module(modulename).await.unwrap();
-            bot.enable_module(msg.channel_login.clone(), modl.get_names().first().unwrap().clone()).await;
-            println!("Debug enabled for channel {}",msg.channel_login);
+            bot.enable_module(
+                msg.channel_login.clone(),
+                modl.get_names().first().unwrap().clone(),
+            )
+            .await;
+            println!("Debug enabled for channel {}", msg.channel_login);
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     /* 3. Set Command flags */
@@ -71,24 +72,28 @@ fn cmd_debug_on() -> Command {
 /// Command definition for debug off command
 fn cmd_debug_off() -> Command {
     /* 1. Create a new cmd */
-    let mut cmd = Command::new(vec!["debug off".to_string()],"".to_string());
+    let mut cmd = Command::new(vec!["debug off".to_string()], "".to_string());
 
     /* 2. Define exec callback  */
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
             // dbg!("debug cmd on executed");
 
-            let modulename="debug listener".to_string();
+            let modulename = "debug listener".to_string();
 
             // if let Status::NotLoaded = bot.get_channel_module_status(msg.channel_login.clone(), modulename.clone()).await  {
             //     let module = create_listener_module(modulename.clone());
             //     bot.load_module(module.clone()).await;
             // }
             let modl = bot.get_module(modulename).await.unwrap();
-            bot.disable_module(msg.channel_login.clone(), modl.get_names().first().unwrap().clone()).await;
-            println!("Debug disabled for channel {}",msg.channel_login);
+            bot.disable_module(
+                msg.channel_login.clone(),
+                modl.get_names().first().unwrap().clone(),
+            )
+            .await;
+            println!("Debug disabled for channel {}", msg.channel_login);
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     /* 3. Set Command flags */
@@ -100,41 +105,34 @@ fn cmd_debug_off() -> Command {
     cmd
 }
 
-fn create_listener_module(name:String) -> Module {
-
-    let mut custom_mod = Module::new(
-        vec![name], 
-        "".to_string());
-        // dbg!("debug listener module created");
+fn create_listener_module(name: String) -> Module {
+    let mut custom_mod = Module::new(vec![name], "".to_string());
+    // dbg!("debug listener module created");
     custom_mod.load_listener(cmd_debug_listener());
     custom_mod.set_status_by_default(Status::Disabled);
 
     custom_mod
-
 }
 
 /// Listener for debug
 fn cmd_debug_listener() -> Listener {
-        // dbg!("Creating debug listener");
-       /* 2a. Create a new blank Listener */
-       let mut listener = Listener::new();
+    // dbg!("Creating debug listener");
+    /* 2a. Create a new blank Listener */
+    let mut listener = Listener::new();
 
-       /* 2b. Set a trigger condition function for listener */
-       listener.set_trigger_cond_fn(
-           |_:Arc<Bot>,_:ServerMessage| true
-       );
-   
-       /* 2c. Define an async fn callback execution */
-       async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-            if let ServerMessage::Privmsg(msg) = message {
-                dbg!(msg); /* outputs message to debug */
-            }
-            Result::Err("Not Valid message type".to_string()) 
-       }
-   
-       /* 2d. Set and Store the execution body using `execution_async()`  */
-       listener.set_exec_fn(execution_async(execbody));
+    /* 2b. Set a trigger condition function for listener */
+    listener.set_trigger_cond_fn(|_: Arc<Bot>, _: ServerMessage| true);
 
-       listener 
+    /* 2c. Define an async fn callback execution */
+    async fn execbody(_: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            dbg!(msg); /* outputs message to debug */
+        }
+        Result::Err("Not Valid message type".to_string())
+    }
+
+    /* 2d. Set and Store the execution body using `execution_async()`  */
+    listener.set_exec_fn(execution_async(execbody));
+
+    listener
 }
-
diff --git a/forcebot_core/src/custom_mods/guest_badge.rs b/forcebot_core/src/custom_mods/guest_badge.rs
index 39c121d..2cf4ae7 100644
--- a/forcebot_core/src/custom_mods/guest_badge.rs
+++ b/forcebot_core/src/custom_mods/guest_badge.rs
@@ -1,91 +1,109 @@
-use std::{sync::Arc, time::{Duration, Instant}};
+use std::{
+    sync::Arc,
+    time::{Duration, Instant},
+};
 
 use twitch_irc::message::ServerMessage;
 
 use crate::{execution_async, Badge, Bot, Command, Module};
 
 /// guest_badge / guest module
-/// 
+///
 /// Temporary badges can be issued to chatters. The bot then opens functionality
 /// to that chatter based on the recognized role
 ///  
-/// Chatters with real badge roles will be able to share guest 
+/// Chatters with real badge roles will be able to share guest
 /// badges based on their role
-/// 
-/// 
-/// 
-
-const VIP_GIVEN_DUR_MIN:u64 = 15;
-const MOD_GIVEN_DUR_MIN:u64 = 30;
+///
+///
+///
 
+const VIP_GIVEN_DUR_MIN: u64 = 15;
+const MOD_GIVEN_DUR_MIN: u64 = 30;
 
 /// Use this function when loading modules into the bot
-/// 
+///
 /// For example
 /// ```rust
 /// bot.load_module(guest_badge::create_module());
 /// ```
-/// 
+///
 pub fn create_module() -> Module {
-
     let mut custom_mod = Module::new(
-        vec!["guests".to_string()], 
-        "Temp Guest badges can be given by chatters with badges. ".to_string());
+        vec!["guests".to_string()],
+        "Temp Guest badges can be given by chatters with badges. ".to_string(),
+    );
 
     custom_mod.load_command(create_cmd_mod());
     custom_mod.load_command(create_cmd_vip());
- 
-    custom_mod
 
+    custom_mod
 }
 
 fn create_cmd_vip() -> Command {
+    let mut cmd = Command::new(vec!["vip".to_string()], "".to_string());
 
-    let mut cmd = Command::new(vec!["vip".to_string()],"".to_string());
-
-
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
-
             let guest_dur_min = {
-                let mut result=VIP_GIVEN_DUR_MIN;
+                let mut result = VIP_GIVEN_DUR_MIN;
                 for badge in msg.clone().badges {
-                    if badge.name == "vip" { result = VIP_GIVEN_DUR_MIN ; }
-                    if badge.name == "moderator" { result = MOD_GIVEN_DUR_MIN ; }
+                    if badge.name == "vip" {
+                        result = VIP_GIVEN_DUR_MIN;
+                    }
+                    if badge.name == "moderator" {
+                        result = MOD_GIVEN_DUR_MIN;
+                    }
                 }
                 result
             };
-            
-       
-            let mut badges_issued =false;
-            for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+
+            let mut badges_issued = false;
+            for (i, arg) in msg
+                .message_text
+                .replace("\u{e0000}", "")
+                .trim()
+                .split(" ")
+                .enumerate()
+            {
                 if i > 1 {
                     let mut already_vip = false;
-                  
-                
-                    for guest_badge in bot.get_channel_guest_badges(arg.trim().to_string(), msg.channel_login.clone()).await {
-                        if guest_badge.0 == Badge::Vip { already_vip = true }
+
+                    for guest_badge in bot
+                        .get_channel_guest_badges(arg.trim().to_string(), msg.channel_login.clone())
+                        .await
+                    {
+                        if guest_badge.0 == Badge::Vip {
+                            already_vip = true
+                        }
                     }
                     if !already_vip {
-                        badges_issued= true;
+                        badges_issued = true;
                         bot.issue_new_guest_badge(
-                            arg.trim().to_string(), 
-                        msg.channel_login.clone(), 
-                        Badge::Vip, Instant::now(), Duration::from_secs(60*guest_dur_min)).await;
-                     
+                            arg.trim().to_string(),
+                            msg.channel_login.clone(),
+                            Badge::Vip,
+                            Instant::now(),
+                            Duration::from_secs(60 * guest_dur_min),
+                        )
+                        .await;
                     }
                 }
             }
-            if badges_issued { 
-           
-                
-            let _= bot.chat.lock().await.say_in_reply_to(
-                &msg.clone(), format!("Guest badges issued for {} min",guest_dur_min)).await;
+            if badges_issued {
+                let _ = bot
+                    .chat
+                    .lock()
+                    .await
+                    .say_in_reply_to(
+                        &msg.clone(),
+                        format!("Guest badges issued for {} min", guest_dur_min),
+                    )
+                    .await;
                 return Result::Ok("Success".to_string());
             }
-
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     cmd.set_exec_fn(execution_async(execbody));
@@ -94,41 +112,58 @@ fn create_cmd_vip() -> Command {
     cmd.set_admin_override(true);
     cmd.set_min_badge(Badge::Vip);
     cmd
-
 }
 
 fn create_cmd_mod() -> Command {
+    let mut cmd = Command::new(vec!["mod".to_string()], "".to_string());
 
-    let mut cmd = Command::new(vec!["mod".to_string()],"".to_string());
-
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
-
-       
-            let mut badges_issued =false;
-            for (i,arg) in msg.message_text.replace("\u{e0000}","").trim().split(" ").enumerate() {
+            let mut badges_issued = false;
+            for (i, arg) in msg
+                .message_text
+                .replace("\u{e0000}", "")
+                .trim()
+                .split(" ")
+                .enumerate()
+            {
                 if i > 1 {
-                    
                     let mut already_mod = false;
-                    for guest_badge in bot.get_channel_guest_badges(arg.trim().to_string(), msg.channel_login.clone()).await {
-                        if guest_badge.0 == Badge::Moderator { already_mod = true }
+                    for guest_badge in bot
+                        .get_channel_guest_badges(arg.trim().to_string(), msg.channel_login.clone())
+                        .await
+                    {
+                        if guest_badge.0 == Badge::Moderator {
+                            already_mod = true
+                        }
                     }
                     if !already_mod {
-                        badges_issued= true;
+                        badges_issued = true;
                         bot.issue_new_guest_badge(
-                            arg.trim().to_string(), 
-                        msg.channel_login.clone(), 
-                        Badge::Moderator, Instant::now(), Duration::from_secs(60*MOD_GIVEN_DUR_MIN)).await;
+                            arg.trim().to_string(),
+                            msg.channel_login.clone(),
+                            Badge::Moderator,
+                            Instant::now(),
+                            Duration::from_secs(60 * MOD_GIVEN_DUR_MIN),
+                        )
+                        .await;
                     }
                 }
-            } 
+            }
             if badges_issued {
-            let _= bot.chat.lock().await.say_in_reply_to(
-                &msg, format!("Guest badges issued for {} min",MOD_GIVEN_DUR_MIN)).await;
+                let _ = bot
+                    .chat
+                    .lock()
+                    .await
+                    .say_in_reply_to(
+                        &msg,
+                        format!("Guest badges issued for {} min", MOD_GIVEN_DUR_MIN),
+                    )
+                    .await;
                 return Result::Ok("Success".to_string());
             }
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     cmd.set_exec_fn(execution_async(execbody));
@@ -137,6 +172,4 @@ fn create_cmd_mod() -> Command {
     cmd.set_admin_override(true);
     cmd.set_min_badge(Badge::Moderator);
     cmd
-
-
-}
\ No newline at end of file
+}
diff --git a/forcebot_core/src/custom_mods/pyramid.rs b/forcebot_core/src/custom_mods/pyramid.rs
index 8b36395..99deb25 100644
--- a/forcebot_core/src/custom_mods/pyramid.rs
+++ b/forcebot_core/src/custom_mods/pyramid.rs
@@ -1,169 +1,174 @@
-
-
 use std::sync::{Arc, Mutex};
 
 use twitch_irc::message::{PrivmsgMessage, ServerMessage};
 
 // use crate::{execution_async, listener_condition_async, Badge, Bot, Command, Listener, Module};
-use super::super::botcore::bot_objects::execution_async;
-use super::super::botcore::bot_objects::listener_condition_async;
-use super::super::botcore::bot_objects::listener::Listener;
-use super::super::botcore::modules::Module;
 use super::super::botcore::bot::Bot;
 use super::super::botcore::bot_objects::command::Command;
+use super::super::botcore::bot_objects::execution_async;
+use super::super::botcore::bot_objects::listener::Listener;
+use super::super::botcore::bot_objects::listener_condition_async;
 use super::super::botcore::bot_objects::Badge;
+use super::super::botcore::modules::Module;
 
 /// pyramid module
-/// 
+///
 /// for detecting & handling pyramids
-/// 
+///
 /// - listener - detects pyramid
 /// - cmd & listener - interrupts some chatters temporarily
-/// 
-/// 
+///
+///
 use lazy_static::lazy_static;
 
-
 /// Use this function when loading modules into the bot
-/// 
+///
 /// For example
 /// ```rust
 /// bot.load_module(pyramid::create_module());
 /// ```
-/// 
+///
 pub fn create_module() -> Module {
-
     let mut custom_mod = Module::new(
-        vec!["pyramid".to_string(),
-        "pyramids".to_string()], 
-        "o7 I can handle pyramids".to_string());
+        vec!["pyramid".to_string(), "pyramids".to_string()],
+        "o7 I can handle pyramids".to_string(),
+    );
     custom_mod.load_listener(create_pyramid_detector());
 
     custom_mod
-
 }
 
 fn create_pyramid_detector() -> Listener {
+    /* 1. Create a new blank Listener */
+    let mut listener = Listener::new();
 
-        /* 1. Create a new blank Listener */
-        let mut listener = Listener::new();
-
-        /* 2. Define an async trigger condition callback */
-        async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
-            if let ServerMessage::Privmsg(msg) = message {
-                if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await && get_pyramid_size(msg.channel_login) > 3 {
-                    return true;
-                }
+    /* 2. Define an async trigger condition callback */
+    async fn condition01(bot: Arc<Bot>, message: ServerMessage) -> bool {
+        if let ServerMessage::Privmsg(msg) = message {
+            if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await
+                && get_pyramid_size(msg.channel_login) > 3
+            {
+                return true;
             }
-            false 
         }
+        false
+    }
 
-        /* 3. Set a trigger condition function for listener */
-        listener.set_trigger_cond_async(listener_condition_async(condition01));
+    /* 3. Set a trigger condition function for listener */
+    listener.set_trigger_cond_async(listener_condition_async(condition01));
 
-    
-        /* 4. Define an async fn callback execution */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-            if let ServerMessage::Privmsg(msg) = message {
-                dbg!("enter pyramid listener execution - after pyramid complete");
-                // if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
+    /* 4. Define an async fn callback execution */
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            dbg!("enter pyramid listener execution - after pyramid complete");
+            // if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
 
-                    // let _ = bot.chat.lock().await.
-                    dbg!("> get start pattern");
-                    let pattern = get_start_pattern(msg.channel_login.clone());
-                    let mut outmsg ;
+            // let _ = bot.chat.lock().await.
+            dbg!("> get start pattern");
+            let pattern = get_start_pattern(msg.channel_login.clone());
+            let mut outmsg;
 
-                    /* Prefer emote before pattern in case pattern is command */
-                    if pattern.len() < 50 {
-                        outmsg = format!("Clap {}",pattern);
-                    } else {
-                        outmsg = "Clap".to_string();
-                    }
-
-                    dbg!(get_pyramid_size(msg.channel_login.clone()));
-                    if get_pyramid_size(msg.channel_login.clone()) < 4 {
-                        outmsg = format!("{} annytfMagniGlass",outmsg);
-                    }
-                    
-                    dbg!("> start pattern :",pattern);
-                    
-                    dbg!("> say_in_reply_to completed :",outmsg.clone());
-                    let _ = bot.chat.lock().await.say_in_reply_to(&msg, outmsg).await ;
-
-                    dbg!("> set pyramid started - false");
-                    set_pyramid_started(msg.channel_login.clone(),false);
-
-                    return Result::Ok("Success".to_string()) ;
-                // }
+            /* Prefer emote before pattern in case pattern is command */
+            if pattern.len() < 50 {
+                outmsg = format!("Clap {}", pattern);
+            } else {
+                outmsg = "Clap".to_string();
             }
-            Result::Err("Not Valid message type".to_string()) 
+
+            dbg!(get_pyramid_size(msg.channel_login.clone()));
+            if get_pyramid_size(msg.channel_login.clone()) < 4 {
+                outmsg = format!("{} annytfMagniGlass", outmsg);
+            }
+
+            dbg!("> start pattern :", pattern);
+
+            dbg!("> say_in_reply_to completed :", outmsg.clone());
+            let _ = bot.chat.lock().await.say_in_reply_to(&msg, outmsg).await;
+
+            dbg!("> set pyramid started - false");
+            set_pyramid_started(msg.channel_login.clone(), false);
+
+            return Result::Ok("Success".to_string());
+            // }
         }
-    
-        /* 5. Set and Store the execution body using `execution_async()`  */
-        listener.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
+        Result::Err("Not Valid message type".to_string())
+    }
 
-        listener
+    /* 5. Set and Store the execution body using `execution_async()`  */
+    listener.set_exec_fn(Box::new(move |a, b| Box::pin(execbody(a, b))));
 
+    listener
 }
 
 /// detect pyramid based on latest message and channel
-/// 
-/// 
-async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
+///
+///
+async fn detect_pyramid_complete_ok(_bot: Arc<Bot>, msg: PrivmsgMessage) -> bool {
     dbg!("enter detect_pyramid_complete()");
 
-    let msgtext = msg.message_text.replace("󠀀","").replace("\u{e0000}","").trim().to_string();
+    let msgtext = msg
+        .message_text
+        .replace("󠀀", "")
+        .replace("\u{e0000}", "")
+        .trim()
+        .to_string();
     let msgchannel = msg.channel_login;
     let msgchatter = msg.sender.login;
 
     // 1. Check if Pyramid started in chat > and recognize pyramid started
-    if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
+    if !is_pyramid_started(msgchannel.clone())
+        & check_start_pyramid(msgchannel.clone(), msgtext.clone())
+    {
         dbg!("> set pyramid started - true");
-        set_pyramid_started(msgchannel.clone(),true);
-        push_to_compare(msgchannel.clone(),msgchatter.clone(),get_start_pattern(msgchannel.clone()));
-        
-                
+        set_pyramid_started(msgchannel.clone(), true);
+        push_to_compare(
+            msgchannel.clone(),
+            msgchatter.clone(),
+            get_start_pattern(msgchannel.clone()),
+        );
     }
 
     if is_pyramid_started(msgchannel.clone()) {
-        push_to_compare(msgchannel.clone(),msgchatter.clone(),msgtext.clone());
+        push_to_compare(msgchannel.clone(), msgchatter.clone(), msgtext.clone());
     }
 
     // 2a. If Pyramid Not Started, Assume message is a potential start pattern
     if !is_pyramid_started(msgchannel.clone()) {
-        set_start_pattern(msgchannel.clone(),msgtext.clone());
+        set_start_pattern(msgchannel.clone(), msgtext.clone());
     }
 
     // 2b. If Pyramid is Started, and the latest message is the pattern, check for
-    // symmetry to determine pyramid 
+    // symmetry to determine pyramid
 
-    if is_pyramid_started(msgchannel.clone()) && msgtext.clone() == get_start_pattern(msgchannel.clone()) {
+    if is_pyramid_started(msgchannel.clone())
+        && msgtext.clone() == get_start_pattern(msgchannel.clone())
+    {
         if symmetry_ok(msgchannel.clone()) {
             return true;
         } else {
             dbg!("> set pyramid started - false");
-            set_pyramid_started(msgchannel,false);
+            set_pyramid_started(msgchannel, false);
 
-            return false ; 
+            return false;
         }
     }
 
     // 2c. if Pyramid is strted but latest message does not ontain pattern
-    if is_pyramid_started(msgchannel.clone()) && !msgtext.clone().contains( get_start_pattern(msgchannel.clone()).as_str()) {
-    
+    if is_pyramid_started(msgchannel.clone())
+        && !msgtext
+            .clone()
+            .contains(get_start_pattern(msgchannel.clone()).as_str())
+    {
         dbg!("> set pyramid started - false");
-        set_pyramid_started(msgchannel,false);
+        set_pyramid_started(msgchannel, false);
 
-        return false ; 
+        return false;
     } else {
         return false;
-
     };
-
 }
 
-
-lazy_static!{
+lazy_static! {
     /// Message Compare stack per channel (channel:String,msgstack:Vec<(chatter:String,message:String)>)
     pub static ref COMPARE_MSG_STACK_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<(String,String)>>)>>  = Mutex::new(vec![]);
     #[derive(Debug)]
@@ -173,35 +178,34 @@ lazy_static!{
     pub static ref START_PATTERNS_PER_CHNL: Mutex<Vec<(String,Mutex<String>)>> = Mutex::new(vec![]);
     /// Pyramid sze per channel (channel:String,started:bool)
     pub static ref PYRAMID_SIZE_PER_CHNL:  Mutex<Vec<(String,Mutex<i32>)>> = Mutex::new(vec![]);
-    /// temp message stack checker 
+    /// temp message stack checker
     pub static ref TEMP_MSG_STACK: Mutex<Vec<String>> = Mutex::new(vec![]);
-    
+
     /// interruptor targets - (channel:String,chatters:Vec<String>>)
     pub static ref INTERRUPT_TRG_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<String>>)>>  = Mutex::new(vec![]);
 
 }
 
-fn read_top_of_compare(channel:String) -> Option<(String,String)> {
-
+fn read_top_of_compare(channel: String) -> Option<(String, String)> {
     let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
 
     for rec in comp_perchnl.iter() {
         if rec.0 == channel {
             let msg_stack = rec.1.lock().unwrap();
-            
+
             return msg_stack.last().cloned();
         }
     }
     None
 }
 
-fn pop_top_of_compare(channel:String) -> Option<(String,String)> {
+fn pop_top_of_compare(channel: String) -> Option<(String, String)> {
     let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
 
     for rec in comp_perchnl.iter() {
         if rec.0 == channel {
             let mut msg_stack = rec.1.lock().unwrap();
-            
+
             let popped = msg_stack.pop();
             return popped;
         }
@@ -209,7 +213,7 @@ fn pop_top_of_compare(channel:String) -> Option<(String,String)> {
     None
 }
 
-fn set_pyramid_started(channel:String,started:bool) {
+fn set_pyramid_started(channel: String, started: bool) {
     let mut start_perchnl = PYRAMID_STARTED_PER_CHNL.lock().unwrap();
     let mut found = false;
     for rec in start_perchnl.iter() {
@@ -217,14 +221,14 @@ fn set_pyramid_started(channel:String,started:bool) {
             found = true;
             let mut rec_started = rec.1.lock().unwrap();
             *rec_started = started;
-        } 
+        }
     }
     if !found {
-        start_perchnl.push((channel,Mutex::new(started)));
+        start_perchnl.push((channel, Mutex::new(started)));
     }
 }
 
-fn is_pyramid_started(channel:String) -> bool {
+fn is_pyramid_started(channel: String) -> bool {
     let start_perchnl = PYRAMID_STARTED_PER_CHNL.lock().unwrap();
     for rec in start_perchnl.iter() {
         if rec.0 == channel {
@@ -235,42 +239,37 @@ fn is_pyramid_started(channel:String) -> bool {
     false
 }
 
-fn set_start_pattern(channel:String,pattern:String) {
+fn set_start_pattern(channel: String, pattern: String) {
     let mut start_patterns = START_PATTERNS_PER_CHNL.lock().unwrap();
 
     let mut found = false;
     for rec in start_patterns.iter() {
-
         if rec.0 == channel {
             found = true;
             let mut patternlock = rec.1.lock().unwrap();
             *patternlock = pattern.clone();
-        } 
-        
-    }  
+        }
+    }
     if !found {
-        start_patterns.push((channel.clone(),Mutex::new(pattern.clone())));
+        start_patterns.push((channel.clone(), Mutex::new(pattern.clone())));
     }
 }
 
-
-fn get_start_pattern(channel:String) -> String {
+fn get_start_pattern(channel: String) -> String {
     let start_patterns = START_PATTERNS_PER_CHNL.lock().unwrap();
 
     for rec in start_patterns.iter() {
-
         if rec.0 == channel {
             let patternlock = rec.1.lock().unwrap();
             return patternlock.clone();
-        } 
-    }  
+        }
+    }
 
     return "".to_string();
 }
 
-
 /// pushes message to compare stack
-fn push_to_compare(channel:String,chatter:String,message:String) {
+fn push_to_compare(channel: String, chatter: String, message: String) {
     let mut comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
 
     let mut found = false;
@@ -278,20 +277,23 @@ fn push_to_compare(channel:String,chatter:String,message:String) {
         if rec.0 == channel {
             found = true;
             let mut msg_stack = rec.1.lock().unwrap();
-            msg_stack.push((chatter.clone(),message.clone()));
+            msg_stack.push((chatter.clone(), message.clone()));
             // dbg!("Push message to cmp stack ; result last cmp_pchnl - ",comp_perchnl.last());
         }
     }
     if !found {
-        comp_perchnl.push((channel,Mutex::new(vec![(chatter,message)])));
+        comp_perchnl.push((channel, Mutex::new(vec![(chatter, message)])));
     }
-
 }
 
-
 /// checks latest and next latest messages for potential start
-fn check_start_pyramid(channel:String,msgtext: String) -> bool {
-    msgtext == format!("{} {}",get_start_pattern(channel.clone()),get_start_pattern(channel.clone()))
+fn check_start_pyramid(channel: String, msgtext: String) -> bool {
+    msgtext
+        == format!(
+            "{} {}",
+            get_start_pattern(channel.clone()),
+            get_start_pattern(channel.clone())
+        )
     // msgtext == format!("{} {} {}",
     //     get_start_pattern(channel.clone()),
     //     get_start_pattern(channel.clone()),
@@ -299,60 +301,80 @@ fn check_start_pyramid(channel:String,msgtext: String) -> bool {
     // )
 }
 
-
 /// pops the compare stack to determine symmetry
-fn symmetry_ok(channel:String) -> bool {
+fn symmetry_ok(channel: String) -> bool {
     let mut temp_stack = TEMP_MSG_STACK.lock().unwrap();
     let mut checking_started = false;
-    if !(read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone())) {
+    if !(read_top_of_compare(channel.clone())
+        .unwrap_or(("".to_string(), "".to_string()))
+        .1
+        == get_start_pattern(channel.clone()))
+    {
         return false;
     }
 
     let mut pyramid_size = 0;
     loop {
-        
-        if !checking_started && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone()) {  
+        if !checking_started
+            && read_top_of_compare(channel.clone())
+                .unwrap_or(("".to_string(), "".to_string()))
+                .1
+                == get_start_pattern(channel.clone())
+        {
             checking_started = true;
-        } 
+        }
 
-        if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
-            temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1);
+        if temp_stack.last().is_none()
+            || read_top_of_compare(channel.clone())
+                .unwrap_or(("".to_string(), "".to_string()))
+                .1
+                .len()
+                > temp_stack.last().unwrap_or(&"".to_string()).len()
+        {
+            temp_stack.push(
+                pop_top_of_compare(channel.clone())
+                    .unwrap_or(("".to_string(), "".to_string()))
+                    .1,
+            );
             pyramid_size += 1;
-            
-        } else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
-    
+        } else if temp_stack.last().is_some()
+            && read_top_of_compare(channel.clone())
+                .unwrap_or(("".to_string(), "".to_string()))
+                .1
+                .len()
+                < temp_stack.last().unwrap_or(&"".to_string()).len()
+        {
             temp_stack.pop();
-            if temp_stack.last().unwrap_or(&"".to_string()).clone() == read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 {
+            if temp_stack.last().unwrap_or(&"".to_string()).clone()
+                == read_top_of_compare(channel.clone())
+                    .unwrap_or(("".to_string(), "".to_string()))
+                    .1
+            {
                 temp_stack.pop();
-                
+
                 continue;
             } else {
-                
                 set_pyramid_size(channel.clone(), 0);
                 temp_stack.clear();
                 return false;
             }
-
-        } else { 
+        } else {
             set_pyramid_size(channel.clone(), 0);
-            return false; 
+            return false;
         }
 
-        if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {  
-            
+        if checking_started
+            && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone())
+        {
             /* leave pyramid size set for exection */
-            set_pyramid_size(channel.clone(), pyramid_size*2-1);
+            set_pyramid_size(channel.clone(), pyramid_size * 2 - 1);
             temp_stack.clear();
             return true;
-        } 
-    
+        }
     }
-
-
 }
 
-
-fn set_pyramid_size(channel:String,size:i32) {
+fn set_pyramid_size(channel: String, size: i32) {
     let mut size_perchnl = PYRAMID_SIZE_PER_CHNL.lock().unwrap();
     let mut found = false;
     for rec in size_perchnl.iter() {
@@ -360,14 +382,14 @@ fn set_pyramid_size(channel:String,size:i32) {
             found = true;
             let mut rec_started = rec.1.lock().unwrap();
             *rec_started = size;
-        } 
+        }
     }
     if !found {
-        size_perchnl.push((channel,Mutex::new(size)));
+        size_perchnl.push((channel, Mutex::new(size)));
     }
 }
 
-fn get_pyramid_size(channel:String) -> i32 {
+fn get_pyramid_size(channel: String) -> i32 {
     let size_perchnl = PYRAMID_SIZE_PER_CHNL.lock().unwrap();
     for rec in size_perchnl.iter() {
         if rec.0 == channel {
@@ -379,88 +401,85 @@ fn get_pyramid_size(channel:String) -> i32 {
 }
 
 /// #todo
-/// 
-/// pyramid interruptor 
-/// 
+///
+/// pyramid interruptor
+///
 /// pick chatters that will be interrupted if they solo build
-/// 
+///
 /// takes in arguments as chatters
-/// 
+///
 /// chatters are then interrupted for a random duration under 15m
-/// 
-/// if a duration is given, take that duration eg 15m , 25m 
-/// 
-/// 
+///
+/// if a duration is given, take that duration eg 15m , 25m
+///
+///
 fn _create_interruptor_cmd() -> Command {
-    let mut cmd = Command::new(vec![
-        "no pyramid".to_string(),
-        "no pyramids".to_string()
-        ], 
-    "".to_string());
+    let mut cmd = Command::new(
+        vec!["no pyramid".to_string(), "no pyramids".to_string()],
+        "".to_string(),
+    );
 
-        /* 2. Define an async fn callback execution */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
-            if let ServerMessage::Privmsg(msg) = message {
-                let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
-                return Result::Ok("Success".to_string()) ;
-            }
-            Result::Err("Not Valid message type".to_string()) 
+    /* 2. Define an async fn callback execution */
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
+        if let ServerMessage::Privmsg(msg) = message {
+            let _ = bot
+                .chat
+                .lock()
+                .await
+                .say_in_reply_to(&msg, String::from("test success"))
+                .await;
+            return Result::Ok("Success".to_string());
         }
+        Result::Err("Not Valid message type".to_string())
+    }
 
-        /* 3. Set and Store the execution body using `execution_async()`  */
-        cmd.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
+    /* 3. Set and Store the execution body using `execution_async()`  */
+    cmd.set_exec_fn(Box::new(move |a, b| Box::pin(execbody(a, b))));
 
-        /* 4. optionally, remove admin only default flag */
-        cmd.set_admin_only(false);
-    
-        /* 5. optionally, set min badge*/
-        cmd.set_min_badge(Badge::Moderator);
+    /* 4. optionally, remove admin only default flag */
+    cmd.set_admin_only(false);
 
+    /* 5. optionally, set min badge*/
+    cmd.set_min_badge(Badge::Moderator);
 
     cmd
 }
 
 /// #todo
-fn _create_interruptor_module(channel:String) -> Module  {
+fn _create_interruptor_module(channel: String) -> Module {
     /* 1. Create a new module */
-    let modname = format!("interruptor {}",channel);
-    let mut custom_mod = Module::new(
-        vec![modname], 
-        "".to_string());
+    let modname = format!("interruptor {}", channel);
+    let mut custom_mod = Module::new(vec![modname], "".to_string());
 
     /* 2. Load the cmd into a new module */
     custom_mod.load_listener(_create_interruptor_listener());
 
     custom_mod
-
 }
 
 /// #todo
-fn _create_interruptor_listener() -> Listener  {
+fn _create_interruptor_listener() -> Listener {
     /* 2a. Create a new blank Listener */
     let mut listener = Listener::new();
 
     /* 2b. Set a trigger condition function for listener */
-    listener.set_trigger_cond_fn(
-        |_:Arc<Bot>,_:ServerMessage| true
-    );
+    listener.set_trigger_cond_fn(|_: Arc<Bot>, _: ServerMessage| true);
 
     /* 2c. Define an async fn callback execution */
-    async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(_: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         dbg!(message); /* outputs message to debug */
-        Result::Ok("Success".to_string()) 
+        Result::Ok("Success".to_string())
     }
 
     /* 2d. Set and Store the execution body using `execution_async()`  */
     listener.set_exec_fn(execution_async(execbody));
 
     listener
-    
 }
 
 /// #todo
-/// 
+///
 /// Returns Some(chatter) if the pyramid in progress is being built by a solo
-fn _solo_building(_channel:String) -> Option<String> {
+fn _solo_building(_channel: String) -> Option<String> {
     None
 }
diff --git a/forcebot_core/src/lib.rs b/forcebot_core/src/lib.rs
index f33057e..6d3f317 100644
--- a/forcebot_core/src/lib.rs
+++ b/forcebot_core/src/lib.rs
@@ -1,32 +1,32 @@
 //! `forcebot_core` library for `forcebot-rs-v2` Twitch chat bot
-//! 
+//!
 //! Customize by adding additional bot objects
-//! 
+//!
 //! # New Bot
-//! 
+//!
 //! Uses Env defined variables to create and run the bot
-//! 
+//!
 //! ```rust
 //! use forcebot_core::Bot;
-//! 
+//!
 //! #[tokio::main]
 //! pub async fn main() {
-//! 
+//!
 //!     /* 1. Create the bot using env */
 //!     let bot = Bot::new();
-//! 
+//!
 //!     /* 2. Run the bot */
 //!     bot.run().await;
-//! 
+//!
 //! }
-//! 
+//!
 //! ```
-//! 
-//! 
+//!
+//!
 //! # Customize with Modules
-//! 
+//!
 //! A `Module` is a group of bot objects (eg `Command`) that elevated users can manage through built in `disable` and `enable` commands
-//! 
+//!
 //! Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
 //!    
 //! ```rust
@@ -48,16 +48,16 @@
 //! }
 //!    ```
 //!    
-//! 
+//!
 //! # Create your own Custom Modules
-//! 
+//!
 //! Create a custom `Module` by :
-//! 
+//!
 //! 1. Defining Functions that create the Custom Bot Objects (eg `Command`)
-//! 
+//!
 //! 2. Define a function that creates a `Module` with the Custom Bot Objects loaded
-//! 
-//! 
+//!
+//!
 //! ```rust
 //! use forcebot_core::Bot;
 //!    
@@ -86,7 +86,7 @@
 //!        pub fn new() -> Module {
 //!            /* 1. Create a new module */
 //!            let mut custom_mod = Module::new(
-//!                vec!["test".to_string()], 
+//!                vec!["test".to_string()],
 //!                "".to_string());
 //!    
 //!            /* 2. Load the cmd into a new module */
@@ -96,7 +96,7 @@
 //!    
 //!        }
 //!    
-//!        /// Command definition 
+//!        /// Command definition
 //!        pub fn cmd_test() -> Command {
 //!            /* 1. Create a new cmd */
 //!            let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
@@ -107,7 +107,7 @@
 //!                    let _= bot.client.say_in_reply_to(
 //!                        &msg, "test return".to_string()).await;
 //!                }
-//!                Result::Err("Not Valid message type".to_string()) 
+//!                Result::Err("Not Valid message type".to_string())
 //!            }
 //!    
 //!            /* 3. Set Command flags */
@@ -119,99 +119,99 @@
 //!        }
 //!    }
 //!    
-//! 
+//!
 //! ```
-//! 
+//!
 //! # Simple Debug Listener
 //! Bot with a simple listener that listens for all messages and prints in output
-//! 
+//!
 //! ```rust
 //! use std::sync::Arc;
-//! 
+//!
 //! use forcebot_core::{execution_async, Bot, Listener};
 //! use twitch_irc::message::ServerMessage;
-//! 
+//!
 //! #[tokio::main]
 //! pub async fn main() {
-//! 
+//!
 //!     /* 1. Create the bot using env */
 //!     let mut bot = Bot::new();
-//! 
+//!
 //!     /* 2a. Create a new blank Listener */
 //!     let mut listener = Listener::new();
-//! 
+//!
 //!     /* 2b. Set a trigger condition function for listener */
 //!     listener.set_trigger_cond_fn(
 //!         |_:Arc<Bot>,_:ServerMessage| true
 //!     );
-//! 
+//!
 //!     /* 2c. Define an async fn callback execution */
 //!     async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
 //!         dbg!(message); /* outputs message to debug */
-//!         Result::Ok("Success".to_string()) 
+//!         Result::Ok("Success".to_string())
 //!     }
-//! 
+//!
 //!     /* 2d. Set and Store the execution body using `execution_async()`  */
 //!     listener.set_exec_fn(execution_async(execbody));
-//! 
+//!
 //!     /* 3. Load the listener into the bot */
 //!     bot.load_listener(listener);
-//! 
+//!
 //!     /* 4. Run the bot */
 //!     bot.run().await;
-//! 
+//!
 //! }
-//! 
+//!
 //! ```
-//! 
+//!
 //! # Moderator Reactor
-//! 
+//!
 //! ```
-//! 
+//!
 //! use std::sync::Arc;
-//! 
+//!
 //! use forcebot_core::Bot;
 //! use forcebot_core::execution_async;
 //! use forcebot_core::Listener;
 //! use twitch_irc::message::ServerMessage;
-//! 
-//! 
+//!
+//!
 //! #[tokio::main]
 //! pub async fn main() {
-//! 
+//!
 //!     /* Create the bot using env */
 //!     let mut bot = Bot::new();
-//! 
+//!
 //!     /* 1. Create a new blank Listener */
 //!     let mut listener = Listener::new();
-//! 
+//!
 //!     /* 2. Set a trigger condition function for listener */
-//! 
+//!
 //!     listener.set_trigger_cond_fn(
-//!         |_:Arc<Bot>,message:ServerMessage| 
+//!         |_:Arc<Bot>,message:ServerMessage|
 //!            if let ServerMessage::Privmsg(msg) = message {
 //!                 for badge in msg.badges {
 //!                     if matches!(badge, x if x.name == "moderator") {
 //!                         // dbg!("moderator found");
 //!                         return true;
 //!                     }
-//!                 } 
+//!                 }
 //!                 false
 //!             } else { false }
 //!     );
-//! 
+//!
 //!     /* 3. Define an async fn callback execution */
 //!     async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
 //!         if let ServerMessage::Privmsg(msg) = message {
 //!             let _ = bot.client.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
 //!             return Result::Ok("Success".to_string()) ;
 //!         }
-//!         Result::Err("Not Valid message type".to_string()) 
+//!         Result::Err("Not Valid message type".to_string())
 //!     }
-//! 
+//!
 //!     /* 4. Set and Store the execution body using `execution_async()`  */
 //!     listener.set_exec_fn(execution_async(execbody));
-//! 
+//!
 //!     /* 5. Load the listener into the bot */
 //!     bot.load_listener(listener);
 //!
@@ -220,17 +220,15 @@
 //!
 //! }
 
-
-
 pub mod botcore;
 pub mod custom_mods;
 pub use botcore::bot::Bot;
-pub use botcore::bot_objects::execution_async;
 pub use botcore::bot_objects::command_condition_async;
-pub use botcore::bot_objects::listener_condition_async;
+pub use botcore::bot_objects::execution_async;
 pub use botcore::bot_objects::listener::Listener;
+pub use botcore::bot_objects::listener_condition_async;
 // pub use crate::botcore::bot_objects::command::Command;
 pub use botcore::bot_objects::command::Command;
-pub use botcore::modules::Module;
 pub use botcore::bot_objects::Badge;
 pub use botcore::modules;
+pub use botcore::modules::Module;
diff --git a/moderator_reactor/src/main.rs b/moderator_reactor/src/main.rs
index 5705fbe..284dd0a 100644
--- a/moderator_reactor/src/main.rs
+++ b/moderator_reactor/src/main.rs
@@ -1,27 +1,25 @@
 //! Simple bot with a custom listeners that listens for moderators and respond to the moderator
-//! 
-//! Be sure the followig is defined in `.env` 
+//!
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
 use std::sync::Arc;
 
-use forcebot_core::Bot;
 use forcebot_core::execution_async;
+use forcebot_core::Bot;
 use forcebot_core::Listener;
 use twitch_irc::message::ServerMessage;
 
-
 #[tokio::main]
 pub async fn main() {
-
     /* Create the bot using env */
     let bot = Bot::new().await;
 
@@ -30,26 +28,32 @@ pub async fn main() {
 
     /* 2. Set a trigger condition function for listener */
 
-    listener.set_trigger_cond_fn(
-        |_:Arc<Bot>,message:ServerMessage| 
-            if let ServerMessage::Privmsg(msg) = message {
-                for badge in msg.badges {
-                    if matches!(badge, x if x.name == "moderator") {
-                        // dbg!("moderator found");
-                        return true;
-                    }
-                } 
-                false
-            } else { false }
-    );
+    listener.set_trigger_cond_fn(|_: Arc<Bot>, message: ServerMessage| {
+        if let ServerMessage::Privmsg(msg) = message {
+            for badge in msg.badges {
+                if matches!(badge, x if x.name == "moderator") {
+                    // dbg!("moderator found");
+                    return true;
+                }
+            }
+            false
+        } else {
+            false
+        }
+    });
 
     /* 3. Define an async fn callback execution */
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
-            let _ = bot.chat.lock().await.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
-            return Result::Ok("Success".to_string()) ;
+            let _ = bot
+                .chat
+                .lock()
+                .await
+                .say_in_reply_to(&msg, "pepeKneel".to_string())
+                .await;
+            return Result::Ok("Success".to_string());
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     /* 4. Set and Store the execution body using `execution_async()`  */
@@ -60,5 +64,4 @@ pub async fn main() {
 
     /* Run the bot */
     bot.run().await;
-
 }
diff --git a/new_empty_bot/src/main.rs b/new_empty_bot/src/main.rs
index a81462e..856f6b1 100644
--- a/new_empty_bot/src/main.rs
+++ b/new_empty_bot/src/main.rs
@@ -1,12 +1,12 @@
 //! Example simple Binary crate that creates & runs bot based on `.env`
-//! Be sure the followig is defined in `.env` 
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
@@ -14,11 +14,9 @@ use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
-
     /* 1. Create the bot using env */
     let bot = Bot::new().await;
 
     /* 2. Run the bot */
     bot.run().await;
-
 }
diff --git a/simple_command_bot/src/main.rs b/simple_command_bot/src/main.rs
index 418e1eb..c49e55c 100644
--- a/simple_command_bot/src/main.rs
+++ b/simple_command_bot/src/main.rs
@@ -1,43 +1,46 @@
 //! Bot with custom example commands that responds to caller if allowed
-//! 
+//!
 //! Commands that are passed a blank prefix will use the bot prefix
-//! 
-//! Be sure the followig is defined in `.env` 
+//!
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
 use std::sync::Arc;
 
+use forcebot_core::execution_async;
 use forcebot_core::Badge;
 use forcebot_core::Bot;
-use forcebot_core::execution_async;
 use forcebot_core::Command;
 use twitch_irc::message::ServerMessage;
 
-
 #[tokio::main]
 pub async fn main() {
-
     /* Create the bot using env */
     let bot = Bot::new().await;
 
     /* 1. Create a new blank cmd */
-    let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
+    let mut cmd = Command::new(vec!["test".to_string()], "".to_string());
 
     /* 2. Define an async fn callback execution */
-    async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         if let ServerMessage::Privmsg(msg) = message {
-            let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
-            return Result::Ok("Success".to_string()) ;
+            let _ = bot
+                .chat
+                .lock()
+                .await
+                .say_in_reply_to(&msg, String::from("test success"))
+                .await;
+            return Result::Ok("Success".to_string());
         }
-        Result::Err("Not Valid message type".to_string()) 
+        Result::Err("Not Valid message type".to_string())
     }
 
     /* 3. Set and Store the execution body using `execution_async()`  */
@@ -54,5 +57,4 @@ pub async fn main() {
 
     /* Run the bot */
     bot.run().await;
-
 }
diff --git a/simple_debug_listener/src/main.rs b/simple_debug_listener/src/main.rs
index 27ab37e..b6dc626 100644
--- a/simple_debug_listener/src/main.rs
+++ b/simple_debug_listener/src/main.rs
@@ -1,12 +1,12 @@
 //! Example simple Binary crate that creates & runs bot based on `.env`
-//! Be sure the followig is defined in `.env` 
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
@@ -17,7 +17,6 @@ use twitch_irc::message::ServerMessage;
 
 #[tokio::main]
 pub async fn main() {
-
     /* 1. Create the bot using env */
     let bot = Bot::new().await;
 
@@ -25,14 +24,12 @@ pub async fn main() {
     let mut listener = Listener::new();
 
     /* 2b. Set a trigger condition function for listener */
-    listener.set_trigger_cond_fn(
-        |_:Arc<Bot>,_:ServerMessage| true
-    );
+    listener.set_trigger_cond_fn(|_: Arc<Bot>, _: ServerMessage| true);
 
     /* 2c. Define an async fn callback execution */
-    async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+    async fn execbody(_: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
         dbg!(message); /* outputs message to debug */
-        Result::Ok("Success".to_string()) 
+        Result::Ok("Success".to_string())
     }
 
     /* 2d. Set and Store the execution body using `execution_async()`  */
@@ -43,5 +40,4 @@ pub async fn main() {
 
     /* 4. Run the bot */
     bot.run().await;
-
 }
diff --git a/simple_module_example/src/main.rs b/simple_module_example/src/main.rs
index e588b22..0c07084 100644
--- a/simple_module_example/src/main.rs
+++ b/simple_module_example/src/main.rs
@@ -1,19 +1,19 @@
 //! Simple Module with a Command
-//! 
-//! Adding objects through packages provides controls , 
+//!
+//! Adding objects through packages provides controls ,
 //! such as moderators, and brodcasters can disable or enable mods
-//! 
-//! Here, moderators or above can enable or disable the `test` 
+//!
+//! Here, moderators or above can enable or disable the `test`
 //! module with the command `<prefix> disable test`
-//! 
-//! Be sure the followig is defined in `.env` 
+//!
+//! Be sure the followig is defined in `.env`
 //! - login_name
 //! - access_token
 //! - bot_channels
 //! - prefix
 //! - bot_admins
-//! 
-//! Bot access tokens be generated here - 
+//!
+//! Bot access tokens be generated here -
 //! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
 //! - More Info - <https://dev.twitch.tv/docs/authentication>
 
@@ -21,7 +21,6 @@ use forcebot_core::Bot;
 
 #[tokio::main]
 pub async fn main() {
-
     /* Create the bot using env */
     let bot = Bot::new().await;
 
@@ -30,43 +29,41 @@ pub async fn main() {
 
     /* Run the bot */
     bot.run().await;
-
 }
 
-
 pub mod custom_mod {
     use std::sync::Arc;
 
     use forcebot_core::{execution_async, Badge, Bot, Command, Module};
     use twitch_irc::message::ServerMessage;
 
-
     /// Module definition with a loaded command
     pub fn new() -> Module {
         /* 1. Create a new module */
-        let mut custom_mod = Module::new(
-            vec!["test".to_string()], 
-            "".to_string());
+        let mut custom_mod = Module::new(vec!["test".to_string()], "".to_string());
 
         /* 2. Load the cmd into a new module */
         custom_mod.load_command(cmd_test());
 
         custom_mod
-
     }
 
-    /// Command definition 
+    /// Command definition
     pub fn cmd_test() -> Command {
         /* 1. Create a new cmd */
-        let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
+        let mut cmd = Command::new(vec!["test".to_string()], "".to_string());
 
         /* 2. Define exec callback  */
-        async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
+        async fn execbody(bot: Arc<Bot>, message: ServerMessage) -> Result<String, String> {
             if let ServerMessage::Privmsg(msg) = message {
-                let _= bot.chat.lock().await.say_in_reply_to(
-                    &msg, "test return".to_string()).await;
+                let _ = bot
+                    .chat
+                    .lock()
+                    .await
+                    .say_in_reply_to(&msg, "test return".to_string())
+                    .await;
             }
-            Result::Err("Not Valid message type".to_string()) 
+            Result::Err("Not Valid message type".to_string())
         }
 
         /* 3. Set Command flags */
@@ -76,4 +73,4 @@ pub mod custom_mod {
 
         cmd
     }
-}
\ No newline at end of file
+}
-- 
2.49.0