Merge pull request 'Format Cleanup' (#18) from format-cleanup into main
All checks were successful
ci/woodpecker/push/cargo-checks Pipeline was successful

Reviewed-on: #18
This commit is contained in:
modulatingforce 2024-03-02 17:30:28 -05:00
commit 9cfc54394d
13 changed files with 704 additions and 2145 deletions

View file

@ -16,5 +16,5 @@ casual_logger = "0.6.5"
[lib] [lib]
name = "botLib" name = "bot_lib"
path = "src/lib.rs" path = "src/lib.rs"

View file

@ -1,9 +1,7 @@
pub mod bot_actions;
pub mod botinstance; pub mod botinstance;
pub mod botlog;
pub mod botmodules; pub mod botmodules;
pub mod chat;
pub mod identity; pub mod identity;
pub mod ratelimiter; pub mod ratelimiter;
// pub fn init() -> ()
// {
// println!("I was here");
// }

27
src/core/bot_actions.rs Normal file
View file

@ -0,0 +1,27 @@
pub mod actions_util {
use std::boxed::Box;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use twitch_irc::message::PrivmsgMessage;
use crate::core::botinstance::BotInstance;
pub type BotAM = Arc<Mutex<BotInstance>>;
pub type BotAR = Arc<RwLock<BotInstance>>;
pub type ExecBody = Box<
dyn Fn(BotAR, PrivmsgMessage) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync,
>;
pub fn asyncbox<T>(f: fn(BotAR, PrivmsgMessage) -> T) -> ExecBody
where
T: Future<Output = ()> + Send + 'static,
{
Box::new(move |a, b| Box::pin(f(a, b)))
}
}

View file

@ -1,246 +1,27 @@
// use futures::lock::Mutex;
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::RwLock;
use twitch_irc::login::StaticLoginCredentials;
use twitch_irc::message::PrivmsgMessage;
use twitch_irc::message::ServerMessage;
use twitch_irc::transport::tcp::TCPTransport;
use twitch_irc::transport::tcp::TLS;
use twitch_irc::ClientConfig;
use twitch_irc::SecureTCPTransport;
use twitch_irc::TwitchIRCClient;
// use std::borrow::Borrow;
use dotenv::dotenv;
use std::borrow::BorrowMut;
use std::boxed;
use std::cell::Ref;
use std::env;
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::sync::Arc;
use rand::Rng; use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::{Mutex, RwLock};
// Important to use tokios Mutex here since std Mutex doesn't work with async functions use twitch_irc::login::StaticLoginCredentials;
use tokio::sync::Mutex; use twitch_irc::message::{PrivmsgMessage, ServerMessage};
use twitch_irc::transport::tcp::{TCPTransport, TLS};
use twitch_irc::{ClientConfig, SecureTCPTransport, TwitchIRCClient};
use dotenv::dotenv;
use casual_logger::Log;
use crate::core::botmodules::BotAction;
use crate::core::ratelimiter;
use crate::core::ratelimiter::RateLimiter; use crate::core::ratelimiter::RateLimiter;
use crate::core::botmodules; use crate::core::bot_actions::actions_util::BotAR;
use crate::core::botmodules::ModulesManager; use crate::core::botmodules::ModulesManager;
use crate::core::identity::{ChangeResult, IdentityManager, Permissible}; use crate::core::identity::{ChangeResult, IdentityManager, Permissible};
use std::cell::RefCell; use crate::core::botlog;
use std::rc::Rc; use crate::core::chat::Chat;
use std::sync::Arc;
// use futures::lock::Mutex;
use std::pin::Pin;
//use std::borrow::Borrow;
use core::borrow::Borrow;
// pub type BotAR = Arc<RwLock<BotInstance>>;
use super::botmodules::bot_actions::actions_util::BotAR;
use casual_logger::{Level, Log};
pub mod botlog {
/*
Module intends to add some layers to logging with the module user only requiring to pass :
- String Log message
- Option<String> - Code_Module
- Option<PrivmsgMessage> - this is used to parse out Chatter & Channel into the logs
*/
use casual_logger::{Level, Log};
use twitch_irc::message::PrivmsgMessage;
// trace, debug, info, notice, warn, error, fatal
/*
in main : Log::debug("Checking bot actions", Some("main()".to_string()), None);
in log :
[blalba@timestmp]
debug = "Checking bot actions",
*/
pub fn trace(
in_msg: &str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> () {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::trace_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn debug(
in_msg: &str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> () {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::debug_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn info(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) -> () {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::info_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn notice(
in_msg: &str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> () {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::notice_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn warn(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) -> () {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::warn_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn error(
in_msg: &str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> () {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::error_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn fatal<'a>(
in_msg: &'a str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> &'a str {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::fatal_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
in_msg
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ChType { pub enum ChType {
@ -249,105 +30,8 @@ pub enum ChType {
pub use ChType::Channel; pub use ChType::Channel;
#[derive(Clone)]
pub struct Chat {
pub ratelimiters: Arc<Mutex<HashMap<ChType, RateLimiter>>>, // used to limit messages sent per channel
pub client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
}
impl Chat {
pub fn init(
ratelimiters: HashMap<ChType, RateLimiter>,
client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
) -> Chat {
Chat {
ratelimiters: Arc::new(Mutex::new(ratelimiters)),
client: client,
}
}
pub async fn init_channel(&mut self, chnl: ChType) -> () {
let n = RateLimiter::new();
self.ratelimiters.lock().await.insert(chnl, n);
}
// pub async fn say_in_reply_to(&mut self, msg:& PrivmsgMessage , mut outmsg:String) -> () {
pub async fn say_in_reply_to(&self, msg: &PrivmsgMessage, mut outmsg: String) -> () {
/*
formats message before sending to TwitchIRC
- [x] Custom String Formatting (e.g., adding random black spaces)
- [x] Ratelimiter Handling
- [ ] Checkf if BotActions is Enabled & Caller is Allowed to Run
*/
let a = Arc::clone(&self.ratelimiters);
let mut a = a.lock().await;
let contextratelimiter = a
// .get_mut()
.get_mut(&Channel(String::from(&msg.channel_login)))
.expect("ERROR: Issue with Rate limiters");
match contextratelimiter.check_limiter() {
ratelimiter::LimiterResp::Allow => {
let maxblanks = rand::thread_rng().gen_range(1..=20);
for _i in 1..maxblanks {
let blankspace: &str = "󠀀";
outmsg.push_str(blankspace);
}
self.client.say_in_reply_to(msg, outmsg).await.unwrap();
// println!("(#{}) > {}", msg.channel_login, "rate limit counter increase");
// Log::trace(&format!("(#{}) > {}", msg.channel_login, "rate limit counter increase"));
botlog::trace(
&format!(
"(#{}) > {}",
msg.channel_login, "rate limit counter increase"
),
Some("Chat > say_in_reply_to".to_string()),
Some(&msg),
);
contextratelimiter.increment_counter();
// println!("{:?}",self.ratelimiters);
// Log::trace(&format!("{:?}",self.ratelimiters));
botlog::trace(
&format!("{:?}", self.ratelimiters),
Some("Chat > say_in_reply_to".to_string()),
Some(&msg),
);
}
ratelimiter::LimiterResp::Skip => {
(); // do nothing otherwise
}
}
Log::flush();
}
async fn say(&self, _: String, _: String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.say(msg,outmsg).await.unwrap();
}
async fn me(&self, _: String, _: String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
async fn me_in_reply_to(&self, _: String, _: String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct BotManagers { pub struct BotManagers {
// pub botmodules : ModulesManager,
pub identity: Arc<RwLock<IdentityManager>>, pub identity: Arc<RwLock<IdentityManager>>,
pub chat: Chat, pub chat: Chat,
} }
@ -363,7 +47,7 @@ impl BotManagers {
} }
} }
pub fn rIdentity(self) -> Arc<RwLock<IdentityManager>> { pub fn r_identity(self) -> Arc<RwLock<IdentityManager>> {
self.identity self.identity
} }
} }
@ -376,8 +60,6 @@ impl<T: Clone> ArcBox<T> {
} }
} }
//#[derive(Clone)]
// #[derive(Copy)] // <-- Cannot be derived
pub struct BotInstance { pub struct BotInstance {
pub prefix: char, pub prefix: char,
pub bot_channel: ChType, pub bot_channel: ChType,
@ -386,7 +68,7 @@ pub struct BotInstance {
pub twitch_oauth: String, pub twitch_oauth: String,
pub bot_channels: Vec<ChType>, pub bot_channels: Vec<ChType>,
pub botmgrs: BotManagers, pub botmgrs: BotManagers,
//modesmgr : ModesManager, // Silent/Quiet , uwu , frisky/horny //modesmgr : ModesManager, // [FUTURE] Silent/Quiet , uwu , frisky/horny
} }
impl BotInstance { impl BotInstance {
@ -402,14 +84,9 @@ impl BotInstance {
.next() .next()
.expect("ERROR : when defining prefix"); .expect("ERROR : when defining prefix");
/*
Vector of channels to join
*/
let mut botchannels = Vec::new(); let mut botchannels = Vec::new();
for chnl in env::var("bot_channels").unwrap().split(',') { for chnl in env::var("bot_channels").unwrap().split(',') {
// println!("(Env Var # {})",chnl);
botchannels.push(Channel(String::from(chnl))); botchannels.push(Channel(String::from(chnl)));
} }
@ -421,32 +98,19 @@ impl BotInstance {
let (incoming_messages, client) = let (incoming_messages, client) =
TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config); TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config);
// hashmap for channels and their associated ratelimiters
let mut ratelimiters = HashMap::new(); let mut ratelimiters = HashMap::new();
for Channel(chnl) in &botchannels { for Channel(chnl) in &botchannels {
// For each channel in botchannels // For each channel in botchannels , join & create ratelimiters
client.join(chnl.to_owned()).unwrap(); client.join(chnl.to_owned()).unwrap();
// ratelimiters are a hashmap of channel and a corresponding rate limiter
let n = RateLimiter::new(); let n = RateLimiter::new();
ratelimiters.insert(Channel(String::from(chnl)), n); ratelimiters.insert(Channel(String::from(chnl)), n);
//self.chat.ratelimiters.insert(Channel(String::from(chnl)),n);
} }
// let b = BotInstance {
// prefix : prefix,
// bot_channel : Channel(login_name) ,
// incoming_messages : Arc::new(RwLock::new(incoming_messages)),
// botmodules : ModulesManager::init().await,
// twitch_oauth : oauth_token,
// bot_channels : botchannels,
// botmgrs : BotManagers::init(ratelimiters,client),
// };
BotInstance { BotInstance {
prefix: prefix, prefix,
bot_channel: Channel(login_name), bot_channel: Channel(login_name),
incoming_messages: Arc::new(RwLock::new(incoming_messages)), incoming_messages: Arc::new(RwLock::new(incoming_messages)),
botmodules: ModulesManager::init().await, botmodules: ModulesManager::init().await,
@ -454,56 +118,34 @@ impl BotInstance {
bot_channels: botchannels, bot_channels: botchannels,
botmgrs: BotManagers::init(ratelimiters, client), botmgrs: BotManagers::init(ratelimiters, client),
} }
// b
} }
pub async fn runner(self) -> () { pub async fn runner(self) {
// Main Game Loop
let bot = Arc::new(RwLock::new(self)); let bot = Arc::new(RwLock::new(self));
let join_handle = tokio::spawn(async move { let join_handle = tokio::spawn(async move {
let a = bot.read().await; let botlock = bot.read().await;
let mut a = a.incoming_messages.write().await; let mut msglock = botlock.incoming_messages.write().await;
while let Some(message) = a.recv().await {
while let Some(message) = msglock.recv().await {
match message { match message {
ServerMessage::Notice(msg) => { ServerMessage::Notice(msg) => {
match &msg.channel_login {
Some(chnl) => {
// println!("NOTICE : (#{}) {}", chnl, msg.message_text)
// Log::notice(&format!("NOTICE : (#{}) {}", chnl, msg.message_text));
botlog::notice( botlog::notice(
&format!("NOTICE : (#{}) {}", chnl, msg.message_text), format!("NOTICE : (#{:?}) {}", msg.channel_login, msg.message_text)
.as_str(),
Some("BotInstance > runner()".to_string()), Some("BotInstance > runner()".to_string()),
None, None,
); );
} }
None => {
// println!("NOTICE : {}", msg.message_text);
// Log::notice(&format!("NOTICE : {}", msg.message_text));
botlog::notice(
&format!("NOTICE : {}", msg.message_text),
Some("BotInstance > runner()".to_string()),
None,
);
}
}
}
ServerMessage::Privmsg(msg) => { ServerMessage::Privmsg(msg) => {
// println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
// Log::trace(&format!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text));
botlog::debug( botlog::debug(
&format!( format!(
"Twitch Chat > {} @ #{}: {}", "[Twitch Chat > {}] > {}: {}",
msg.channel_login, msg.sender.name, msg.message_text msg.channel_login, msg.sender.name, msg.message_text
), )
Some("BotInstance > runner()".to_string()), .as_str(),
Some(&msg),
);
// println!("Privmsg section");
// Log::debug(&format!("Privmsg section"));
botlog::trace(
&format!("Privmsg section"),
Some("BotInstance > runner()".to_string()), Some("BotInstance > runner()".to_string()),
Some(&msg), Some(&msg),
); );
@ -511,28 +153,22 @@ impl BotInstance {
BotInstance::listener_main_prvmsg(Arc::clone(&bot), &msg).await; BotInstance::listener_main_prvmsg(Arc::clone(&bot), &msg).await;
} }
ServerMessage::Whisper(msg) => { ServerMessage::Whisper(msg) => {
// println!("(w) {}: {}", msg.sender.name, msg.message_text); botlog::debug(
// Log::trace(&format!("(w) {}: {}", msg.sender.name, msg.message_text)); format!("[Whisper] {}: {}", msg.sender.name, msg.message_text).as_str(),
botlog::trace(
&format!("(w) {}: {}", msg.sender.name, msg.message_text),
Some("BotInstance > runner()".to_string()), Some("BotInstance > runner()".to_string()),
None, None,
); );
} }
ServerMessage::Join(msg) => { ServerMessage::Join(msg) => {
// println!("JOINED: {}", msg.channel_login);
// Log::notice(&format!("JOINED: {}", msg.channel_login));
botlog::notice( botlog::notice(
&format!("JOINED: {}", msg.channel_login), format!("JOINED: {}", msg.channel_login).as_str(),
Some("BotInstance > runner()".to_string()), Some("BotInstance > runner()".to_string()),
None, None,
); );
} }
ServerMessage::Part(msg) => { ServerMessage::Part(msg) => {
// println!("PARTED: {}", msg.channel_login);
// Log::notice(&format!("PARTED: {}", msg.channel_login));
botlog::notice( botlog::notice(
&format!("PARTED: {}", msg.channel_login), format!("PARTED: {}", msg.channel_login).as_str(),
Some("BotInstance > runner()".to_string()), Some("BotInstance > runner()".to_string()),
None, None,
); );
@ -546,81 +182,39 @@ impl BotInstance {
join_handle.await.unwrap(); join_handle.await.unwrap();
} }
pub fn get_botmodules(self) -> Arc<ModulesManager> {
self.botmodules
}
pub async fn get_botmgrs(self) -> BotManagers {
let a = self.botmgrs;
a
}
pub fn get_identity(&self) -> Arc<RwLock<IdentityManager>> { pub fn get_identity(&self) -> Arc<RwLock<IdentityManager>> {
Arc::clone(&self.botmgrs.identity) Arc::clone(&self.botmgrs.identity)
} }
pub fn get_prefix(&self) -> char { pub fn get_prefix(&self) -> char {
(*self).prefix self.prefix
} }
// ----------------- // -----------------
// PRIVATE FUNCTIONS // PRIVATE FUNCTIONS
pub async fn listener_main_prvmsg(bot: BotAR, msg: &PrivmsgMessage) -> () { async fn listener_main_prvmsg(bot: BotAR, msg: &PrivmsgMessage) {
// println!(">> Inner listenermain_prvmsg()");
// Log::trace(">> Inner listenermain_prvmsg()");
botlog::trace( botlog::trace(
">> Inner listenermain_prvmsg()", ">> Inner listenermain_prvmsg()",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
// let a = a; // // [ ] #todo Need to run through all Listener Bodies for Enabled Modules for the context of the message (e.g., ModStatus is Enabled in the context for the channel)
// println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
// // [ ] Need to run through all Listener Bodies for Enabled Modules for the context of the message (e.g., ModStatus is Enabled in the context for the channel)
let botlock = bot.read().await; let botlock = bot.read().await;
let hacts = Arc::clone(&botlock.botmodules.botactions); let actsdb = Arc::clone(&botlock.botmodules.botactions);
// let hacts = hacts.read().await; let actsdblock = actsdb.read().await;
let a = hacts.read().await;
// println!("hacts size : {}",(*a).len()); botlog::debug(
// Log::debug(&format!("hacts size : {}",(*a).len())); format!("# of BotModules: {}", (*actsdblock).len()).as_str(),
botlog::trace(
&format!("hacts size : {}", (*a).len()),
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
// println!(">> Inner listenermain_prvmsg() >> before for loop of bot actions"); for acts in (*actsdblock).values() {
// Log::trace(">> Inner listenermain_prvmsg() >> before for loop of bot actions");
botlog::trace(
">> Inner listenermain_prvmsg() >> before for loop of bot actions",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
for (_m, acts) in &*hacts.read().await {
// println!(">> Inner listenermain_prvmsg() >> checking bot actions");
// Log::trace(">> Inner listenermain_prvmsg() >> checking bot actions");
botlog::trace(
">> Inner listenermain_prvmsg() >> checking bot actions",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// let bot = bot;
for a in acts { for a in acts {
// println!(">> Inner listenermain_prvmsg() >> checking bot actions >> 2"); match a {
// Log::trace(">> Inner listenermain_prvmsg() >> checking bot actions >> 2");
botlog::trace(
">> Inner listenermain_prvmsg() >> checking bot actions >> 2",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let _act = match a {
crate::core::botmodules::BotAction::C(c) => { crate::core::botmodules::BotAction::C(c) => {
/* /*
BotCommand handling - BotCommand handling -
@ -632,22 +226,15 @@ impl BotInstance {
_cmdreqroles:Vec<UserRole>) _cmdreqroles:Vec<UserRole>)
*/ */
// for v in msg.message_text.split(" ") {
// println!("args : {v}");
// }
// println!("Reviewing internal commands");
// Log::trace("Reviewing internal commands");
botlog::trace( botlog::trace(
"Reviewing internal commands", "Reviewing internal commands",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
// let inpt = msg.message_text.split("\n").next().expect("ERROR during BotCommand");
let inpt = msg let inpt = msg
.message_text .message_text
.split(" ") .split(' ')
.next() .next()
.expect("ERROR during BotCommand"); .expect("ERROR during BotCommand");
@ -670,118 +257,78 @@ impl BotInstance {
} }
if confirmed_bot_command { if confirmed_bot_command {
// println!("Confirmed bot command");
// Log::debug("Confirmed bot command");
botlog::debug( botlog::debug(
"Confirmed bot command", format!("Confirmed bot command ; Msg : {}", msg.message_text)
.as_str(),
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), // Some(&msg),
); Some(msg),
// println!("Going for botlock");
// Log::trace("Going for botlock");
botlog::trace(
"Going for botlock",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
); );
let botlock = bot.read().await; let botlock = bot.read().await;
// println!("Going for identity");
// Log::trace("Going for identity");
botlog::trace(
"Going for identity",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let id = botlock.get_identity(); let id = botlock.get_identity();
let eval = { let eval = {
let mut id = id.write().await; let mut idlock = id.write().await;
// println!("Unlocking identity"); let (permissability, chngrslt) = idlock
// Log::trace("Unlocking identity"); .can_user_run_prvmsg(msg, c.required_roles.clone())
botlog::trace( .await;
"Unpacking identity",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let (a, b) = (permissability, chngrslt)
id.can_user_run_PRVMSG(&msg, c.required_roles.clone()).await;
// // [-] #todo : need ot add functionality around here to do an o7 when a mod has been promoted => Preferring to do this outside the mutex
// if let ChangeResult::Success(b) = b {
// // let b = b.to_lowercase();
// // let b = b.contains(&"Auto Promoted Mod".to_lowercase());
// if b.to_lowercase().contains(&"Auto Promoted Mod".to_lowercase()) {
// let chat =
// }
// }
(a, b)
}; };
// println!("Checking if permissible");
Log::trace("Checking if permissible");
botlog::trace( botlog::trace(
"Checking if permissible", "Checking if permissible",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
let (eval, rolechange) = eval; let (eval, rolechange) = eval;
if let ChangeResult::Success(b) = rolechange { if let ChangeResult::Success(innerstr) = rolechange {
if b.to_lowercase() if innerstr
.to_lowercase()
.contains(&"Auto Promoted Mod".to_lowercase()) .contains(&"Auto Promoted Mod".to_lowercase())
{ {
botlog::notice( botlog::notice(
"Assigning Mod UserRole to Mod", "Assigning Mod UserRole to Mod",
Some("botinstance > listener_main_prvmsg()".to_string()), Some("botinstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
);
// println!("Read() lock Bot");
// Log::trace("Read() lock Bot");
botlog::trace(
"Read() lock Bot",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
); );
let botlock = bot.read().await; let botlock = bot.read().await;
let outstr = let outstr =
"o7 a Mod. I kneel to serve! pepeKneel ".to_string(); "o7 a Mod. I kneel to serve! pepeKneel ".to_string();
(*botlock).botmgrs.chat.say_in_reply_to(msg, outstr).await; botlock.botmgrs.chat.say_in_reply_to(msg, outstr).await;
} }
} }
match eval { match eval {
Permissible::Allow => { Permissible::Allow => {
// println!("Executed as permissible");
// Log::debug("Executed as permissible");
botlog::debug( botlog::debug(
"Executed as permissible", "Executed as permissible",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
let a = Arc::clone(&bot); let a = Arc::clone(&bot);
c.execute(a, msg.clone()).await; c.execute(a, msg.clone()).await;
// println!("exit out of execution");
// Log::trace("exit out of execution");
botlog::trace( botlog::trace(
"exit out of execution", "exit out of execution",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), // Some(&msg),
Some(msg),
); );
} }
Permissible::Block => { Permissible::Block => {
// println!("User Not allowed to run command");
// Log::info("User Not allowed to run command");
botlog::info( botlog::info(
"User Not allowed to run command", "User Not allowed to run command",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
} // _ => (), }
}; };
} }
} }
@ -796,19 +343,12 @@ impl BotInstance {
} }
} }
// // [ ] There should be a BotCommand Listener to check for prefixes ran
// println!("End of Separate Listener Main prvmsg");
// Log::trace("End of Separate Listener Main prvmsg");
botlog::trace( botlog::trace(
"End of Separate Listener Main prvmsg", "End of Separate Listener Main prvmsg",
Some("BotInstance > listener_main_prvmsg()".to_string()), Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg), Some(msg),
); );
// self
// bot
Log::flush(); Log::flush();
} }
} }

175
src/core/botlog.rs Normal file
View file

@ -0,0 +1,175 @@
/*
Module intends to add some layers to logging with the module user only requiring to pass :
- String Log message
- Option<String> - Code_Module
- Option<PrivmsgMessage> - this is used to parse out Chatter & Channel into the logs
*/
// use casual_logger::{Level, Log};
use casual_logger::Log;
use twitch_irc::message::PrivmsgMessage;
// trace, debug, info, notice, warn, error, fatal
/*
in main : Log::debug("Checking bot actions", Some("main()".to_string()), None);
in log :
[blalba@timestmp]
debug = "Checking bot actions",
*/
pub fn trace(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::trace_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn debug(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::debug_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn info(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::info_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn notice(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::notice_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn warn(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::warn_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn error(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::error_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn fatal<'a>(
in_msg: &'a str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> &'a str {
let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
(
Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()),
) // <-- Clone fine atm while we're just working with Strings
}
None => (None, None),
};
Log::fatal_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
in_msg
}

View file

@ -1,28 +1,3 @@
use core::panic;
use std::error::Error;
use std::collections::HashMap;
use crate::core::identity;
use std::cell::RefCell;
use std::sync::Arc;
use tokio::sync::RwLock;
use std::future::Future;
// use futures::lock::Mutex;
// Important to use tokios Mutex here since std Mutex doesn't work with async functions
use tokio::sync::Mutex;
use crate::core::botinstance::{self, botlog, BotInstance};
use std::rc::Rc;
// use tokio::sync::RwLock;
use async_trait::async_trait;
use casual_logger::{Level, Log};
/* /*
ModulesManager is used to manage Modules and BotActions associated with those modules ModulesManager is used to manage Modules and BotActions associated with those modules
@ -44,30 +19,36 @@ Example
*/ */
use core::panic;
use std::collections::HashMap;
use std::error::Error;
use std::sync::Arc;
use twitch_irc::message::PrivmsgMessage;
use tokio::sync::RwLock;
use async_trait::async_trait;
use self::bot_actions::actions_util::BotAR;
use crate::core::botinstance::{BotInstance, ChType};
use crate::core::botlog;
use crate::core::identity;
use crate::core::bot_actions;
pub use ChType::Channel;
pub use ModType::BotModule;
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ModType { pub enum ModType {
BotModule(String), BotModule(String),
} }
pub use ModType::BotModule;
// #[derive(Debug, PartialEq, Eq, Hash, Clone)]
// pub enum ChType {
// Channel(String),
// }
use botinstance::ChType;
use twitch_irc::message::PrivmsgMessage;
pub use ChType::Channel;
use self::bot_actions::actions_util;
use self::bot_actions::actions_util::BotAR;
#[derive(Debug)] #[derive(Debug)]
enum StatusLvl { pub enum StatusLvl {
Instance, Instance,
Ch(ChType), _Ch(ChType),
} }
#[derive(Debug)] #[derive(Debug)]
@ -76,7 +57,6 @@ pub enum ModStatusType {
Disabled(StatusLvl), Disabled(StatusLvl),
} }
// #[derive(Clone)]
pub enum BotAction { pub enum BotAction {
C(BotCommand), C(BotCommand),
L(Listener), L(Listener),
@ -84,7 +64,7 @@ pub enum BotAction {
} }
impl BotAction { impl BotAction {
pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) -> () { pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
match self { match self {
BotAction::L(a) => a.execute(m, n).await, BotAction::L(a) => a.execute(m, n).await,
BotAction::C(a) => a.execute(m, n).await, BotAction::C(a) => a.execute(m, n).await,
@ -99,20 +79,18 @@ pub trait BotActionTrait {
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>); async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>);
} }
// #[derive(Clone)]
pub struct BotCommand { pub struct BotCommand {
pub module: ModType, pub module: ModType,
pub command: String, // command call name pub command: String, // command call name
pub alias: Vec<String>, // String of alternative names pub alias: Vec<String>, // String of alternative names
// bot_prefix : char, // although should be global?
pub exec_body: bot_actions::actions_util::ExecBody, pub exec_body: bot_actions::actions_util::ExecBody,
pub help: String, pub help: String,
pub required_roles: Vec<identity::UserRole>, pub required_roles: Vec<identity::UserRole>,
} }
impl BotCommand { impl BotCommand {
pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) -> () { pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
((*self).exec_body)(m, n).await; (*self.exec_body)(m, n).await;
} }
} }
@ -122,7 +100,6 @@ impl BotActionTrait for BotCommand {
self.add_to_modmgr(bot.botmodules).await; self.add_to_modmgr(bot.botmodules).await;
} }
// async fn add_to_modmgr(self, modmgr:Arc<Mutex<ModulesManager>>) {
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) { async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
modmgr modmgr
.add_botaction(self.module.clone(), BotAction::C(self)) .add_botaction(self.module.clone(), BotAction::C(self))
@ -130,40 +107,6 @@ impl BotActionTrait for BotCommand {
} }
} }
pub mod bot_actions {
pub mod actions_util {
use std::boxed::Box;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use crate::core::botinstance::{BotInstance, BotManagers, Chat};
use std::cell::RefCell;
use std::sync::Arc;
use twitch_irc::message::PrivmsgMessage;
// use futures::lock::Mutex;
// Important to use tokios Mutex here since std Mutex doesn't work with async functions
use tokio::sync::{Mutex, RwLock};
pub type BotAM = Arc<Mutex<BotInstance>>;
pub type BotAR = Arc<RwLock<BotInstance>>;
pub type ExecBody = Box<
dyn Fn(BotAR, PrivmsgMessage) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync,
>;
pub fn asyncbox<T>(f: fn(BotAR, PrivmsgMessage) -> T) -> ExecBody
where
T: Future<Output = ()> + Send + 'static,
{
Box::new(move |a, b| Box::pin(f(a, b)))
}
}
}
pub struct Listener { pub struct Listener {
pub module: ModType, pub module: ModType,
pub name: String, pub name: String,
@ -172,17 +115,15 @@ pub struct Listener {
} }
impl Listener { impl Listener {
pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) -> () { pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) {
((*self).exec_body)(m, n).await; (self.exec_body)(m, n).await;
} }
} }
#[async_trait] #[async_trait]
impl BotActionTrait for Listener { impl BotActionTrait for Listener {
async fn add_to_bot(self, bot: BotInstance) { async fn add_to_bot(self, bot: BotInstance) {
// println!("Adding action to bot"); botlog::trace(
// Log::trace("Adding action to bot");
botinstance::botlog::trace(
"Adding action to bot", "Adding action to bot",
Some("BotModules > BotActionTrait > add_to_bot()".to_string()), Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
None, None,
@ -191,10 +132,7 @@ impl BotActionTrait for Listener {
} }
async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) { async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
// let modmgr = *modmgr.lock().await; botlog::trace(
// println!("Adding action to module manager");
// Log::trace("Adding action to module manager");
botinstance::botlog::trace(
"Adding action to module manager", "Adding action to module manager",
Some("BotModules > BotActionTrait > add_to_bot()".to_string()), Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
None, None,
@ -207,9 +145,7 @@ impl BotActionTrait for Listener {
} }
#[derive(Debug)] #[derive(Debug)]
struct Routine {} pub struct Routine {}
// #[derive(Clone)]
pub struct ModulesManager { pub struct ModulesManager {
statusdb: Arc<RwLock<HashMap<ModType, Vec<ModStatusType>>>>, statusdb: Arc<RwLock<HashMap<ModType, Vec<ModStatusType>>>>,
@ -232,35 +168,33 @@ botactions
impl ModulesManager { impl ModulesManager {
pub async fn init() -> Arc<ModulesManager> { pub async fn init() -> Arc<ModulesManager> {
let m = HashMap::new(); let mgr = ModulesManager {
let act = HashMap::new(); statusdb: Arc::new(RwLock::new(HashMap::new())),
botactions: Arc::new(RwLock::new(HashMap::new())),
let mut mgr = ModulesManager {
statusdb: Arc::new(RwLock::new(m)),
botactions: Arc::new(RwLock::new(act)),
}; };
// :: [x] initialize core modules // :: [x] initialize core modules
botlog::trace(
// println!("ModulesManager > init() > Adding modules");
botlog::debug(
"ModulesManager > init() > Adding modules", "ModulesManager > init() > Adding modules",
Some("ModulesManager > init()".to_string()), Some("ModulesManager > init()".to_string()),
None, None,
); );
let mgra = Arc::new(mgr);
crate::core::identity::init(Arc::clone(&mgra)).await;
crate::modules::init(Arc::clone(&mgra)).await; let mgrarc = Arc::new(mgr);
// 1. load core modules
crate::core::identity::init(Arc::clone(&mgrarc)).await;
// 2. load custom modules
crate::custom::init(Arc::clone(&mgrarc)).await;
// println!(">> Modules Manager : End of Init");
botlog::trace( botlog::trace(
">> Modules Manager : End of Init", ">> Modules Manager : End of Init",
Some("ModulesManager > init()".to_string()), Some("ModulesManager > init()".to_string()),
None, None,
); );
mgra mgrarc
} }
pub fn modstatus(&self, _: ModType, _: ChType) -> ModStatusType { pub fn modstatus(&self, _: ModType, _: ChType) -> ModStatusType {
@ -286,8 +220,6 @@ impl ModulesManager {
} }
pub async fn add_botaction(&self, in_module: ModType, in_action: BotAction) { pub async fn add_botaction(&self, in_module: ModType, in_action: BotAction) {
// println!("Add botaction called");
botlog::trace( botlog::trace(
"Add botaction called", "Add botaction called",
Some("ModulesManager > init()".to_string()), Some("ModulesManager > init()".to_string()),
@ -306,37 +238,15 @@ impl ModulesManager {
both would be called separately, even if they both have the same or different logic both would be called separately, even if they both have the same or different logic
*/ */
// let newlistener = Listener {
// // module : BotModule(String::from("experiments").to_owned()),
// module : in_module.clone(),
// name : String::from("socklistener"),
// help : String::from("This will listen and react to sock randomly"),
// };
// As a Demonstration, the listener's Module is added and Enabled at Instance level
// [x] Before Adding, validate the following : // [x] Before Adding, validate the following :
// - If BotAction to Add is a BotCommand , In Module Manager DB (botactions), // - If BotAction to Add is a BotCommand , In Module Manager DB (botactions),
// Check All Other BotAction Command Names & Aliases to ensure they don't conflict // Check All Other BotAction Command Names & Aliases to ensure they don't conflict
async fn find_conflict_module(mgr: &ModulesManager, act: &BotAction) -> Option<ModType> { async fn find_conflict_module(mgr: &ModulesManager, act: &BotAction) -> Option<ModType> {
// Some(BotModule(String::from("GambaCore")))
// match act {
// BotAction::C(c) => {
// Some(BotModule(String::from("GambaCore")))
// },
// BotAction::L(l) => None,
// BotAction::R(r) => None,
// }
if let BotAction::C(incmd) = act { if let BotAction::C(incmd) = act {
// let n = & mgr.botactions; let actdb = mgr.botactions.read().await;
let d = mgr.botactions.read().await; for (module, moduleactions) in &(*actdb) {
let d = &(*d);
for (module, moduleactions) in d {
for modact in moduleactions.iter() { for modact in moduleactions.iter() {
if let BotAction::C(dbcmd) = &modact { if let BotAction::C(dbcmd) = &modact {
// At this point, there is an command incmd and looked up dbcmd // At this point, there is an command incmd and looked up dbcmd
@ -345,17 +255,13 @@ impl ModulesManager {
if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() { if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() {
// Returning State - with the identified module // Returning State - with the identified module
// return Some((module.clone(),BotAction::C(*dbcmd.clone())));
// return Some(incmd); // for some reason I keep getting issues
//return Some(BotModule(String::from("GambaCore"))); // works
return Some(module.clone()); // works return Some(module.clone()); // works
// return Some(dbcmd.clone());
} }
for a in &dbcmd.alias { for a in &dbcmd.alias {
if incmd.command.to_lowercase() == a.to_lowercase() { if incmd.command.to_lowercase() == a.to_lowercase() {
// Returning State - with the identified module // Returning State - with the identified module
// return Some((module.clone(),BotAction::C(dbcmd)));
return Some(module.clone()); // works return Some(module.clone()); // works
} }
} }
@ -365,14 +271,14 @@ impl ModulesManager {
for inalias in &incmd.alias { for inalias in &incmd.alias {
if inalias.to_lowercase() == dbcmd.command.to_lowercase() { if inalias.to_lowercase() == dbcmd.command.to_lowercase() {
// Returning State - with the identified module // Returning State - with the identified module
// return Some((module.clone(),BotAction::C(dbcmd)));
return Some(module.clone()); // works return Some(module.clone()); // works
} }
for a in &dbcmd.alias { for a in &dbcmd.alias {
if inalias.to_lowercase() == a.to_lowercase() { if inalias.to_lowercase() == a.to_lowercase() {
// Returning State - with the identified module // Returning State - with the identified module
// return Some((module.clone(),BotAction::C(dbcmd)));
return Some(module.clone()); // works return Some(module.clone()); // works
} }
} }
@ -380,65 +286,46 @@ impl ModulesManager {
} }
} }
} }
// return Some(BotModule(String::from("GambaCore")))
} }
// for all other scenarios (e.g., Listener, Routine), find no conflicts // for all other scenarios (e.g., Listener, Routine), find no conflicts
None None
} }
// if let probmod = find_conflict_module(&self, &in_action) { if let Some(c) = find_conflict_module(self, &in_action).await {
// // () // return because there was a conflict? panic!(
// panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , probmod );
// }
match find_conflict_module(&self, &in_action).await {
// Some(c) => panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , c ),
Some(c) => panic!(
"ERROR: Could not add module; there was a conflict with existing module {:?}", "ERROR: Could not add module; there was a conflict with existing module {:?}",
c c
), )
None => (),
} }
let mut dbt = self.statusdb.write().await; let mut dbt = self.statusdb.write().await;
let statusvector = dbt let statusvector = dbt.entry(in_module.clone()).or_insert(Vec::new());
// .entry(BotModule(String::from("experiments")))
.entry(in_module.clone())
.or_insert(Vec::new());
statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level
let mut a = self.botactions.write().await; let mut a = self.botactions.write().await;
let modactions = a let modactions = a.entry(in_module.clone()).or_insert(Vec::new());
//.entry( BotModule(String::from("experiments")))
.entry(in_module.clone())
.or_insert(Vec::new());
// modactions.push(BotAction::L(newlistener));
modactions.push(in_action); modactions.push(in_action);
// println!(">> Modules Manager : Called Add bot Action");
botlog::trace( botlog::trace(
">> Modules Manager : Called Add bot Action", format!(
Some("ModulesManager > init()".to_string()), "Modules Manager> add_botaction called - botactions size : {}",
None, modactions.len()
); )
// println!("add_botaction - botactions size : {}",modactions.len()); .as_str(),
botlog::trace(
&format!("add_botaction - botactions size : {}", modactions.len()),
Some("ModulesManager > init()".to_string()), Some("ModulesManager > init()".to_string()),
None, None,
); );
} }
fn statuscleanup(&self, _: Option<ChType>) -> () { fn _statuscleanup(&self, _: Option<ChType>) {
// internal cleans up statusdb . For example : // internal cleans up statusdb . For example :
// - remove redudancies . If we see several Enabled("m"), only keep 1x // - remove redudancies . If we see several Enabled("m"), only keep 1x
// - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m") // - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m")
// the IDEAL is that this is ran before every read/update operation to ensure quality // the IDEAL is that this is ran before every read/update operation to ensure quality
// Option<ChType> can pass Some(Channel("m")) (as an example) so statuscleanup only works on the given channel // Option<ChType> can pass Some(Channel("m")) (as an example) so statuscleanup only works on the given channel
// Passing None to chnl may be a heavy operation, as this will review and look at the whole table // Passing None to chnl may be a heavy operation, as this will review and look at the whole table
()
} }
} }

110
src/core/chat.rs Normal file
View file

@ -0,0 +1,110 @@
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use twitch_irc::login::StaticLoginCredentials;
use twitch_irc::message::PrivmsgMessage;
use twitch_irc::transport::tcp::{TCPTransport, TLS};
use twitch_irc::TwitchIRCClient;
use casual_logger::Log;
use rand::Rng;
use crate::core::ratelimiter;
use crate::core::ratelimiter::RateLimiter;
use crate::core::botinstance::ChType;
use crate::core::botlog;
pub use ChType::Channel;
#[derive(Clone)]
pub struct Chat {
pub ratelimiters: Arc<Mutex<HashMap<ChType, RateLimiter>>>, // used to limit messages sent per channel
pub client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
}
impl Chat {
pub fn init(
ratelimiters: HashMap<ChType, RateLimiter>,
client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
) -> Chat {
Chat {
ratelimiters: Arc::new(Mutex::new(ratelimiters)),
client,
}
}
pub async fn init_channel(&mut self, chnl: ChType) {
let n = RateLimiter::new();
self.ratelimiters.lock().await.insert(chnl, n);
}
pub async fn say_in_reply_to(&self, msg: &PrivmsgMessage, mut outmsg: String) {
/*
formats message before sending to TwitchIRC
- [x] Custom String Formatting (e.g., adding random black spaces)
- [x] Ratelimiter Handling
- [ ] Checkf if BotActions is Enabled & Caller is Allowed to Run
*/
let rl = Arc::clone(&self.ratelimiters);
let mut rllock = rl.lock().await;
let contextratelimiter = rllock
// .get_mut()
.get_mut(&Channel(String::from(&msg.channel_login)))
.expect("ERROR: Issue with Rate limiters");
match contextratelimiter.check_limiter() {
ratelimiter::LimiterResp::Allow => {
let maxblanks = rand::thread_rng().gen_range(1..=20);
for _i in 1..maxblanks {
let blankspace: &str = "󠀀";
outmsg.push_str(blankspace);
}
self.client.say_in_reply_to(msg, outmsg).await.unwrap();
contextratelimiter.increment_counter();
let logstr = format!(
"(#{}) > {} ; Ratelimiers : {:?}",
msg.channel_login, "rate limit counter increase", self.ratelimiters
);
botlog::trace(
logstr.as_str(),
Some("Chat > say_in_reply_to".to_string()),
Some(msg),
);
}
ratelimiter::LimiterResp::Skip => {
// (); // do nothing otherwise
}
}
Log::flush();
}
async fn _say(&self, _: String, _: String) {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.say(msg,outmsg).await.unwrap();
}
async fn _me(&self, _: String, _: String) {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
async fn _me_in_reply_to(&self, _: String, _: String) {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
use std::time::Instant;
const TIME_THRESHOLD_S: u64 = 30; const TIME_THRESHOLD_S: u64 = 30;
const MSG_THRESHOLD: u32 = 20; const MSG_THRESHOLD: u32 = 20;
use std::time::Instant;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RateLimiter { pub struct RateLimiter {
timer: Instant, timer: Instant,
@ -12,6 +12,13 @@ pub struct RateLimiter {
pub enum LimiterResp { pub enum LimiterResp {
Allow, // when it's evaluated to be within limits Allow, // when it's evaluated to be within limits
Skip, // as outside of rate limits Skip, // as outside of rate limits
// Enqueue, // [FUTURE]
}
impl Default for RateLimiter {
fn default() -> Self {
Self::new()
}
} }
impl RateLimiter { impl RateLimiter {
@ -24,21 +31,18 @@ impl RateLimiter {
pub fn check_limiter(&mut self) -> LimiterResp { pub fn check_limiter(&mut self) -> LimiterResp {
if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S { if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S {
// # [x] elapsed >= TIME_THRESHOLD_S
self.timer = Instant::now(); self.timer = Instant::now();
self.msgcounter = 0; self.msgcounter = 0;
LimiterResp::Allow LimiterResp::Allow
} else if self.msgcounter < MSG_THRESHOLD { } else if self.msgcounter < MSG_THRESHOLD {
// # [x] elapsed < TIME_THRESHOLD_S && msgcounter < MSG_THRESHOLD
LimiterResp::Allow LimiterResp::Allow
// } else if self.msgcounter >= MSG_THRESHOLD {
} else { } else {
// # [x] elapsed < TIME_THRESHOLD_S && msgcounter >= MSG_THRESHOLD // when elapsed() < TIME_THRESHOLD_S && msgcounter >= MSG_THRESHOLD
LimiterResp::Skip LimiterResp::Skip
} }
} }
pub fn increment_counter(&mut self) -> () { pub fn increment_counter(&mut self) {
self.msgcounter += 1; self.msgcounter += 1;
} }
} }

View file

@ -4,15 +4,11 @@
*/ */
//mod crate::core::botmodules;
// use crate::core::botmodules;
pub use crate::core::botmodules::ModulesManager;
// use crate::core::botinstance;
pub use crate::core::botinstance::BotInstance;
use futures::lock::Mutex;
use std::sync::Arc; use std::sync::Arc;
pub use crate::core::botinstance::BotInstance;
pub use crate::core::botmodules::ModulesManager;
// [ ] Load submodules // [ ] Load submodules
mod experiments; mod experiments;
@ -24,6 +20,4 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// this is achieved by calling submodules that also have fn init() defined // this is achieved by calling submodules that also have fn init() defined
experiments::init(mgr).await experiments::init(mgr).await
//();
} }

View file

@ -1,47 +1,29 @@
/* /*
Submodules - Custom Modules -
- should have definitions of BotAction that will be added to a bit Usage :
- therefore, will be defined in modules.rs file [ ] within the file's init(), define BotActions & Load them into the ModulesManager
- will define one init(&BotInstance) take within the module that will contain : [ ] Define Execution Bodies for these BotActions
- BotAction definitions that each call &BotInstance module manager to add itself [ ] Afterwards, add the following to parent modules.rs file
- mod <modulename>;
- within init(), <modulename>::init(mgr).await
*/ */
// mod crate::modules; use rand::Rng;
//use crate::modules; use std::sync::Arc;
use std::future::Future;
use crate::core::botmodules::bot_actions::actions_util::{self, BotAR};
use crate::core::botmodules::{BotActionTrait, BotCommand, BotModule, Listener, ModulesManager};
use crate::core::botinstance::{self, BotInstance, ChType};
use futures::lock::Mutex;
use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::PrivmsgMessage;
use crate::core::botlog;
use crate::core::bot_actions::actions_util::{self, BotAR};
use crate::core::botmodules::{BotActionTrait, BotCommand, BotModule, Listener, ModulesManager};
use crate::core::identity; use crate::core::identity;
use rand::Rng;
use std::rc::Rc;
use std::sync::{Arc, RwLock};
// pub fn init(mgr:&mut ModulesManager)
pub async fn init(mgr: Arc<ModulesManager>) { pub async fn init(mgr: Arc<ModulesManager>) {
// BotCommand { // 1. Define the BotAction
// module : BotModule(String::from("experiments 004")),
// command : String::from("test1"), // command call name
// alias : vec![String::from("tester1"),String::from("testy1")], // String of alternative names
// exec_body : actions_util::asyncbox(testy) ,
// help : String::from("DUPCMD4 tester"),
// required_roles : vec![
// //identity::UserRole::Mod(ChType::Channel(String::new())),
// identity::UserRole::SupMod(ChType::Channel(String::new()))
// ],
// }.add_to_modmgr(mgr);
let botc1 = BotCommand { let botc1 = BotCommand {
module: BotModule(String::from("experiments001")), module: BotModule(String::from("experiments001")),
command: String::from("test1"), // command call name command: String::from("test1"), // command call name
@ -51,8 +33,10 @@ pub async fn init(mgr: Arc<ModulesManager>) {
required_roles: vec![identity::UserRole::BotAdmin], required_roles: vec![identity::UserRole::BotAdmin],
}; };
// 2. Add the BotAction to ModulesManager
botc1.add_to_modmgr(Arc::clone(&mgr)).await; botc1.add_to_modmgr(Arc::clone(&mgr)).await;
// 1. Define the BotAction
let list1 = Listener { let list1 = Listener {
module: BotModule(String::from("experiments001")), module: BotModule(String::from("experiments001")),
name: String::from("GoodGirl Listener"), name: String::from("GoodGirl Listener"),
@ -60,46 +44,38 @@ pub async fn init(mgr: Arc<ModulesManager>) {
help: String::from(""), help: String::from(""),
}; };
// 2. Add the BotAction to ModulesManager
list1.add_to_modmgr(Arc::clone(&mgr)).await; list1.add_to_modmgr(Arc::clone(&mgr)).await;
} }
async fn good_girl(mut bot: BotAR, msg: PrivmsgMessage) { async fn good_girl(bot: BotAR, msg: PrivmsgMessage) {
// println!("In GoodGirl() Listener");
// Change below from debug to trace if required later
botinstance::botlog::debug(
"In GoodGirl() Listener",
Some("experiments > goodgirl()".to_string()),
Some(&msg),
);
//println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
// [ ] Uses gen_ratio() to output bool based on a ratio probability . // [ ] Uses gen_ratio() to output bool based on a ratio probability .
// - For example gen_ratio(2,3) is 2 out of 3 or 0.67% (numerator,denomitator) // - For example gen_ratio(2,3) is 2 out of 3 or 0.67% (numerator,denomitator)
// - More Info : https://rust-random.github.io/rand/rand/trait.Rng.html#method.gen_ratio // - More Info : https://rust-random.github.io/rand/rand/trait.Rng.html#method.gen_ratio
if msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase() if msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase()
|| msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase() || msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase()
// && msg.message_text.contains("GoodGirl")
{ {
// chat.say_in_reply_to(&msg,String::from("GoodGirl")).await; botlog::debug(
//if rng.gen_ratio(1,5) { "Good Girl Detected > Pausechamp",
// println!("In GoodGirl() > Pausechamp");
botinstance::botlog::debug(
"In GoodGirl() > Pausechamp",
Some("experiments > goodgirl()".to_string()), Some("experiments > goodgirl()".to_string()),
Some(&msg), Some(&msg),
); );
let rollwin = rand::thread_rng().gen_ratio(1, 8); let rollwin = rand::thread_rng().gen_ratio(1, 8);
if rollwin { if rollwin {
// println!("In GoodGirl() > Win"); botlog::debug(
botinstance::botlog::debug( "Oh that's a good girl!",
"In GoodGirl() > Win",
Some("experiments > goodgirl()".to_string()), Some("experiments > goodgirl()".to_string()),
Some(&msg), Some(&msg),
); );
let a = Arc::clone(&bot);
let botlock = a.read().await; let bot = Arc::clone(&bot);
let botlock = bot.read().await;
// uses chat.say_in_reply_to() for the bot controls for messages
botlock botlock
.botmgrs .botmgrs
.chat .chat
@ -111,7 +87,7 @@ async fn good_girl(mut bot: BotAR, msg: PrivmsgMessage) {
async fn testy(mut _chat: BotAR, msg: PrivmsgMessage) { async fn testy(mut _chat: BotAR, msg: PrivmsgMessage) {
println!("testy triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call println!("testy triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call
botinstance::botlog::debug( botlog::debug(
"testy triggered!", "testy triggered!",
Some("experiments > testy()".to_string()), Some("experiments > testy()".to_string()),
Some(&msg), Some(&msg),

View file

@ -1,2 +1,2 @@
pub mod core; pub mod core;
pub mod modules; pub mod custom;

View file

@ -1,23 +1,15 @@
// pub mod core;
// pub mod modules;
//use myLib;
//pub mod lib;
use std::process::Output;
// use crate::core::botinstance::ArcBox;
use botLib::core::botinstance::ArcBox;
use botLib::core::botinstance::{self, BotInstance};
// use core::botinstance::{self,BotInstance};
use casual_logger::Extension;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use casual_logger::{Extension, Level, Log};
use bot_lib::core::botinstance::BotInstance;
use bot_lib::core::botlog;
use bot_lib::core::botmodules;
pub type BotAR = Arc<RwLock<BotInstance>>; pub type BotAR = Arc<RwLock<BotInstance>>;
use casual_logger::{Level, Log}; // God I love anime girls
// Cweamcat can cweam on me - Forcen
#[tokio::main] #[tokio::main]
pub async fn main() { pub async fn main() {
@ -27,58 +19,36 @@ pub async fn main() {
let bot = BotInstance::init().await; let bot = BotInstance::init().await;
// Log::debug("Checking bot actions"); {
botinstance::botlog::debug("Checking bot actions", Some("main()".to_string()), None); botlog::trace("Reading bot actions", Some("main()".to_string()), None);
let a = Arc::clone(&bot.botmodules.botactions);
let a = a.read().await;
// let a = *a;
for (_, acts) in &*a { let actsdb = Arc::clone(&bot.botmodules.botactions);
let actsdb_lock = actsdb.read().await;
for acts in (*actsdb_lock).values() {
for act in acts { for act in acts {
match act { let outstr = match act {
botLib::core::botmodules::BotAction::C(b) => { botmodules::BotAction::C(b) => {
// println!("bot actiions: {}",b.command) format!("bot actions > Command : {}", b.command)
// Log::info(&format!("bot actions: {}",b.command));
botinstance::botlog::info(
&format!("bot actions: {}", b.command),
Some("main()".to_string()),
None,
);
} }
botLib::core::botmodules::BotAction::L(l) => { botmodules::BotAction::L(l) => {
// println!("bot actiions: {}",l.name) format!("bot actions > Listener : {}", l.name)
// Log::info(&format!("bot actions: {}",l.name));
botinstance::botlog::info(
&format!("bot actions: {}", l.name),
Some("main()".to_string()),
None,
);
}
_ => {
// println!("Not a valid match??")
// Log::info("Not a valid match??");
botinstance::botlog::info(
"Not a valid match??",
Some("main()".to_string()),
None,
);
} }
_ => "Not a valid match??".to_string(),
};
botlog::info(outstr.as_str(), Some("main()".to_string()), None);
} }
} }
} }
// println!("Starting runner.."); botlog::notice("Starting Bot Runner", Some("main()".to_string()), None);
// Log::notice("Starting Bot Runner");
botinstance::botlog::notice("Starting Bot Runner", Some("main()".to_string()), None);
println!("Starting Bot Runner"); println!("Starting Bot Runner");
Log::flush(); Log::flush();
bot.runner().await; bot.runner().await;
// println!("ERROR : EXIT Game loop"); let pstr = botlog::fatal("ERROR : EXIT Game loop", Some("main()".to_string()), None);
// let msg = Log::fatal("ERROR : EXIT Game loop"); panic!("{}", pstr);
// panic!("{}",Log::fatal("ERROR : EXIT Game loop"));
let a = botinstance::botlog::fatal("ERROR : EXIT Game loop", Some("main()".to_string()), None);
panic!("{}", a);
} }