diff --git a/.gitignore b/.gitignore index c8270c9..5c5e6b4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,7 @@ target/ # log -*.log \ No newline at end of file +*.log + +# debug +.vscode/ \ No newline at end of file diff --git a/src/core/botinstance.rs b/src/core/botinstance.rs index 7823673..1bc9d21 100644 --- a/src/core/botinstance.rs +++ b/src/core/botinstance.rs @@ -66,6 +66,16 @@ pub mod botlog { // 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,in_prvmsg:Option<&PrivmsgMessage>) -> () { @@ -86,6 +96,8 @@ pub mod botlog { .str("Chatter",&format!("{:?}",chatter)) .str("Code_Module",&format!("{:?}",in_module)) ); + + } @@ -385,6 +397,8 @@ pub struct BotInstance pub twitch_oauth : String, pub bot_channels : Vec, pub botmgrs : BotManagers, + //modesmgr : ModesManager, // Silent/Quiet , uwu , frisky/horny + } @@ -435,7 +449,17 @@ impl BotInstance - let b = BotInstance { + // 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 { prefix : prefix, bot_channel : Channel(login_name) , incoming_messages : Arc::new(RwLock::new(incoming_messages)), @@ -443,10 +467,9 @@ impl BotInstance twitch_oauth : oauth_token, bot_channels : botchannels, botmgrs : BotManagers::init(ratelimiters,client), - }; + } - - b + // b } pub async fn runner(self) -> () { diff --git a/src/core/botmodules.rs b/src/core/botmodules.rs index 43af6d7..86c6314 100644 --- a/src/core/botmodules.rs +++ b/src/core/botmodules.rs @@ -55,11 +55,12 @@ pub enum ModType { pub use ModType::BotModule; -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub enum ChType { - Channel(String), -} +// #[derive(Debug, PartialEq, Eq, Hash, Clone)] +// pub enum ChType { +// Channel(String), +// } +use botinstance::ChType; pub use ChType::Channel; use twitch_irc::message::PrivmsgMessage; @@ -234,6 +235,20 @@ pub struct ModulesManager pub botactions: Arc>>>, } +/* + +statusdb + <-- shows Enabled/Disabled per Status level + +botactions + HashMap< + ModType, <-- e.g., BotModule(String::from("experiments001")) + Vec> BotCommand, Listener + +*/ + impl ModulesManager { diff --git a/src/core/identity.rs b/src/core/identity.rs index 3401bc1..7251646 100644 --- a/src/core/identity.rs +++ b/src/core/identity.rs @@ -6,11 +6,11 @@ use std::error::Error; use crate::core::botmodules::{ModulesManager,Listener,BotModule,BotActionTrait, BotCommand}; use crate::core::botmodules::bot_actions::actions_util; -use crate::core::botinstance::{self, botlog, BotInstance}; +use crate::core::botinstance::{self, botlog, BotInstance,ChType}; use futures::lock::Mutex; use twitch_irc::message::{Badge, PrivmsgMessage}; -use crate::core::botmodules::ChType; +// use crate::core::botmodules::ChType; use crate::core::botinstance::ArcBox; @@ -29,6 +29,7 @@ use super::botmodules::bot_actions::actions_util::BotAR; fn adminvector() -> Vec { vec![String::from("ModulatingForce")] + //vec![] } @@ -97,6 +98,8 @@ pub async fn init(mgr:Arc) botinstance::botlog::trace(&format!("Twich Message > {}",msg.message_text), Some("identity.rs > cmd_promote()".to_string()), None); + let sendername = msg.clone().sender.name; + let mut argv = msg.message_text.split(" "); argv.next(); // Skip the command name @@ -115,7 +118,92 @@ pub async fn init(mgr:Arc) } } + let targetchnl = msg.channel_login.to_lowercase(); + /* + + [x] 1. Get trgusr (regardless of -admin flag) + [x] 2. promote trguser + [x] 3. Output resulting change + + */ + + // [x] 1. Get trgusr (regardless of -admin flag) + + let targetusr = if arg1 == Some("-admin") {arg2} else {arg1}; + + + // [x] 2. promote trguser + + // [x] Get a required lock first + + let botlock = bot.read().await; + let id = botlock.get_identity(); + let idlock = id.read().await; + + let rslt = match targetusr { + Some(targetusr) => { + + botinstance::botlog::debug(&format!("running promote()"), + Some("identity.rs > cmd_promote()".to_string()), None); + Log::flush(); + + let target_bot_admin_role = if arg1 == Some("-admin") {Some(UserRole::BotAdmin)} else {None}; + + idlock.promote( + sendername.clone(), + &sender_badge, + targetusr.to_string(), + Some(ChType::Channel(targetchnl.clone())), + target_bot_admin_role, + ).await + } + + None => { + + botinstance::botlog::debug(&format!("No Targer User argument"), + Some("identity.rs > cmd_demote()".to_string()), None); + Log::flush(); + + ChangeResult::NoChange("No Targer User".to_string()) + + } + + }; + + + + // [x] 3. Output resulting change + + match rslt { + ChangeResult::Success(a) => { + // println!("Succesfully promoted : {a} ;"); + let outmsg = &format!("o7 Successfully promoted : {a}"); + botinstance::botlog::debug(outmsg, + Some("identity.rs > cmd_prommote()".to_string()), Some(&msg)); + // let outmsg = "o7 Successfully promoted : ".to_string(); + botlock.botmgrs.chat.say_in_reply_to(&msg, outmsg.to_string()).await; + }, + ChangeResult::Failed(a) => { + // println!("Failed to promote : {a} ; "); + let outmsg = &format!("PoroSad failed to promote : {a}"); + botinstance::botlog::debug(outmsg, + Some("identity.rs > cmd_prommote()".to_string()), Some(&msg)); + // let outmsg = "PoroSad failed to promote : ".to_string(); + botlock.botmgrs.chat.say_in_reply_to(&msg, outmsg.to_string()).await; + }, + ChangeResult::NoChange(a) => { + // println!("No Changes Made : {a} ; "); + let outmsg = &format!("uuh No Promotion Change : {a}"); + botinstance::botlog::debug(outmsg, + Some("identity.rs > cmd_prommote()".to_string()), Some(&msg)); + // let outmsg = "uuh No Promotion Change : ".to_string(); + botlock.botmgrs.chat.say_in_reply_to(&msg, outmsg.to_string()).await; + }, + } + + + /* match arg1 { Some(a1) if a1 == String::from("-admin") => { // - [x] BotAdmins can promote admin to give BotAdmin UserRole @@ -252,7 +340,7 @@ pub async fn init(mgr:Arc) }, ChangeResult::NoChange(a) => { // println!("No Changes Made : {a} ; "); - let outmsg = &format!("uuh No Change in Promotion : {a}"); + let outmsg = &format!("uuh Not making any changes : {a}"); botinstance::botlog::debug(outmsg, Some("identity.rs > cmd_prommote()".to_string()), Some(&msg)); Log::flush(); @@ -270,10 +358,10 @@ pub async fn init(mgr:Arc) Some("identity.rs > cmd_prommote()".to_string()), Some(&msg)); Log::flush(); - // broadcaster & botadmins can make mods into superadmins + // broadcaster & botadmins can make mods into SupMod let ta = idlock.promote(arg1.to_string().to_lowercase(), Some(ChType::Channel(msg.channel_login.to_lowercase())), - Some(UserRole::Mod(ChType::Channel(msg.channel_login.to_lowercase())))).await; + Some(UserRole::SupMod(ChType::Channel(msg.channel_login.to_lowercase())))).await; match ta { ChangeResult::Success(a) => { @@ -367,6 +455,7 @@ pub async fn init(mgr:Arc) let targetchnl = arg2; + */ botinstance::botlog::trace(&format!("End of cmd_promote()"), @@ -406,7 +495,7 @@ pub async fn init(mgr:Arc) tempb.add_to_modmgr(Arc::clone(&mgr)).await; // async fn cmd_demote(mut _chat:Arc>,_msg:PrivmsgMessage) { - async fn cmd_demote(mut _chat:BotAR,msg:PrivmsgMessage) { + async fn cmd_demote(mut bot:BotAR,msg:PrivmsgMessage) { // println!("Called cmd demote"); botinstance::botlog::debug("Called cmd demote", Some("identity.rs > cmd_demote()".to_string()), Some(&msg)); @@ -439,6 +528,227 @@ pub async fn init(mgr:Arc) */ + // [ ] #TODO : Need to define the body that calls demote() + + // [x] Unwraps arguments from message + + // let mut argv = msg.message_text.split(" "); + + // argv.next(); // Skip the command name + + // let arg1 = argv.next(); + + + // let arg2 = argv.next(); + + + let (arg1,arg2) = { + + let mut argv = msg.message_text.split(" "); + + argv.next(); // Skip the command name + + let arg1 = argv.next(); + + let arg2 = argv.next(); + + (arg1,arg2) + + }; + + /* + let mut sender_badge:Option = None; + + for b in &msg.badges { + if b.name == "moderator" { + sender_badge = Some(ChatBadge::Mod); + } else if b.name == "broadcaster" { + sender_badge = Some(ChatBadge::Broadcaster); + } + } + */ + + // --- + + /* + => 2024.02.15 - The business logic seems embeded straight into demote() with the following in mind : + - demote() atm doesn't take sender ChatBadge <-- the assumption is canuserrun() was done + for this user, and automatically assigned any roles that should get auto assigned + - demote() returns a ChangeResult + + - [ ] So I think all I need to do here is parse out and pass input args to demote(), and output quirky messages based on ChangeResult + + - [x] 1. Parse out the following + - Sender (e.g., Name & Badge) + - Target User (arg1) + - Target Channel (current channel) + - Msg or Msg.Message_Text (for later) + + - [x] 2. Run Demote() + - within demote(), getspecialuserroles() is called on both the sender and the target + - getspecialuserroles() only sends current db , while canuserrun() may change db depending on the most current state of the sender + - getspecialuserroles also borrows the sender's badge to evaluate + + - [ ] 3. Take ChangeResult and output response + + + */ + + + /* + + + - [x] 1. Parse out the following + - Sender (e.g., Name & Badge) + - Target User (arg1) + - Target Channel (current channel) + - (no need) Msg or Msg.Message_Text (for later) + + */ + + + let sendername = msg.clone().sender.name; + + let mut sender_badge_mut:Option = None; + + for b in &msg.badges { + if b.name == "moderator" { + sender_badge_mut = Some(ChatBadge::Mod); + } else if b.name == "broadcaster" { + sender_badge_mut = Some(ChatBadge::Broadcaster); + } + } + + let sender_badge = sender_badge_mut; + + let targetusr = arg1; + + let targetchnl = msg.channel_login.to_lowercase(); + + /* + + - [x] 2. Run Demote() + - within demote(), getspecialuserroles() is called on both the sender and the target + - getspecialuserroles() only sends current db , while canuserrun() may change db depending on the most current state of the sender + - getspecialuserroles also borrows the sender's badge to evaluate + + + */ + + // [x] Get a required lock first + + let botlock = bot.read().await; + let id = botlock.get_identity(); + let idlock = id.read().await; + + + + let rslt = match targetusr { + Some(targetusr) => { + + botinstance::botlog::debug(&format!("running demote()"), + Some("identity.rs > cmd_demote()".to_string()), None); + Log::flush(); + + idlock.demote( + sendername.clone(), + &sender_badge, + targetusr.to_string(), + Some(ChType::Channel(targetchnl.clone()))).await + } + + None => { + + botinstance::botlog::debug(&format!("No Targer User argument"), + Some("identity.rs > cmd_demote()".to_string()), None); + Log::flush(); + + ChangeResult::NoChange("No Targer User".to_string()) + + } + + }; + + /* + + - [x] 3. Take ChangeResult and output response + + */ + + + // let senderUserRole = { + + // // note : getspecialuserroles will cover : + // // - Internal roles stored at db for Mod & SupMod & BotAdmin + // // - Broadcaster (based on target hchatter & channel) + // // It MAY NOT COVER sutations where Sender has a Mod Badge, but not in DB yet as Mod + // // - So ideally this covers that (at least returns that they're a mod and go ahead and run for now) + // // - [ ] #TODO : This should also go ahead and add that mod to DB if possible as channel mod + + + + // // let evalroles = vec![]; + + // let evalroles = match sender_badge { + + // Some(ChatBadge::Mod) => { + + + // let mut rslroles = idlock.getspecialuserroles( + // sendername.clone(), + // Some(ChType::Channel(targetchnl.clone()))).await; + + // rslroles.push(UserRole::Mod(ChType::Channel(targetchnl))); + + // rslroles + // }, + // _ => { + // idlock.getspecialuserroles( + // sendername, + // Some(ChType::Channel(targetchnl.clone()))).await + // } + // }; + + // // => 02.16 - I think better would just migrate over the logic within demote + // // - If there's business reqs to evaluate , better to keep the ChangeResult + // // consistent and also pass ChatBadge + + // }; // senderUserRole + + + + + match rslt { + ChangeResult::Success(a) => { + // println!("Succesfully promoted : {a} ;"); + let outmsg = &format!("o7 Successfully demoted : {a}"); + botinstance::botlog::debug(outmsg, + Some("identity.rs > cmd_demote()".to_string()), Some(&msg)); + // let outmsg = "o7 Successfully promoted : ".to_string(); + botlock.botmgrs.chat.say_in_reply_to(&msg, outmsg.to_string()).await; + }, + ChangeResult::Failed(a) => { + // println!("Failed to promote : {a} ; "); + let outmsg = &format!("PoroSad failed to demote : {a}"); + botinstance::botlog::debug(outmsg, + Some("identity.rs > cmd_demote()".to_string()), Some(&msg)); + // let outmsg = "PoroSad failed to promote : ".to_string(); + botlock.botmgrs.chat.say_in_reply_to(&msg, outmsg.to_string()).await; + }, + ChangeResult::NoChange(a) => { + // println!("No Changes Made : {a} ; "); + let outmsg = &format!("uuh No Demotion Change : {a}"); + botinstance::botlog::debug(outmsg, + Some("identity.rs > cmd_demote()".to_string()), Some(&msg)); + // let outmsg = "uuh No Promotion Change : ".to_string(); + botlock.botmgrs.chat.say_in_reply_to(&msg, outmsg.to_string()).await; + }, + + + } + + // println!("tester"); + // println!("tester2"); } @@ -750,7 +1060,23 @@ pub struct IdentityManager { // parent_mgr : Box, //parent_mgr : Option>, } +/* + HashMap< + String, <-- Chatter / Username + Vec -- <-- Vectors are basically arrays + > + -- [ ] + let a = vec![] + + modulatingforce : vec![UserRole::BotAdmin, + UserRole::Mod(ChType::Channel("modulatingforcebot"))] + modulatingforce : vec![UserRole::BotAdmin, + UserRole::Mod(ChType::Channel("modulatingforcebot"))] + +*/ + +#[derive(Debug)] pub enum ChatBadge { Broadcaster, Mod, @@ -876,7 +1202,9 @@ impl IdentityManager { ) -> (Permissible,ChangeResult) { // println!{"Checking within can_user_run()"}; - botinstance::botlog::debug("Checking within can_user_run()", + botinstance::botlog::debug(&format!("Checking within can_user_run() : + usr : {} ; channel : {:?} ; badge : {:?} ; cmdreqroles : {:?}", + usr,channelname,chat_badge,cmdreqroles), Some("identity.rs > can_user_run()".to_string()), None); /* canUserRun - @@ -918,6 +1246,8 @@ impl IdentityManager { // let idar = Arc::new(RwLock::new(self)); + let usr = usr.to_lowercase(); + if cmdreqroles.len() == 0 { // return Ok(Permissible::Allow) return (Permissible::Allow , ChangeResult::NoChange("Command has no required cmdreqroles".to_string())) @@ -959,7 +1289,7 @@ impl IdentityManager { // match self.special_roles_users.get_mut(&usr.to_lowercase()) { // match self.special_roles_users.get(&usr.to_lowercase()) { // println!("Creating clone"); - botinstance::botlog::trace("Creating clone", + botinstance::botlog::trace("Creating arc clone", Some("identity.rs > can_user_run()".to_string()), None); let roleslock = Arc::clone(&(*self).special_roles_users); @@ -968,6 +1298,16 @@ impl IdentityManager { botinstance::botlog::trace("Read lock on : Special_Roles_User", Some("identity.rs > can_user_run()".to_string()), None); + { + + // If target user doesn't exist in special_roles_users , add with blank vector roles + let mut srulock = self.special_roles_users.write().await; + srulock.entry(usr.clone()).or_insert(Arc::new(RwLock::new(vec![]))); + botinstance::botlog::trace(&format!("Ensuring Chatter in Roles {:?}",srulock.entry(usr.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + let mut roleslock = roleslock.write().await; match (*roleslock).get(&usr.to_lowercase()) { Some(usrroles) if @@ -1089,51 +1429,370 @@ impl IdentityManager { // pub async fn promote(&mut self,trgchatter:String,channel:Option,trg_role:Option) -> ChangeResult { pub async fn promote(&self, + authorizer:String, + authorizer_badge:&Option, trgchatter:String, channel:Option, - trg_role:Option) -> ChangeResult + trg_role:Option + ) -> ChangeResult { - botinstance::botlog::trace(&format!("IN VARS for promote() : Target Chatter : {} ; Target Channel : {:?} ; Targer Role {:?}", - trgchatter,channel,trg_role), + botinstance::botlog::trace(&format!( + "IN VARS for promote() : auth : {} ; authbadge : {:?} ; trg : {} ; Channel {:?} ; {:?}", + authorizer,authorizer_badge,trgchatter,channel,trg_role), Some("identity.rs > promote()".to_string()), None); Log::flush(); - // Note : If channel is none, getspecialuserroles() returns all roles for the user - - // let chatterroles = self.getspecialuserroles(trgchatter, channel); - // let chatterroles = self.getspecialuserroles(trgchatter.clone(), channel.clone()); - // let chatterroles = *self.getspecialuserroles(trgchatter.clone(), channel.clone()).await; - // let chatterroles = chatterroles.lock().await; - // // let chatterroles = *chatterroles; - // let chatterroles = chatterroles.unwrap(); + + /* + [x] 1. Check if Authorizer Mod Badge then Auto Promote to Mod if not Mod + [x] 2. Get Authorizer & Target Chatter Roles with a Given Channel + [x] 3. If the authorizer & Target Chatters are the same, and the Authorizer is not a Admin, return no change + [x] 4a. If Authorizer is BotAdmin & trg_role is Some(BotAdmin) , set Target as BotAdmin and return + [x] 4b. If target is Broadcaster, return NoChange + [ ] 4c. If Authorizer is a SupMod,Broadcaster,BotAdmin , can Promote Target Chatter > Mod + - NOTE : We do not validate trg_role here - app logic requires you to promote 1 to Mod and 1 more to SupMod + [ ] 4d. If Authorizer is a Broadcaster,BotAdmin , can Promote a Target Mod > SupMod + - NOTE : We do not validate trg_role here - app logic requires you to promote 1 to Mod and 1 more to SupMod + + + */ + + // [x] 1. Check if Authorizer Mod Badge then Auto Promote to Mod if not Mod + + let (authusrroles,trgusrroles) = if let Some(channel) = channel.clone() { + let mut authusrroles = self.getspecialuserroles( + authorizer.to_lowercase().clone(), + Some(channel.clone())) + .await; + + + { // mut block + + // let authusrroles_mut = &mut authusrroles; + // [x] Add Mod(channel) to authusrroles + // [x] #TODO also add to DB if possible? + match *authorizer_badge { + Some(ChatBadge::Mod) if ( !authusrroles.contains(&UserRole::Mod(channel.clone())) + && !authusrroles.contains(&UserRole::SupMod(channel.clone()))) => { + // (*authusrroles_mut).push(UserRole::Mod(channel.clone())); + authusrroles.push(UserRole::Mod(channel.clone())); + + let mut srulock = self.special_roles_users.write().await; + srulock + .get_mut(&trgchatter) + .expect("Error getting roles") + // !! [ ] Unsure what happens if promoting a chatter that doesn't exist at + .write().await + .push(UserRole::Mod(channel.clone())); + + } + _ => (), + } + } // mut block + + + // [x] 2. Get Authorizer & Target Chatter Roles + + let trgusrroles = self.getspecialuserroles( + trgchatter.to_lowercase().clone(), + Some(channel.clone())) + .await; + + (authusrroles,trgusrroles) + } else { + let mut authusrroles = self.getspecialuserroles( + authorizer.to_lowercase().clone(), + None) + .await; + let trgusrroles = self.getspecialuserroles( + trgchatter.to_lowercase().clone(), + None) + .await; + (authusrroles,trgusrroles) + }; + + + + + // [x] 3. If the authorizer & Target Chatters are the same, and the Authorizer is not a Admin, return no change + if trgchatter == authorizer && !authusrroles.contains(&UserRole::BotAdmin) { + return ChangeResult::NoChange("Can't target yourself".to_string()); + } + + + // [x] 4a. If Authorizer is BotAdmin & trg_role is Some(BotAdmin) , set Target as BotAdmin and return + if authusrroles.contains(&UserRole::BotAdmin) && trg_role == Some(UserRole::BotAdmin) { + if trgusrroles.contains(&UserRole::BotAdmin) { + return ChangeResult::NoChange("Already has the role".to_string()); + } else { + + { + let mut srulock = self.special_roles_users.write().await; + + srulock.entry(trgchatter.clone()).or_insert(Arc::new(RwLock::new(vec![]))); + botinstance::botlog::trace(&format!("Ensuring Target Chatter in Roles > {:?}",srulock.entry(trgchatter.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + + { + + let mut srulock = self.special_roles_users.write().await; + + srulock + .get_mut(&trgchatter) + .expect("Error getting roles for the user") + .write().await + .push(UserRole::BotAdmin); // <-- Adds the specific role + botinstance::botlog::trace(&format!("Inserting Role > {:?}",srulock.entry(trgchatter.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + + return ChangeResult::Success("Promotion Successful".to_string()); + + } + } + + // [x] 4b. If target is Broadcaster, return NoChange + + if trgusrroles.contains(&UserRole::Broadcaster) { + return ChangeResult::NoChange("Can't target broadcaster".to_string()); + } + + + /* + [ ] 4c. If Authorizer is a SupMod,Broadcaster,BotAdmin , can Promote Target Chatter > Mod + - NOTE : We do not validate trg_role here - app logic requires you to promote 1 to Mod and 1 more to SupMod + [ ] 4d. If Authorizer is a Broadcaster,BotAdmin , can Promote a Target Mod > SupMod + - NOTE : We do not validate trg_role here - app logic requires you to promote 1 to Mod and 1 more to SupMod + */ + + + // let authhasnsreqroles = match channel.clone() { + + // Some(channel) => authusrroles.contains(&UserRole::SupMod(channel.clone())) || + // authusrroles.contains(&UserRole::BotAdmin) || + // authusrroles.contains(&UserRole::Broadcaster) , + // None => authusrroles.contains(&UserRole::BotAdmin), + // }; + + + if let Some(trg_chnl) = channel.clone() { + + + if !trgusrroles.contains(&UserRole::Broadcaster) + && !trgusrroles.contains(&UserRole::Mod(trg_chnl.clone())) + && !trgusrroles.contains(&UserRole::SupMod(trg_chnl.clone())) { + // target user is neither Mod nor SupMod && not broadcaster + // target's Next Role would be Mod + // Authorizer must be SupMod,Broadcaster,BotAdmin + // > Promote target to Mod + if authusrroles.contains(&UserRole::SupMod(trg_chnl.clone())) + || authusrroles.contains(&UserRole::Broadcaster) + || authusrroles.contains(&UserRole::BotAdmin) + { + + { + + // If target user doesn't exist in special_roles_users , add with blank vector roles + let mut srulock = self.special_roles_users.write().await; + srulock.entry(trgchatter.clone()).or_insert(Arc::new(RwLock::new(vec![]))); + botinstance::botlog::trace(&format!("Ensuring Chatter in Roles {:?}",srulock.entry(trgchatter.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + { + // promote target after + let mut srulock = self.special_roles_users.write().await; + srulock + .get_mut(&trgchatter) + .expect("Error getting roles") + .write().await + .push(UserRole::Mod(trg_chnl.clone())); // Role to Add + botinstance::botlog::trace(&format!("Adding Roles to Chatter {:?}",srulock.entry(trgchatter.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + + return ChangeResult::Success(String::from("Promotion Successful")); + + } + // Other else conditions would be mostly spcecial responses like ChangeResult::NoChange or ChangeResult::Fail + // related to authusrroles + + else { + return ChangeResult::Failed(String::from("You're not permitted to do that")); + } + + + } else if !trgusrroles.contains(&UserRole::Broadcaster) + && trgusrroles.contains(&UserRole::Mod(trg_chnl.clone())) + && !trgusrroles.contains(&UserRole::SupMod(trg_chnl.clone())) // + { + // target user is a Mod && not broadcaster + // target's Next Role would be SupMod + // [ ] #todo Check first if target have SupMod - Optional but could be done to cleanup first + // Authorizer must be Broadcaster,BotAdmin + // > Promote target to SupMod + + if authusrroles.contains(&UserRole::Broadcaster) + || authusrroles.contains(&UserRole::BotAdmin) + { + + { // Inserts user if doesn't exist + let mut srulock = self.special_roles_users.write().await; + srulock.entry(trgchatter.clone()).or_insert(Arc::new(RwLock::new(vec![]))); + botinstance::botlog::trace(&format!("Ensuring User in Roles {:?}",srulock.entry(trgchatter.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + { // Adds the requested role for the user + let mut srulock = self.special_roles_users.write().await; + srulock + .get_mut(&trgchatter) + .expect("Error getting roles") + // !! [ ] Unsure what happens if promoting a chatter that doesn't exist at + .write().await + .push(UserRole::SupMod(trg_chnl.clone())); + botinstance::botlog::trace(&format!("Adding Required Role > {:?}",srulock.entry(trgchatter.clone())), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + { // Removes the lower role (mod) from the user + let mut srulock = self.special_roles_users.write().await; + let mut uroleslock = srulock + .get_mut(&trgchatter) + .expect("Error getting roles") + .write().await; + if let Some(indx) = uroleslock.iter().position(|value| *value == UserRole::Mod(trg_chnl.clone())){ + uroleslock.swap_remove(indx); + } + + botinstance::botlog::trace(&format!("Removing lower role > {:?}",uroleslock), + Some("identity.rs > promote()".to_string()), None); + Log::flush(); + } + + return ChangeResult::Success(String::from("Promotion Successful")); + + + } else { + return ChangeResult::Failed(String::from("You're not permitted to do that")); + } + + } else if !trgusrroles.contains(&UserRole::Broadcaster) + && trgusrroles.contains(&UserRole::SupMod(trg_chnl.clone())) { + // target user is a SuMod && not broadcaster + // No Change + return ChangeResult::Failed(String::from("Already highest available role")); + + + } else { + // since handling for channel is already done, will be handling other trguserroles situations here + // other situations includes : + /* + [-] targetuser is broadcaster >> no need - this was done earlier in the function + [?] ? + */ + + // At the moment, without any new roles, this should not be reached + + botinstance::botlog::warn(&format!("Code Warning : add handing for other trgusrroles"), + Some("identity.rs > promote()".to_string()), None); + return ChangeResult::Failed(String::from("Code Warning")); + + + } + + + + // let trghasreqroles = + + // { + + // // If target user doesn't exist in special_roles_users , add with blank vector roles + // let mut srulock = self.special_roles_users.write().await; + // srulock.entry(trgchatter.clone()).or_insert(Arc::new(RwLock::new(vec![]))); + // botinstance::botlog::trace(&format!("Ensuring Chatter in Roles {:?}",srulock.entry(trgchatter.clone())), + // Some("identity.rs > promote()".to_string()), None); + // Log::flush(); + // } + // { + // // promote target after + // let mut srulock = self.special_roles_users.write().await; + // srulock + // .get_mut(&trgchatter) + // .expect("Error getting roles") + // // !! [ ] Unsure what happens if promoting a chatter that doesn't exist at + // .write().await + // .push(UserRole::Mod(trg_chnl.clone())); // Role to Add + // botinstance::botlog::trace(&format!("Adding Roles to Chatter {:?}",srulock.entry(trgchatter.clone())), + // Some("identity.rs > promote()".to_string()), None); + // Log::flush(); + // } + + // return ChangeResult::Success(String::from("Promotion Successful")); + + + }; + + // if authhasnsreqroles { + + // { + + // // If target user doesn't exist in special_roles_users , add with blank vector roles + // let mut srulock = self.special_roles_users.write().await; + // srulock.entry(trgchatter.clone()).or_insert(Arc::new(RwLock::new(vec![]))); + // botinstance::botlog::trace(&format!("SRLOCK - 1st write > {:?}",srulock.entry(trgchatter.clone())), + // Some("identity.rs > promote()".to_string()), None); + // Log::flush(); + // } + // { + // // promote target after + // let mut srulock = self.special_roles_users.write().await; + // srulock + // .get_mut(&trgchatter) + // .expect("Error getting roles") + // // !! [ ] Unsure what happens if promoting a chatter that doesn't exist at + // .write().await + // .push(UserRole::Mod(trg_chnl)); + // botinstance::botlog::trace(&format!("SRLOCK - 2st write > {:?}",srulock.entry(trgchatter.clone())), + // Some("identity.rs > promote()".to_string()), None); + // Log::flush(); + // } + + // return ChangeResult::Success(String::from("Promotion Successful")); + + // } + + // authusrroles.contains(&UserRole::Mod(())) + + + + + + + + + /* let chatterroles = self.getspecialuserroles(trgchatter.clone(), channel.clone()).await; - // let chatterroles = chatterroles.lock().await; - // let chatterroles = *chatterroles; - // let chatterroles = chatterroles.unwrap(); - - // let emptyvec = vec![]; - - // let chatterroles = match chatterroles { - // Some(a) => a, - // _ => &(emptyvec), - // }; - - // let chatterroles = chatterroles.unwrap(); - // let chatterroles = chatterroles.read().await; - //let rolemap = &(*chatterroles); + let rolemap = chatterroles; - - match trg_role { Some(UserRole::Mod(_)) => { + + if let Some(trg_chnl) = channel.clone() { + // [ ] 1. If trg_role & trgchatter is a Mod or SupMod of the target channel, return NoChange + let chatterroles = self.getspecialuserroles(trgchatter.clone(), channel.clone()).await; let rolemap = chatterroles; // let rolemap = rolemap.unwrap(); @@ -1150,9 +1809,13 @@ impl IdentityManager { // // let c = b.get_mut(&trgchatter); // let c = (*b).; + // [ ] 2. Ensure an entry in Special_Roles_user for trgchatter, and push Mod(Channel) for the Target User + // [x] (!!) AROUND HERE - check if the user exists first, and at least add the user as we're promoting anyway { + + // If target user doesn't exist in special_roles_users , add with blank vector roles let mut srulock = self.special_roles_users.write().await; srulock.entry(trgchatter.clone()).or_insert(Arc::new(RwLock::new(vec![]))); botinstance::botlog::trace(&format!("SRLOCK - 1st write > {:?}",srulock.entry(trgchatter.clone())), @@ -1160,6 +1823,7 @@ impl IdentityManager { Log::flush(); } { + // promote target after let mut srulock = self.special_roles_users.write().await; srulock .get_mut(&trgchatter) @@ -1181,6 +1845,8 @@ impl IdentityManager { { if let Some(trg_chnl) = channel.clone() { + // [ ] 1. If trg_role & trgchatter is a Mod or SupMod of the target channel, return NoChange + let chatterroles = self.getspecialuserroles(trgchatter.clone(), channel.clone()).await; let rolemap = chatterroles; // let rolemap = rolemap.unwrap(); @@ -1196,6 +1862,9 @@ impl IdentityManager { // // let c = b.get_mut(&trgchatter); // let c = (*b).; + + // [ ] 2. Ensure an entry in Special_Roles_user for trgchatter, and push SupMod(Channel) for the Target User + // [x] (!!) AROUND HERE - check if the user exists first, and at least add the user as we're promoting anyway { @@ -1245,6 +1914,8 @@ impl IdentityManager { } , Some(UserRole::BotAdmin) => { + + let chatterroles = self.getspecialuserroles(trgchatter.clone(), channel.clone()).await; let rolemap = chatterroles; @@ -1308,6 +1979,7 @@ impl IdentityManager { }, } + */ // match chatterroles { // Some(chatterroles) => { @@ -1371,42 +2043,81 @@ impl IdentityManager { // }, // _ => (), // } - - ChangeResult::Success(String::from("TEST > Promotion Successful")) + botinstance::botlog::warn(&format!("Runtime reached undeveloped code"), + Some("identity.rs > promote()".to_string()), None); + ChangeResult::Failed(String::from("ERROR")) } pub async fn demote(&self, authorizer:String, + authorizer_badge:&Option, trgchatter:String, channel:Option, - trg_role:Option) -> ChangeResult + // trg_role:Option + ) -> ChangeResult { - botinstance::botlog::trace(&format!("IN VARS for demote() : Authorizer : {:?} ; Target Chatter : {} ; Target Channel : {:?} ; Targer Role {:?}", - authorizer,trgchatter,channel,trg_role), + // botinstance::botlog::trace(&format!("IN VARS for demote() : Authorizer : {:?} ; Target Chatter : {} ; Target Channel : {:?} ; Targer Role {:?}", + // authorizer,trgchatter,channel,trg_role), + botinstance::botlog::trace(&format!("IN VARS for demote() : Authorizer : {:?} ; Target Chatter : {} ; Target Channel : {:?}", + authorizer,trgchatter,channel), Some("identity.rs > demote()".to_string()), None); Log::flush(); /* Check authorizer roles (if any) for the target channel - Check Targer User roles (if any) for the target channel + Check Targer User's roles (if any) for the target channel Target Channel may be NONE in the case of Non-Channel related roles (FUTURE ENH) Use the roles of the above to determine whether the authorizer can demote the target user or not */ + // [x] 1. If Authorizer's Badge is Mod, ensuring Sender is in DB as Mod(Channel) if let Some(channel) = channel { - let authusrroles = self.getspecialuserroles( + let mut authusrroles = self.getspecialuserroles( authorizer.to_lowercase().clone(), Some(channel.clone())) .await; // let authusrroles = authusrroles; + { + + // let authusrroles_mut = &mut authusrroles; + // [x] Add Mod(channel) to authusrroles + // [x] #TODO also add to DB if possible? + match *authorizer_badge { + Some(ChatBadge::Mod) if (!authusrroles.contains(&UserRole::Mod(channel.clone())) + && !authusrroles.contains(&UserRole::SupMod(channel.clone())) + ) => { + // (*authusrroles_mut).push(UserRole::Mod(channel.clone())); + authusrroles.push(UserRole::Mod(channel.clone())); + + let mut srulock = self.special_roles_users.write().await; + srulock + .get_mut(&trgchatter) + .expect("Error getting roles") + // !! [ ] Unsure what happens if promoting a chatter that doesn't exist at + .write().await + .push(UserRole::Mod(channel.clone())); + + } + _ => (), + } + } + + // [x] 2. Targer User's Vec let trgusrroles = self.getspecialuserroles( trgchatter.to_lowercase().clone(), Some(channel.clone())) .await; + // [x] 3. Return if Authorizer & Target are same chatter and Authorizer is not a BotAdmin + if trgchatter == authorizer && !authusrroles.contains(&UserRole::BotAdmin) { + return ChangeResult::NoChange("Can't target yourself".to_string()) + } + + // [x] 4a. Authorizers who are BotAdmin, Broadcaster or Supermod can demote a Mod + if ( authusrroles.contains(&UserRole::BotAdmin) || authusrroles.contains(&UserRole::Broadcaster) || authusrroles.contains(&UserRole::SupMod(channel.clone())) ) && @@ -1422,6 +2133,8 @@ impl IdentityManager { return ChangeResult::Success("Demoted successfully".to_string()) } } + + // [x] 4b. Authorizers who are BotAdmin, Broadcaster can demote a SupMod else if ( authusrroles.contains(&UserRole::BotAdmin) || authusrroles.contains(&UserRole::Broadcaster) ) && trgusrroles.contains(&UserRole::SupMod(channel.clone())) @@ -1431,20 +2144,38 @@ impl IdentityManager { .get_mut(&trgchatter) .expect("Error getting roles") .write().await; - usrrolelock.push(UserRole::SupMod(channel.clone())); + usrrolelock.push(UserRole::Mod(channel.clone())); // pushes Mod , and removes SupMod if let Some(indx) = usrrolelock.iter().position(|value| *value == UserRole::SupMod(channel.clone())){ usrrolelock.swap_remove(indx); return ChangeResult::Success("Demoted successfully".to_string()) } } + + // [x] 4c. When Target chatter isnt a Mod or SupMod to demote + else if !trgusrroles.contains(&UserRole::Mod(channel.clone())) && + !trgusrroles.contains(&UserRole::SupMod(channel.clone())) { + return ChangeResult::Failed("Target chatter does not have a role that can be demoted".to_string()) + } + // [x] 4d. WHhen they're only a Mod + else if authusrroles.contains(&UserRole::Mod(channel.clone())) { + return ChangeResult::Failed("You're not permitted to do that".to_string()) + } } + botinstance::botlog::warn(&format!("Potential Unhandled Demotion Condition : Consider explicitely adding in for better handling"), + Some("identity.rs > demote()".to_string()), None); + Log::flush(); ChangeResult::Failed(String::from("Did not meet criteria to demote succesfully")) + } + + // pub async fn getspecialuserroles(&self,chattername:String,channel:Option) -> Option>>> { - pub async fn getspecialuserroles(&self,chattername:String,channel:Option) -> Vec { + pub async fn getspecialuserroles( + &self,chattername:String, + channel:Option) -> Vec { /* Note : Ideally this be called for a given chatter name ? */ diff --git a/src/modules/experiments.rs b/src/modules/experiments.rs index 2c4a4e2..c75574f 100644 --- a/src/modules/experiments.rs +++ b/src/modules/experiments.rs @@ -14,10 +14,10 @@ use std::future::Future; -use crate::core::botmodules::{ModulesManager,Listener,BotModule,BotActionTrait, BotCommand,ChType}; +use crate::core::botmodules::{ModulesManager,Listener,BotModule,BotActionTrait, BotCommand}; use crate::core::botmodules::bot_actions::actions_util::{self, BotAR}; -use crate::core::botinstance::{self,BotInstance}; +use crate::core::botinstance::{self,BotInstance,ChType}; use futures::lock::Mutex; use twitch_irc::message::PrivmsgMessage;