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);