Merge pull request 'dev into main' (#15) from dev into main

Reviewed-on: #15
This commit is contained in:
modulatingforce 2024-02-25 11:31:10 -05:00
commit abae060f45
12 changed files with 4244 additions and 453 deletions

11
.gitignore vendored
View file

@ -14,5 +14,12 @@ target/
/target /target
# env # env
.envrc *.envrc
.env *.env
# log
*.log
# debug
.vscode/

233
Cargo.lock generated
View file

@ -17,6 +17,30 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.77" version = "0.1.77"
@ -61,12 +85,29 @@ version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.5.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "casual_logger"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77d02b2f025328b7f0a232815634c840e206350cf7c8e8fb36ab7095e264f59c"
dependencies = [
"chrono",
"lazy_static",
"regex",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.83"
@ -88,7 +129,12 @@ version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits", "num-traits",
"wasm-bindgen",
"windows-targets 0.48.5",
] ]
[[package]] [[package]]
@ -151,7 +197,10 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
name = "forcebot_rs" name = "forcebot_rs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait",
"casual_logger",
"dotenv", "dotenv",
"futures",
"rand", "rand",
"tokio", "tokio",
"twitch-irc", "twitch-irc",
@ -198,7 +247,67 @@ checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task", "futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
@ -227,6 +336,38 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "js-sys"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -487,6 +628,35 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "regex"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -756,6 +926,69 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"

View file

@ -10,3 +10,11 @@ dotenv = "0.15.0"
tokio = { version = "1.33.0", features = ["full"] } tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1" twitch-irc = "5.0.1"
rand = { version = "0.8.5", features = [] } rand = { version = "0.8.5", features = [] }
futures = "0.3"
async-trait = "0.1.77"
casual_logger = "0.6.5"
[lib]
name = "botLib"
path = "src/lib.rs"

View file

@ -1,6 +1,7 @@
pub mod botinstance; pub mod botinstance;
pub mod ratelimiter;
pub mod botmodules; pub mod botmodules;
pub mod identity;
pub mod ratelimiter;
// pub fn init() -> () // pub fn init() -> ()
// { // {

View file

@ -1,157 +1,406 @@
// use futures::lock::Mutex;
use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::RwLock;
use twitch_irc::login::StaticLoginCredentials; use twitch_irc::login::StaticLoginCredentials;
use twitch_irc::ClientConfig;
use twitch_irc::SecureTCPTransport;
use twitch_irc::TwitchIRCClient;
use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::PrivmsgMessage;
use twitch_irc::message::ServerMessage; use twitch_irc::message::ServerMessage;
use twitch_irc::transport::tcp::TCPTransport; use twitch_irc::transport::tcp::TCPTransport;
use twitch_irc::transport::tcp::TLS; use twitch_irc::transport::tcp::TLS;
use std::env; use twitch_irc::ClientConfig;
use twitch_irc::SecureTCPTransport;
use twitch_irc::TwitchIRCClient;
// use std::borrow::Borrow;
use dotenv::dotenv; use dotenv::dotenv;
use std::borrow::BorrowMut;
use std::boxed;
use std::cell::Ref;
use std::env;
use std::collections::HashMap; use std::collections::HashMap;
use rand::Rng; use rand::Rng;
// Important to use tokios Mutex here since std Mutex doesn't work with async functions
use tokio::sync::Mutex;
use crate::core::ratelimiter::RateLimiter;
use crate::core::botmodules::BotAction;
use crate::core::ratelimiter; use crate::core::ratelimiter;
use crate::core::ratelimiter::RateLimiter;
use crate::core::botmodules; use crate::core::botmodules;
use crate::core::botmodules::ModulesManager; use crate::core::botmodules::ModulesManager;
use crate::core::identity::{ChangeResult, IdentityManager, Permissible};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
// use futures::lock::Mutex;
use std::pin::Pin;
//use std::borrow::Borrow;
use core::borrow::Borrow;
// pub type BotAR = Arc<RwLock<BotInstance>>;
use super::botmodules::bot_actions::actions_util::BotAR;
use casual_logger::{Level, Log};
pub mod botlog {
/*
Module intends to add some layers to logging with the module user only requiring to pass :
- String Log message
- Option<String> - Code_Module
- Option<PrivmsgMessage> - this is used to parse out Chatter & Channel into the logs
*/
use casual_logger::{Level, Log};
use twitch_irc::message::PrivmsgMessage;
// trace, debug, info, notice, warn, error, fatal
/*
in main : Log::debug("Checking bot actions", Some("main()".to_string()), None);
in log :
[blalba@timestmp]
debug = "Checking bot actions",
*/
pub fn trace(
in_msg: &str,
in_module: Option<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),
};
Log::trace_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
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),
};
Log::debug_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
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),
};
Log::info_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
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),
};
Log::notice_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
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),
};
Log::warn_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
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),
};
Log::error_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
}
pub fn fatal<'a>(
in_msg: &'a str,
in_module: Option<String>,
in_prvmsg: Option<&PrivmsgMessage>,
) -> &'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),
};
Log::fatal_t(
in_msg,
casual_logger::Table::default() //
.str("Channel", &format!("{:?}", chnl))
.str("Chatter", &format!("{:?}", chatter))
.str("Code_Module", &format!("{:?}", in_module)),
);
in_msg
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ChType { pub enum ChType {
Channel(String), Channel(String),
} }
pub use ChType::Channel; pub use ChType::Channel;
pub enum ModType {
BotModule(String),
}
pub use ModType::BotModule;
#[derive(Clone)] #[derive(Clone)]
pub struct Chat { pub struct Chat {
pub ratelimiters : HashMap<ChType,RateLimiter>, // used to limit messages sent per channel pub ratelimiters: Arc<Mutex<HashMap<ChType, RateLimiter>>>, // used to limit messages sent per channel
pub client : TwitchIRCClient<TCPTransport<TLS>,StaticLoginCredentials>, pub client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
} }
impl Chat { impl Chat {
pub fn init(
ratelimiters: HashMap<ChType, RateLimiter>,
client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
) -> Chat {
Chat {
ratelimiters: Arc::new(Mutex::new(ratelimiters)),
client: client,
}
}
pub async fn init_channel(&mut self, chnl: ChType) -> () {
pub fn init_channel(&mut self, chnl:ChType) -> () {
let n = RateLimiter::new(); let n = RateLimiter::new();
self.ratelimiters.insert(chnl,n); self.ratelimiters.lock().await.insert(chnl, n);
} }
// pub async fn say_in_reply_to(&mut self, msg:& PrivmsgMessage , mut outmsg:String) -> () {
pub async fn say_in_reply_to(&self, msg: &PrivmsgMessage, mut outmsg: String) -> () {
pub async fn say_in_reply_to(&mut self, msg:& PrivmsgMessage , mut outmsg:String) -> () {
/* /*
formats message before sending to TwitchIRC formats message before sending to TwitchIRC
- [x] Custom String Formatting (e.g., adding random black spaces) - [x] Custom String Formatting (e.g., adding random black spaces)
- [x] Ratelimiter Handling - [x] Ratelimiter Handling
- [ ] Checkf if BotActions is Enabled & Caller is Allowed to Run - [ ] Checkf if BotActions is Enabled & Caller is Allowed to Run
*/ */
// self.client.say_in_reply_to(msg,outmsg).await.unwrap();
// // let contextratelimiter = ratelimiters.get_mut(&msg.channel_login).expect("ERROR: Issue with Rate limiters"); let a = Arc::clone(&self.ratelimiters);
let contextratelimiter = self.ratelimiters let mut a = a.lock().await;
let contextratelimiter = a
// .get_mut()
.get_mut(&Channel(String::from(&msg.channel_login))) .get_mut(&Channel(String::from(&msg.channel_login)))
.expect("ERROR: Issue with Rate limiters"); .expect("ERROR: Issue with Rate limiters");
// let contextratelimiter = self.ratelimiters.get(&msg.channel_login).expect("ERROR: Issue with Rate limiters");
match contextratelimiter.check_limiter() { match contextratelimiter.check_limiter() {
ratelimiter::LimiterResp::Allow => { ratelimiter::LimiterResp::Allow => {
let maxblanks = rand::thread_rng().gen_range(1..=20); let maxblanks = rand::thread_rng().gen_range(1..=20);
//let mut outmsg = "GotTrolled ".to_owned();
// let mut outmsg = "annytfLurk ".to_owned();
for _i in 1..maxblanks { for _i in 1..maxblanks {
let blankspace: &str = "󠀀"; let blankspace: &str = "󠀀";
outmsg.push_str(blankspace); outmsg.push_str(blankspace);
} }
// client.say_in_reply_to(&msg,outmsg).await.unwrap(); self.client.say_in_reply_to(msg, outmsg).await.unwrap();
self.client.say_in_reply_to(msg,outmsg).await.unwrap(); // println!("(#{}) > {}", msg.channel_login, "rate limit counter increase");
println!("(#{}) > {}", msg.channel_login, "rate limit counter increase"); // Log::trace(&format!("(#{}) > {}", msg.channel_login, "rate limit counter increase"));
botlog::trace(
&format!(
"(#{}) > {}",
msg.channel_login, "rate limit counter increase"
),
Some("Chat > say_in_reply_to".to_string()),
Some(&msg),
);
contextratelimiter.increment_counter(); contextratelimiter.increment_counter();
println!("{:?}",self.ratelimiters); // println!("{:?}",self.ratelimiters);
}, // Log::trace(&format!("{:?}",self.ratelimiters));
botlog::trace(
&format!("{:?}", self.ratelimiters),
Some("Chat > say_in_reply_to".to_string()),
Some(&msg),
);
}
ratelimiter::LimiterResp::Skip => { ratelimiter::LimiterResp::Skip => {
(); // do nothing otherwise (); // do nothing otherwise
} }
}
} Log::flush();
} }
async fn say(&self, _:String, _:String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.say(msg,outmsg).await.unwrap();
}
async fn me(&self, _:String, _:String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
async fn me_in_reply_to(&self, _:String, _:String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
async fn say(&self, _: String, _: String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.say(msg,outmsg).await.unwrap();
}
async fn me(&self, _: String, _: String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
async fn me_in_reply_to(&self, _: String, _: String) -> () {
// more info https://docs.rs/twitch-irc/latest/twitch_irc/client/struct.TwitchIRCClient.html#method.say
// self.client.me(msg,outmsg).await.unwrap();
}
} }
#[derive(Clone)]
pub struct BotManagers {
pub struct BotInstance // pub botmodules : ModulesManager,
{ pub identity: Arc<RwLock<IdentityManager>>,
prefix : char, pub chat: Chat,
bot_channel : ChType,
pub incoming_messages : UnboundedReceiver<ServerMessage>,
pub chat : Chat,
pub botmodules : ModulesManager,
twitch_oauth : String,
pub bot_channels : Vec<ChType>,
} }
impl BotManagers {
pub fn init(
ratelimiters: HashMap<ChType, RateLimiter>,
client: TwitchIRCClient<TCPTransport<TLS>, StaticLoginCredentials>,
) -> BotManagers {
BotManagers {
identity: Arc::new(RwLock::new(IdentityManager::init())),
chat: Chat::init(ratelimiters, client),
}
}
pub fn rIdentity(self) -> Arc<RwLock<IdentityManager>> {
self.identity
}
}
impl BotInstance pub struct ArcBox<T: Clone>(pub Arc<Mutex<T>>);
{
impl<T: Clone> ArcBox<T> {
pub fn inst(&self) -> &Mutex<T> {
&self.0
}
}
pub fn init() -> BotInstance //#[derive(Clone)]
{ // #[derive(Copy)] // <-- Cannot be derived
pub struct BotInstance {
pub prefix: char,
pub bot_channel: ChType,
pub incoming_messages: Arc<RwLock<UnboundedReceiver<ServerMessage>>>,
pub botmodules: Arc<ModulesManager>,
pub twitch_oauth: String,
pub bot_channels: Vec<ChType>,
pub botmgrs: BotManagers,
//modesmgr : ModesManager, // Silent/Quiet , uwu , frisky/horny
}
impl BotInstance {
pub async fn init() -> BotInstance {
dotenv().ok(); dotenv().ok();
let login_name = env::var("login_name").unwrap().to_owned(); let login_name = env::var("login_name").unwrap().to_owned();
let oauth_token = env::var("access_token").unwrap().to_owned(); let oauth_token = env::var("access_token").unwrap().to_owned();
let prefix = env::var("prefix").unwrap().to_owned().chars().next().expect("ERROR : when defining prefix"); let prefix = env::var("prefix")
.unwrap()
.to_owned()
.chars()
.next()
.expect("ERROR : when defining prefix");
/* /*
Vector of channels to join Vector of channels to join
@ -164,12 +413,13 @@ impl BotInstance
botchannels.push(Channel(String::from(chnl))); botchannels.push(Channel(String::from(chnl)));
} }
let config = ClientConfig::new_simple( let config = ClientConfig::new_simple(StaticLoginCredentials::new(
StaticLoginCredentials::new(login_name.to_owned(), Some(oauth_token.to_owned())) login_name.to_owned(),
); Some(oauth_token.to_owned()),
));
let (incoming_messages, client) = let (incoming_messages, client) =
TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config); TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config);
// hashmap for channels and their associated ratelimiters // hashmap for channels and their associated ratelimiters
let mut ratelimiters = HashMap::new(); let mut ratelimiters = HashMap::new();
@ -179,148 +429,390 @@ impl BotInstance
client.join(chnl.to_owned()).unwrap(); client.join(chnl.to_owned()).unwrap();
// ratelimiters are a hashmap of channel and a corresponding rate limiter // ratelimiters are a hashmap of channel and a corresponding rate limiter
let n = RateLimiter::new(); let n = RateLimiter::new();
ratelimiters.insert(Channel(String::from(chnl)),n); ratelimiters.insert(Channel(String::from(chnl)), n);
//self.chat.ratelimiters.insert(Channel(String::from(chnl)),n); //self.chat.ratelimiters.insert(Channel(String::from(chnl)),n);
} }
// let b = BotInstance {
// prefix : prefix,
// bot_channel : Channel(login_name) ,
// incoming_messages : Arc::new(RwLock::new(incoming_messages)),
// botmodules : ModulesManager::init().await,
// twitch_oauth : oauth_token,
// bot_channels : botchannels,
// botmgrs : BotManagers::init(ratelimiters,client),
// };
BotInstance {
prefix: prefix,
bot_channel: Channel(login_name),
incoming_messages: Arc::new(RwLock::new(incoming_messages)),
botmodules: ModulesManager::init().await,
twitch_oauth: oauth_token,
bot_channels: botchannels,
botmgrs: BotManagers::init(ratelimiters, client),
}
// let bm = &mut ModulesManager::init(); // b
let b = BotInstance {
prefix : prefix,
bot_channel : Channel(login_name) ,
incoming_messages : incoming_messages,
//client : client,
chat : Chat {
ratelimiters : ratelimiters,
client : client,
} ,
botmodules : ModulesManager::init(),
twitch_oauth : oauth_token,
bot_channels : botchannels,
};
println!("{:?}",b.chat.ratelimiters);
b
} }
pub async fn runner(mut self) -> () { pub async fn runner(self) -> () {
let bot = Arc::new(RwLock::new(self));
let join_handle = tokio::spawn(async move { let join_handle = tokio::spawn(async move {
let a = bot.read().await;
while let Some(message) = self.incoming_messages.recv().await { let mut a = a.incoming_messages.write().await;
// Below can be used to debug if I want to capture all messages while let Some(message) = a.recv().await {
//println!("Received message: {:?}", message);
match message { match message {
ServerMessage::Notice(msg) => { ServerMessage::Notice(msg) => {
// if let Some(chnl) = msg.channel_login { match &msg.channel_login {
// println!("NOTICE : (#{}) {}", chnl, msg.message_text); Some(chnl) => {
// } // println!("NOTICE : (#{}) {}", chnl, msg.message_text)
match msg.channel_login { // Log::notice(&format!("NOTICE : (#{}) {}", chnl, msg.message_text));
Some(chnl) => println!("NOTICE : (#{}) {}", chnl, msg.message_text), botlog::notice(
None => println!("NOTICE : {}", msg.message_text), &format!("NOTICE : (#{}) {}", chnl, msg.message_text),
Some("BotInstance > runner()".to_string()),
None,
);
}
None => {
// println!("NOTICE : {}", msg.message_text);
// Log::notice(&format!("NOTICE : {}", msg.message_text));
botlog::notice(
&format!("NOTICE : {}", msg.message_text),
Some("BotInstance > runner()".to_string()),
None,
);
}
} }
} }
ServerMessage::Privmsg(msg) => { ServerMessage::Privmsg(msg) => {
println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); // println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
// Log::trace(&format!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text));
botlog::debug(
&format!(
"Twitch Chat > {} @ #{}: {}",
msg.channel_login, msg.sender.name, msg.message_text
),
Some("BotInstance > runner()".to_string()),
Some(&msg),
);
println!("Privmsg section"); // println!("Privmsg section");
// Log::debug(&format!("Privmsg section"));
botlog::trace(
&format!("Privmsg section"),
Some("BotInstance > runner()".to_string()),
Some(&msg),
);
// b.listener_main_prvmsg(&msg); BotInstance::listener_main_prvmsg(Arc::clone(&bot), &msg).await;
self.listener_main_prvmsg(msg).await; }
// - BotCommand listener should likely need to be called within the above
},
ServerMessage::Whisper(msg) => { ServerMessage::Whisper(msg) => {
println!("(w) {}: {}", msg.sender.name, msg.message_text); // println!("(w) {}: {}", msg.sender.name, msg.message_text);
}, // Log::trace(&format!("(w) {}: {}", msg.sender.name, msg.message_text));
botlog::trace(
&format!("(w) {}: {}", msg.sender.name, msg.message_text),
Some("BotInstance > runner()".to_string()),
None,
);
}
ServerMessage::Join(msg) => { ServerMessage::Join(msg) => {
println!("JOINED: {}", msg.channel_login); // println!("JOINED: {}", msg.channel_login);
}, // Log::notice(&format!("JOINED: {}", msg.channel_login));
botlog::notice(
&format!("JOINED: {}", msg.channel_login),
Some("BotInstance > runner()".to_string()),
None,
);
}
ServerMessage::Part(msg) => { ServerMessage::Part(msg) => {
println!("PARTED: {}", msg.channel_login); // println!("PARTED: {}", msg.channel_login);
}, // Log::notice(&format!("PARTED: {}", msg.channel_login));
botlog::notice(
&format!("PARTED: {}", msg.channel_login),
Some("BotInstance > runner()".to_string()),
None,
);
}
_ => {} _ => {}
} };
Log::flush();
} }
}); });
join_handle.await.unwrap(); join_handle.await.unwrap();
} }
pub fn get_botmodules(self) -> Arc<ModulesManager> {
self.botmodules
}
// ----------------- pub async fn get_botmgrs(self) -> BotManagers {
// PRIVATE FUNCTIONS let a = self.botmgrs;
a
}
pub fn get_identity(&self) -> Arc<RwLock<IdentityManager>> {
Arc::clone(&self.botmgrs.identity)
}
async fn listener_main_prvmsg(&mut self,msg:PrivmsgMessage) -> () { pub fn get_prefix(&self) -> char {
(*self).prefix
}
// -----------------
// PRIVATE FUNCTIONS
pub async fn listener_main_prvmsg(bot: BotAR, msg: &PrivmsgMessage) -> () {
// println!(">> Inner listenermain_prvmsg()");
// Log::trace(">> Inner listenermain_prvmsg()");
botlog::trace(
">> Inner listenermain_prvmsg()",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// let a = a;
// println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); // println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
// // [ ] Need to run through all Listener Bodies for Enabled Modules for the context of the message (e.g., ModStatus is Enabled in the context for the channel) // // [ ] Need to run through all Listener Bodies for Enabled Modules for the context of the message (e.g., ModStatus is Enabled in the context for the channel)
for (_m,acts) in &self.botmodules.botactions { let botlock = bot.read().await;
let hacts = Arc::clone(&botlock.botmodules.botactions);
// let hacts = hacts.read().await;
let a = hacts.read().await;
// println!("hacts size : {}",(*a).len());
// Log::debug(&format!("hacts size : {}",(*a).len()));
botlog::trace(
&format!("hacts size : {}", (*a).len()),
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// println!(">> Inner listenermain_prvmsg() >> before for loop of bot actions");
// Log::trace(">> Inner listenermain_prvmsg() >> before for loop of bot actions");
botlog::trace(
">> Inner listenermain_prvmsg() >> before for loop of bot actions",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
for (_m, acts) in &*hacts.read().await {
// println!(">> Inner listenermain_prvmsg() >> checking bot actions");
// Log::trace(">> Inner listenermain_prvmsg() >> checking bot actions");
botlog::trace(
">> Inner listenermain_prvmsg() >> checking bot actions",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// let bot = bot;
for a in acts { for a in acts {
// println!(">> Inner listenermain_prvmsg() >> checking bot actions >> 2");
// Log::trace(">> Inner listenermain_prvmsg() >> checking bot actions >> 2");
botlog::trace(
">> Inner listenermain_prvmsg() >> checking bot actions >> 2",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
match a { let _act = match a {
crate::core::botmodules::BotAction::C(c) => { crate::core::botmodules::BotAction::C(c) => {
/* /*
BotCommand handling - BotCommand handling -
- [x] Checks if the input message is a prefix with command name or alias - [x] Checks if the input message is a prefix with command name or alias
- [ ] Validate User can run based on identityModule(From_Bot)::can_user_run(
_usr:String,
_channelname:ChType,
_chat_badge:ChatBadge,
_cmdreqroles:Vec<UserRole>)
*/ */
let inpt = msg.message_text.split("\n").next().expect("ERROR during BotCommand"); // for v in msg.message_text.split(" ") {
// println!("args : {v}");
// }
// println!("Reviewing internal commands");
// Log::trace("Reviewing internal commands");
botlog::trace(
"Reviewing internal commands",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// let inpt = msg.message_text.split("\n").next().expect("ERROR during BotCommand");
let inpt = msg
.message_text
.split(" ")
.next()
.expect("ERROR during BotCommand");
// [x] Check if a bot command based on ... // [x] Check if a bot command based on ...
// [x] prefix + command // [x] prefix + command
if inpt == self.prefix.to_string() + c.command.as_str() {
c.execute(self.chat.clone(), msg.clone()).await; let mut confirmed_bot_command = false;
}
let instr = bot.read().await.get_prefix();
if inpt == String::from(instr) + c.command.as_str() {
confirmed_bot_command = true;
}
// [x] prefix + alias // [x] prefix + alias
for alias in &c.alias { for alias in &c.alias {
if inpt == self.prefix.to_string() + alias.as_str() { let instr = bot.read().await.get_prefix();
c.execute(self.chat.clone(), msg.clone()).await; if inpt == String::from(instr) + alias.as_str() {
} confirmed_bot_command = true;
}
} }
},
crate::core::botmodules::BotAction::L(l) => l.execute(self.chat.clone(), msg.clone()).await, if confirmed_bot_command {
// println!("Confirmed bot command");
// Log::debug("Confirmed bot command");
botlog::debug(
"Confirmed bot command",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// println!("Going for botlock");
// Log::trace("Going for botlock");
botlog::trace(
"Going for botlock",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let botlock = bot.read().await;
// println!("Going for identity");
// Log::trace("Going for identity");
botlog::trace(
"Going for identity",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let id = botlock.get_identity();
let eval = {
let mut id = id.write().await;
// println!("Unlocking identity");
// Log::trace("Unlocking identity");
botlog::trace(
"Unpacking identity",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let (a, b) =
id.can_user_run_PRVMSG(&msg, c.required_roles.clone()).await;
// // [-] #todo : need ot add functionality around here to do an o7 when a mod has been promoted => Preferring to do this outside the mutex
// if let ChangeResult::Success(b) = b {
// // let b = b.to_lowercase();
// // let b = b.contains(&"Auto Promoted Mod".to_lowercase());
// if b.to_lowercase().contains(&"Auto Promoted Mod".to_lowercase()) {
// let chat =
// }
// }
(a, b)
};
// println!("Checking if permissible");
Log::trace("Checking if permissible");
botlog::trace(
"Checking if permissible",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let (eval, rolechange) = eval;
if let ChangeResult::Success(b) = rolechange {
if b.to_lowercase()
.contains(&"Auto Promoted Mod".to_lowercase())
{
botlog::notice(
"Assigning Mod UserRole to Mod",
Some("botinstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// println!("Read() lock Bot");
// Log::trace("Read() lock Bot");
botlog::trace(
"Read() lock Bot",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let botlock = bot.read().await;
let outstr =
"o7 a Mod. I kneel to serve! pepeKneel ".to_string();
(*botlock).botmgrs.chat.say_in_reply_to(msg, outstr).await;
}
}
match eval {
Permissible::Allow => {
// println!("Executed as permissible");
// Log::debug("Executed as permissible");
botlog::debug(
"Executed as permissible",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
let a = Arc::clone(&bot);
c.execute(a, msg.clone()).await;
// println!("exit out of execution");
// Log::trace("exit out of execution");
botlog::trace(
"exit out of execution",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
}
Permissible::Block => {
// println!("User Not allowed to run command");
// Log::info("User Not allowed to run command");
botlog::info(
"User Not allowed to run command",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
} // _ => (),
};
}
}
crate::core::botmodules::BotAction::L(l) => {
let a = Arc::clone(&bot);
l.execute(a, msg.clone()).await;
}
_ => (), _ => (),
} };
} }
}; }
// // [ ] There should be a BotCommand Listener to check for prefixes ran // // [ ] There should be a BotCommand Listener to check for prefixes ran
println!("End of Separate Listener Main prvmsg"); // println!("End of Separate Listener Main prvmsg");
// Log::trace("End of Separate Listener Main prvmsg");
botlog::trace(
"End of Separate Listener Main prvmsg",
Some("BotInstance > listener_main_prvmsg()".to_string()),
Some(&msg),
);
// self
// bot
Log::flush();
} }
} }
// ====================================== // ======================================
// ====================================== // ======================================
// ====================================== // ======================================
@ -328,13 +820,9 @@ impl BotInstance
// UNIT TEST MODULES // UNIT TEST MODULES
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
fn always() { fn always() {
assert_eq!(1,1); assert_eq!(1, 1);
} }
} }

View file

@ -1,25 +1,44 @@
use core::{panic}; use core::panic;
use std::error::Error; use std::error::Error;
use std::collections::HashMap; use std::collections::HashMap;
use crate::core::identity;
use std::cell::RefCell;
use std::sync::Arc;
use tokio::sync::RwLock;
use std::future::Future;
// use futures::lock::Mutex;
// Important to use tokios Mutex here since std Mutex doesn't work with async functions
use tokio::sync::Mutex;
use crate::core::botinstance::{self, botlog, BotInstance};
use std::rc::Rc;
// use tokio::sync::RwLock;
use async_trait::async_trait;
use casual_logger::{Level, Log};
/* /*
ModulesManager is used to manage Modules and BotActions associated with those modules ModulesManager is used to manage Modules and BotActions associated with those modules
pub struct ModulesManager { pub struct ModulesManager {
statusdb: HashMap<ModType,Vec<ModStatusType>>, statusdb: HashMap<ModType,Vec<ModStatusType>>,
botactions: HashMap<ModType,Vec<BotAction>>, botactions: HashMap<ModType,Vec<BotAction>>,
} }
- statusdb: HashMap<ModType,Vec<ModStatusType>> - Defines Modules and their ModStatusType (e.g., Enabled at an Instance level, Disabled at a Channel Level) - statusdb: HashMap<ModType,Vec<ModStatusType>> - Defines Modules and their ModStatusType (e.g., Enabled at an Instance level, Disabled at a Channel Level)
- botactions: HashMap<ModType,Vec<BotAction>> - Defines Modules and their BotActions (e.g., BotCommand , Listener, Routine) - botactions: HashMap<ModType,Vec<BotAction>> - Defines Modules and their BotActions (e.g., BotCommand , Listener, Routine)
Example Example
{ {
ModulesManager { ModulesManager {
statusdb: {BotModule("experiments 004"): [Enabled(Instance)]}, statusdb: {BotModule("experiments 004"): [Enabled(Instance)]},
botactions: {BotModule("experiments 004"): [C(BotCommand { module: BotModule("experiments 004"), command: "DUPCMD4", alias: ["DUPALIAS4A", "DUPALIAS4B"], help: "DUPCMD4 tester" })]} } botactions: {BotModule("experiments 004"): [C(BotCommand { module: BotModule("experiments 004"), command: "DUPCMD4", alias: ["DUPALIAS4A", "DUPALIAS4B"], help: "DUPCMD4 tester" })]} }
} }
@ -29,20 +48,21 @@ Example
pub enum ModType { pub enum ModType {
BotModule(String), BotModule(String),
} }
pub use ModType::BotModule; pub use ModType::BotModule;
#[derive(Debug, PartialEq, Eq, Hash)] // #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ChType { // pub enum ChType {
Channel(String), // Channel(String),
} // }
use botinstance::ChType;
pub use ChType::Channel;
use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::PrivmsgMessage;
pub use ChType::Channel;
use crate::core::botinstance::{self, BotInstance}; use self::bot_actions::actions_util;
use self::bot_actions::actions_util::BotAR;
#[derive(Debug)] #[derive(Debug)]
enum StatusLvl { enum StatusLvl {
@ -55,186 +75,225 @@ pub enum ModStatusType {
Enabled(StatusLvl), Enabled(StatusLvl),
Disabled(StatusLvl), Disabled(StatusLvl),
} }
pub enum BotAction // #[derive(Clone)]
{ pub enum BotAction {
C(BotCommand), C(BotCommand),
L(Listener), L(Listener),
R(Routine), R(Routine),
} }
impl BotAction { impl BotAction {
pub async fn execute(&self,m:botinstance::Chat,n:PrivmsgMessage){ pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) -> () {
match self { match self {
BotAction::L(a) => a.execute(m,n).await, BotAction::L(a) => a.execute(m, n).await,
BotAction::C(a) => a.execute(m,n).await, BotAction::C(a) => a.execute(m, n).await,
_ => (), _ => (),
} }
} }
} }
pub trait BotActionTrait #[async_trait]
{ pub trait BotActionTrait {
fn add_to_bot(self, bot:BotInstance); async fn add_to_bot(self, bot: BotInstance);
fn add_to_modmgr(self,modmgr:&mut ModulesManager); async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>);
} }
// #[derive(Clone)]
pub struct BotCommand { pub struct BotCommand {
pub module : ModType, pub module: ModType,
pub command : String, // command call name pub command: String, // command call name
pub alias : Vec<String>, // String of alternative names pub alias: Vec<String>, // String of alternative names
// bot_prefix : char, // although should be global? // bot_prefix : char, // although should be global?
pub exec_body : bot_actions::actions_util::ExecBody, pub exec_body: bot_actions::actions_util::ExecBody,
pub help : String, pub help: String,
pub required_roles: Vec<identity::UserRole>,
} }
impl BotCommand impl BotCommand {
{ pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) -> () {
pub async fn execute(&self,m:botinstance::Chat,n:PrivmsgMessage){ ((*self).exec_body)(m, n).await;
(self.exec_body)(m,n).await;
} }
} }
#[async_trait]
impl BotActionTrait for BotCommand impl BotActionTrait for BotCommand {
{ async fn add_to_bot(self, bot: BotInstance) {
fn add_to_bot(self, mut bot:BotInstance) { self.add_to_modmgr(bot.botmodules).await;
let mgr = &mut bot.botmodules;
self.add_to_modmgr(mgr);
} }
fn add_to_modmgr(self, modmgr:&mut ModulesManager) { // async fn add_to_modmgr(self, modmgr:Arc<Mutex<ModulesManager>>) {
modmgr.add_botaction(self.module.clone(), BotAction::C(self)) async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
modmgr
.add_botaction(self.module.clone(), BotAction::C(self))
.await
} }
} }
pub mod bot_actions { pub mod bot_actions {
pub mod actions_util { pub mod actions_util {
use std::future::Future;
use std::boxed::Box; use std::boxed::Box;
use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use crate::core::botinstance::Chat; use std::rc::Rc;
use twitch_irc::message::PrivmsgMessage;
pub type ExecBody = Box<dyn Fn(Chat,PrivmsgMessage) -> Pin<Box<dyn Future<Output=()> + Send>> + Send + Sync>;
pub fn asyncbox<T>(f: fn(Chat,PrivmsgMessage) -> T) -> ExecBody use crate::core::botinstance::{BotInstance, BotManagers, Chat};
use std::cell::RefCell;
use std::sync::Arc;
use twitch_irc::message::PrivmsgMessage;
// use futures::lock::Mutex;
// Important to use tokios Mutex here since std Mutex doesn't work with async functions
use tokio::sync::{Mutex, RwLock};
pub type BotAM = Arc<Mutex<BotInstance>>;
pub type BotAR = Arc<RwLock<BotInstance>>;
pub type ExecBody = Box<
dyn Fn(BotAR, PrivmsgMessage) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync,
>;
pub fn asyncbox<T>(f: fn(BotAR, PrivmsgMessage) -> T) -> ExecBody
where where
T: Future<Output=()> + Send + 'static, T: Future<Output = ()> + Send + 'static,
{ {
Box::new(move |a,b| Box::pin(f(a,b))) Box::new(move |a, b| Box::pin(f(a, b)))
} }
} }
} }
pub struct Listener {
pub module: ModType,
pub struct Listener pub name: String,
{ pub exec_body: bot_actions::actions_util::ExecBody,
pub module : ModType, pub help: String,
pub name : String,
pub exec_body : bot_actions::actions_util::ExecBody,
pub help : String
} }
impl Listener impl Listener {
{ pub async fn execute(&self, m: BotAR, n: PrivmsgMessage) -> () {
pub async fn execute(&self,m:botinstance::Chat,n:PrivmsgMessage){ ((*self).exec_body)(m, n).await;
(self.exec_body)(m,n).await;
} }
} }
impl BotActionTrait for Listener #[async_trait]
{ impl BotActionTrait for Listener {
fn add_to_bot(self, mut bot:BotInstance) { async fn add_to_bot(self, bot: BotInstance) {
// println!("Adding action to bot");
let mgr = &mut bot.botmodules; // Log::trace("Adding action to bot");
botinstance::botlog::trace(
self.add_to_modmgr(mgr); "Adding action to bot",
Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
None,
);
self.add_to_modmgr(bot.botmodules).await;
} }
fn add_to_modmgr(self, modmgr:&mut ModulesManager) { async fn add_to_modmgr(self, modmgr: Arc<ModulesManager>) {
modmgr.add_botaction(self.module.clone(), BotAction::L(self)) // let modmgr = *modmgr.lock().await;
} // println!("Adding action to module manager");
// Log::trace("Adding action to module manager");
botinstance::botlog::trace(
"Adding action to module manager",
Some("BotModules > BotActionTrait > add_to_bot()".to_string()),
None,
);
modmgr
.add_botaction(self.module.clone(), BotAction::L(self))
.await;
}
} }
#[derive(Debug)] #[derive(Debug)]
struct Routine {} struct Routine {}
pub struct ModulesManager // #[derive(Clone)]
{
statusdb: HashMap<ModType,Vec<ModStatusType>>, pub struct ModulesManager {
pub botactions: HashMap<ModType,Vec<BotAction>>, statusdb: Arc<RwLock<HashMap<ModType, Vec<ModStatusType>>>>,
pub botactions: Arc<RwLock<HashMap<ModType, Vec<BotAction>>>>,
} }
impl ModulesManager /*
{
pub fn init() -> ModulesManager statusdb
{ <HashMap
<ModType, <-- e.g., BotModule(String::from("experiments001"))
Vec<ModStatusType> <-- shows Enabled/Disabled per Status level
botactions
HashMap<
ModType, <-- e.g., BotModule(String::from("experiments001"))
Vec<BotAction>> BotCommand, Listener
*/
impl ModulesManager {
pub async fn init() -> Arc<ModulesManager> {
let m = HashMap::new(); let m = HashMap::new();
let act = HashMap::new(); let act = HashMap::new();
let mut mgr = ModulesManager { let mut mgr = ModulesManager {
statusdb : m, statusdb: Arc::new(RwLock::new(m)),
botactions : act, botactions: Arc::new(RwLock::new(act)),
}; };
// :: [x] initialize core modules
// initialize custom crate modules // println!("ModulesManager > init() > Adding modules");
crate::modules::init(&mut mgr); botlog::debug(
"ModulesManager > init() > Adding modules",
Some("ModulesManager > init()".to_string()),
None,
);
let mgra = Arc::new(mgr);
crate::core::identity::init(Arc::clone(&mgra)).await;
crate::modules::init(Arc::clone(&mgra)).await;
// println!(">> Modules Manager : End of Init");
botlog::trace(
">> Modules Manager : End of Init",
Some("ModulesManager > init()".to_string()),
None,
);
println!(">> Modules Manager : End of Init"); mgra
}
mgr pub fn modstatus(&self, _: ModType, _: ChType) -> ModStatusType {
}
pub fn modstatus(&self, _:ModType, _:ChType) -> ModStatusType {
// Example usage : botmanager.modstatus( // Example usage : botmanager.modstatus(
// BotModule("GambaCore"), // BotModule("GambaCore"),
// Channel("modulatingforce") // Channel("modulatingforce")
// ) // )
// - The ModStatusType checks in the context of the given channel , // - The ModStatusType checks in the context of the given channel ,
// but also validates based on wheher the module is disabled at a bot instance // but also validates based on wheher the module is disabled at a bot instance
// level as well // level as well
ModStatusType::Enabled(StatusLvl::Instance) ModStatusType::Enabled(StatusLvl::Instance)
} }
pub fn togglestatus(&self, _: ModType, _: ChType) -> ModStatusType {
pub fn togglestatus(&self, _:ModType, _:ChType) -> ModStatusType {
// enables or disables based on current status // enables or disables based on current status
ModStatusType::Enabled(StatusLvl::Instance) ModStatusType::Enabled(StatusLvl::Instance)
} }
pub fn setstatus(&self, _: ModType, _: ModStatusType) -> Result<&str, Box<dyn Error>> {
pub fn setstatus(&self, _:ModType, _:ModStatusType) -> Result<&str,Box<dyn Error>> {
// sets the status based given ModSatusType // sets the status based given ModSatusType
// e.g., b.setstatus(BodModule("GambaCore"), Enabled(Channel("modulatingforce"))).expect("ERROR") // e.g., b.setstatus(BodModule("GambaCore"), Enabled(Channel("modulatingforce"))).expect("ERROR")
Ok("") Ok("")
} }
//pub fn add_botaction(mut self, in_module:ModType, in_action:BotAction ) -> ModulesManager { pub async fn add_botaction(&self, in_module: ModType, in_action: BotAction) {
// pub fn add_botaction(mut self, in_module:ModType, in_action:BotAction<F> ) -> ModulesManager<F> { // println!("Add botaction called");
//pub fn add_botaction(&mut self, in_module:ModType, in_action:BotAction ) -> () {
pub fn add_botaction(&mut self, in_module:ModType, in_action:BotAction ) { botlog::trace(
"Add botaction called",
Some("ModulesManager > init()".to_string()),
None,
);
/* /*
adds a BotAction to the Modules Manager - This will require a BotModule passed as well adds a BotAction to the Modules Manager - This will require a BotModule passed as well
This will including the logic of a valid add This will including the logic of a valid add
@ -244,7 +303,7 @@ impl ModulesManager
-- In particular to BotCommands, which must have Unique command call names and aliases that to not conflict with any other -- In particular to BotCommands, which must have Unique command call names and aliases that to not conflict with any other
already BotCommand added name or alias already BotCommand added name or alias
Other types might be fine? For example, if 2 modules have their own listeners but each have the name "targetchatter" , Other types might be fine? For example, if 2 modules have their own listeners but each have the name "targetchatter" ,
both would be called separately, even if they both have the same or different logic both would be called separately, even if they both have the same or different logic
*/ */
// let newlistener = Listener { // let newlistener = Listener {
@ -254,20 +313,15 @@ impl ModulesManager
// help : String::from("This will listen and react to sock randomly"), // help : String::from("This will listen and react to sock randomly"),
// }; // };
// As a Demonstration, the listener's Module is added and Enabled at Instance level // As a Demonstration, the listener's Module is added and Enabled at Instance level
// [x] Before Adding, validate the following :
// [x] Before Adding, validate the following : // - If BotAction to Add is a BotCommand , In Module Manager DB (botactions),
// - If BotAction to Add is a BotCommand , In Module Manager DB (botactions),
// Check All Other BotAction Command Names & Aliases to ensure they don't conflict // Check All Other BotAction Command Names & Aliases to ensure they don't conflict
fn find_conflict_module(mgr:& ModulesManager, act:& BotAction) -> Option<ModType> async fn find_conflict_module(mgr: &ModulesManager, act: &BotAction) -> Option<ModType> {
{
// Some(BotModule(String::from("GambaCore"))) // Some(BotModule(String::from("GambaCore")))
// match act { // match act {
// BotAction::C(c) => { // BotAction::C(c) => {
// Some(BotModule(String::from("GambaCore"))) // Some(BotModule(String::from("GambaCore")))
@ -276,118 +330,110 @@ impl ModulesManager
// BotAction::R(r) => None, // BotAction::R(r) => None,
// } // }
// if let BotAction::C(incmd) = act { if let BotAction::C(incmd) = act {
// let n = & mgr.botactions;
// // let n = & mgr.botactions; let d = mgr.botactions.read().await;
let d = &(*d);
// let d = &mgr.botactions; for (module, moduleactions) in d {
for modact in moduleactions.iter() {
if let BotAction::C(dbcmd) = &modact {
// At this point, there is an command incmd and looked up dbcmd
// for (module,moduleactions) in d { // [x] check if given botcommand c.command:String conflicts with any in botactions
// for modact in moduleactions.iter() {
// if let BotAction::C(dbcmd) = &modact {
// // At this point, there is an command incmd and looked up dbcmd
// // [x] check if given botcommand c.command:String conflicts with any in botactions if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() {
// Returning State - with the identified module
// return Some((module.clone(),BotAction::C(*dbcmd.clone())));
// return Some(incmd); // for some reason I keep getting issues
//return Some(BotModule(String::from("GambaCore"))); // works
return Some(module.clone()); // works
// return Some(dbcmd.clone());
}
// if incmd.command.to_lowercase() == dbcmd.command.to_lowercase() { for a in &dbcmd.alias {
// // Returning State - with the identified module if incmd.command.to_lowercase() == a.to_lowercase() {
// // return Some((module.clone(),BotAction::C(*dbcmd.clone()))); // Returning State - with the identified module
// // return Some(incmd); // for some reason I keep getting issues // return Some((module.clone(),BotAction::C(dbcmd)));
// //return Some(BotModule(String::from("GambaCore"))); // works return Some(module.clone()); // works
// return Some(module.clone()); // works }
// // return Some(dbcmd.clone()); }
// }
// for a in &dbcmd.alias { // [x] Then do the same check except for each c.alias
// if incmd.command.to_lowercase() == a.to_lowercase() {
// // Returning State - with the identified module
// // return Some((module.clone(),BotAction::C(dbcmd)));
// return Some(module.clone()); // works
// } for inalias in &incmd.alias {
// } if inalias.to_lowercase() == dbcmd.command.to_lowercase() {
// Returning State - with the identified module
// return Some((module.clone(),BotAction::C(dbcmd)));
return Some(module.clone()); // works
}
for a in &dbcmd.alias {
if inalias.to_lowercase() == a.to_lowercase() {
// Returning State - with the identified module
// return Some((module.clone(),BotAction::C(dbcmd)));
return Some(module.clone()); // works
}
}
}
}
}
}
// // [x] Then do the same check except for each c.alias // return Some(BotModule(String::from("GambaCore")))
}
// for inalias in &incmd.alias {
// if inalias.to_lowercase() == dbcmd.command.to_lowercase() {
// // Returning State - with the identified module
// // return Some((module.clone(),BotAction::C(dbcmd)));
// return Some(module.clone()); // works
// }
// for a in &dbcmd.alias {
// if inalias.to_lowercase() == a.to_lowercase() {
// // Returning State - with the identified module
// // return Some((module.clone(),BotAction::C(dbcmd)));
// return Some(module.clone()); // works
// }
// }
// }
// }
// }
// }
// // return Some(BotModule(String::from("GambaCore")))
// }
// for all other scenarios (e.g., Listener, Routine), find no conflicts // for all other scenarios (e.g., Listener, Routine), find no conflicts
None None
} }
// if let probmod = find_conflict_module(&self, &in_action) { // if let probmod = find_conflict_module(&self, &in_action) {
// // () // return because there was a conflict? // // () // return because there was a conflict?
// panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , probmod ); // panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , probmod );
// } // }
match find_conflict_module(&self, &in_action) { match find_conflict_module(&self, &in_action).await {
// Some(c) => panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , c ), // Some(c) => panic!("ERROR: Could not add {:?} ; there was a conflict with existing module {:?}", in_action , c ),
Some(c) => panic!("ERROR: Could not add module; there was a conflict with existing module {:?}", c ), Some(c) => panic!(
"ERROR: Could not add module; there was a conflict with existing module {:?}",
c
),
None => (), None => (),
} }
let statusvector = self.statusdb let mut dbt = self.statusdb.write().await;
let statusvector = dbt
// .entry(BotModule(String::from("experiments"))) // .entry(BotModule(String::from("experiments")))
.entry(in_module.clone()) .entry(in_module.clone())
.or_insert(Vec::new()); .or_insert(Vec::new());
statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level statusvector.push(ModStatusType::Enabled(StatusLvl::Instance)); // Pushes the Module as Enabled at Instance Level
let modactions = self.botactions let mut a = self.botactions.write().await;
let modactions = a
//.entry( BotModule(String::from("experiments"))) //.entry( BotModule(String::from("experiments")))
.entry( in_module.clone()) .entry(in_module.clone())
.or_insert(Vec::new()); .or_insert(Vec::new());
// modactions.push(BotAction::L(newlistener)); // modactions.push(BotAction::L(newlistener));
modactions.push(in_action); modactions.push(in_action);
println!(">> Modules Manager : Called Add bot Action"); // println!(">> Modules Manager : Called Add bot Action");
//println!(">> Modules Manager : {:?}",&self); botlog::trace(
">> Modules Manager : Called Add bot Action",
//(); Some("ModulesManager > init()".to_string()),
//let mgr = self; None,
);
//mgr // println!("add_botaction - botactions size : {}",modactions.len());
botlog::trace(
//self &format!("add_botaction - botactions size : {}", modactions.len()),
Some("ModulesManager > init()".to_string()),
None,
);
} }
fn statuscleanup(&self, _: Option<ChType>) -> () {
// internal cleans up statusdb . For example :
fn statuscleanup(&self,_:Option<ChType>) -> () {
// internal cleans up statusdb . For example :
// - remove redudancies . If we see several Enabled("m"), only keep 1x // - remove redudancies . If we see several Enabled("m"), only keep 1x
// - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m") // - Clarify Conflict. If we see Enabled("m") and Disabled("m") , we remove Enabled("m") and keep Disabled("m")
// the IDEAL is that this is ran before every read/update operation to ensure quality // the IDEAL is that this is ran before every read/update operation to ensure quality
@ -395,7 +441,4 @@ impl ModulesManager
// Passing None to chnl may be a heavy operation, as this will review and look at the whole table // Passing None to chnl may be a heavy operation, as this will review and look at the whole table
() ()
} }
}
}

2900
src/core/identity.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,8 @@
use std::time::Instant; use std::time::Instant;
const TIME_THRESHOLD_S: u64 = 30; const TIME_THRESHOLD_S: u64 = 30;
const MSG_THRESHOLD: u32 = 20; const MSG_THRESHOLD: u32 = 20;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RateLimiter { pub struct RateLimiter {
timer: Instant, timer: Instant,
@ -13,10 +11,9 @@ pub struct RateLimiter {
pub enum LimiterResp { pub enum LimiterResp {
Allow, // when it's evaluated to be within limits Allow, // when it's evaluated to be within limits
Skip, // as outside of rate limits Skip, // as outside of rate limits
} }
impl RateLimiter { impl RateLimiter {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -25,24 +22,23 @@ impl RateLimiter {
} }
} }
pub fn check_limiter(&mut self) -> LimiterResp { pub fn check_limiter(&mut self) -> LimiterResp {
if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S {
if self.timer.elapsed().as_secs() >= TIME_THRESHOLD_S { // # [x] elapsed >= TIME_THRESHOLD_S
// # [x] elapsed >= TIME_THRESHOLD_S self.timer = Instant::now();
self.timer = Instant::now(); self.msgcounter = 0;
self.msgcounter = 0; LimiterResp::Allow
LimiterResp::Allow } else if self.msgcounter < MSG_THRESHOLD {
} else if self.msgcounter < MSG_THRESHOLD { // # [x] elapsed < TIME_THRESHOLD_S && msgcounter < MSG_THRESHOLD
// # [x] elapsed < TIME_THRESHOLD_S && msgcounter < MSG_THRESHOLD LimiterResp::Allow
LimiterResp::Allow // } else if self.msgcounter >= MSG_THRESHOLD {
// } else if self.msgcounter >= MSG_THRESHOLD { } else {
} else { // # [x] elapsed < TIME_THRESHOLD_S && msgcounter >= MSG_THRESHOLD
// # [x] elapsed < TIME_THRESHOLD_S && msgcounter >= MSG_THRESHOLD LimiterResp::Skip
LimiterResp::Skip }
} }
}
pub fn increment_counter(&mut self) -> () { pub fn increment_counter(&mut self) -> () {
self.msgcounter += 1; self.msgcounter += 1;
} }
} }

2
src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod core;
pub mod modules;

View file

@ -1,16 +1,82 @@
// pub mod core;
// pub mod modules;
pub mod core; //use myLib;
pub mod modules; //pub mod lib;
use std::process::Output; use std::process::Output;
use crate::core::botinstance::BotInstance; // use crate::core::botinstance::ArcBox;
use botLib::core::botinstance::ArcBox;
use botLib::core::botinstance::{self, BotInstance};
// use core::botinstance::{self,BotInstance};
use casual_logger::Extension;
use std::sync::Arc;
use tokio::sync::RwLock;
pub type BotAR = Arc<RwLock<BotInstance>>;
use casual_logger::{Level, Log};
#[tokio::main] #[tokio::main]
pub async fn main() { pub async fn main() {
Log::set_file_ext(Extension::Log);
Log::set_level(Level::Trace);
// Log::set_level(Level::Notice);
let bot = BotInstance::init(); let bot = BotInstance::init().await;
// Log::debug("Checking bot actions");
botinstance::botlog::debug("Checking bot actions", Some("main()".to_string()), None);
let a = Arc::clone(&bot.botmodules.botactions);
let a = a.read().await;
// let a = *a;
for (_, acts) in &*a {
for act in acts {
match act {
botLib::core::botmodules::BotAction::C(b) => {
// println!("bot actiions: {}",b.command)
// Log::info(&format!("bot actions: {}",b.command));
botinstance::botlog::info(
&format!("bot actions: {}", b.command),
Some("main()".to_string()),
None,
);
}
botLib::core::botmodules::BotAction::L(l) => {
// println!("bot actiions: {}",l.name)
// Log::info(&format!("bot actions: {}",l.name));
botinstance::botlog::info(
&format!("bot actions: {}", l.name),
Some("main()".to_string()),
None,
);
}
_ => {
// println!("Not a valid match??")
// Log::info("Not a valid match??");
botinstance::botlog::info(
"Not a valid match??",
Some("main()".to_string()),
None,
);
}
}
}
}
// println!("Starting runner..");
// Log::notice("Starting Bot Runner");
botinstance::botlog::notice("Starting Bot Runner", Some("main()".to_string()), None);
println!("Starting Bot Runner");
Log::flush();
bot.runner().await; bot.runner().await;
} // println!("ERROR : EXIT Game loop");
// let msg = Log::fatal("ERROR : EXIT Game loop");
// panic!("{}",Log::fatal("ERROR : EXIT Game loop"));
let a = botinstance::botlog::fatal("ERROR : EXIT Game loop", Some("main()".to_string()), None);
panic!("{}", a);
}

View file

@ -10,27 +10,20 @@ pub use crate::core::botmodules::ModulesManager;
// use crate::core::botinstance; // use crate::core::botinstance;
pub use crate::core::botinstance::BotInstance; pub use crate::core::botinstance::BotInstance;
use futures::lock::Mutex;
use std::sync::Arc;
// [ ] Load submodules // [ ] Load submodules
mod experiments; mod experiments;
// [ ] 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
// pub fn init<F>(mgr:ModulesManager<F>) -> ModulesManager<F> pub async fn init(mgr: Arc<ModulesManager>) {
// pub fn init<F>(mgr:ModulesManager<F>)
// where
// // F: std::future::Future + Send,
// // F : Send,
// F : Send + ?Sized,
pub fn init(mgr:&mut ModulesManager)
{
// Modules initializer loads modules into the bot // Modules initializer loads modules into the bot
// 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) experiments::init(mgr).await
//(); //();
} }

View file

@ -1,9 +1,8 @@
/* /*
Submodules - Submodules -
- should have definitions of BotAction that will be added to a bit - should have definitions of BotAction that will be added to a bit
- therefore, will be defined in modules.rs file - therefore, will be defined in modules.rs file
- will define one init(&BotInstance) take within the module that will contain : - will define one init(&BotInstance) take within the module that will contain :
- BotAction definitions that each call &BotInstance module manager to add itself - BotAction definitions that each call &BotInstance module manager to add itself
@ -14,52 +13,107 @@
use std::future::Future; use std::future::Future;
use crate::core::botmodules::{ModulesManager,Listener,BotModule,BotActionTrait, BotCommand}; use crate::core::botmodules::bot_actions::actions_util::{self, BotAR};
use crate::core::botmodules::bot_actions::actions_util; use crate::core::botmodules::{BotActionTrait, BotCommand, BotModule, Listener, ModulesManager};
use crate::core::botinstance::{self}; use crate::core::botinstance::{self, BotInstance, ChType};
use futures::lock::Mutex;
use twitch_irc::message::PrivmsgMessage; use twitch_irc::message::PrivmsgMessage;
pub fn init(mgr:&mut ModulesManager) use crate::core::identity;
{
use rand::Rng;
BotCommand { use std::rc::Rc;
module : BotModule(String::from("experiments 004")),
command : String::from("test"), // command call name
alias : vec![String::from("tester"),String::from("testy")], // String of alternative names
exec_body : actions_util::asyncbox(testy) ,
help : String::from("DUPCMD4 tester"),
}.add_to_modmgr(mgr);
use std::sync::{Arc, RwLock};
let list1 = Listener { // pub fn init(mgr:&mut ModulesManager)
module : BotModule(String::from("experiments 004")), pub async fn init(mgr: Arc<ModulesManager>) {
name : String::from("GoodGirl Listener"), // BotCommand {
exec_body : actions_util::asyncbox(good_girl) , // module : BotModule(String::from("experiments 004")),
help : String::from("") // command : String::from("test1"), // command call name
// alias : vec![String::from("tester1"),String::from("testy1")], // String of alternative names
// exec_body : actions_util::asyncbox(testy) ,
// help : String::from("DUPCMD4 tester"),
// required_roles : vec![
// //identity::UserRole::Mod(ChType::Channel(String::new())),
// identity::UserRole::SupMod(ChType::Channel(String::new()))
// ],
// }.add_to_modmgr(mgr);
let botc1 = BotCommand {
module: BotModule(String::from("experiments001")),
command: String::from("test1"), // command call name
alias: vec![String::from("tester1"), String::from("testy1")], // String of alternative names
exec_body: actions_util::asyncbox(testy),
help: String::from("Test Command tester"),
required_roles: vec![identity::UserRole::BotAdmin],
}; };
list1.add_to_modmgr(mgr); botc1.add_to_modmgr(Arc::clone(&mgr)).await;
let list1 = Listener {
module: BotModule(String::from("experiments001")),
name: String::from("GoodGirl Listener"),
exec_body: actions_util::asyncbox(good_girl),
help: String::from(""),
};
list1.add_to_modmgr(Arc::clone(&mgr)).await;
} }
async fn good_girl(mut bot: BotAR, msg: PrivmsgMessage) {
// println!("In GoodGirl() Listener");
// Change below from debug to trace if required later
botinstance::botlog::debug(
"In GoodGirl() Listener",
Some("experiments > goodgirl()".to_string()),
Some(&msg),
);
async fn good_girl(mut chat:botinstance::Chat,msg:PrivmsgMessage)
{
println!("In GoodGirl()");
//println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text); //println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
if msg.sender.name == "ModulatingForce" && msg.message_text.contains("GoodGirl") { // [ ] Uses gen_ratio() to output bool based on a ratio probability .
chat.say_in_reply_to(&msg,String::from("GoodGirl")).await; // - 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 msg.sender.name.to_lowercase() == "ModulatingForce".to_lowercase()
|| msg.sender.name.to_lowercase() == "mzNToRi".to_lowercase()
// && msg.message_text.contains("GoodGirl")
{
// chat.say_in_reply_to(&msg,String::from("GoodGirl")).await;
//if rng.gen_ratio(1,5) {
// println!("In GoodGirl() > Pausechamp");
botinstance::botlog::debug(
"In GoodGirl() > Pausechamp",
Some("experiments > goodgirl()".to_string()),
Some(&msg),
);
let rollwin = rand::thread_rng().gen_ratio(1, 8);
if rollwin {
// println!("In GoodGirl() > Win");
botinstance::botlog::debug(
"In GoodGirl() > Win",
Some("experiments > goodgirl()".to_string()),
Some(&msg),
);
let a = Arc::clone(&bot);
let botlock = a.read().await;
botlock
.botmgrs
.chat
.say_in_reply_to(&msg, String::from("GoodGirl xdd "))
.await;
}
}
} }
async fn testy(mut _chat:botinstance::Chat,_msg:PrivmsgMessage) async fn testy(mut _chat: BotAR, msg: PrivmsgMessage) {
{ println!("testy triggered!"); // NOTE : This test function intends to print (e.g., to stdout) at fn call
println!("testy triggered!") botinstance::botlog::debug(
"testy triggered!",
} Some("experiments > testy()".to_string()),
Some(&msg),
);
}