Compare commits

..

48 commits

Author SHA1 Message Date
e1021c269d Merge pull request 'Custom this BotCommand' (#48) from say-this-guy into master
All checks were successful
ci/woodpecker/push/cargo-checks Pipeline was successful
Reviewed-on: #48
2024-04-09 15:49:58 -04:00
e41f7b0524 warning changed
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-09 16:44:58 -03:00
55aeaa7fc1 changed reply message on thisguy
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-09 16:22:11 -03:00
93768de4dc Merge branch 'master' into say-this-guy
Some checks failed
ci/woodpecker/pr/cargo-checks Pipeline failed
2024-04-09 14:45:27 -04:00
b8c33d2339 Merge pull request 'Bot admins in env file' (#22) from haru_test into master
All checks were successful
ci/woodpecker/push/cargo-checks Pipeline was successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
Reviewed-on: #22
2024-04-09 13:48:55 -04:00
7f49693a94 clippy
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-09 13:47:06 -04:00
b949c70fa8 Merge branch 'master' into haru_test
Some checks failed
ci/woodpecker/pr/cargo-checks Pipeline failed
2024-04-09 13:39:57 -04:00
018e8306e4 Merge pull request 'Cucked alt for promote' (#75) from cucked into master
All checks were successful
ci/woodpecker/push/cargo-checks Pipeline was successful
Reviewed-on: #75
2024-04-09 13:39:35 -04:00
764a912122 Merge branch 'master' into cucked
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-09 13:37:20 -04:00
f86f48178d
ci: fix branch naming 2024-04-09 13:27:25 -04:00
35247ac5ba Merge branch 'master' into haru_test 2024-04-09 12:56:21 -04:00
4b849cfed6 comments cleanup 2024-04-09 12:51:53 -04:00
dfb37717b3 clippy 2024-04-09 12:36:46 -04:00
16c6b0eebb self-invoking identity methods 2024-04-09 12:32:02 -04:00
74d938751f cucked promote alt 2024-04-09 10:00:01 -04:00
2bf0e0e738 Merge pull request 'flake.lock: update' (#74) from flake-lock-update into master
Reviewed-on: #74
2024-04-09 08:47:36 -04:00
dd2670c766 flake.lock: update 2024-04-09 01:02:55 +00:00
200d2a86d3
nix: add pre-commit-hooks
Some checks failed
ci/woodpecker/pr/cargo-checks Pipeline was successful
ci/woodpecker/cron/flake-update Pipeline failed
2024-04-08 18:45:30 -04:00
926f213923 Merge pull request 'nix-rewrite' (#73) from nix-rewrite into main
good enough for now
2024-04-08 18:05:05 -04:00
604c26647e
nix: read version from cargo.toml
fix spelling error
2024-04-08 18:00:13 -04:00
087e76388d
nix: init rewrite
first pass of my nix rewrite

nix: rename module

nix: update module

nix: update module

nix: update module

nix: refactor

nix: remove package option

nix: fix serviceConfig

nix: add package back to module

nix: update module

nix: update module

nix: update module

nix: update module

remove default.nix
2024-04-08 17:46:08 -04:00
91d62bf0ff Merge pull request 'flake.lock: update' (#72) from flake-lock-update into main
Reviewed-on: #72
2024-04-08 10:00:55 -04:00
78634f3ffe flake.lock: update 2024-04-08 13:59:49 +00:00
e67c8582c1 Merge pull request 'Retrieve Reply information from ExecBodyParams' (#52) from retrieve-reply-details into main
Some checks failed
ci/woodpecker/push/cargo-checks Pipeline was successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
ci/woodpecker/cron/flake-update Pipeline failed
Reviewed-on: #52
2024-04-03 19:51:04 -04:00
fde96ac895 clippy cleanup
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-03 19:45:18 -04:00
8adab21761 comments cleanup 2024-04-03 19:40:18 -04:00
62b6399e69 chat.say_in_reply 2024-04-03 19:04:21 -04:00
6867cc7af8 adj sayinreply to use channel & msgid
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-03 18:33:15 -04:00
7199703766 reply-to-parent working
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-03 15:26:04 -03:00
345cf97922 enh chat.say_in_reply
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-04-03 13:47:59 -04:00
57725ee840 Merge branch 'retrieve-reply-details' of https://git.flake.sh/modulatingforce/forcebot_rs into retrieve-reply-details 2024-04-03 12:54:52 -04:00
b43d6f8159 Rewriten the code
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
working with command instead listener
2024-04-03 10:26:14 -03:00
7e04699b67 making changes 2024-04-03 10:25:24 -03:00
d972eb7726 Thisguy working partially
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
not fully implemented reply to the reply
2024-04-02 20:11:01 -03:00
72d4cf6d70 Small changes on thisguy.rs 2024-03-31 14:37:50 -03:00
a048003e93 oops forgot to add the use statement
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-30 01:47:32 +01:00
f2893791b9 add reply parent functionality
Some checks failed
ci/woodpecker/pr/cargo-checks Pipeline failed
2024-03-30 01:42:06 +01:00
a6ae5b3c47 Merge branch 'say-this-guy' of ssh://git.flake.sh:2222/modulatingforce/forcebot_rs into say-this-guy
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-28 19:35:25 -03:00
fda7afb191 custom module working 2024-03-28 19:33:06 -03:00
40aa836b24 Merge branch 'main' of https://git.flake.sh/modulatingforce/forcebot_rs 2024-03-28 17:56:56 -04:00
3ec51d66e5 Merge branch 'main' into say-this-guy
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-28 17:55:00 -04:00
f8de595290 custom module update
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-28 18:50:28 -03:00
d7634663fe Merge pull request 'Fix Logs Flushing only every 60 days' (#50) from fix-log-purge-timer into main
All checks were successful
ci/woodpecker/push/cargo-checks Pipeline was successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
Reviewed-on: #50
2024-03-28 16:05:44 -04:00
mzntori
6a026b7791 reduce seconds to minutes
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-28 20:12:51 +01:00
95a9962721 new module test
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
trying to add a new module
2024-03-28 15:38:49 -03:00
f7881fa07d adj adminvector()
All checks were successful
ci/woodpecker/pr/cargo-checks Pipeline was successful
2024-03-17 19:29:22 -04:00
5fb373c522 bot admins change
Some checks failed
ci/woodpecker/pr/cargo-checks Pipeline failed
2024-03-17 19:12:33 -03:00
fb1617d6d9 minor spelling mistake 2024-03-17 18:40:12 -03:00
21 changed files with 666 additions and 1853 deletions

8
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

View file

@ -6,11 +6,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1706768574,
"narHash": "sha256-4o6TMpzBHO659EiJTzd/EGQGUDdbgwKwhqf3u6b23U8=",
"lastModified": 1711952616,
"narHash": "sha256-WJvDdOph001fA1Ap3AyaQtz/afJAe7meSG5uJAdSE+A=",
"owner": "nix-community",
"repo": "fenix",
"rev": "668102037129923cd0fc239d864fce71eabdc6a3",
"rev": "209048d7c545905c470f6f8c05c5061f391031a8",
"type": "github"
},
"original": {
@ -20,13 +20,68 @@
"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": {
"locked": {
"lastModified": 1706550542,
"narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=",
"lastModified": 1711703276,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "97b17f32362e475016f942bbdfda4a4a72a8a652",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
"type": "github"
},
"original": {
@ -36,36 +91,92 @@
"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": {
"locked": {
"lastModified": 1708296515,
"narHash": "sha256-FyF489fYNAUy7b6dkYV6rGPyzp+4tThhr80KNAaF/yY=",
"owner": "nixos",
"lastModified": 1712573573,
"narHash": "sha256-xxon7WwNm4/EadMKg1eF40/5s0O78nXUy2ILZt6vT7E=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b98a4e1746acceb92c509bc496ef3d0e5ad8d4aa",
"rev": "0d28066770464d19d637f6e8e42e8688420b6ac6",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"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": {
"inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs_2"
"nixpkgs": "nixpkgs_2",
"pre-commit-hooks": "pre-commit-hooks",
"systems": "systems_2"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1706735270,
"narHash": "sha256-IJk+UitcJsxzMQWm9pa1ZbJBriQ4ginXOlPyVq+Cu40=",
"lastModified": 1711885694,
"narHash": "sha256-dyezzeSbWMpflma+E9USmvSxuLgGcNGcGw3cOnX36ko=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "42cb1a2bd79af321b0cc503d2960b73f34e2f92b",
"rev": "e4a405f877efd820bef9c0e77a02494e47c17512",
"type": "github"
},
"original": {
@ -74,6 +185,36 @@
"repo": "rust-analyzer",
"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",

View file

@ -1,39 +1,80 @@
{
description = "forcebot_rs flake";
description = "A basic flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
systems.url = "github:nix-systems/default-linux";
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
fenix.url = "github:nix-community/fenix/monthly";
pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
};
outputs = {
self,
systems,
nixpkgs,
fenix,
...
} @ inputs: let
system = "x86_64-linux";
overlays = [fenix.overlays.default];
pkgs = import nixpkgs {
inherit system overlays;
};
pre-commit-hooks,
}: let
eachSystem = nixpkgs.lib.genAttrs (import systems);
pkgsFor = eachSystem (system:
import nixpkgs {
localSystem.system = system;
overlays = [fenix.overlays.default];
});
in {
devShells.${system}.default = pkgs.mkShell {
name = "forcebot_rs-devenv";
nativeBuildInputs = [pkgs.pkg-config];
buildInputs = with pkgs; [openssl libiconv];
packages = with pkgs; [
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";
};
packages = eachSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
inherit ((builtins.fromTOML (builtins.readFile ./Cargo.toml)).package) version;
in {
default = pkgsFor.${system}.rustPlatform.buildRustPackage {
pname = "forcebot_rs";
version = "${version}";
src = self;
cargoLock = {
lockFile = ./Cargo.lock;
};
nativeBuildInputs = with pkgs; [pkg-config];
buildInputs = with pkgs; [openssl];
doCheck = false;
};
});
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;};
};
}

30
nix/module.nix Normal file
View file

@ -0,0 +1,30 @@
{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,87 +1,43 @@
use twitch_irc::message::PrivmsgMessage;
use twitch_irc::message::{PrivmsgMessage, TwitchUserBasics};
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::core::botinstance::BotInstance;
use super::{botinstance::Channel, botmodules::{BotAction, BotModule, Routine}, identity::ChatBadge};
use super::{botmodules::{BotAction, BotModule}, identity::ChatBadge};
pub type BotAR = Arc<RwLock<BotInstance>>;
pub type ActAR = Arc<RwLock<BotAction>>;
pub type RoutineAR = Arc<RwLock<Routine>>;
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct ExecBodyParams {
pub bot : BotAR,
pub msg : PrivmsgMessage,
pub parent_act : Option<ActAR> ,
pub curr_act : ActAR ,
pub parent_act : ActAR ,
}
impl ExecBodyParams {
// pub async fn get_parent_module(&self) -> Option<BotModule> {
// pub async fn get_parent_module(&self) -> BotModule {
pub async fn get_module(&self) -> BotModule {
pub async fn get_parent_module(&self) -> Option<BotModule> {
let curr_act = Arc::clone(&self.curr_act);
let parent_act_lock = curr_act.read().await;
let parent_act = Arc::clone(&self.parent_act);
let parent_act_lock = parent_act.read().await;
let act = &(*parent_act_lock);
match act {
BotAction::C(c) => {
// let temp = c.module.clone();
// Some(temp)
c.module.clone()
let temp = c.module.clone();
Some(temp)
},
BotAction::L(l) => {
// let temp = l.module.clone();
// Some(temp)
l.module.clone()
let temp = l.module.clone();
Some(temp)
},
BotAction::R(r) => {
// let temp = r.module.clone();
// Some(temp)
r.read().await.module.clone()
}
// _ => None
_ => 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()
}
@ -100,9 +56,68 @@ impl ExecBodyParams {
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 {

View file

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

View file

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

View file

@ -25,26 +25,14 @@ const OF_CMD_CHANNEL:Channel = Channel(String::new());
use core::panic;
use std::borrow::Borrow;
use std::borrow::BorrowMut;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::ops::DerefMut;
use std::sync::Arc;
use casual_logger::Log;
use chrono::DateTime;
// use chrono::Duration;
use chrono::Local;
use chrono::OutOfRangeError;
use tokio::sync::RwLock;
use async_trait::async_trait;
use tokio::task::JoinHandle;
use tokio::time::Instant;
use tokio::time::{sleep, Duration};
use crate::core::bot_actions::actions_util;
use crate::core::bot_actions::ExecBodyParams;
@ -56,8 +44,6 @@ use crate::core::bot_actions;
use std::hash::{Hash, Hasher};
use super::bot_actions::ActAR;
use super::bot_actions::RoutineAR;
use super::identity::ChatBadge;
@ -83,6 +69,7 @@ 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
@ -471,11 +458,10 @@ pub enum StatusType {
Disabled(StatusLvl),
}
#[derive(Debug)]
pub enum BotAction {
C(BotCommand),
L(Listener),
R(Arc<RwLock<Routine>>),
R(Routine),
}
impl BotAction {
@ -534,12 +520,6 @@ impl BotActionTrait for BotCommand {
}
}
impl Debug for BotCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} {:?} {:?} {:?} {:?}", self.module, self.command, self.alias, self.help, self.required_roles)
}
}
pub struct Listener {
pub module: BotModule,
pub name: String,
@ -587,913 +567,12 @@ impl BotActionTrait for Listener {
}
}
impl Debug for Listener {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} {:?} {:?}", self.module, self.name, self.help)
}
}
// #[derive(Debug, PartialEq, Eq, Hash, Clone)]
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum RoutineAttr {
DelayedStart,
ScheduledStart(DateTime<Local>), // Scheduled Date (if any) after which, if not started, may trigger
LoopDuration(Duration), // How long to wait between iterations
LoopInfinitely,
RunOnce,
MaxTimeThreshold(DateTime<Local>), // DateTime after which, it will abort/cancel or stop
MaxIterations(i64),
}
/*
a Routine can be given different combinations of the above, but business logic may validate
these at Routine construction
For example, a Routine could have the following characteristics
- DelayedStart (so it skips the first iteration)
- ScheduledStart(DateTime<Local>) - With a Start Date-Time for the first iteration to trigger
- LoopDuration(Duration) - How long to wait between iterations
The above without any other thresholds would loop infinitely with the above characteristics
Another example,
- LoopDuration(Duration) - How long to wait between iterations
- MaxTimeThreshold(DateTime<Local>)
- MaxIterations(i64)
The above has thresholds , so if either are reached, it would abort/cancel or stop . Since there is no
ScheduledStart, the routine would have to be started manually elsewhere
Another example ,
- (no RoutineAttr)
The above would only run once, and only when the Start() is called
*/
// For some key statuses and in particular Stopping to Gracefully stop
#[derive(Debug)]
pub 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())
}
}
pub struct Routine {}
type StatusdbEntry = (ModGroup, Vec<StatusType>);
type ModuleActions = Vec<Arc<RwLock<BotAction>>>;
#[derive(Debug)]
pub struct ModulesManager {
statusdb: Arc<RwLock<HashMap<BotModule, StatusdbEntry>>>,
pub botactions: Arc<RwLock<HashMap<BotModule, ModuleActions>>>,

View file

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

View file

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

View file

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

View file

@ -12,7 +12,6 @@ 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
@ -22,5 +21,4 @@ pub async fn init(mgr: Arc<ModulesManager>) {
experiment001::init(Arc::clone(&mgr)).await;
experiment002::init(Arc::clone(&mgr)).await;
experiment003::init(Arc::clone(&mgr)).await;
}

View file

@ -15,6 +15,7 @@ const OF_CMD_CHANNEL:Channel = Channel(String::new());
use rand::Rng;
use twitch_irc::message::ReplyToMessage;
use std::sync::Arc;
use crate::core::bot_actions::ExecBodyParams;
@ -95,8 +96,65 @@ pub async fn init(mgr: Arc<ModulesManager>) {
// 2. Add the BotAction to ModulesManager
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) {
// [ ] Uses gen_ratio() to output bool based on a ratio probability .
@ -105,6 +163,7 @@ async fn good_girl(params : ExecBodyParams) {
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() == "haruyuumei".to_lowercase()
{
botlog::debug(
"Good Girl Detected > Pausechamp",
@ -122,8 +181,6 @@ async fn good_girl(params : ExecBodyParams) {
);
let bot = Arc::clone(&params.bot);
let botlock = bot.read().await;
// uses chat.say_in_reply_to() for the bot controls for messages
@ -131,12 +188,11 @@ async fn good_girl(params : ExecBodyParams) {
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
Channel(params.clone().msg.channel_login().to_string()),
params.clone().msg.message_id().to_string(),
String::from("GoodGirl xdd "),
params.clone()
).await;
}
}
}
@ -170,8 +226,8 @@ async fn babygirl(params : ExecBodyParams) {
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
.say_in_reply(
Channel(params.clone().msg.channel_login().to_string()),
String::from("16:13 notohh: cafdk"),
params.clone()
).await;
@ -182,8 +238,8 @@ async fn babygirl(params : ExecBodyParams) {
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
.say_in_reply(
Channel(params.clone().msg.channel_login().to_string()),
String::from("16:13 notohh: have fun eating princess"),
params.clone()
).await;
@ -194,8 +250,8 @@ async fn babygirl(params : ExecBodyParams) {
botlock
.botmgrs
.chat
.say_in_reply_to(
&params.msg,
.say_in_reply(
Channel(params.clone().msg.channel_login().to_string()),
String::from("16:13 notohh: baby girl"),
params.clone()
).await;

View file

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

View file

@ -1,666 +0,0 @@
/*
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
}

62
src/custom/thisguy.rs Normal file
View file

@ -0,0 +1,62 @@
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 custom;
pub mod custom;

3
statix.toml Normal file
View file

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