Compare commits

...

26 commits

Author SHA1 Message Date
7db048937b call loopbody on a read lock
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-01 18:39:03 +02:00
fcf4f3f7cf add debug implementations that helped (kinda) 2024-03-29 21:06:48 +01:00
9d94328cd6 mark call that locks execbody from using that routine 2024-03-29 21:05:21 +01:00
b5e95668a5 ISSUE - potential lock issue
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-29 09:03:16 -04:00
66195138f8 corrected error with validate_attr 2024-03-28 20:21:49 -04:00
5c35ad114a ISSUE - commented out problem code? 2024-03-28 17:52:43 -04:00
26f67787d7 addl debugging 2024-03-28 15:07:24 -04:00
1b534ebeb7 fixed routine attr validation logic 2024-03-28 13:46:02 -04:00
c27dd3b86f custom countdown 2024-03-28 13:18:02 -04:00
6d3b5eee41 routine assigns self reference at construct 2024-03-28 11:50:08 -04:00
2a1a7f8503 ExecBody & routine addl references 2024-03-28 04:39:03 -04:00
ca9361cc93 routine methods 2024-03-28 03:18:41 -04:00
4637312da9 impl stop 2024-03-28 02:13:38 -04:00
6c3a151668 impl other routine attr 2024-03-27 23:56:34 -04:00
e963ae250d (cont) routine methods 2024-03-27 23:00:04 -04:00
60fae25419 comments 2024-03-27 20:47:56 -04:00
537c3565a2 custom start test fn 2024-03-27 17:28:47 -04:00
669b2da871 cleanup start impl 2024-03-27 17:03:58 -04:00
226da4362a comments cleanup 2024-03-27 16:04:15 -04:00
8da8460e47 proper join handling at custom
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-27 14:51:56 -04:00
b08d91af5d (cont) start runonce
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-27 10:18:13 -04:00
3f8e798050 (cont) routine start
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-27 02:35:31 -04:00
5249c3af25 smol debug in identity 2024-03-27 01:29:09 -04:00
4e9316ad49 (init) experiment test module
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-26 16:56:30 -04:00
2ba92388a2 runonce implementation 2024-03-26 14:30:13 -04:00
6c7290883f (init) routine methods 2024-03-26 11:29:47 -04:00
7 changed files with 1675 additions and 27 deletions

View file

@ -5,40 +5,83 @@ use tokio::sync::RwLock;
use crate::core::botinstance::BotInstance;
use super::{botmodules::{BotAction, BotModule}, identity::ChatBadge};
use super::{botinstance::Channel, botmodules::{BotAction, BotModule, Routine}, identity::ChatBadge};
pub type BotAR = Arc<RwLock<BotInstance>>;
pub type ActAR = Arc<RwLock<BotAction>>;
pub type RoutineAR = Arc<RwLock<Routine>>;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ExecBodyParams {
pub bot : BotAR,
pub msg : PrivmsgMessage,
pub parent_act : ActAR ,
pub parent_act : Option<ActAR> ,
pub curr_act : ActAR ,
}
impl ExecBodyParams {
pub async fn get_parent_module(&self) -> Option<BotModule> {
// pub async fn get_parent_module(&self) -> Option<BotModule> {
// pub async fn get_parent_module(&self) -> BotModule {
pub async fn get_module(&self) -> BotModule {
let parent_act = Arc::clone(&self.parent_act);
let parent_act_lock = parent_act.read().await;
let curr_act = Arc::clone(&self.curr_act);
let parent_act_lock = curr_act.read().await;
let act = &(*parent_act_lock);
match act {
BotAction::C(c) => {
let temp = c.module.clone();
Some(temp)
// let temp = c.module.clone();
// Some(temp)
c.module.clone()
},
BotAction::L(l) => {
let temp = l.module.clone();
Some(temp)
// let temp = l.module.clone();
// Some(temp)
l.module.clone()
},
_ => None
BotAction::R(r) => {
// let temp = r.module.clone();
// Some(temp)
r.read().await.module.clone()
}
// _ => None
}
}
pub async fn get_channel(&self) -> Option<Channel> {
// THIS IS INCORRECT - BELOW MAY BE PULLING THE PARENT BOTACTION
// NOT THE CURRENT BOT ACTION
let curr_act = Arc::clone(&self.curr_act);
let parent_act_lock = curr_act.read().await;
let act = &(*parent_act_lock);
match act {
BotAction::C(_) => {
// let temp = c.module.clone();
// Some(temp)
Some(Channel(self.msg.channel_login.clone()))
},
BotAction::L(_) => {
// let temp = l.module.clone();
// Some(temp)
// l.module.clone()
Some(Channel(self.msg.channel_login.clone()))
},
BotAction::R(r) => {
// let temp = r.module.clone();
// Some(temp)
Some(r.read().await.channel.clone())
}
// _ => None
}
}
pub fn get_sender(&self) -> String {
self.msg.sender.name.clone()
}

View file

@ -40,7 +40,7 @@ pub struct Channel(pub String);
use super::bot_actions::ExecBodyParams;
use super::botmodules::StatusType;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct BotManagers {
pub identity: Arc<RwLock<IdentityManager>>,
pub chat: Chat,
@ -70,6 +70,7 @@ impl<T: Clone> ArcBox<T> {
}
}
#[derive(Debug)]
pub struct BotInstance {
pub prefix: char,
pub bot_channel: Channel,
@ -404,7 +405,8 @@ impl BotInstance {
let params = ExecBodyParams {
bot : Arc::clone(&bot),
msg : (*msg).clone(),
parent_act : Arc::clone(&act_clone),
parent_act : None,
curr_act : Arc::clone(&act_clone),
};
// When sending a BotMsgTypeNotif, send_botmsg does Roles related validation as required
@ -461,7 +463,8 @@ impl BotInstance {
let params = ExecBodyParams {
bot : Arc::clone(&bot),
msg : (*msg).clone(),
parent_act : Arc::clone(&act_clone),
parent_act : None,
curr_act : Arc::clone(&act_clone),
};
@ -491,7 +494,8 @@ impl BotInstance {
let params = ExecBodyParams {
bot : Arc::clone(&bot),
msg : (*msg).clone(),
parent_act : Arc::clone(&act_clone),
parent_act : None,
curr_act : Arc::clone(&act_clone),
};
@ -516,7 +520,8 @@ impl BotInstance {
c.execute(ExecBodyParams {
bot : a,
msg : msg.clone() ,
parent_act : Arc::clone(&act_clone),
parent_act : None,
curr_act : Arc::clone(&act_clone),
}).await;
botlog::trace(
@ -564,7 +569,8 @@ impl BotInstance {
l.execute(ExecBodyParams {
bot : a,
msg : msg.clone() ,
parent_act : Arc::clone(&act_clone),
parent_act : None,
curr_act : Arc::clone(&act_clone),
} ).await;
}

View file

@ -25,14 +25,26 @@ const OF_CMD_CHANNEL:Channel = Channel(String::new());
use core::panic;
use std::borrow::Borrow;
use std::borrow::BorrowMut;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::ops::DerefMut;
use std::sync::Arc;
use casual_logger::Log;
use chrono::DateTime;
// use chrono::Duration;
use chrono::Local;
use chrono::OutOfRangeError;
use tokio::sync::RwLock;
use async_trait::async_trait;
use tokio::task::JoinHandle;
use tokio::time::Instant;
use tokio::time::{sleep, Duration};
use crate::core::bot_actions::actions_util;
use crate::core::bot_actions::ExecBodyParams;
@ -44,6 +56,8 @@ use crate::core::bot_actions;
use std::hash::{Hash, Hasher};
use super::bot_actions::ActAR;
use super::bot_actions::RoutineAR;
use super::identity::ChatBadge;
@ -69,7 +83,6 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// 2. Add the BotAction to ModulesManager
botc1.add_core_to_modmgr(Arc::clone(&mgr)).await;
// async fn cmd_enable(bot: BotAR, msg: PrivmsgMessage) {
async fn cmd_enable(params : ExecBodyParams) {
/*
There should be additional validation checks
@ -253,7 +266,6 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// 2. Add the BotAction to ModulesManager
botc1.add_core_to_modmgr(Arc::clone(&mgr)).await;
// async fn cmd_disable(bot: BotAR, msg: PrivmsgMessage) {
async fn cmd_disable(params : ExecBodyParams) {
/*
There should be additional validation checks
@ -459,10 +471,11 @@ pub enum StatusType {
Disabled(StatusLvl),
}
#[derive(Debug)]
pub enum BotAction {
C(BotCommand),
L(Listener),
R(Routine),
R(Arc<RwLock<Routine>>),
}
impl BotAction {
@ -521,6 +534,12 @@ impl BotActionTrait for BotCommand {
}
}
impl Debug for BotCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} {:?} {:?} {:?} {:?}", self.module, self.command, self.alias, self.help, self.required_roles)
}
}
pub struct Listener {
pub module: BotModule,
pub name: String,
@ -568,12 +587,913 @@ impl BotActionTrait for Listener {
}
}
impl Debug for Listener {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} {:?} {:?}", self.module, self.name, self.help)
}
}
// #[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum RoutineAttr {
DelayedStart,
ScheduledStart(DateTime<Local>), // Scheduled Date (if any) after which, if not started, may trigger
LoopDuration(Duration), // How long to wait between iterations
LoopInfinitely,
RunOnce,
MaxTimeThreshold(DateTime<Local>), // DateTime after which, it will abort/cancel or stop
MaxIterations(i64),
}
/*
a Routine can be given different combinations of the above, but business logic may validate
these at Routine construction
For example, a Routine could have the following characteristics
- DelayedStart (so it skips the first iteration)
- ScheduledStart(DateTime<Local>) - With a Start Date-Time for the first iteration to trigger
- LoopDuration(Duration) - How long to wait between iterations
The above without any other thresholds would loop infinitely with the above characteristics
Another example,
- LoopDuration(Duration) - How long to wait between iterations
- MaxTimeThreshold(DateTime<Local>)
- MaxIterations(i64)
The above has thresholds , so if either are reached, it would abort/cancel or stop . Since there is no
ScheduledStart, the routine would have to be started manually elsewhere
Another example ,
- (no RoutineAttr)
The above would only run once, and only when the Start() is called
*/
// For some key statuses and in particular Stopping to Gracefully stop
#[derive(Debug)]
pub struct Routine {}
pub enum RoutineSignal {
Stopping, // Gracefully Stopping
Stopped, // When cancelling or aborting, this also is represented by Stopped
Started, // After Routine Started
NotStarted,
}
pub struct Routine {
pub name : String ,
pub module : BotModule , // from() can determine this if passed parents_params
pub channel : Channel , // Requiring some channel context
exec_body: bot_actions::actions_util::ExecBody,
pub parent_params : ExecBodyParams ,
pub join_handle : Option<Arc<RwLock<JoinHandle<RoutineAR>>>> ,
start_time : Option<DateTime<Local>> ,
pub complete_iterations : i64 ,
pub remaining_iterations : Option<i64> ,
routine_attr : Vec<RoutineAttr> ,
pub internal_signal : RoutineSignal ,
pub self_routine_ar : Option<RoutineAR> ,
pub self_act_ar : Option<ActAR> ,
}
// implement Debug manually witouth `exec_body` since you cant debug `ExecBody`.
impl Debug for Routine {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.name)?;
write!(f, "{:?}", self.module)?;
write!(f, "{:?}", self.channel)?;
write!(f, "{:?}", self.parent_params)?;
write!(f, "{:?}", self.join_handle)?;
write!(f, "{:?}", self.start_time)?;
write!(f, "{:?}", self.complete_iterations)?;
write!(f, "{:?}", self.remaining_iterations)?;
write!(f, "{:?}", self.routine_attr)?;
write!(f, "{:?}", self.internal_signal)?;
write!(f, "{:?}", self.self_routine_ar)?;
write!(f, "{:?}", self.self_act_ar)
}
}
impl Routine {
// // pub fn set
// // pub async fn refresh_self_ref(self) {
// pub async fn refresh_routine_internal(routine_ar : RoutineAR)
// {
// /*
// Execute after the Routine is constructed
// - If not, a start() will also call this
// */
// // 1. Update the self reference to itself
// let mut mut_lock = routine_ar.write().await;
// mut_lock.self_routine_ar = Some(routine_ar.clone());
// // 2. Update the current self_act_ar
// mut_lock.self_act_ar = Some(Arc::new(RwLock::new(BotAction::R(routine_ar.clone()))));
// }
pub async fn validate_attr(routine_attr : &Vec<RoutineAttr>)
-> Result<String,String>
// [ ] => 03.27 - REVIEW FOR COMPLETION
{
/*
GENERAL LOGIC :
[x] 1. Define RoutineAttr in a broad level that are known to be implented or are work in progress
[x] 2. Built in Logic will check these vectors, and return if Not Implemented
[x] 3. If Implemented , then there are additional internal validation based on combination done later
*/
// [x] 1. Define RoutineAttr in a broad level that are known to be implented or are work in progress
// adjust the below for those that are work in progress or that are implemented
// - This will allow other functions to validate that it is implemented
// // WORK IN PROGRESS VECTOR - Vec<$RoutineAttr>
// let wip_attr:Vec<RoutineAttr> = vec![
// RoutineAttr::DelayedStart,
// RoutineAttr::ScheduledStart(chrono::offset::Local::now()),
// RoutineAttr::LoopDuration(Duration::from_secs(1)),
// RoutineAttr::LoopInfinitely, // Note : There's no added implementation for this
// RoutineAttr::RunOnce,
// RoutineAttr::MaxTimeThreshold(chrono::offset::Local::now()),
// RoutineAttr::MaxIterations(1),
// ];
// let implemented_attr:Vec<RoutineAttr> = vec![
// ];
// [x] 2. Built in Logic will check these vectors, and return if Not Implemented
// let mut unimplemented = routine_attr.iter()
// .filter(|x| {
// let inx = x;
// wip_attr.iter().filter(|y| matches!(y,i if i == inx)).next().is_none()
// && implemented_attr.iter().filter(|y| matches!(y,i if i == inx)).next().is_none()
// }
// );
let mut attribute_supported = false;
for given_routine in routine_attr {
// if !matches!(given_routine,RoutineAttr::DelayedStart)
// && !matches!(given_routine,RoutineAttr::ScheduledStart(_))
// && !matches!(given_routine,RoutineAttr::LoopDuration(_))
// && !matches!(given_routine,RoutineAttr::LoopInfinitely)
// && !matches!(given_routine,RoutineAttr::RunOnce)
// && !matches!(given_routine,RoutineAttr::MaxTimeThreshold(_))
// && !matches!(given_routine,RoutineAttr::MaxIterations(_))
if matches!(given_routine,RoutineAttr::DelayedStart)
|| matches!(given_routine,RoutineAttr::ScheduledStart(_))
|| matches!(given_routine,RoutineAttr::LoopDuration(_))
|| matches!(given_routine,RoutineAttr::LoopInfinitely)
|| matches!(given_routine,RoutineAttr::RunOnce)
|| matches!(given_routine,RoutineAttr::MaxTimeThreshold(_))
|| matches!(given_routine,RoutineAttr::MaxIterations(_))
{
attribute_supported = true;
}
};
if !attribute_supported {
botlog::trace(
"[ERROR][Routine Feature NOT IMPLEMENTED]",
Some("Routine > Validate_attr()".to_string()),
None,
);
Log::flush();
botlog::trace(
format!(
"[ERROR][Routine Feature NOT IMPLEMENTED] > Problem Routine - {:?}"
,routine_attr).as_str(),
Some("Routine > Validate_attr()".to_string()),
None,
);
Log::flush();
return Err("NOT IMPLEMENTED".to_string());
}
// if unimplemented.next().is_some() {
// botlog::trace(
// "[ERROR][Routine Feature NOT IMPLEMENTED]",
// Some("Routine > Validate_attr()".to_string()),
// None,
// );
// Log::flush();
// return Err("NOT IMPLEMENTED".to_string());
// }
// [x] 3. If Implemented , then there are additional internal validation based on combination done later to ERR
// Below ensures a routine_attr containing LoopInfinitely does not contain conflicit attributes
if routine_attr.contains(&RoutineAttr::LoopInfinitely) &&
( routine_attr.contains(&RoutineAttr::RunOnce)
|| routine_attr.iter().filter(|y| matches!(y,&&RoutineAttr::MaxIterations(_))).next().is_some()
|| routine_attr.iter().filter(|y| matches!(y,&&RoutineAttr::MaxTimeThreshold(_))).next().is_some()
)
{
return Err("Conflicting Routine Attributes".to_string())
}
// [x] if there is no RunOnce, there must be a LoopDuration
if !routine_attr.contains(&RoutineAttr::RunOnce)
&& routine_attr.iter()
.filter(|x| matches!(x,&&RoutineAttr::LoopDuration(_)) )
.next().is_none()
{
return Err("LoopDuration is required if not RunOnce".to_string());
}
// [x] Err if DelayedStart but no LoopDuration
if routine_attr.contains(&RoutineAttr::DelayedStart) &&
routine_attr.iter()
.filter(|x| matches!(x,&&RoutineAttr::LoopDuration(_)) )
.next().is_none()
{
return Err("DelayedStart must include a LoopDuration".to_string())
}
// [x] 4. If all other failure checks above works, ensure one more time that the atstribute is implemented
// - If not, routine NOT IMPLEMENTED error
// if routine_attr.iter()
// .filter(|x| {
// let inx = x;
// wip_attr.iter().filter(|y| matches!(y,i if i == inx)).next().is_none()
// || implemented_attr.iter().filter(|y| matches!(y,i if i == inx)).next().is_none()
// })
// .next()
// .is_none()
// {
botlog::trace(
"[OK][Implemented & Validated]",
Some("Routine > Validate_attr()".to_string()),
None,
);
Log::flush();
Ok("Implemented & Validated".to_string())
}
pub async fn validate_self_attr(self)
-> Result<String,String>
// [x] => 03.27 - COMPLETED
{
Routine::validate_attr(&self.routine_attr).await
}
// Constructor
pub async fn from(
name : String ,
module : BotModule ,
channel : Channel,
routine_attr : Vec<RoutineAttr> ,
exec_body : bot_actions::actions_util::ExecBody ,
parent_params : ExecBodyParams
) -> Result<
Arc<RwLock<Routine>>,
String
>
// [x] => 03.27 - COMPLETED
{
Routine::validate_attr(&routine_attr).await?;
let routine_ar = Arc::new(RwLock::new(Routine {
name ,
module ,
channel ,
exec_body ,
parent_params ,
join_handle : None ,
start_time : None ,
complete_iterations : 0 ,
remaining_iterations : None ,
routine_attr : routine_attr ,
internal_signal : RoutineSignal::NotStarted ,
self_routine_ar : None ,
self_act_ar : None ,
}));
let mut mut_lock = routine_ar.write().await;
mut_lock.self_routine_ar = Some(routine_ar.clone());
// 2. Update the current self_act_ar
mut_lock.self_act_ar = Some(Arc::new(RwLock::new(BotAction::R(routine_ar.clone()))));
Ok(routine_ar.clone())
// return Ok(Arc::new(RwLock::new(Routine {
// name ,
// module ,
// channel ,
// exec_body ,
// parent_params ,
// join_handle : None ,
// start_time : None ,
// complete_iterations : 0 ,
// remaining_iterations : None ,
// routine_attr : routine_attr ,
// internal_signal : RoutineSignal::NotStarted ,
// self_routine_ar : None ,
// self_act_ar : None ,
// }))) ;
}
pub async fn start(
trg_routine_ar : Arc<RwLock<Routine>>
// ) -> Result<String,String>
) -> Result<Arc<RwLock<Routine>>,String>
// [ ] => 03.27 - REVIEW FOR COMPLETION
{
// // [x] Prep by updating it's own self reference
// Routine::refresh_routine_internal(trg_routine_ar.clone()).await;
// [x] Asyncio Spawn likely around here
// [x] & Assigns self.join_handle
/*
UMBRELLA ROUTINE LOGIC
1. Create a loop scenario based on routine_attr such as RunOnce
2. Run the loop depending on how the Routine is setup
START LOGIC :
1. Ideally validate the routineattr that they're not a problem scenario (However, this should have been validated At Setup)
a. Extra helper validation function though would help in case the attributes were changes between setup
2. Use these attributes only in Critical areas of the loop logic to determine changes in Loop logic
*/
// Use the following to stop the function from going any further if not implemented
Routine::validate_attr(&trg_routine_ar.read().await.routine_attr).await?;
// if !trg_routine_ar.read().await.routine_attr.contains(&RoutineAttr::RunOnce) {
// botlog::trace(
// format!(
// "[ERROR][Routine Feature NOT IMPLEMENTED] {} in {}",
// trg_routine_ar.read().await.name,
// trg_routine_ar.read().await.channel.0
// )
// .as_str(),
// Some(format!(
// "Routine > start() > (In Tokio Spawn) > {:?}",
// trg_routine_ar.read().await.module
// )),
// Some(&trg_routine_ar.read().await.parent_params.msg),
// );
// Log::flush();
// return Err("NOT IMPLEMENTED".to_string())
// }
let trg_routine_arout = Arc::clone(&trg_routine_ar);
botlog::trace(
"innerhelper() started",
Some(format!(
"Routine > start() > (In Tokio Spawn)",
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
Log::flush();
// Spawn the task
let join_handle = tokio::spawn(async move {
botlog::trace(
">> Within Spawn",
Some(format!(
"Routine > start() > (In Tokio Spawn)",
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
Log::flush();
// [x] If Scheduled Start or Delayed Start, Handle that first
fn duration_to_datetime(future_dt: DateTime<Local>) -> Result<Duration,OutOfRangeError>
{
(future_dt - chrono::offset::Local::now()).to_std()
}
let delayduration = {
let lock = trg_routine_ar.read().await;
let mut related_attrs = lock
.routine_attr.iter()
.filter(|x| matches!(x,&&RoutineAttr::DelayedStart) || matches!(x,&&RoutineAttr::ScheduledStart(_)) );
// match related_attrs.next() {
// }
async fn duration_from_attr(attr: &RoutineAttr,trg_routine_ar : RoutineAR) -> Option<Duration> {
// let duration_from_attr = async {
let lock = trg_routine_ar.read().await;
match attr {
RoutineAttr::ScheduledStart(dt) => {
if let Ok(dur) = duration_to_datetime(*dt) {
Some(dur)
} else { None }
},
RoutineAttr::DelayedStart => {
let mut loopdur_attr_iter = lock
.routine_attr.iter()
.filter(|x| matches!(x,&&RoutineAttr::LoopDuration(_)) );
if let Some(loopdur_attr) = loopdur_attr_iter.next() {
if let RoutineAttr::LoopDuration(dur) = loopdur_attr {
Some(*dur)
} else { None }
} else { None }
// None
},
_ => { None } // Handle no other combination
}
}
// The following is done twice just in case ScheduledStart and DelayedStart are defined
let delayduration01 = if let Some(attr) = related_attrs.next() {
duration_from_attr(attr, trg_routine_ar.clone()).await
} else { None };
let delayduration02 = if let Some(attr) = related_attrs.next() {
duration_from_attr(attr, trg_routine_ar.clone()).await
} else { None };
// if there is a 2nd related duration, pick the minimum, otherwise, pick the results of delayduration01
if delayduration02.is_some() {
Some(Duration::min(delayduration01.unwrap(),delayduration02.unwrap()))
} else { delayduration01 }
};
botlog::trace(
format!(
"[TRACE][Routine Processing] {} in {} > Delay Duration - {:?} ",
trg_routine_ar.read().await.name,
trg_routine_ar.read().await.channel.0 ,
delayduration ,
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
trg_routine_ar.read().await.module
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
if let Some(dur) = delayduration {
sleep(dur).await;
}
{ // [x] Loop Initialization - Prior to Loop that calls Custom Routine Execution Body
let mut a = trg_routine_ar.write().await;
a.start_time = Some(chrono::offset::Local::now());
if let Some(&RoutineAttr::MaxIterations(iternum)) =
a.routine_attr.iter()
.filter(|x| matches!(x,RoutineAttr::MaxIterations(_)))
.next()
{
a.remaining_iterations = Some(iternum);
}
}
loop { // [x] Routine loop
{
let mut trg_routine = trg_routine_ar.write().await;
trg_routine.parent_params.parent_act = Some(trg_routine.parent_params.curr_act.clone());
trg_routine.parent_params.curr_act = trg_routine.self_act_ar.to_owned().unwrap();
}
// [x] execution body
trg_routine_ar.read().await.loopbody().await;
{ // [x] End of Loop iteration
let mut a = trg_routine_ar.write().await;
// [x] Check if Gracefully Stopping Signal was sent
if matches!(a.internal_signal,RoutineSignal::Stopping) {
a.internal_signal = RoutineSignal::Stopped;
break ;
}
// [x] Check and adjust iterations
a.complete_iterations += 1;
if let Some(i) = a.remaining_iterations {
if i > 0 { a.remaining_iterations = Some(i-1) ; }
else { break ; } // if remaining iterations is 0, exit
}
}
// [x] End of Loop Validation
// These generally may include routine_attr related checks to , for example, break out of the loop
if trg_routine_ar.read().await.routine_attr.contains(&RoutineAttr::RunOnce) {
if trg_routine_ar.read().await.complete_iterations > 0 { break; }
}
// return if max time has passed
if let Some(&RoutineAttr::MaxTimeThreshold(dt)) = trg_routine_ar.read().await.routine_attr.iter()
.filter(|x| matches!(x,&&RoutineAttr::MaxTimeThreshold(_)) )
.next() {
if chrono::offset::Local::now() > dt { break; }
}
// [x] Checks for Loop duration to sleep
if let Some(&RoutineAttr::LoopDuration(dur)) = trg_routine_ar.read().await.routine_attr.iter()
.filter(|x| matches!(x,&&RoutineAttr::LoopDuration(_)) )
.next()
{
sleep(dur).await;
};
}
botlog::trace(
format!(
"[TRACE][Routine Completed] {} in {}",
trg_routine_ar.read().await.name,
trg_routine_ar.read().await.channel.0
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
trg_routine_ar.read().await.module
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
botlog::trace(
format!(
"[TRACE][Routine Completed][Routine Header Test] {} in {} > Completed Iterations : {}",
trg_routine_ar.read().await.name,
trg_routine_ar.read().await.channel.0 ,
trg_routine_ar.read().await.complete_iterations,
)
.as_str(),
Some(format!(
"Routine > start() > (In Tokio Spawn) > {:?}",
trg_routine_ar.read().await.module
)),
Some(&trg_routine_ar.read().await.parent_params.msg),
);
Log::flush();
trg_routine_ar
});
{ // Recommendation to ensure a clean update is to use one write() lock that was awaited
// - We can isolate the write lock by ensuring it's in it's own block
let mut lock = trg_routine_arout.write().await;
lock.join_handle = Some(Arc::new(RwLock::new(join_handle)));
lock.internal_signal = RoutineSignal::Started;
}
trg_routine_arout.write().await.internal_signal = RoutineSignal::Started;
return Ok(trg_routine_arout);
}
async fn loopbody(&self)
// [x] => 03.27 - COMPLETED
{
botlog::trace(
"loopbody() started",
Some("Routine > start() > (During Tokio Spawn) > Execution body".to_string()),
None,
);
Log::flush();
let self_ar = Arc::new(RwLock::new(self));
// `self_ar` is the routine you want to read in the execbody i think, but its used to call that execbody.
// So execbody waits for itself to finish.
(self_ar.read().await.exec_body)(
self_ar.read().await.parent_params.clone()
).await;
}
pub async fn stop(&mut self) -> Result<String,String>
// [ ] => 03.27 - REVIEW FOR COMPLETION
{
let self_rw = Arc::new(RwLock::new(self));
{
let mut self_lock = self_rw.write().await;
self_lock.internal_signal = RoutineSignal::Stopping;
}
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ROUTINE][Sent Gracefully Stop Signal] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > stop() > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
Ok("Sent Gracefully Stop Signal".to_string())
// botlog::trace(
// format!(
// "[ERROR][Routine NOT IMPLEMENTED] {} in {}",
// self_lock.name,self_lock.channel.0
// )
// .as_str(),
// Some(format!(
// "Routine > start() > (In Tokio Spawn) > {:?}",
// self_lock.module
// )),
// Some(&self_lock.parent_params.msg),
// );
// Log::flush();
// Err("NOT IMPLEMENTED".to_string())
}
pub async fn cancel(&mut self) -> Result<String,String>
// [ ] => 03.27 - REVIEW FOR COMPLETION
{
// [ ] Likely calls abort()
// Related :
// https://docs.rs/tokio/latest/tokio/task/struct.JoinHandle.html#method.abort
let self_rw = Arc::new(RwLock::new(self));
let self_lock = self_rw.read().await;
match &self_lock.join_handle {
None => return Err("No Join Handle on the Routine to Cancel".to_string()),
Some(a) => {
a.read().await.abort();
{
let mut lock_mut = self_rw.write().await;
lock_mut.internal_signal = RoutineSignal::Stopped;
}
}
}
botlog::trace(
format!(
"[ROUTINE][Cancelled Routine] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > cancel() > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
Ok("Cancelled Successfully".to_string())
// botlog::trace(
// format!(
// "[ERROR][Routine NOT IMPLEMENTED] {} in {}",
// self_lock.name,self_lock.channel.0
// )
// .as_str(),
// Some(format!(
// "Routine > start() > (In Tokio Spawn) > {:?}",
// self_lock.module
// )),
// Some(&self_lock.parent_params.msg),
// );
// Log::flush();
// Err("NOT IMPLEMENTED".to_string())
}
pub async fn restart(
// &mut self,
self,
force : bool
) -> Result<String,String>
// [ ] => 03.27 - REVIEW FOR COMPLETION
{
// force flag aborts the routine immediately (like cancel())
let self_rw = Arc::new(RwLock::new(self));
if force
{
let mut self_lock = self_rw.write().await;
self_lock.cancel().await?;
} else {
let mut self_lock = self_rw.write().await;
self_lock.stop().await?;
}
Routine::start(self_rw.clone()).await?;
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ROUTINE][Restarted Routine] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > restart() > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
Ok("Restarted successfully".to_string())
}
pub async fn change_channel(
&mut self,
channel : Channel
) -> Result<String,String>
// [ ] => 03.28 - REVIEW FOR COMPLETION
{
// [x] Think Ideally it should try to
// change the target channel of the
// internal process too if possible?
self.channel = channel;
let self_rw = Arc::new(RwLock::new(self));
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ROUTINE][Change Channel] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > restart() > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
// Err("NOT IMPLEMENTED".to_string())
Ok("Changed Successfully".to_string())
}
pub async fn set_routine_attributes(
&mut self,
routine_attr : Vec<RoutineAttr>
) -> Result<String,String>
// [ ] => 03.27 - WIP - NOT IMPLEMENTED
{
// This is way to custom set attributes first
// They will Be Validated before being applied
// IDEALLY the routine also be restarted afterwards externally
Routine::validate_attr(&routine_attr).await?;
let self_rw = Arc::new(RwLock::new(self));
{
let mut self_lock = self_rw.write().await;
self_lock.routine_attr = routine_attr;
}
// let self_rw = Arc::new(RwLock::new(self));
let self_lock = self_rw.read().await;
botlog::trace(
format!(
"[ROUTINE][Set Routine Attributes] {} in {}",
self_lock.name,self_lock.channel.0
)
.as_str(),
Some(format!(
"Routine > restart() > {:?}",
self_lock.module
)),
Some(&self_lock.parent_params.msg),
);
Log::flush();
Ok("Changed Successfully".to_string())
}
}
type StatusdbEntry = (ModGroup, Vec<StatusType>);
type ModuleActions = Vec<Arc<RwLock<BotAction>>>;
#[derive(Debug)]
pub struct ModulesManager {
statusdb: Arc<RwLock<HashMap<BotModule, StatusdbEntry>>>,
pub botactions: Arc<RwLock<HashMap<BotModule, ModuleActions>>>,

View file

@ -27,7 +27,7 @@ use super::identity;
use async_recursion::async_recursion;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Chat {
pub ratelimiters: Arc<Mutex<HashMap<Channel, RateLimiter>>>, // used to limit messages sent per channel
pub client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
@ -101,14 +101,15 @@ impl Chat {
Some(&params.msg),
);
let parent_module = params.get_parent_module().await;
let parent_module = params.get_module().await;
let params_clone = params.clone();
let botclone = Arc::clone(&params_clone.bot);
let botlock = botclone.read().await;
let modmgr = Arc::clone(&botlock.botmodules);
let modstatus = (*modmgr).modstatus(
parent_module.clone().expect("ERROR - Expected a module"),
// parent_module.clone().expect("ERROR - Expected a module"),
parent_module.clone(),
Channel(channel_login.clone())
).await;
@ -180,7 +181,8 @@ impl Chat {
self.send_botmsg(BotMsgType::Notif(
format!("uuh {:?} is disabled on {} : {:?}",
parent_module.clone().unwrap(),
// parent_module.clone().unwrap(),
parent_module.clone(),
channel_login.clone(),
lvl
),

View file

@ -553,7 +553,16 @@ async fn getroles(params : ExecBodyParams) {
let arg1 = argv.next();
let targetuser = match arg1 {
None => return, // exit if no arguments
None => {
botlog::debug(
"Exittingcmd getroles - Invalid arguments ",
Some("identity.rs > init > getroles()".to_string()),
Some(&params.msg),
);
return
}, // exit if no arguments
Some(arg) => arg,
};
@ -689,7 +698,7 @@ pub enum Permissible {
type UserRolesDB = HashMap<String, Arc<RwLock<Vec<UserRole>>>>;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct IdentityManager {
special_roles_users: Arc<RwLock<UserRolesDB>>,
}

View file

@ -12,6 +12,7 @@ pub use crate::core::botmodules::ModulesManager;
mod experiment001;
mod experiment002;
mod experiment003;
// [ ] init() function that accepts bot instance - this is passed to init() on submodules
@ -21,4 +22,5 @@ pub async fn init(mgr: Arc<ModulesManager>) {
experiment001::init(Arc::clone(&mgr)).await;
experiment002::init(Arc::clone(&mgr)).await;
experiment003::init(Arc::clone(&mgr)).await;
}

View file

@ -0,0 +1,666 @@
/*
Custom Modules -
Usage :
[ ] within the file's init(), define BotActions & Load them into the ModulesManager
[ ] Define Execution Bodies for these BotActions
[ ] Afterwards, add the following to parent modules.rs file
- mod <modulename>;
- within init(), <modulename>::init(mgr).await
*/
const OF_CMD_CHANNEL:Channel = Channel(String::new());
use casual_logger::Log;
use rand::Rng;
use rand::thread_rng;
use rand::rngs::StdRng;
use rand::seq::SliceRandom;
use tokio::sync::RwLock;
use std::borrow::Borrow;
use std::borrow::BorrowMut;
use std::sync::Arc;
use crate::core::bot_actions::ExecBodyParams;
use crate::core::botinstance::Channel;
use crate::core::botlog;
use crate::core::bot_actions::actions_util;
use crate::core::botmodules::{BotAction, BotActionTrait, BotCommand, BotModule, Listener, ModulesManager, Routine, RoutineAttr};
use crate::core::identity::UserRole::*;
use tokio::time::{sleep, Duration};
pub async fn init(mgr: Arc<ModulesManager>) {
// 1. Define the BotAction
let botc1 = BotCommand {
module: BotModule(String::from("experiments003")),
command: String::from("test3"), // command call name
alias: vec![], // String of alternative names
exec_body: actions_util::asyncbox(test3_body),
help: String::from("Test Command tester"),
required_roles: vec![
BotAdmin,
Mod(OF_CMD_CHANNEL),
],
};
// 2. Add the BotAction to ModulesManager
botc1.add_to_modmgr(Arc::clone(&mgr)).await;
// 1. Define the BotAction
let botc1 = BotCommand {
module: BotModule(String::from("experiments003")),
command: String::from("countdown"), // command call name
alias: vec![], // String of alternative names
exec_body: actions_util::asyncbox(countdown_chnl_v1),
help: String::from("Test Command tester"),
required_roles: vec![
BotAdmin,
Mod(OF_CMD_CHANNEL),
],
};
// 2. Add the BotAction to ModulesManager
botc1.add_to_modmgr(Arc::clone(&mgr)).await;
}
async fn countdown_chnl_v1(params : ExecBodyParams) {
botlog::debug(
"[CHILDFN] countdown_chnl() triggered!",
Some("Experiments003 > countdown_chnl()".to_string()),
Some(&params.msg),
);
/*
create a fun countdown BotCommand that allows an Elevated Chatter to
-a add channels to target a routine message
-start to start the routine with an input String, that sends a number
of messages to the targeted channels with a countdown, until it
reaches 0 when it sends a cute or funny message
NOTE : At the moment, I don't have customizable persistence, so I would just use
counters from the Routine itself
*/
/*
Because of some bot core features are not available, v1.0 of this could be :
[x] 1. Create a Routine & start a routine
[x] 2. Have the routine go through each joined channel randomly and countdown
[x] 3. At the end, say "0, I love you uwu~" in the last chosen channel
*/
/*
Usage => 03.28 - skipping arguments as functinoality isn't enhanced
-a <channel> => 03.28 - Not sure if this is possible at the moment?
-start
-stop => 03.28 - Not sure if this is possible at the moment?
*/
/*
[ ] Functional Use Case
1. -a <channel> adds targetted channels
*/
// [-] Unwraps arguments from message
// let (arg1, arg2) = {
// let mut argv = params.msg.message_text.split(' ');
// argv.next(); // Skip the command name
// let arg1 = argv.next();
// let arg2 = argv.next();
// (arg1, arg2)
// };
// [ ] 1. Create a Routine & start a routine
// let parentmodule = params.get_parent_module().await;
let module = params.get_module().await;
let channel = params.get_channel().await;
let routine_attr = vec![
// RoutineAttr::RunOnce
RoutineAttr::MaxIterations(5),
RoutineAttr::LoopDuration(Duration::from_secs(1))
];
// let exec_body = actions_util::asyncbox(rtestbody);
let exec_body = actions_util::asyncbox(innertester); // <-- 03.27 - when below is uncommented, this is throwing an issue
async fn innertester(params : ExecBodyParams) {
{
let curract_guard = params.curr_act.read().await;
// let logmsg_botact = match *params.curr_act.read().await {
let logmsg_botact = match *curract_guard {
BotAction::C(_) => "command",
BotAction::R(_) => "routine",
BotAction::L(_) => "listener",
} ;
botlog::trace(
format!("Params > Curr_act type : {:?}", logmsg_botact).as_str(),
Some("Experiments003 > countdown_chnl()".to_string()),
Some(&params.msg),
);
Log::flush();
}
{
let bot = Arc::clone(&params.bot);
let botlock = bot.read().await;
let curract_guard = params.curr_act.write().await;
// let routine_lock = arr.write().await;
if let BotAction::R(arr) = &*curract_guard {
// if let BotAction::R(arr) = &*params.curr_act.read().await {
botlog::trace(
"Before loading remaining iterations",
Some("Experiments003 > countdown_chnl()".to_string()),
None,
);
Log::flush();
// let iterleft = arr.read().await.remaining_iterations.unwrap_or(0);
// // let iterleft = if arr.read().await.remaining_iterations.is_none() { 0i64 }
// // else { arr.read().await.remaining_iterations.unwrap() };
// let iterleft = match arr.read().await.remaining_iterations {
// None => 0,
// Some(a) => a,
// };
// let routine_lock = arr.read().await;
// if let Some(a) = routine_lock.remaining_iterations.clone() {
// println!("Remaining iterations > {}",a)
// }
{
let routine_lock = arr.write().await;
let a = routine_lock.remaining_iterations;
println!("remaining iterations : {:?}", a);
}
botlog::trace(
"after loading remaining iterations",
Some("Experiments003 > countdown_chnl()".to_string()),
None,
);
Log::flush();
// [ ] get joined channels
let joinedchannels = botlock.bot_channels.clone();
fn pick_a_channel(chnlvec : Vec<Channel>) -> Channel {
botlog::trace(
"In Pick_a_Channel()",
Some("Experiments003 > countdown_chnl()".to_string()),
None,
);
Log::flush();
// More Information : https://docs.rs/rand/0.7.2/rand/seq/trait.SliceRandom.html#tymethod.choose
let mut rng = thread_rng();
// let joinedchannels = botlock.bot_channels.clone();
(*chnlvec.choose(&mut rng).unwrap()).clone()
}
let chosen_channel = pick_a_channel(joinedchannels);
botlog::trace(
format!("Picked a channel: {:?}", chosen_channel).as_str(),
Some("Experiments003 > countdown_chnl()".to_string()),
Some(&params.msg),
);
Log::flush();
// let outmsg = if iterleft == 1 {
// format!("{} I love you uwu~",iterleft)
// } else { format!("{}",iterleft) };
// botlock.botmgrs.chat
// .say(
// // joinedchannels.choose(&mut rng).unwrap().0.clone(),
// chosen_channel.0.clone(),
// outmsg,
// params.clone()
// ).await;
}
}
}
// [ ] setup the routine
if let Ok(newr) = Routine::from(
"Routine Test".to_string(),
module,
channel.unwrap(),
routine_attr,
exec_body,
params.clone()
).await {
let newr_ar = newr.clone();
// [ ] start the routine
if let Ok(_) = Routine::start(newr_ar.clone()).await {
botlog::debug(
"Successfully started",
Some("experiment003 > countdown_chnl()".to_string()),
Some(&params.msg),
);
Log::flush();
let bot = Arc::clone(&params.bot);
let botlock = bot.read().await;
// uses chat.say_in_reply_to() for the bot controls for messages
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
"Started Routine!".to_string(),
params.clone()
).await;
// let jhandle = newr.clone().read().await.join_handle.clone().unwrap();
// let a = jhandle.write().await;
// a.
// sleep(Duration::from_secs(300)).await;
}
}
// botlock
// .botmgrs
// .chat
// .say_in_reply_to(
// &params.msg,
// format!("{:?}",),
// params.clone()
// ).await;
}
async fn test3_body(params : ExecBodyParams) {
// println!("testy triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call
botlog::debug(
"testy triggered!",
Some("Experiments003 > test3 command body".to_string()),
Some(&params.msg),
);
/*
Test Routine Start() by :
1. In this single exec body , create a Routine
2. Create a Routine Execution Body
3. Pass the Execution Body & Routine Attributes to create the Routine
4. Start the Routine
5. For RunOnce , we should see it only trigger once, and then complete in the logs
*/
// [x] Get the module from params
// let parentmodule = params.get_parent_module().await;
let module = params.get_module().await;
let channel = params.get_channel().await;
let routine_attr = vec![
RoutineAttr::RunOnce
];
// let exec_body = actions_util::asyncbox(rtestbody);
let exec_body = actions_util::asyncbox(rtestbody); // <-- 03.27 - when below is uncommented, this is throwing an issue
// let parent_params = params.clone();
// let params_clone = params.clone();
async fn rtestbody(params : ExecBodyParams) {
let guard = params.curr_act.read().await;
{
let logmsg_botact = match *guard {
BotAction::C(_) => "command",
BotAction::R(_) => "routine",
BotAction::L(_) => "listener",
} ;
botlog::trace(
format!("Params > Curr_act type : {:?}", logmsg_botact).as_str(),
Some("Experiments003 > test3 command body".to_string()),
Some(&params.msg),
);
Log::flush();
}
{
let logmsg_botact = match *guard {
BotAction::C(_) => "command 2",
BotAction::R(_) => "routine 2",
BotAction::L(_) => "listener 2",
} ;
botlog::trace(
format!("Params > Curr_act type : {:?}", logmsg_botact).as_str(),
Some("Experiments003 > test3 command body".to_string()),
Some(&params.msg),
);
Log::flush();
}
{
println!("Critical code area start"); // <= 03.29 - This is printed
if let BotAction::R(c) = &*guard {
println!("{:?}",c.read().await.channel);
}
println!("Critical code area end"); // <= 03.29 - ISSUE This is NOT printed
// if let BotAction::R(arr) = &*params.curr_act.read().await {
// for curriter in 0..5 {
// println!("tester - Routine - Completed Iterations : {}",
// arr.read().await.complete_iterations);
// println!("tester - Custom Loop - Completed Iterations : {}",
// curriter);
// sleep(Duration::from_secs_f64(0.5)).await;
// }
// }
}
}
botlog::debug(
format!("RTESTBODY : module - {:?} ; channel - {:?}",
module,channel
).as_str(),
Some("experiment003 > test3_body".to_string()),
Some(&params.msg),
);
let a = Routine::from(
"Routine Test".to_string(),
module,
channel.unwrap(),
routine_attr,
exec_body,
params.clone()
).await;
if let Ok(newr) = a {
// NOTE : The below is a "Playing aound" feature
// In the below, we're unnecessarily adjusting the ExecBodyParams of the parent
// To get the reference to the Routine or the BotAction there are now refernces to itself
// [ ] before execute , be sure to adjust curr_act
// let mut params_mut = params;
let newr_ar = newr.clone();
// params_mut.curr_act = Arc::new(RwLock::new(
// BotAction::R(newr_ar.clone())
// ));
// {
// newr_ar.write().await.parent_params = params_mut.clone();
// }
let rslt = Routine::start(newr_ar.clone()).await;
// let rslt = newr_ar.read().await.start().await;
let rsltstr = match rslt {
Ok(_) => "successful".to_string(),
Err(a) => a,
};
botlog::debug(
format!("TEST3_BODY RESULT : {:?}",
rsltstr
).as_str(),
Some("experiment003 > test3_body".to_string()),
Some(&params.msg),
);
Log::flush();
let bot = Arc::clone(&params.bot);
let botlock = bot.read().await;
// uses chat.say_in_reply_to() for the bot controls for messages
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
format!("Routine Result : {:?}",rsltstr),
params.clone()
).await;
// [x] Will not be handling JoinHandles here . If immediate abort() handling is required, below is an example that works
/*
let a = newr.clone().read().await.join_handle.clone();
match a {
Some(b) => {
b.read().await.borrow().abort(); // [x] <-- This aborts if wanting to abort immediately
//()
},
None => (),
}
*/
}
Log::flush();
}
async fn good_girl(params : ExecBodyParams) {
// [ ] 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)
// - More Info : https://rust-random.github.io/rand/rand/trait.Rng.html#method.gen_ratio
if params.msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase()
|| params.msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase()
{
botlog::debug(
"Good Girl Detected > Pausechamp",
Some("experiments > goodgirl()".to_string()),
Some(&params.msg),
);
let rollwin = rand::thread_rng().gen_ratio(1, 10);
if rollwin {
botlog::debug(
"Oh that's a good girl!",
Some("experiments > goodgirl()".to_string()),
Some(&params.msg),
);
let bot = Arc::clone(&params.bot);
let botlock = bot.read().await;
// uses chat.say_in_reply_to() for the bot controls for messages
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
String::from("GoodGirl xdd "),
params.clone()
).await;
}
}
}
async fn testy(params : ExecBodyParams) {
println!("testy triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call
botlog::debug(
"testy triggered!",
Some("experiments > testy()".to_string()),
Some(&params.msg),
);
}
async fn babygirl(params : ExecBodyParams) {
println!("babygirl triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call
botlog::debug(
"babygirl triggered!",
Some("experiments > babygirl()".to_string()),
Some(&params.msg),
);
let bot = Arc::clone(&params.bot);
let botlock = bot.read().await;
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
String::from("16:13 notohh: cafdk"),
params.clone()
).await;
sleep(Duration::from_secs_f64(0.5)).await;
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
String::from("16:13 notohh: have fun eating princess"),
params.clone()
).await;
sleep(Duration::from_secs_f64(2.0)).await;
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
String::from("16:13 notohh: baby girl"),
params.clone()
).await;
}
async fn routinelike(params : ExecBodyParams) {
println!("routinelike triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call
botlog::debug(
"routinelike triggered!",
Some("experiments > routinelike()".to_string()),
Some(&params.msg),
);
// spawn an async block that runs independently from others
tokio::spawn( async {
for _ in 0..5 {
println!(">> Innterroutine triggered!");
sleep(Duration::from_secs_f64(5.0)).await;
}
}
);
// lines are executed after in conjunction to the spawn
}