use tokio::sync::mpsc::UnboundedReceiver; use twitch_irc::login::StaticLoginCredentials; use twitch_irc::ClientConfig; use twitch_irc::SecureTCPTransport; use twitch_irc::TwitchIRCClient; use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::ServerMessage; use twitch_irc::transport::tcp::TCPTransport; use twitch_irc::transport::tcp::TLS; use std::env; use dotenv::dotenv; use std::collections::HashMap; use rand::Rng; //mod sub::ratelimiter; use crate::core::ratelimiter::RateLimiter; use crate::core::ratelimiter; // use crate::core::ratelimiter; // pub fn init() -> () // { // println!("I was here"); // } use crate::core::botmodules; use crate::core::botmodules::ModulesManager; #[derive(Debug, PartialEq, Eq, Hash)] pub enum ChType { Channel(String), } pub use ChType::Channel; pub enum ModType { BotModule(String), } pub use ModType::BotModule; // pub enum EnType { // Enabled(ChType), // } // pub use EnType::Enabled; // pub enum ModStatusType { // Enabled(EnType), // Disabled(EnType), // Enabled(ModType), // Disabled(ModType), // } pub struct Chat { pub ratelimiters : HashMap, // used to limit messages sent per channel pub client : TwitchIRCClient,StaticLoginCredentials>, } impl Chat { pub fn init_channel(&mut self, chnl:ChType) -> () { let n = RateLimiter::new(); self.ratelimiters.insert(chnl,n); } pub async fn say_in_reply_to(&mut self, msg:& PrivmsgMessage , mut outmsg:String) -> () { // envelops a message before sending a message // [x] This could include additional formatting (e.g., add in random number of blank spaces) // [x] Incrementing or checking with RateLimiters // [ ] For BotActions of Enabled Modules , checking whether the caller is Permissible to run the command ? // self.client.say_in_reply_to(msg,outmsg).await.unwrap(); // // let contextratelimiter = ratelimiters.get_mut(&msg.channel_login).expect("ERROR: Issue with Rate limiters"); let contextratelimiter = self.ratelimiters .get_mut(&Channel(String::from(&msg.channel_login))) .expect("ERROR: Issue with Rate limiters"); // let contextratelimiter = self.ratelimiters.get(&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); //let mut outmsg = "GotTrolled ".to_owned(); // let mut outmsg = "annytfLurk ".to_owned(); for _i in 1..maxblanks { let blankspace: &str = "󠀀"; outmsg.push_str(blankspace); } // client.say_in_reply_to(&msg,outmsg).await.unwrap(); self.client.say_in_reply_to(msg,outmsg).await.unwrap(); println!("(#{}) > {}", msg.channel_login, "rate limit counter increase"); contextratelimiter.increment_counter(); println!("{:?}",self.ratelimiters); }, ratelimiter::LimiterResp::Skip => { (); // do nothing otherwise } } } 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(); } } pub struct BotInstance where F: std::future::Future + ?Sized, { prefix : char, bot_channel : ChType, //pub client : TwitchIRCClient,StaticLoginCredentials>, pub incoming_messages : UnboundedReceiver, // pub ratelimiters : HashMap, // used to limit messages sent per channel pub chat : Chat, // botmodules : HashMap>, pub botmodules : ModulesManager, twitch_oauth : String, pub bot_channels : Vec, /*bot_commands : Vec[BotCommand], bot_listeners : Vec[Listener], bot_routines : Vec[Routine],*/ // botactionsdb : botactionsdb:botactions, // identity : identitymodule, } impl BotInstance where F: std::future::Future + 'static, { pub fn init() -> BotInstance where F: std::future::Future + 'static, { dotenv().ok(); let 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().chars().next().expect("ERROR : when defining prefix"); /* Vector of channels to join */ let mut botchannels = Vec::new(); for chnl in env::var("bot_channels").unwrap().split(',') { // println!("(Env Var # {})",chnl); botchannels.push(Channel(String::from(chnl))); } let config = ClientConfig::new_simple( StaticLoginCredentials::new(login_name.to_owned(), Some(oauth_token.to_owned())) ); let (incoming_messages, client) = TwitchIRCClient::::new(config); // hashmap for channels and their associated ratelimiters let mut ratelimiters = HashMap::new(); for Channel(chnl) in &botchannels { // For each channel in botchannels client.join(chnl.to_owned()).unwrap(); // ratelimiters are a hashmap of channel and a corresponding rate limiter let n = RateLimiter::new(); 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 : incoming_messages, //client : client, chat : Chat { ratelimiters : ratelimiters, client : client, } , // ratelimiters : ratelimiters, // used to limit messages sent per channel // botmodules : HashMap::new(), botmodules : ModulesManager::init(), twitch_oauth : oauth_token, bot_channels : botchannels, /*bot_commands : Vec[BotCommand], bot_listeners : Vec[Listener], bot_routines : Vec[Routine],*/ // botactionsdb : botactionsdb:botactions, // identity : identitymodule, }; println!("{:?}",b.chat.ratelimiters); b } pub async fn runner(mut self) -> () { let join_handle = tokio::spawn(async move { while let Some(message) = self.incoming_messages.recv().await { // Below can be used to debug if I want to capture all messages // println!("Received message: {:?}", message); match message { ServerMessage::Notice(msg) => { if let Some(chnl) = msg.channel_login { println!("NOTICE : (#{}) {}", chnl, msg.message_text); } } ServerMessage::Privmsg(msg) => { println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); println!("Privmsg section"); // b.listener_main_prvmsg(&msg); self.listener_main_prvmsg(&msg).await; // - BotCommand listener should likely need to be called within the above }, ServerMessage::Whisper(msg) => { println!("(w) {}: {}", msg.sender.name, msg.message_text); }, ServerMessage::Join(msg) => { println!("JOINED: {}", msg.channel_login); }, ServerMessage::Part(msg) => { println!("PARTED: {}", msg.channel_login); }, _ => {} } } }); join_handle.await.unwrap(); } pub async fn run(mut self) -> () { let join_handle = tokio::spawn(async move { while let Some(message) = self.incoming_messages.recv().await { // Below can be used to debug if I want to capture all messages // println!("Received message: {:?}", message); match message { ServerMessage::Notice(msg) => { if let Some(chnl) = msg.channel_login { println!("NOTICE : (#{}) {}", chnl, msg.message_text); } } ServerMessage::Privmsg(msg) => { println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); println!("Privmsg section"); // b.listener_main_prvmsg(&msg); self.listener_main_prvmsg(&msg).await; // - BotCommand listener should likely need to be called within the above }, ServerMessage::Whisper(msg) => { println!("(w) {}: {}", msg.sender.name, msg.message_text); }, ServerMessage::Join(msg) => { println!("JOINED: {}", msg.channel_login); }, ServerMessage::Part(msg) => { println!("PARTED: {}", msg.channel_login); }, _ => {} } } }); join_handle.await.unwrap(); } // ----------------- // PRIVATE FUNCTIONS async fn listener_main_prvmsg(&mut self,msg:& PrivmsgMessage) -> () { println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); // // // let contextratelimiter = ratelimiters.get_mut(&msg.channel_login).expect("ERROR: Issue with Rate limiters"); // let contextratelimiter = self.ratelimiters // .get_mut(&Channel(String::from(&msg.channel_login))) // .expect("ERROR: Issue with Rate limiters"); // // let contextratelimiter = self.ratelimiters.get(&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); // //let mut outmsg = "GotTrolled ".to_owned(); // let mut outmsg = "annytfLurk ".to_owned(); // for _i in 1..maxblanks { // let blankspace: &str = "󠀀"; // outmsg.push_str(blankspace); // } // // client.say_in_reply_to(&msg,outmsg).await.unwrap(); // self.client.say_in_reply_to(msg,outmsg).await.unwrap(); // println!("(#{}) > {}", msg.channel_login, "rate limit counter increase"); // contextratelimiter.increment_counter(); // println!("{:?}",self.ratelimiters); // }, // ratelimiter::LimiterResp::Skip => { // (); // do nothing otherwise // } self.chat.say_in_reply_to(msg,String::from("annytfLurk")).await; // // [ ] 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) // // [ ] There should be a BotCommand Listener to check for prefixes ran println!("End of Separate Listener Main prvmsg"); } } // ====================================== // ====================================== // ====================================== // ====================================== // UNIT TEST MODULES #[cfg(test)] mod tests { fn always() { assert_eq!(1,1); } }