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
21 changed files with 1853 additions and 666 deletions

8
.gitignore vendored
View file

@ -22,10 +22,4 @@ target/
*.log *.log
# debug # debug
.vscode/ .vscode/
# nix
result/
# pre-commit
/.pre-commit-config.yaml

View file

@ -1,5 +1,5 @@
when: when:
branch: master branch: main
event: [push, pull_request] event: [push, pull_request]
path: path:
include: include:

View file

@ -37,7 +37,7 @@ steps:
owner: ${CI_REPO_OWNER} owner: ${CI_REPO_OWNER}
repo: ${CI_REPO_NAME} repo: ${CI_REPO_NAME}
branch: flake-lock-update branch: flake-lock-update
base_branch: master base_branch: main
pr_title: "flake.lock: update" pr_title: "flake.lock: update"
pr_body: PR automatically created by Woodpecker CI pr_body: PR automatically created by Woodpecker CI
close_pr_if_empty: true close_pr_if_empty: true

View file

@ -9,10 +9,9 @@ test ModulatingForceBot
``` ```
login_name = <botname> login_name = <botname>
access_token = <oauth token> access_token = <oath token>
bot_channels = <chnl1>,<chnl2> bot_channels = <chnl1>,<chnl2>
prefix = <prefix> prefix = <prefix>
bot_admins = <admins>
``` ```

View file

@ -6,11 +6,11 @@
"rust-analyzer-src": "rust-analyzer-src" "rust-analyzer-src": "rust-analyzer-src"
}, },
"locked": { "locked": {
"lastModified": 1711952616, "lastModified": 1706768574,
"narHash": "sha256-WJvDdOph001fA1Ap3AyaQtz/afJAe7meSG5uJAdSE+A=", "narHash": "sha256-4o6TMpzBHO659EiJTzd/EGQGUDdbgwKwhqf3u6b23U8=",
"owner": "nix-community", "owner": "nix-community",
"repo": "fenix", "repo": "fenix",
"rev": "209048d7c545905c470f6f8c05c5061f391031a8", "rev": "668102037129923cd0fc239d864fce71eabdc6a3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -20,68 +20,13 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1711703276, "lastModified": 1706550542,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -91,92 +36,36 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-stable": {
"locked": {
"lastModified": 1710695816,
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1712573573, "lastModified": 1708296515,
"narHash": "sha256-xxon7WwNm4/EadMKg1eF40/5s0O78nXUy2ILZt6vT7E=", "narHash": "sha256-FyF489fYNAUy7b6dkYV6rGPyzp+4tThhr80KNAaF/yY=",
"owner": "NixOS", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "0d28066770464d19d637f6e8e42e8688420b6ac6", "rev": "b98a4e1746acceb92c509bc496ef3d0e5ad8d4aa",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "nixos",
"ref": "nixpkgs-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_3": {
"locked": {
"lastModified": 1710765496,
"narHash": "sha256-p7ryWEeQfMwTB6E0wIUd5V2cFTgq+DRRBz2hYGnJZyA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e367f7a1fb93137af22a3908f00b9a35e2d286a7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": "nixpkgs_3",
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1712579741,
"narHash": "sha256-igpsH+pa6yFwYOdah3cFciCk8gw+ytniG9quf5f/q84=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "70f504012f0a132ac33e56988e1028d88a48855c",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"fenix": "fenix", "fenix": "fenix",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2"
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems_2"
} }
}, },
"rust-analyzer-src": { "rust-analyzer-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1711885694, "lastModified": 1706735270,
"narHash": "sha256-dyezzeSbWMpflma+E9USmvSxuLgGcNGcGw3cOnX36ko=", "narHash": "sha256-IJk+UitcJsxzMQWm9pa1ZbJBriQ4ginXOlPyVq+Cu40=",
"owner": "rust-lang", "owner": "rust-lang",
"repo": "rust-analyzer", "repo": "rust-analyzer",
"rev": "e4a405f877efd820bef9c0e77a02494e47c17512", "rev": "42cb1a2bd79af321b0cc503d2960b73f34e2f92b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -185,36 +74,6 @@
"repo": "rust-analyzer", "repo": "rust-analyzer",
"type": "github" "type": "github"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -1,80 +1,39 @@
{ {
description = "A basic flake"; description = "forcebot_rs flake";
inputs = { inputs = {
systems.url = "github:nix-systems/default-linux"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
fenix.url = "github:nix-community/fenix/monthly"; fenix.url = "github:nix-community/fenix/monthly";
pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
}; };
outputs = { outputs = {
self,
systems,
nixpkgs, nixpkgs,
fenix, fenix,
pre-commit-hooks, ...
}: let } @ inputs: let
eachSystem = nixpkgs.lib.genAttrs (import systems); system = "x86_64-linux";
pkgsFor = eachSystem (system: overlays = [fenix.overlays.default];
import nixpkgs { pkgs = import nixpkgs {
localSystem.system = system; inherit system overlays;
overlays = [fenix.overlays.default]; };
});
in { in {
packages = eachSystem (system: let devShells.${system}.default = pkgs.mkShell {
pkgs = nixpkgs.legacyPackages.${system}; name = "forcebot_rs-devenv";
inherit ((builtins.fromTOML (builtins.readFile ./Cargo.toml)).package) version; nativeBuildInputs = [pkgs.pkg-config];
in { buildInputs = with pkgs; [openssl libiconv];
default = pkgsFor.${system}.rustPlatform.buildRustPackage { packages = with pkgs; [
pname = "forcebot_rs"; nil
version = "${version}"; alejandra
rust-analyzer-nightly
src = self; (fenix.packages.${system}.complete.withComponents [
"cargo"
cargoLock = { "clippy"
lockFile = ./Cargo.lock; "rust-src"
}; "rustc"
"rustfmt"
nativeBuildInputs = with pkgs; [pkg-config]; ])
buildInputs = with pkgs; [openssl]; ];
RUST_BACKTRACE = 1;
doCheck = false; RUST_SRC_PATH = "${fenix.packages.${system}.complete.rust-src}/lib/rustlib/src/rust/library";
}; };
});
checks = eachSystem (system: {
pre-commit-check = pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
# rust
rustfmt.enable = true;
clippy.enable = true;
# nix
statix.enable = true;
alejandra.enable = true;
deadnix.enable = true;
};
};
});
devShells = eachSystem (system: {
default = pkgsFor.${system}.mkShell {
inherit (self.checks.${system}.pre-commit-check) shellHook;
packages = with pkgsFor.${system}; [
nil
alejandra
rust-analyzer-nightly
(fenix.packages.${system}.complete.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
])
];
RUST_BACKTRACE = 1;
RUST_SRC_PATH = "${fenix.packages.${system}.complete.rust-src}/lib/rustlib/src/rust/library";
};
});
nixosModules.default = import ./nix/module.nix {inherit self;};
}; };
} }

View file

@ -1,30 +0,0 @@
{self}: {
pkgs,
config,
lib,
...
}: let
inherit (lib) types;
inherit (lib.modules) mkIf;
inherit (lib.options) mkOption mkEnableOption;
inherit (pkgs.stdenv.hostPlatform) system;
cfg = config.services.forcebot_rs;
in {
options.services.forcebot_rs = {
enable = mkEnableOption ''
Enable forcebot
'';
package = mkOption {
type = types.package;
inherit (self.packages.${system}) default;
};
};
config = mkIf cfg.enable {
systemd.services.forcebot_rs = {
wantedBy = ["multi-user.target"];
serviceConfig.ExecStart = "${cfg.package}/bin/forcebot_rs";
};
};
}

View file

@ -1,43 +1,87 @@
use twitch_irc::message::{PrivmsgMessage, TwitchUserBasics};
use twitch_irc::message::PrivmsgMessage;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use crate::core::botinstance::BotInstance; 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 BotAR = Arc<RwLock<BotInstance>>;
pub type ActAR = Arc<RwLock<BotAction>>; pub type ActAR = Arc<RwLock<BotAction>>;
pub type RoutineAR = Arc<RwLock<Routine>>;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct ExecBodyParams { pub struct ExecBodyParams {
pub bot : BotAR, pub bot : BotAR,
pub msg : PrivmsgMessage, pub msg : PrivmsgMessage,
pub parent_act : ActAR , pub parent_act : Option<ActAR> ,
pub curr_act : ActAR ,
} }
impl ExecBodyParams { 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 curr_act = Arc::clone(&self.curr_act);
let parent_act_lock = parent_act.read().await; let parent_act_lock = curr_act.read().await;
let act = &(*parent_act_lock); let act = &(*parent_act_lock);
match act { match act {
BotAction::C(c) => { BotAction::C(c) => {
let temp = c.module.clone(); // let temp = c.module.clone();
Some(temp) // Some(temp)
c.module.clone()
}, },
BotAction::L(l) => { BotAction::L(l) => {
let temp = l.module.clone(); // let temp = l.module.clone();
Some(temp) // 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 { pub fn get_sender(&self) -> String {
self.msg.sender.name.clone() self.msg.sender.name.clone()
} }
@ -56,68 +100,9 @@ impl ExecBodyParams {
requestor_badge_mut requestor_badge_mut
} }
/// Returns some information about the message that was replied to by the `PrivmsgMessage` contained
/// in the `msg` field of this struct.
///
/// If that message replied to message return that information in form of `Some<ReplyParent>`.
/// Otherwise, return `None`.
pub fn get_parent_reply(&self) -> Option<ReplyParent> {
let map = &self.msg.source.tags.0;
let tags = [
"reply-parent-user-id",
"reply-parent-user-login",
"reply-parent-display-name",
"reply-parent-msg-id",
"reply-parent-msg-body"
];
// filter out all tags that do not have content.
let tag_contents: Vec<String> = tags.iter().filter_map(|tag| {
// if let Some(&Some(ref t)) = map.get(*tag) {
if let Some(Some(t)) = map.get(*tag) {
Some(t.clone())
} else {
None
}
}).collect();
// if no tags got filtered out return the struct.
// else return `None`.
if tag_contents.len() == 5 {
Some(ReplyParent {
sender: TwitchUserBasics {
id: tag_contents[0].clone(),
login: tag_contents[1].clone(),
name: tag_contents[2].clone(),
},
message_id: tag_contents[3].clone(),
message_text: tag_contents[4].clone(),
channel_login: self.msg.channel_login.clone(),
channel_id: self.msg.channel_id.clone(),
})
} else {
None
}
}
} }
/// Represents the message a `PrivmsgMessage` replies to.
/// Similar to a less detailed `PrivmsgMessage`.
///
/// This should not be constructed manually but only from calling `get_parent_reply()` on
/// `ExecBodyParams`.
///
/// Fields that will be the same as the `PrivmsgMessage` this was generated from:
/// - `channel_login`
/// - `channel_id`
#[derive(Debug, Clone, PartialEq)]
pub struct ReplyParent {
pub sender: TwitchUserBasics,
pub message_id: String,
pub message_text: String,
pub channel_login: String,
pub channel_id: String,
}
pub mod actions_util { pub mod actions_util {

View file

@ -40,7 +40,7 @@ pub struct Channel(pub String);
use super::bot_actions::ExecBodyParams; use super::bot_actions::ExecBodyParams;
use super::botmodules::StatusType; use super::botmodules::StatusType;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct BotManagers { pub struct BotManagers {
pub identity: Arc<RwLock<IdentityManager>>, pub identity: Arc<RwLock<IdentityManager>>,
pub chat: Chat, pub chat: Chat,
@ -70,6 +70,7 @@ impl<T: Clone> ArcBox<T> {
} }
} }
#[derive(Debug)]
pub struct BotInstance { pub struct BotInstance {
pub prefix: char, pub prefix: char,
pub bot_channel: Channel, pub bot_channel: Channel,
@ -122,7 +123,7 @@ impl BotInstance {
tokio::spawn(async { tokio::spawn(async {
loop { loop {
let routine_mins = 60 * 24 ; // Every 1 Day let routine_mins = 60 * 60 * 24 ; // Every 1 Day
// let routine_mins = 1; // Every 1 Minute // let routine_mins = 1; // Every 1 Minute
Log::remove_old_logs(); Log::remove_old_logs();
Log::info(&format!("Internal Purge Routine Triggered - running every {} mins",routine_mins)); Log::info(&format!("Internal Purge Routine Triggered - running every {} mins",routine_mins));
@ -404,7 +405,8 @@ impl BotInstance {
let params = ExecBodyParams { let params = ExecBodyParams {
bot : Arc::clone(&bot), bot : Arc::clone(&bot),
msg : (*msg).clone(), 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 // When sending a BotMsgTypeNotif, send_botmsg does Roles related validation as required
@ -461,7 +463,8 @@ impl BotInstance {
let params = ExecBodyParams { let params = ExecBodyParams {
bot : Arc::clone(&bot), bot : Arc::clone(&bot),
msg : (*msg).clone(), 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 { let params = ExecBodyParams {
bot : Arc::clone(&bot), bot : Arc::clone(&bot),
msg : (*msg).clone(), 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 { c.execute(ExecBodyParams {
bot : a, bot : a,
msg : msg.clone() , msg : msg.clone() ,
parent_act : Arc::clone(&act_clone), parent_act : None,
curr_act : Arc::clone(&act_clone),
}).await; }).await;
botlog::trace( botlog::trace(
@ -564,7 +569,8 @@ impl BotInstance {
l.execute(ExecBodyParams { l.execute(ExecBodyParams {
bot : a, bot : a,
msg : msg.clone() , msg : msg.clone() ,
parent_act : Arc::clone(&act_clone), parent_act : None,
curr_act : Arc::clone(&act_clone),
} ).await; } ).await;
} }

View file

@ -24,10 +24,11 @@ debug = "Checking bot actions",
pub fn trace(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) { pub fn trace(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };
@ -44,10 +45,11 @@ pub fn trace(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&Privmsg
pub fn debug(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) { pub fn debug(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };
@ -64,10 +66,11 @@ pub fn debug(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&Privmsg
pub fn info(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) { pub fn info(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };
@ -84,10 +87,11 @@ pub fn info(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgM
pub fn notice(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) { pub fn notice(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };
@ -104,10 +108,11 @@ pub fn notice(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&Privms
pub fn warn(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) { pub fn warn(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };
@ -124,10 +129,11 @@ pub fn warn(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgM
pub fn error(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) { pub fn error(in_msg: &str, in_module: Option<String>, in_prvmsg: Option<&PrivmsgMessage>) {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };
@ -148,10 +154,11 @@ pub fn fatal<'a>(
) -> &'a str { ) -> &'a str {
let (chnl, chatter) = match in_prvmsg { let (chnl, chatter) = match in_prvmsg {
Some(prvmsg) => { Some(prvmsg) => {
//Log::trace(&format!("(#{}) {}: {}", prvmsg.channel_login, prvmsg.sender.name, prvmsg.message_text));
( (
Some(prvmsg.channel_login.clone()), Some(prvmsg.channel_login.clone()),
Some(prvmsg.sender.name.clone()), Some(prvmsg.sender.name.clone()),
) ) // <-- Clone fine atm while we're just working with Strings
} }
None => (None, None), None => (None, None),
}; };

View file

@ -25,14 +25,26 @@ const OF_CMD_CHANNEL:Channel = Channel(String::new());
use core::panic; use core::panic;
use std::borrow::Borrow;
use std::borrow::BorrowMut;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::ops::DerefMut;
use std::sync::Arc; use std::sync::Arc;
use casual_logger::Log; use casual_logger::Log;
use chrono::DateTime;
// use chrono::Duration;
use chrono::Local;
use chrono::OutOfRangeError;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use async_trait::async_trait; 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::actions_util;
use crate::core::bot_actions::ExecBodyParams; use crate::core::bot_actions::ExecBodyParams;
@ -44,6 +56,8 @@ use crate::core::bot_actions;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use super::bot_actions::ActAR;
use super::bot_actions::RoutineAR;
use super::identity::ChatBadge; use super::identity::ChatBadge;
@ -69,7 +83,6 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// 2. Add the BotAction to ModulesManager // 2. Add the BotAction to ModulesManager
botc1.add_core_to_modmgr(Arc::clone(&mgr)).await; botc1.add_core_to_modmgr(Arc::clone(&mgr)).await;
// async fn cmd_enable(bot: BotAR, msg: PrivmsgMessage) {
async fn cmd_enable(params : ExecBodyParams) { async fn cmd_enable(params : ExecBodyParams) {
/* /*
There should be additional validation checks There should be additional validation checks
@ -458,10 +471,11 @@ pub enum StatusType {
Disabled(StatusLvl), Disabled(StatusLvl),
} }
#[derive(Debug)]
pub enum BotAction { pub enum BotAction {
C(BotCommand), C(BotCommand),
L(Listener), L(Listener),
R(Routine), R(Arc<RwLock<Routine>>),
} }
impl BotAction { impl BotAction {
@ -520,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 struct Listener {
pub module: BotModule, pub module: BotModule,
pub name: String, pub name: String,
@ -567,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)] #[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 StatusdbEntry = (ModGroup, Vec<StatusType>);
type ModuleActions = Vec<Arc<RwLock<BotAction>>>; type ModuleActions = Vec<Arc<RwLock<BotAction>>>;
#[derive(Debug)]
pub struct ModulesManager { pub struct ModulesManager {
statusdb: Arc<RwLock<HashMap<BotModule, StatusdbEntry>>>, statusdb: Arc<RwLock<HashMap<BotModule, StatusdbEntry>>>,
pub botactions: Arc<RwLock<HashMap<BotModule, ModuleActions>>>, pub botactions: Arc<RwLock<HashMap<BotModule, ModuleActions>>>,

View file

@ -4,7 +4,7 @@ use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use twitch_irc::login::StaticLoginCredentials; use twitch_irc::login::StaticLoginCredentials;
use twitch_irc::message::ReplyToMessage; use twitch_irc::message::PrivmsgMessage;
use twitch_irc::transport::tcp::{TCPTransport, TLS}; use twitch_irc::transport::tcp::{TCPTransport, TLS};
use twitch_irc::TwitchIRCClient; use twitch_irc::TwitchIRCClient;
@ -27,16 +27,16 @@ use super::identity;
use async_recursion::async_recursion; use async_recursion::async_recursion;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Chat { pub struct Chat {
pub ratelimiters: Arc<Mutex<HashMap<Channel, RateLimiter>>>, // used to limit messages sent per channel pub ratelimiters: Arc<Mutex<HashMap<Channel, RateLimiter>>>, // used to limit messages sent per channel
pub client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>, pub client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
} }
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub enum BotMsgType { pub enum BotMsgType<'a> {
SayInReplyTo(Channel,String,String), // ( Destination Channel , Message ID to reply to , OutMessage ) // https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say_in_reply_to SayInReplyTo(&'a PrivmsgMessage,String),
Say(String,String), Say(String,String),
Notif(String), // For Bot Sent Notifications Notif(String), // For Bot Sent Notifications
} }
@ -59,7 +59,7 @@ impl Chat {
} }
#[async_recursion] #[async_recursion]
pub async fn send_botmsg(&self, msginput: BotMsgType, params : ExecBodyParams) { pub async fn send_botmsg(&self, msginput: BotMsgType<'async_recursion>, params : ExecBodyParams) {
@ -72,18 +72,17 @@ impl Chat {
*/ */
botlog::trace(
botlog::trace(
format!("send_bot_msg params : {:?}",msginput).as_str(), format!("send_bot_msg params : {:?}",msginput).as_str(),
Some("chat.rs > send_botmsg ".to_string()), Some("chat.rs > send_botmsg ".to_string()),
Some(&params.msg), Some(&params.msg),
); );
Log::flush(); Log::flush();
let (channel_login,mut outmsg) = match msginput.clone() { let (channel_login,mut outmsg) = match msginput.clone() {
BotMsgType::SayInReplyTo(chnl, _, outmsg) => { BotMsgType::SayInReplyTo(msg, outmsg) => {
(chnl.0.to_lowercase(), // Desintation Channel (msg.channel_login.clone(),outmsg)
outmsg)
}, },
BotMsgType::Say(a,b ) => { BotMsgType::Say(a,b ) => {
(a.clone(),b.clone()) (a.clone(),b.clone())
@ -102,27 +101,26 @@ impl Chat {
Some(&params.msg), Some(&params.msg),
); );
let parent_module = params.get_parent_module().await; let parent_module = params.get_module().await;
let params_clone = params.clone(); let params_clone = params.clone();
let botclone = Arc::clone(&params_clone.bot); let botclone = Arc::clone(&params_clone.bot);
let botlock = botclone.read().await; let botlock = botclone.read().await;
let modmgr = Arc::clone(&botlock.botmodules); let modmgr = Arc::clone(&botlock.botmodules);
let modstatus = (*modmgr).modstatus( 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()) Channel(channel_login.clone())
).await; ).await;
if !params.bot.read().await.bot_channels.contains(&Channel(channel_login.clone())) { if !params.bot.read().await.bot_channels.contains(&Channel(channel_login.clone())) {
dbg!("ISSUE : NONJOINED CHANNEL",&params.bot.read().await.bot_channels,Channel(channel_login.clone()));
botlog::warn( botlog::warn(
&format!("A message attempted to send for a Non-Joined Channel : {}",channel_login.clone()), &format!("A message attempted to send for a Non-Joined Channel : {}",channel_login.clone()),
Some("Chat > send_botmsg".to_string()), Some("Chat > send_botmsg".to_string()),
None, None,
); );
if let BotMsgType::SayInReplyTo(_chnl,_msgid, _outmsg) = msginput { if let BotMsgType::SayInReplyTo(_prvmsg,_outmsg) = msginput {
self.send_botmsg(BotMsgType::Notif( self.send_botmsg(BotMsgType::Notif(
"uuh Bot can't send to a channel it isn't joined".to_string(), "uuh Bot can't send to a channel it isn't joined".to_string(),
@ -170,7 +168,7 @@ impl Chat {
match msginput { match msginput {
BotMsgType::Notif(_) => (), // Do nothing with Notif > We'll validate the user later to handle BotMsgType::Notif(_) => (), // Do nothing with Notif > We'll validate the user later to handle
BotMsgType::SayInReplyTo(_, _, _) | BotMsgType::Say(_,_) => { BotMsgType::SayInReplyTo(_, _) | BotMsgType::Say(_,_) => {
botlog::trace( botlog::trace(
"BEFORE potential Async recursion", "BEFORE potential Async recursion",
@ -183,7 +181,8 @@ impl Chat {
self.send_botmsg(BotMsgType::Notif( self.send_botmsg(BotMsgType::Notif(
format!("uuh {:?} is disabled on {} : {:?}", format!("uuh {:?} is disabled on {} : {:?}",
parent_module.clone().unwrap(), // parent_module.clone().unwrap(),
parent_module.clone(),
channel_login.clone(), channel_login.clone(),
lvl lvl
), ),
@ -235,7 +234,7 @@ impl Chat {
let botlock = botclone.read().await; let botlock = botclone.read().await;
let id = botlock.get_identity(); let id = botlock.get_identity();
let id = Arc::clone(&id); let id = Arc::clone(&id);
let idlock = id.read().await; let idlock = id.read().await; // <-- [x] 03.24 - seems to work
let user_roles = idlock.getspecialuserroles( let user_roles = idlock.getspecialuserroles(
params.get_sender(), params.get_sender(),
Some(Channel(channel_login.clone())) Some(Channel(channel_login.clone()))
@ -269,7 +268,7 @@ impl Chat {
return; return;
} }
}, },
BotMsgType::SayInReplyTo(_,_,_ ) | BotMsgType::Say(_,_) => { BotMsgType::SayInReplyTo(_,_ ) | BotMsgType::Say(_,_) => {
// If the BotMsg a Say/SayInReplyTo (from Developer or Chatter) , and the Sender does not have Specific Roles in the Source Channel Sent // If the BotMsg a Say/SayInReplyTo (from Developer or Chatter) , and the Sender does not have Specific Roles in the Source Channel Sent
self.send_botmsg(BotMsgType::Notif( self.send_botmsg(BotMsgType::Notif(
@ -323,6 +322,7 @@ impl Chat {
); );
let contextratelimiter = rllock let contextratelimiter = rllock
// .get_mut()
.get_mut(&Channel(channel_login.to_lowercase().clone())) .get_mut(&Channel(channel_login.to_lowercase().clone()))
.expect("ERROR: Issue with Rate limiters"); .expect("ERROR: Issue with Rate limiters");
@ -341,21 +341,13 @@ impl Chat {
} }
match msginput.clone() { match msginput.clone() {
BotMsgType::SayInReplyTo(chnl,msgid, _) => { BotMsgType::SayInReplyTo(msg, _) => {
self.client.say_in_reply_to(msg, outmsg).await.unwrap();
dbg!(chnl.clone(),msgid.clone(),outmsg.clone());
self.client.say_in_reply_to(&(
chnl.0,
msgid),
outmsg).await.unwrap();
}, },
BotMsgType::Say(a, _) => { BotMsgType::Say(a, _) => {
self.client.say(a, outmsg).await.unwrap(); self.client.say(a, outmsg).await.unwrap();
} }
BotMsgType::Notif(outmsg) => { BotMsgType::Notif(outmsg) => {
dbg!(params.msg.channel_login(),params.msg.message_id());
self.client.say_in_reply_to(&params.msg, outmsg).await.unwrap(); self.client.say_in_reply_to(&params.msg, outmsg).await.unwrap();
} }
} }
@ -367,11 +359,11 @@ impl Chat {
channel_login.clone(), "rate limit counter increase", contextratelimiter channel_login.clone(), "rate limit counter increase", contextratelimiter
); );
if let BotMsgType::SayInReplyTo(_,_,_ ) = msginput { if let BotMsgType::SayInReplyTo(msg,_ ) = msginput {
botlog::trace( botlog::trace(
logstr.as_str(), logstr.as_str(),
Some("Chat > send_botmsg".to_string()), Some("Chat > send_botmsg".to_string()),
None, Some(msg),
); );
} else { } else {
botlog::trace( botlog::trace(
@ -397,49 +389,73 @@ impl Chat {
} }
pub async fn say_in_reply(
&self,
destination_channel : Channel ,
outmsg: String ,
params : ExecBodyParams)
{
self.send_botmsg(BotMsgType::SayInReplyTo( // pub async fn say_in_reply_to(&self, msg: &PrivmsgMessage, outmsg: String) {
destination_channel, // #[async_recursion]
params.msg.message_id().to_string(), pub async fn say_in_reply_to(&self, msg: &PrivmsgMessage, outmsg: String , params : ExecBodyParams) {
outmsg) , params).await;
// let params_clone = params.clone();
// let botclone = Arc::clone(&params_clone.bot);
// let botlock = botclone.read().await;
// let id = botlock.get_identity();
// let id = Arc::clone(&id);
// // botlog::trace(
// // "ACQUIRING WRITE LOCK : ID",
// // Some("Chat > send_botmsg".to_string()),
// // Some(&params.msg),
// // );
// // Log::flush();
// botlog::trace(
// "ACQUIRING READ LOCK : ID",
// Some("Chat > send_botmsg".to_string()),
// Some(&params.msg),
// );
// Log::flush();
// // let idlock = id.write().await; // <-- [ ] 03.24 - This is definitely locking it
// let idlock = id.read().await; // <-- [ ] 03.24 - seems to work
// let a = idlock.getspecialuserroles(params.get_sender(), Some(Channel(msg.channel_login.clone()))).await;
// botlog::trace(
// format!("GETSPECIALUSERROLES RESULT : {:?}",a).as_str(),
// Some("Chat > send_botmsg".to_string()),
// Some(&params.msg),
// );
// Log::flush();
// // botlog::trace(
// // "ACQUIRED WRITE LOCK : ID",
// // Some("Chat > send_botmsg".to_string()),
// // Some(&params.msg),
// // );
// // Log::flush();
// botlog::trace(
// "ACQUIRED READ LOCK : ID",
// Some("Chat > send_botmsg".to_string()),
// Some(&params.msg),
// );
// Log::flush();
self.send_botmsg(BotMsgType::SayInReplyTo(msg, outmsg) , params).await;
} }
pub async fn say_in_reply_to( // pub async fn say(&self, channel_login: String, message: String) {
&self,
destination_channel : Channel ,
reply_message_id : String ,
outmsg: String ,
params : ExecBodyParams)
{
self.send_botmsg(BotMsgType::SayInReplyTo(
destination_channel,
reply_message_id,
outmsg) , params).await;
}
pub async fn say(&self, channel_login: String, message: String , params : ExecBodyParams) { pub async fn say(&self, channel_login: String, message: String , params : ExecBodyParams) {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say // more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
self.send_botmsg(BotMsgType::Say(channel_login.to_lowercase(), message), params).await; self.send_botmsg(BotMsgType::Say(channel_login.to_lowercase(), message), params).await;
} }
async fn _me(&self, _: String, _: String) { async fn _me(&self, _: String, _: String) {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say // more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say

View file

@ -8,7 +8,6 @@ use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tokio::time::{sleep,Duration};
use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::PrivmsgMessage;
use casual_logger::Log; use casual_logger::Log;
@ -18,24 +17,19 @@ use crate::core::bot_actions::ExecBodyParams;
use crate::core::botinstance::{Channel,ChangeResult}; use crate::core::botinstance::{Channel,ChangeResult};
use crate::core::botlog; use crate::core::botlog;
use crate::core::botmodules::{BotActionTrait, BotCommand, BotModule, ModulesManager}; use crate::core::botmodules::{BotActionTrait, BotCommand, BotModule, ModulesManager};
use dotenv::dotenv; use dotenv::dotenv;
use std::env; use std::env;
fn adminvector() -> Vec<String> { fn adminvector() -> Vec<String> {
dotenv().ok(); vec![String::from("ModulatingForce")]
let mut admins = Vec::new(); //vec![]
if let Ok(value) = env::var("bot_admins") {
for admin in value.split(',') {
admins.push(String::from(admin))
}
}
admins
} }
pub fn otherbots_vector() -> Vec<String> { pub fn otherbots_vector() -> Vec<String> {
// vec![String::from("ModulatingForce")]
// //vec![]
dotenv().ok(); dotenv().ok();
let mut other_bots = Vec::new(); let mut other_bots = Vec::new();
@ -68,10 +62,7 @@ pub async fn init(mgr: Arc<ModulesManager>) {
let tempb = BotCommand { let tempb = BotCommand {
module: BotModule(String::from("identity")), module: BotModule(String::from("identity")),
command: String::from("promote"), // command call name command: String::from("promote"), // command call name
alias: vec![ alias: vec![], // String of alternative names
"cucked".to_string(),
"cuck".to_string(),
], // String of alternative names
exec_body: actions_util::asyncbox(cmd_promote), exec_body: actions_util::asyncbox(cmd_promote),
help: String::from("promote"), help: String::from("promote"),
required_roles: vec![ required_roles: vec![
@ -176,7 +167,7 @@ async fn cmd_promote(params : ExecBodyParams) {
let mut argv = params.msg.message_text.split(' '); let mut argv = params.msg.message_text.split(' ');
let cmdname = argv.next(); // Skip the command name argv.next(); // Skip the command name
let arg1 = argv.next(); let arg1 = argv.next();
@ -214,7 +205,7 @@ async fn cmd_promote(params : ExecBodyParams) {
// [x] 1. Get trgusr (regardless of -admin flag) // [x] 1. Get trgusr (regardless of -admin flag)
// let targetusr = if arg1 == Some("-admin") { arg2 } else { arg1 }; // let targetusr = if arg1 == Some("-admin") { arg2 } else { arg1 };
let mut targetusr = if let targetusr = if
arg1 == Some("-admin") arg1 == Some("-admin")
|| arg1 == Some("-v") || arg1 == Some("-v")
|| arg1 == Some("-vip") || arg1 == Some("-vip")
@ -234,50 +225,6 @@ async fn cmd_promote(params : ExecBodyParams) {
} }
else { arg1 }; else { arg1 };
// [x] Check if target is an existing user
targetusr = if let Some(chkusr) = targetusr {
match twitch_irc::validate::validate_login(chkusr.to_lowercase().as_str()) {
Ok(_) => Some(chkusr),
Err(_) => None,
}
} else { None } ;
// [x] Check if cmd passed is cucked, then go through special cucked handling
if let Some(cmd_to_check) = cmdname {
if cmd_to_check.to_lowercase() == String::from(botlock.get_prefix()) + "cucked"
|| cmd_to_check.to_lowercase() == String::from(botlock.get_prefix()) + "cuck"
{
let idlock = botlock.botmgrs.identity.read().await;
let senderroles = idlock.getspecialuserroles(sendername.clone(), Some(Channel(targetchnl.to_lowercase()))).await;
if senderroles.contains(&UserRole::BotAdmin) && targetusr.is_none() {
targetusr = Some(sendername.as_str())
}
botlock.botmgrs.chat.send_botmsg(super::chat::BotMsgType::Notif(
"uuh ".to_string()
),
params.clone(),
).await;
sleep(Duration::from_secs_f64(2.0)).await;
botlock.botmgrs.chat.send_botmsg(super::chat::BotMsgType::Notif(
"... chatter getting cucked ...".to_string()
),
params.clone(),
).await;
sleep(Duration::from_secs_f64(1.0)).await;
}
}
// [x] 2. promote trguser // [x] 2. promote trguser
// [x] Get a required lock first // [x] Get a required lock first
@ -328,23 +275,14 @@ async fn cmd_promote(params : ExecBodyParams) {
// [x] 3. Output resulting change // [x] 3. Output resulting change
let outmsg = match rslt { let outmsg = match rslt {
ChangeResult::Success(rsltstr) => { ChangeResult::Success(a) => {
format!("o7 Successfully promoted {} : {}", format!("o7 Successfully promoted : {a}")
targetusr.unwrap(),
rsltstr
)
} }
ChangeResult::Failed(rsltstr) => { ChangeResult::Failed(a) => {
format!("PoroSad failed to promote {} : {}", format!("PoroSad failed to promote : {a}")
targetusr.unwrap(),
rsltstr
)
} }
ChangeResult::NoChange(rsltstr) => { ChangeResult::NoChange(a) => {
format!("uuh No Promotion Change {} : {}", format!("uuh No Promotion Change : {a}")
targetusr.unwrap(),
rsltstr
)
} }
}; };
@ -564,23 +502,14 @@ async fn cmd_demote(params : ExecBodyParams) {
*/ */
let outmsg = match rslt { let outmsg = match rslt {
ChangeResult::Success(rsltstr) => { ChangeResult::Success(a) => {
format!("o7 Successfully demoted {} : {}", format!("o7 Successfully demoted : {a}")
targetusr.unwrap(),
rsltstr
)
} }
ChangeResult::Failed(rsltstr) => { ChangeResult::Failed(a) => {
format!("PoroSad failed to demote {} : {}", format!("PoroSad failed to demote : {a}")
targetusr.unwrap(),
rsltstr
)
} }
ChangeResult::NoChange(rsltstr) => { ChangeResult::NoChange(a) => {
format!("uuh No Demotion Change {} : {}", format!("uuh No Demotion Change : {a}")
targetusr.unwrap(),
rsltstr
)
} }
}; };
@ -616,8 +545,6 @@ async fn getroles(params : ExecBodyParams) {
*/ */
let sendername = params.msg.clone().sender.name;
let mut argv = params.msg.message_text.split(' '); let mut argv = params.msg.message_text.split(' ');
@ -625,15 +552,18 @@ async fn getroles(params : ExecBodyParams) {
let arg1 = argv.next(); let arg1 = argv.next();
let mut targetuser = match arg1 { let targetuser = match arg1 {
// None => return, // exit if no arguments None => {
None => sendername.as_str(), // self-invoke in this case
Some(arg) => arg,
};
targetuser = match twitch_irc::validate::validate_login(targetuser.to_lowercase().as_str()) { botlog::debug(
Ok(_) => targetuser, "Exittingcmd getroles - Invalid arguments ",
Err(_) => sendername.as_str(), // self-invoke in this case Some("identity.rs > init > getroles()".to_string()),
Some(&params.msg),
);
return
}, // exit if no arguments
Some(arg) => arg,
}; };
let arg2 = argv.next(); let arg2 = argv.next();
@ -719,9 +649,7 @@ async fn getroles(params : ExecBodyParams) {
params.msg.channel_login.to_lowercase(), params.msg.channel_login.to_lowercase(),
))) || sproles.contains(&UserRole::BotAdmin) ))) || sproles.contains(&UserRole::BotAdmin)
{ {
// targetuser outmsg += format!("Target chatter's user roles are : {:?}", sproles).as_str();
// outmsg += format!("Target chatter's user roles are : {:?}", sproles).as_str();
outmsg += format!("{}'s user roles are : {:?}", targetuser, sproles).as_str();
} }
outmsg outmsg
} else if sproles.contains(&UserRole::Mod(Channel( } else if sproles.contains(&UserRole::Mod(Channel(
@ -730,10 +658,9 @@ async fn getroles(params : ExecBodyParams) {
params.msg.channel_login.to_lowercase(), params.msg.channel_login.to_lowercase(),
))) || sproles.contains(&UserRole::BotAdmin) ))) || sproles.contains(&UserRole::BotAdmin)
{ {
// format!("Target chatter's user roles are : {:?}", sproles) format!("Target chatter's user roles are : {:?}", sproles)
format!("{}'s user roles are : {:?}", targetuser, sproles)
} else { } else {
format!("{} has no special roles LULE ",targetuser) "Target chatter has no special roles LULE ".to_string()
}; };
botlog::debug( botlog::debug(
@ -771,7 +698,7 @@ pub enum Permissible {
type UserRolesDB = HashMap<String, Arc<RwLock<Vec<UserRole>>>>; type UserRolesDB = HashMap<String, Arc<RwLock<Vec<UserRole>>>>;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct IdentityManager { pub struct IdentityManager {
special_roles_users: Arc<RwLock<UserRolesDB>>, special_roles_users: Arc<RwLock<UserRolesDB>>,
} }
@ -1549,6 +1476,7 @@ impl IdentityManager {
return ChangeResult::NoChange("Already does not have VIP role".to_string()); return ChangeResult::NoChange("Already does not have VIP role".to_string());
} }
else { else {
// self.affirm_chatter_in_db(trgchatter.clone()).await;
self.remove_role(trgchatter.clone(), UserRole::VIP(channel.clone())).await; self.remove_role(trgchatter.clone(), UserRole::VIP(channel.clone())).await;

View file

@ -13,7 +13,6 @@ pub use crate::core::botmodules::ModulesManager;
// mod experiments; // mod experiments;
mod experimental; mod experimental;
mod thisguy;
// [ ] init() function that accepts bot instance - this is passed to init() on submodules // [ ] init() function that accepts bot instance - this is passed to init() on submodules
@ -22,6 +21,5 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// this is achieved by calling submodules that also have fn init() defined // this is achieved by calling submodules that also have fn init() defined
// experiments::init(mgr).await // experiments::init(mgr).await
experimental::init(mgr.clone()).await; experimental::init(mgr).await;
thisguy::init(&mgr).await;
} }

View file

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

View file

@ -15,7 +15,6 @@ const OF_CMD_CHANNEL:Channel = Channel(String::new());
use rand::Rng; use rand::Rng;
use twitch_irc::message::ReplyToMessage;
use std::sync::Arc; use std::sync::Arc;
use crate::core::bot_actions::ExecBodyParams; use crate::core::bot_actions::ExecBodyParams;
@ -96,65 +95,8 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// 2. Add the BotAction to ModulesManager // 2. Add the BotAction to ModulesManager
botc1.add_to_modmgr(Arc::clone(&mgr)).await; botc1.add_to_modmgr(Arc::clone(&mgr)).await;
let bc1 = BotCommand {
module: BotModule(String::from("experiments001")),
command: String::from("rp1"), // command call name
alias: vec![
String::from("rp2"),
String::from("rp3")], // String of alternative names
exec_body: actions_util::asyncbox(rp),
help: String::from("Test Command tester"),
required_roles: vec![
BotAdmin,
Mod(OF_CMD_CHANNEL),
],
};
bc1.add_core_to_modmgr(Arc::clone(&mgr)).await;
} }
async fn rp(params : ExecBodyParams)
{
//triggers if the message is a reply
if params.get_parent_reply().is_some(){
//getting the channel id where the message was sent
let channel_id = params.get_parent_reply().unwrap().channel_login;
//getting the first message id that was sent
let message_id = params.get_parent_reply().unwrap().message_id;
//just for testing purposes
//print!("{} , {}",channel_id, message_id);
//creating a tuple with the channel id and message id
let answear =
(
channel_id.clone(),
message_id.clone()
);
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(
//using the tuple as param to the message being replied
Channel(answear.0),
answear.1,
String::from("hey there"),
params.clone()
).await;
}
else
{
println!("no reply")
}
}
async fn good_girl(params : ExecBodyParams) { async fn good_girl(params : ExecBodyParams) {
// [ ] Uses gen_ratio() to output bool based on a ratio probability . // [ ] Uses gen_ratio() to output bool based on a ratio probability .
@ -163,7 +105,6 @@ async fn good_girl(params : ExecBodyParams) {
if params.msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase() if params.msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase()
|| params.msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase() || params.msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase()
|| params.msg.sender.name.to_lowercase() == "haruyuumei".to_lowercase()
{ {
botlog::debug( botlog::debug(
"Good Girl Detected > Pausechamp", "Good Girl Detected > Pausechamp",
@ -181,6 +122,8 @@ async fn good_girl(params : ExecBodyParams) {
); );
let bot = Arc::clone(&params.bot); let bot = Arc::clone(&params.bot);
let botlock = bot.read().await; let botlock = bot.read().await;
// uses chat.say_in_reply_to() for the bot controls for messages // uses chat.say_in_reply_to() for the bot controls for messages
@ -188,11 +131,12 @@ async fn good_girl(params : ExecBodyParams) {
.botmgrs .botmgrs
.chat .chat
.say_in_reply_to( .say_in_reply_to(
Channel(params.clone().msg.channel_login().to_string()), &params.msg,
params.clone().msg.message_id().to_string(),
String::from("GoodGirl xdd "), String::from("GoodGirl xdd "),
params.clone() params.clone()
).await; ).await;
} }
} }
} }
@ -226,8 +170,8 @@ async fn babygirl(params : ExecBodyParams) {
botlock botlock
.botmgrs .botmgrs
.chat .chat
.say_in_reply( .say_in_reply_to(
Channel(params.clone().msg.channel_login().to_string()), &params.msg,
String::from("16:13 notohh: cafdk"), String::from("16:13 notohh: cafdk"),
params.clone() params.clone()
).await; ).await;
@ -238,8 +182,8 @@ async fn babygirl(params : ExecBodyParams) {
botlock botlock
.botmgrs .botmgrs
.chat .chat
.say_in_reply( .say_in_reply_to(
Channel(params.clone().msg.channel_login().to_string()), &params.msg,
String::from("16:13 notohh: have fun eating princess"), String::from("16:13 notohh: have fun eating princess"),
params.clone() params.clone()
).await; ).await;
@ -250,8 +194,8 @@ async fn babygirl(params : ExecBodyParams) {
botlock botlock
.botmgrs .botmgrs
.chat .chat
.say_in_reply( .say_in_reply_to(
Channel(params.clone().msg.channel_login().to_string()), &params.msg,
String::from("16:13 notohh: baby girl"), String::from("16:13 notohh: baby girl"),
params.clone() params.clone()
).await; ).await;

View file

@ -17,7 +17,6 @@ const OF_CMD_CHANNEL:Channel = Channel(String::new());
use std::sync::Arc; use std::sync::Arc;
use chrono::{TimeZone,Local}; use chrono::{TimeZone,Local};
use twitch_irc::message::ReplyToMessage;
use crate::core::bot_actions::ExecBodyParams; use crate::core::bot_actions::ExecBodyParams;
@ -181,8 +180,7 @@ async fn sayout(params : ExecBodyParams) {
.botmgrs .botmgrs
.chat .chat
.say_in_reply_to( .say_in_reply_to(
Channel(params.clone().msg.channel_login().to_string()), &params.msg,
params.clone().msg.message_id().to_string(),
String::from("Invalid arguments"), String::from("Invalid arguments"),
params.clone() params.clone()
).await; ).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
}

View file

@ -1,62 +0,0 @@
use crate::core::bot_actions::*;
use crate::core::botinstance::Channel;
use crate::core::botlog;
use crate::core::botmodules::{BotActionTrait, BotCommand, BotModule, ModulesManager};
use crate::core::identity::UserRole::*;
use rand::Rng;
use twitch_irc::message::ReplyToMessage;
use std::sync::Arc;
use tokio::time::{sleep, Duration};
const OF_CMD_CHANNEL:Channel = Channel(String::new());
async fn tsg(params: ExecBodyParams) {
let phrases: [String; 6] = [
"Clueless ".to_string(),
"ICANT This guy....".to_string(),
"He is right tho".to_string(),
"KEKW true!".to_string(),
"OMEGALUL wth man...".to_string(),
"PepeLaugh El no sabe".to_string(),
];
let r = rand::thread_rng().gen_range(0..=4);
let a = phrases[r].clone();
botlog::debug(
"This guy works!",
Some("modules > thisguy()".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(
Channel(params.clone().msg.channel_login().to_string()),
a,
params.clone()
)
.await;
sleep(Duration::from_secs_f64(0.5)).await;
}
pub async fn init(mgr: &Arc<ModulesManager>) {
BotCommand {
module: BotModule(String::from("thisguy")),
command: String::from("thisguy"),
alias: vec![String::from("Thisguy")],
exec_body: actions_util::asyncbox(tsg),
help: String::from("test"),
required_roles: vec![
BotAdmin,
Mod(OF_CMD_CHANNEL),
Broadcaster
],
}
.add_to_modmgr(Arc::clone(mgr))
.await;
}

View file

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

View file

@ -1,3 +0,0 @@
disabled = []
nix_version = '2.4'
ignore = ['.direnv']