WIP: Basic Routine Functionality #40
7 changed files with 1649 additions and 24 deletions
|
@ -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)]
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -404,7 +404,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 +462,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 +493,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 +519,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 +568,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,14 +25,25 @@ 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::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 +55,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 +82,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 +265,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
|
||||
|
@ -462,7 +473,7 @@ pub enum StatusType {
|
|||
pub enum BotAction {
|
||||
C(BotCommand),
|
||||
L(Listener),
|
||||
R(Routine),
|
||||
R(Arc<RwLock<Routine>>),
|
||||
}
|
||||
|
||||
impl BotAction {
|
||||
|
@ -568,8 +579,895 @@ impl BotActionTrait for Listener {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Routine {}
|
||||
// #[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
|
||||
pub enum RoutineSignal {
|
||||
Stopping, // Gracefully Stopping
|
||||
Stopped, // When cancelling or aborting, this also is represented by Stopped
|
||||
Started, // After Routine Started
|
||||
NotStarted,
|
||||
}
|
||||
|
||||
// #[derive(Debug)]
|
||||
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> ,
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
// [x] execution body
|
||||
// trg_routine_ar.read().await.loopbody().await;
|
||||
{
|
||||
trg_routine_ar.write().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(&mut self)
|
||||
// [x] => 03.27 - COMPLETED
|
||||
{
|
||||
botlog::trace(
|
||||
"loopbody() started",
|
||||
Some(format!(
|
||||
"Routine > start() > (During Tokio Spawn) > Execution body",
|
||||
)),
|
||||
None,
|
||||
);
|
||||
|
||||
Log::flush();
|
||||
|
||||
let self_ar = Arc::new(RwLock::new(self));
|
||||
|
||||
{
|
||||
let mut mutlock = self_ar.write().await;
|
||||
|
||||
mutlock.parent_params.parent_act = Some(mutlock.parent_params.curr_act.clone());
|
||||
mutlock.parent_params.curr_act = mutlock.self_act_ar.to_owned().unwrap();
|
||||
}
|
||||
|
||||
(self_ar.read().await.exec_body)(
|
||||
self_ar.read().await.parent_params.clone()
|
||||
).await;
|
||||
|
||||
// (self.exec_body)(
|
||||
// self.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>>>;
|
||||
|
|
|
@ -101,14 +101,15 @@ impl Chat {
|
|||
Some(¶ms.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(¶ms_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
|
||||
),
|
||||
|
|
|
@ -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(¶ms.msg),
|
||||
);
|
||||
return
|
||||
|
||||
}, // exit if no arguments
|
||||
Some(arg) => arg,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
666
src/custom/experimental/experiment003.rs
Normal file
666
src/custom/experimental/experiment003.rs
Normal 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(¶ms.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(¶ms.msg),
|
||||
);
|
||||
Log::flush();
|
||||
}
|
||||
|
||||
{
|
||||
let bot = Arc::clone(¶ms.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(¶ms.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(¶ms.msg),
|
||||
);
|
||||
|
||||
|
||||
Log::flush();
|
||||
|
||||
let bot = Arc::clone(¶ms.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(
|
||||
¶ms.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(
|
||||
// ¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.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(¶ms.msg),
|
||||
);
|
||||
|
||||
Log::flush();
|
||||
|
||||
let bot = Arc::clone(¶ms.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(
|
||||
¶ms.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(¶ms.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(¶ms.msg),
|
||||
);
|
||||
|
||||
let bot = Arc::clone(¶ms.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(
|
||||
¶ms.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(¶ms.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(¶ms.msg),
|
||||
);
|
||||
|
||||
|
||||
let bot = Arc::clone(¶ms.bot);
|
||||
|
||||
let botlock = bot.read().await;
|
||||
|
||||
|
||||
botlock
|
||||
.botmgrs
|
||||
.chat
|
||||
.say_in_reply_to(
|
||||
¶ms.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(
|
||||
¶ms.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(
|
||||
¶ms.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(¶ms.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
|
||||
|
||||
}
|
||||
|
Loading…
Reference in a new issue
ISSUE . The defined Routine's Custom Execution Body is being reached (so the custom
fn
is being triggered as expected); However, I believe at L413 wherec.read().await.channel
, it never goes beyond that point within the same Customfn
bodyEven if the bot continues to be responsive (i.e., the
main
bot continues to run), the rest of the routine is not. I believe this is suggesting there's a lock with one of the objects we're introducing with this feature . In the above line, I believe the lock involvesExecBodyParam
'scurr_act
value