reorg workspaces

This commit is contained in:
modulatingforce 2025-02-02 08:17:38 -05:00
parent 2a6b512ce1
commit 46cff68bc1
27 changed files with 519 additions and 180 deletions

120
Cargo.lock generated
View file

@ -19,9 +19,9 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "async-trait"
version = "0.1.85"
version = "0.1.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
dependencies = [
"proc-macro2",
"quote",
@ -63,9 +63,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]]
name = "cc"
version = "1.2.10"
version = "1.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf"
dependencies = [
"shlex",
]
@ -142,7 +142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "forcebot-rs-v2"
name = "forcebot_core"
version = "0.1.0"
dependencies = [
"dotenv",
@ -200,13 +200,14 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.15"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
@ -271,15 +272,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]]
name = "moderator_reactor"
version = "0.1.0"
dependencies = [
"dotenv",
"forcebot_core",
"lazy_static",
"tokio",
"twitch-irc",
]
[[package]]
name = "native-tls"
version = "0.2.12"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c"
dependencies = [
"libc",
"log",
@ -292,6 +304,17 @@ dependencies = [
"tempfile",
]
[[package]]
name = "new_empty_bot"
version = "0.1.0"
dependencies = [
"dotenv",
"forcebot_core",
"lazy_static",
"tokio",
"twitch-irc",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@ -318,9 +341,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "openssl"
version = "0.10.68"
version = "0.10.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e"
dependencies = [
"bitflags",
"cfg-if",
@ -344,9 +367,9 @@ dependencies = [
[[package]]
name = "openssl-probe"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
@ -436,9 +459,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustix"
version = "0.38.43"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
@ -500,6 +523,39 @@ dependencies = [
"libc",
]
[[package]]
name = "simple_command_bot"
version = "0.1.0"
dependencies = [
"dotenv",
"forcebot_core",
"lazy_static",
"tokio",
"twitch-irc",
]
[[package]]
name = "simple_debug_listener"
version = "0.1.0"
dependencies = [
"dotenv",
"forcebot_core",
"lazy_static",
"tokio",
"twitch-irc",
]
[[package]]
name = "simple_module_example"
version = "0.1.0"
dependencies = [
"dotenv",
"forcebot_core",
"lazy_static",
"tokio",
"twitch-irc",
]
[[package]]
name = "slab"
version = "0.4.9"
@ -527,9 +583,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.96"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
@ -538,9 +594,9 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.15.0"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
dependencies = [
"cfg-if",
"fastrand",
@ -687,9 +743,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "vcpkg"
@ -703,6 +759,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@ -784,3 +849,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]

View file

@ -1,24 +1,7 @@
[package]
name = "forcebot-rs-v2"
version = "0.1.0"
edition = "2021"
default-run = "forcebot-rs-v2"
[dependencies]
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"
# [[bin]]
# name = "simple_bot"
# path = "src/simple_bot.rs"
# [[bin]]
# name = "simple_bot_listener"
# path = "src/simple_bot_listener.rs"
# [lib]
# name = "botlib"
# path = "src/lib.rs"
[workspace]
members = [
"forcebot_core",
"simple_module_example",
"new_empty_bot",
"simple_debug_listener",
"moderator_reactor", "simple_command_bot"]

11
forcebot_core/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "forcebot_core"
version = "0.1.0"
edition = "2021"
default-run = "fun_bot"
[dependencies]
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"

View file

@ -1,9 +1,9 @@
//! WIP Fun forcebot with catered customizations #todo
//!
//! Custom modules that can be managed in chat through `disable` and `enable` commands
//! - funbot
//! - guests
//! - pyramid
//! - `besty`
//! - `guests`
//! - `pyramid`
//!
//!
//! Be sure the followig is defined in `.env`
@ -17,7 +17,10 @@
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
//! - More Info - <https://dev.twitch.tv/docs/authentication>
use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
// use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
#[tokio::main]
pub async fn main() {
@ -41,16 +44,17 @@ pub async fn main() {
pub mod funbot_objs {
use std::sync::Arc;
use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
use forcebot_core::{execution_async, modules::Status, Badge, Bot, Command, Module};
use twitch_irc::message::ServerMessage;
/// Create a Module with a loaded Command object
pub fn create_module() -> Module {
let mut custom_mod = Module::new(
vec!["funbot".to_string()],
"".to_string());
vec!["besty".to_string()],
"Now Aware of besty xdd666 ".to_string());
custom_mod.load_command(create_cmd_test());
custom_mod.set_status_by_default(Status::Disabled);
custom_mod
}
@ -73,6 +77,7 @@ pub mod funbot_objs {
cmd.set_admin_only(false);
cmd.set_min_badge(Badge::Vip);
cmd
}

View file

@ -10,7 +10,7 @@
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
//! - More Info - <https://dev.twitch.tv/docs/authentication>
use forcebot_rs_v2::Bot;
use forcebot_core::Bot;
#[tokio::main]
pub async fn main() {

View file

@ -17,7 +17,7 @@
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
//! - More Info - <https://dev.twitch.tv/docs/authentication>
use forcebot_rs_v2::Bot;
use forcebot_core::Bot;
#[tokio::main]
pub async fn main() {
@ -37,7 +37,7 @@ pub async fn main() {
pub mod custom_mod {
use std::sync::Arc;
use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
use forcebot_core::{execution_async, Badge, Bot, Command, Module};
use twitch_irc::message::ServerMessage;

View file

@ -5,10 +5,16 @@ use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, Server
use dotenv::dotenv;
use std::{env, sync::Arc, time::{Duration, Instant}};
use crate::{Badge, Command, Listener, Module};
// use crate::{Badge, Command, Listener, Module};
use super::bot_objects::command::Command;
use super::{bot_objects::built_in_objects, modules::{self, Status}};
use crate::botcore::bot_objects::Badge;
use crate::botcore::bot_objects::listener::Listener;
use super::super::botcore::modules::Module;
// use super::
use super:: {bot_objects::built_in_objects, modules::{self, Status}};
/// Twitch chat bot
pub struct Bot
@ -73,7 +79,7 @@ impl Bot
/// Creates a new `Bot` using bot information
///
/// Bot joined channels will include channels from `.env` and `botchannels` argument
/// Bot will join `botchannels` argument
pub fn new_from(bot_login_name:String,oauth_token:String,prefix:String,botchannels:Vec<String>) -> Bot {
dotenv().ok();
@ -90,10 +96,6 @@ impl Bot
let mut botchannels_all = Vec::new();
botchannels_all.extend(botchannels);
for chnl in env::var("bot_channels").unwrap().split(',') {
botchannels_all.push(chnl.to_owned());
}
let mut admins = Vec::new();

View file

@ -105,8 +105,16 @@ pub mod built_in_objects {
use std::{sync::Arc, time::{Duration, Instant}};
use twitch_irc::message::ServerMessage;
use super::{execution_async,command::Command,Bot,Badge,super::modules::Status};
// use super::execution_async;
// use super::command::Command;
// use super::Bot;
// use super::Badge;
// use super::super::modules::Status;
// ::{execution_async, modules::Status, Badge, Bot, Command};
use crate::{execution_async, modules::Status, Badge, Bot, Command};
/// create a vector of command build in objects
@ -146,14 +154,14 @@ pub mod built_in_objects {
Result::Err("Not Valid message type".to_string())
}
/* 3. Set and Store the execution body using `async_box()` */
/* 3. Set and Store the execution body using `execution_async()` */
cmd.set_exec_fn(execution_async(execbody));
/* 4. optionally, remove admin only default flag */
cmd.set_admin_only(false);
/* 5. optionally, set min badge*/
cmd.set_min_badge(Badge::Moderator);
cmd.set_min_badge(Badge::Moderator /* ::Moderator */);
cmd
}
@ -201,7 +209,7 @@ pub mod built_in_objects {
Result::Err("Not Valid message type".to_string())
}
/* 3. Set and Store the execution body using `async_box()` */
/* 3. Set and Store the execution body using `execution_async()` */
cmd.set_exec_fn(execution_async(execbody));
/* 4. optionally, remove admin only default flag */
@ -304,7 +312,7 @@ pub mod built_in_objects {
Result::Err("Not Valid message type".to_string())
}
/* 3. Set and Store the execution body using `async_box()` */
/* 3. Set and Store the execution body using `execution_async()` */
cmd.set_exec_fn(execution_async(execbody));
/* 4. optionally, remove admin only default flag */

View file

@ -2,7 +2,9 @@ use std::sync::Arc;
use twitch_irc::message::{PrivmsgMessage, ServerMessage};
use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
// use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
use super::{execution_async,Bot,Badge,command_condition_async};
use super::{CommandTrigger, ExecBody};
@ -80,7 +82,7 @@ impl Command
/// /* 2. define an async function */
/// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
///
/// /* 3. Set and Store the execution body using `async_box()` */
/// /* 3. Set and Store the execution body using `execution_async()` */
/// cmd.set_custom_cond_async(condition_async(condition01));
/// ```
///

View file

@ -2,13 +2,22 @@ use std::sync::Arc;
use twitch_irc::message::ServerMessage;
use crate::{execution_async, listener_condition_async, Bot};
use super::{execution_async,Bot,listener_condition_async};
use super::{ExecBody, ListenerTrigger};
/// Bot `Listener`` that stores trigger condition callback and a execution functon
/// Bot `Listener` that stores trigger condition callback and a execution functon
///
/// Use `asyncfn_box()` on custom async execution bodies
/// Use `Listener` functions to define the Trigger Condition & Execution callbacks.
/// When the Trigger callback is `true`, the Execution callback runs in the bot loop
///
/// Create a new empty `Listener` with `new()`
///
/// Use the following on the empty listener before loading it into the bot to set the callbacks
///
/// - `set_trigger_cond_fn()` - to define the Trigger condition callback
///
/// - `set_exec_fn()` - to define the Execution Callback
#[derive(Clone)]
pub struct Listener
{
@ -23,14 +32,22 @@ pub struct Listener
impl Listener
{
/// Creates a new empty `Listener` .
/// Call `set_trigger_cond_fn()` and `set_exec_fn()` to trigger & execution function callbacks
/// Creates a new empty `Listener`
///
/// Use `Listener` functions to define the Trigger Condition & Execution callbacks.
/// When the Trigger callback is `true`, the Execution callback runs in the bot loop
///
/// Use the following on the empty listener before loading it into the bot to set the callbacks
///
/// - `set_trigger_cond_fn()` - to define the Trigger condition callback
///
/// - `set_exec_fn()` - to define the Execution Callback
pub fn new() -> Listener {
async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
Listener {
trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| false,
trigger_cond_fn : |_:Arc<Bot>,_:ServerMessage| true,
trigger_cond_async : Arc::new(listener_condition_async(condition01)),
exec_fn : Arc::new(execution_async(execbody)),
}
@ -45,7 +62,7 @@ impl Listener
///
/// Same as `set_trigger_cond_fn()` , but async define
///
/// Use`asyncfn_box()` on the async fn when storing
/// Use`condition_async()` on the async fn when storing
///
/// Example -
/// ```rust
@ -55,7 +72,7 @@ impl Listener
/// /* 2. define an async function */
/// async fn condition01(_:Arc<Bot>,_:ServerMessage) -> bool { true }
///
/// /* 3. Set and Store the execution body using `async_box()` */
/// /* 3. Set and Store the execution body using `execution_async()` */
/// listener.set_trigger_cond_async(condition_async(condition01));
/// ```
///
@ -66,7 +83,7 @@ impl Listener
/// sets the execution body of the listener for when it triggers
///
/// Use`asyncfn_box()` on the async fn when storing
/// Use`execution_async()` on the async fn when storing
///
/// Example -
/// ```rust
@ -76,8 +93,8 @@ impl Listener
/// /* 2. define an async function */
/// async fn execbody(_:Arc<Bot>,_:ServerMessage) -> Result<String,String> {Result::Ok("success".to_string()) }
///
/// /* 3. Set and Store the execution body using `async_box()` */
/// listener.set_exec_fn(asyncfn_box(execbody));
/// /* 3. Set and Store the execution body using `execution_async()` */
/// listener.set_exec_fn(execution_async(execbody));
/// ```
///
pub fn set_exec_fn(&mut self,exec_fn:ExecBody ) {

View file

@ -1,6 +1,7 @@
use super::bot_objects::command::Command;
use super::bot_objects::listener::Listener;
use crate::{Command, Listener};
#[derive(PartialEq, Eq,Debug,Clone)]
pub enum Status {
@ -33,7 +34,7 @@ impl Module
bot_read_description,
listeners: vec![],
commands: vec![],
default_status_per_channel: Status::Disabled,
default_status_per_channel: Status::Enabled,
}
}

View file

@ -4,7 +4,14 @@ use std::sync::{Arc, Mutex};
use twitch_irc::message::{PrivmsgMessage, ServerMessage};
use crate::{listener_condition_async, Badge, Bot, Command, Listener, Module};
// use crate::{execution_async, listener_condition_async, Badge, Bot, Command, Listener, Module};
use super::super::botcore::bot_objects::execution_async;
use super::super::botcore::bot_objects::listener_condition_async;
use super::super::botcore::bot_objects::listener::Listener;
use super::super::botcore::modules::Module;
use super::super::botcore::bot::Bot;
use super::super::botcore::bot_objects::command::Command;
use super::super::botcore::bot_objects::Badge;
/// pyramid module
///
@ -58,15 +65,15 @@ fn create_pyramid_detector() -> Listener {
/* 4. Define an async fn callback execution */
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
if let ServerMessage::Privmsg(msg) = message {
if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
// if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
let _ = bot.client.say_in_reply_to(&msg, "Clap".to_string()).await ;
return Result::Ok("Success".to_string()) ;
}
// }
}
Result::Err("Not Valid message type".to_string())
}
/* 5. Set and Store the execution body using `async_box()` */
/* 5. Set and Store the execution body using `execution_async()` */
listener.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
listener
@ -78,16 +85,17 @@ async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
let msgtext = msg.message_text.replace("\u{e0000}","").trim().to_string();
let msgchannel = msg.channel_login;
let msgchatter = msg.sender.login;
// 1. Check if Pyramid started in chat > and recognize pyramid started
if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
set_pyramid_started(msgchannel.clone(),true);
push_to_compare(msgchannel.clone(),get_start_pattern(msgchannel.clone()));
push_to_compare(msgchannel.clone(),msgchatter.clone(),get_start_pattern(msgchannel.clone()));
}
if is_pyramid_started(msgchannel.clone()) {
push_to_compare(msgchannel.clone(),msgtext.clone());
push_to_compare(msgchannel.clone(),msgchatter.clone(),msgtext.clone());
}
// 2a. If Pyramid Not Started, Assume message is a potential start pattern
@ -111,8 +119,8 @@ async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
lazy_static!{
/// Message Compare stack per channel (channel:String,msgstack:Vec<String>)
pub static ref COMPARE_MSG_STACK_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<String>>)>> = Mutex::new(vec![]);
/// Message Compare stack per channel (channel:String,msgstack:Vec<(chatter:String,message:String)>)
pub static ref COMPARE_MSG_STACK_PER_CHNL: Mutex<Vec<(String,Mutex<Vec<(String,String)>>)>> = Mutex::new(vec![]);
#[derive(Debug)]
/// Pyramid Started per channel (channel:String,started:bool)
pub static ref PYRAMID_STARTED_PER_CHNL: Mutex<Vec<(String,Mutex<bool>)>> = Mutex::new(vec![]);
@ -126,7 +134,7 @@ lazy_static!{
}
fn read_top_of_compare(channel:String) -> Option<String> {
fn read_top_of_compare(channel:String) -> Option<(String,String)> {
let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
@ -142,7 +150,7 @@ fn read_top_of_compare(channel:String) -> Option<String> {
}
fn pop_top_of_compare(channel:String) -> Option<String> {
fn pop_top_of_compare(channel:String) -> Option<(String,String)> {
let comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
for rec in comp_perchnl.iter() {
@ -218,7 +226,7 @@ fn get_start_pattern(channel:String) -> String {
/// pushes message to compare stack
fn push_to_compare(channel:String,message:String) {
fn push_to_compare(channel:String,chatter:String,message:String) {
let mut comp_perchnl = COMPARE_MSG_STACK_PER_CHNL.lock().unwrap();
let mut found = false;
@ -226,12 +234,12 @@ fn push_to_compare(channel:String,message:String) {
if rec.0 == channel {
found = true;
let mut msg_stack = rec.1.lock().unwrap();
msg_stack.push(message.clone());
msg_stack.push((chatter.clone(),message.clone()));
// dbg!("Push message to cmp stack ; result last cmp_pchnl - ",comp_perchnl.last());
}
}
if !found {
comp_perchnl.push((channel,Mutex::new(vec![message])));
comp_perchnl.push((channel,Mutex::new(vec![(chatter,message)])));
}
}
@ -247,21 +255,21 @@ fn check_start_pyramid(channel:String,msgtext: String) -> bool {
fn symmetry_ok(channel:String) -> bool {
let mut temp_stack = TEMP_MSG_STACK.lock().unwrap();
let mut checking_started = false;
if !(read_top_of_compare(channel.clone()).unwrap_or("".to_string()) == get_start_pattern(channel.clone())) {
if !(read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone())) {
return false;
}
loop {
if !checking_started && read_top_of_compare(channel.clone()).unwrap_or("".to_string()) == get_start_pattern(channel.clone()) {
if !checking_started && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone()) {
checking_started = true;
}
if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or("".to_string()).len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or("".to_string()));
if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1);
} else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or("".to_string()).len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
} else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
temp_stack.pop();
if temp_stack.last().unwrap_or(&"".to_string()).clone() == read_top_of_compare(channel.clone()).unwrap_or("".to_string()) {
if temp_stack.last().unwrap_or(&"".to_string()).clone() == read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 {
temp_stack.pop();
continue;
@ -272,7 +280,7 @@ fn symmetry_ok(channel:String) -> bool {
}
} else { /* dbg!("failed catchall"); */ return false; }
if checking_started && read_top_of_compare(channel.clone()).unwrap() == get_start_pattern(channel.clone()) {
if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {
temp_stack.clear();
return true;
@ -284,7 +292,9 @@ fn symmetry_ok(channel:String) -> bool {
}
/// pyramid interruptor #todo
/// #todo
///
/// pyramid interruptor
///
/// pick chatters that will be interrupted if they solo build
///
@ -311,7 +321,7 @@ fn create_interruptor_cmd() -> Command {
Result::Err("Not Valid message type".to_string())
}
/* 3. Set and Store the execution body using `async_box()` */
/* 3. Set and Store the execution body using `execution_async()` */
cmd.set_exec_fn(Box::new(move |a,b| Box::pin(execbody(a,b))));
/* 4. optionally, remove admin only default flag */
@ -324,5 +334,47 @@ fn create_interruptor_cmd() -> Command {
cmd
}
/// #todo
fn create_interruptor_module(channel:String) -> Module {
/* 1. Create a new module */
let modname = format!("interruptor {}",channel);
let mut custom_mod = Module::new(
vec![modname],
"".to_string());
/* 2. Load the cmd into a new module */
custom_mod.load_listener(create_interruptor_listener());
custom_mod
}
/// #todo
fn create_interruptor_listener() -> Listener {
/* 2a. Create a new blank Listener */
let mut listener = Listener::new();
/* 2b. Set a trigger condition function for listener */
listener.set_trigger_cond_fn(
|_:Arc<Bot>,_:ServerMessage| true
);
/* 2c. Define an async fn callback execution */
async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
dbg!(message);
Result::Ok("Success".to_string())
}
/* 2d. Set and Store the execution body using `execution_async()` */
listener.set_exec_fn(execution_async(execbody));
listener
}
/// #todo
///
/// Returns Some(chatter) if the pyramid in progress is being built by a solo
fn solo_building(channel:String) -> Option<String> {
None
}

View file

@ -1,4 +1,4 @@
//! `forcebot-rs-v2` : Twitch chat bot written in rust.
//! `forcebot_core` library for `forcebot-rs-v2` Twitch chat bot
//!
//! Customize by adding additional bot objects
//!
@ -7,7 +7,7 @@
//! Uses Env defined variables to create and run the bot
//!
//! ```rust
//! use forcebot_rs_v2::Bot;
//! use forcebot_core::Bot;
//!
//! #[tokio::main]
//! pub async fn main() {
@ -27,11 +27,10 @@
//!
//! A `Module` is a group of bot objects (eg `Command`) that elevated users can manage through built in `disable` and `enable` commands
//!
//!
//! Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
//!
//! ```rust
//! use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
//! use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
//!
//! #[tokio::main]
//! pub async fn main() {
@ -60,7 +59,7 @@
//!
//!
//! ```rust
//! use forcebot_rs_v2::Bot;
//! use forcebot_core::Bot;
//!
//! #[tokio::main]
//! pub async fn main() {
@ -79,7 +78,7 @@
//! pub mod custom_mod {
//! use std::sync::Arc;
//!
//! use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
//! use forcebot_core::{execution_async, Badge, Bot, Command, Module};
//! use twitch_irc::message::ServerMessage;
//!
//!
@ -129,7 +128,7 @@
//! ```rust
//! use std::sync::Arc;
//!
//! use forcebot_rs_v2::{execution_async, Bot, Listener};
//! use forcebot_core::{execution_async, Bot, Listener};
//! use twitch_irc::message::ServerMessage;
//!
//! #[tokio::main]
@ -152,7 +151,7 @@
//! Result::Ok("Success".to_string())
//! }
//!
//! /* 2d. Set and Store the execution body using `async_box()` */
//! /* 2d. Set and Store the execution body using `execution_async()` */
//! listener.set_exec_fn(execution_async(execbody));
//!
//! /* 3. Load the listener into the bot */
@ -171,9 +170,9 @@
//!
//! use std::sync::Arc;
//!
//! use forcebot_rs_v2::Bot;
//! use forcebot_rs_v2::execution_async;
//! use forcebot_rs_v2::Listener;
//! use forcebot_core::Bot;
//! use forcebot_core::execution_async;
//! use forcebot_core::Listener;
//! use twitch_irc::message::ServerMessage;
//!
//!
@ -211,7 +210,7 @@
//! Result::Err("Not Valid message type".to_string())
//! }
//!
//! /* 4. Set and Store the execution body using `async_box()` */
//! /* 4. Set and Store the execution body using `execution_async()` */
//! listener.set_exec_fn(execution_async(execbody));
//!
//! /* 5. Load the listener into the bot */
@ -226,12 +225,13 @@
pub mod botcore;
pub mod custom_mods;
pub use crate::botcore::bot::Bot;
pub use crate::botcore::bot_objects::execution_async;
pub use crate::botcore::bot_objects::command_condition_async;
pub use crate::botcore::bot_objects::listener_condition_async;
pub use crate::botcore::bot_objects::listener::Listener;
pub use crate::botcore::bot_objects::command::Command;
pub use crate::botcore::modules::Module;
pub use crate::botcore::bot_objects::Badge;
pub use crate::botcore::modules;
pub use botcore::bot::Bot;
pub use botcore::bot_objects::execution_async;
pub use botcore::bot_objects::command_condition_async;
pub use botcore::bot_objects::listener_condition_async;
pub use botcore::bot_objects::listener::Listener;
// pub use crate::botcore::bot_objects::command::Command;
pub use botcore::bot_objects::command::Command;
pub use botcore::modules::Module;
pub use botcore::bot_objects::Badge;
pub use botcore::modules;

View file

@ -0,0 +1,11 @@
[package]
name = "moderator_reactor"
version = "0.1.0"
edition = "2021"
[dependencies]
forcebot_core = {path = "../forcebot_core"}
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"

View file

@ -13,9 +13,9 @@
use std::sync::Arc;
use forcebot_rs_v2::Bot;
use forcebot_rs_v2::execution_async;
use forcebot_rs_v2::Listener;
use forcebot_core::Bot;
use forcebot_core::execution_async;
use forcebot_core::Listener;
use twitch_irc::message::ServerMessage;
@ -53,7 +53,7 @@ pub async fn main() {
Result::Err("Not Valid message type".to_string())
}
/* 4. Set and Store the execution body using `async_box()` */
/* 4. Set and Store the execution body using `execution_async()` */
listener.set_exec_fn(execution_async(execbody));
/* 5. Load the listener into the bot */

11
new_empty_bot/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "new_empty_bot"
version = "0.1.0"
edition = "2021"
[dependencies]
forcebot_core = {path = "../forcebot_core"}
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"

View file

@ -10,8 +10,7 @@
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
//! - More Info - <https://dev.twitch.tv/docs/authentication>
use forcebot_rs_v2::botcore::bot::Bot;
use forcebot_core::Bot;
#[tokio::main]
pub async fn main() {

129
readme.md
View file

@ -1,4 +1,4 @@
Twitch chat bot written in rust
Customizable Twitch chat bot written in rust
# Quick Start
@ -22,28 +22,47 @@ bot_admins=ADMIN
3. Build & run
```
cargo run
cargo run -p forcebot_core
```
# Features
- Quick Start to use full feature set bot
- Moderators & Broadcasters can `disable` or `enable` `Modules` of bot functionality through chat `Commands`
- Full Feature Set `forcebot_core` bot has the following modules loaded
- `guest_badge` - Temporary badges can be issued to chatters
- `besty` - Tomfoolery
- `pyramid` - for detecting & handling pyramids
- `forcebot_core` library API provides Custom package developers a way to add functionality by adding `Modules` that contain Bot Objects like `Commands` and `Listeners`
- `Listeners` and `Commands` listen for a defined callback trigger condition and run an defined execution callback
- `Commands` are similar to `Listeners` with refined trigger conditions including using bot `prefix` with the `Command` , triggers based on `Badge` , and more
- Workspace for package developers to independently code their own `Modules`
- Workspace comes with binary crates with working or example bots that use `forcebot_core` library
- `moderator_reactor` - bot kneels to all moderator messages
- `simple_module_example` - bot has a `test` `Module` with a `test` `Command` .Moderators & Broadcasters can manage the `Module` in chat with `enable` / `disable` `Commands`
- `new_empty_bot` - while empty, has `disable` and `enable` chat `Commands` . This is an example of the bot without any loaded modules
- `simple_command_bot` - bot responds to a `test` `Command`. As the command was not loaded through a `Module`, `disable` & `enable` commands don't work on the `test` command. This could be a Global `Command`
- `simple_debug_listener` - bot outputs all twitch `ServerMessages` received to terminal
# Example Bots
Use the following commands to build and run built-in bots. No coding required!
Use the following to build and run built-in bots. No coding required!
## New Bot
## New Empty Bot
Run an empty simple bot that logs into chat and has minimum built in functions
```
cargo run --bin new_bot
cargo run -p new_empty_bot
```
## WIP Customized Fun Bot
## Full Featured Forcebot
Run a forcebot with fun catered customizations
*ongoing work in progress*
```
cargo run --bin fun_bot
cargo run -p forcebot_core
```
@ -51,28 +70,69 @@ cargo run --bin fun_bot
Run a bot that listens to all messages and output to console
```
cargo run --bin simple_debug_listener
cargo run -p simple_debug_listener
```
## Simple Command Bot
Run a bot that uses the `test` chat `Command` . `Commands` are prefixed and must be ran by a chatter with a `vip` badge or above
```
cargo run --bin simple_command_bot
cargo run -p simple_command_bot
```
## Moderator Reactor
Run a bot that listens for messages with the `moderator` badge, and replies to that mod with an emote
```
cargo run --bin moderator_reactor
cargo run -p moderator_reactor
```
## Module loaded Bot
Run a bot that has a `test` chat `Command`. As the command was loaded through a module, moderators or broadcastors can `enable` or `disable` the module through chat commands
```
cargo run --bin simple_module
cargo run -p simple_module_example
```
# Workspace packages
Source is a workspace of packages . In particular, `forcebot_core` is the main library crate to use
*TIP* : if you want to start customizing you own bot, create a binary package in the workspace for your bot's binary crate
More info about workspaces - https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
## Creating a new package
To create a new package
1. Create a new package
For example, to create a new binary crate in the workspace
```
cargo new my_new_bot
```
2. In the newly created directory for your package, adjust the `Cargo.toml` to the following
```
[dependencies]
forcebot_core = {path = "../forcebot_core"}
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"
```
3. Copy `main.rs` from the `new_empty_bot` package into your package
4. Optionally, customize your `main()` to load modules before starting the bot
5. Build and run your package
```
cargo run -p my_new_bot
```
@ -83,7 +143,7 @@ cargo run --bin simple_module
Uses Env defined variables to create and run the bot
```rust
use forcebot_rs_v2::Bot;
use forcebot_core::Bot;
#[tokio::main]
pub async fn main() {
@ -95,7 +155,6 @@ pub async fn main() {
bot.run().await;
}
```
## Customize by Loading Custom Modules
@ -105,7 +164,8 @@ A `Module` is a group of bot objects (eg `Command`) that elevated users can mana
Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
```rust
use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
#[tokio::main]
pub async fn main() {
@ -134,7 +194,7 @@ Create a custom `Module` by :
```rust
use forcebot_rs_v2::Bot;
use forcebot_core::Bot;
#[tokio::main]
pub async fn main() {
@ -153,7 +213,7 @@ pub async fn main() {
pub mod custom_mod {
use std::sync::Arc;
use forcebot_rs_v2::{execution_async, Badge, Bot, Command, Module};
use forcebot_core::{execution_async, Badge, Bot, Command, Module};
use twitch_irc::message::ServerMessage;
@ -193,7 +253,6 @@ pub mod custom_mod {
cmd
}
}
```
## Simple Debug Listener
@ -202,7 +261,7 @@ Bot with a simple listener that listens for all messages and prints in output
```rust
use std::sync::Arc;
use forcebot_rs_v2::{execution_async, Bot, Listener};
use forcebot_core::{execution_async, Bot, Listener};
use twitch_irc::message::ServerMessage;
#[tokio::main]
@ -225,7 +284,7 @@ pub async fn main() {
Result::Ok("Success".to_string())
}
/* 2d. Set and Store the execution body using `async_box()` */
/* 2d. Set and Store the execution body using `execution_async()` */
listener.set_exec_fn(execution_async(execbody));
/* 3. Load the listener into the bot */
@ -235,7 +294,6 @@ pub async fn main() {
bot.run().await;
}
```
## Moderator Reactor
@ -243,12 +301,11 @@ pub async fn main() {
Example listener listens for a moderator badge and reply in chat
```rust
use std::sync::Arc;
use forcebot_rs_v2::Bot;
use forcebot_rs_v2::execution_async;
use forcebot_rs_v2::Listener;
use forcebot_core::Bot;
use forcebot_core::execution_async;
use forcebot_core::Listener;
use twitch_irc::message::ServerMessage;
@ -286,7 +343,7 @@ pub async fn main() {
Result::Err("Not Valid message type".to_string())
}
/* 4. Set and Store the execution body using `async_box()` */
/* 4. Set and Store the execution body using `execution_async()` */
listener.set_exec_fn(execution_async(execbody));
/* 5. Load the listener into the bot */
@ -296,9 +353,6 @@ pub async fn main() {
bot.run().await;
}
```
## Simple Test Command
@ -306,13 +360,12 @@ pub async fn main() {
```rust
use std::sync::Arc;
use forcebot_rs_v2::Badge;
use forcebot_rs_v2::Bot;
use forcebot_rs_v2::execution_async;
use forcebot_rs_v2::Command;
use forcebot_core::Badge;
use forcebot_core::Bot;
use forcebot_core::execution_async;
use forcebot_core::Command;
use twitch_irc::message::ServerMessage;
#[tokio::main]
pub async fn main() {
@ -331,7 +384,7 @@ pub async fn main() {
Result::Err("Not Valid message type".to_string())
}
/* 3. Set and Store the execution body using `async_box()` */
/* 3. Set and Store the execution body using `execution_async()` */
cmd.set_exec_fn(execution_async(execbody));
/* 4. optionally, remove admin only default flag */
@ -347,11 +400,9 @@ pub async fn main() {
bot.run().await;
}
```
# Crate Rust Documentation
# Crate Rust API Documentation
Create `forcebot_rs_v2` Rust Crate documentation

View file

@ -0,0 +1,11 @@
[package]
name = "simple_command_bot"
version = "0.1.0"
edition = "2021"
[dependencies]
forcebot_core = {path = "../forcebot_core"}
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"

View file

@ -15,10 +15,10 @@
use std::sync::Arc;
use forcebot_rs_v2::Badge;
use forcebot_rs_v2::Bot;
use forcebot_rs_v2::execution_async;
use forcebot_rs_v2::Command;
use forcebot_core::Badge;
use forcebot_core::Bot;
use forcebot_core::execution_async;
use forcebot_core::Command;
use twitch_irc::message::ServerMessage;
@ -40,7 +40,7 @@ pub async fn main() {
Result::Err("Not Valid message type".to_string())
}
/* 3. Set and Store the execution body using `async_box()` */
/* 3. Set and Store the execution body using `execution_async()` */
cmd.set_exec_fn(execution_async(execbody));
/* 4. optionally, remove admin only default flag */

View file

@ -0,0 +1,11 @@
[package]
name = "simple_debug_listener"
version = "0.1.0"
edition = "2021"
[dependencies]
forcebot_core = {path = "../forcebot_core"}
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"

View file

@ -12,7 +12,7 @@
use std::sync::Arc;
use forcebot_rs_v2::{execution_async, Bot, Listener};
use forcebot_core::{execution_async, Bot, Listener};
use twitch_irc::message::ServerMessage;
#[tokio::main]
@ -35,7 +35,7 @@ pub async fn main() {
Result::Ok("Success".to_string())
}
/* 2d. Set and Store the execution body using `async_box()` */
/* 2d. Set and Store the execution body using `execution_async()` */
listener.set_exec_fn(execution_async(execbody));
/* 3. Load the listener into the bot */

View file

@ -0,0 +1,11 @@
[package]
name = "simple_module_example"
version = "0.1.0"
edition = "2021"
[dependencies]
forcebot_core = {path = "../forcebot_core"}
dotenv = "0.15.0"
lazy_static = "1.5.0"
tokio = { version = "1.33.0", features = ["full"] }
twitch-irc = "5.0.1"

View file

@ -0,0 +1,79 @@
//! Simple Module with a Command
//!
//! Adding objects through packages provides controls ,
//! such as moderators, and brodcasters can disable or enable mods
//!
//! Here, moderators or above can enable or disable the `test`
//! module with the command `<prefix> disable test`
//!
//! Be sure the followig is defined in `.env`
//! - login_name
//! - access_token
//! - bot_channels
//! - prefix
//! - bot_admins
//!
//! Bot access tokens be generated here -
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
//! - More Info - <https://dev.twitch.tv/docs/authentication>
use forcebot_core::Bot;
#[tokio::main]
pub async fn main() {
/* Create the bot using env */
let mut bot = Bot::new();
/* load the Module */
bot.load_module(custom_mod::new()).await;
/* Run the bot */
bot.run().await;
}
pub mod custom_mod {
use std::sync::Arc;
use forcebot_core::{execution_async, Badge, Bot, Command, Module};
use twitch_irc::message::ServerMessage;
/// Module definition with a loaded command
pub fn new() -> Module {
/* 1. Create a new module */
let mut custom_mod = Module::new(
vec!["test".to_string()],
"".to_string());
/* 2. Load the cmd into a new module */
custom_mod.load_command(cmd_test());
custom_mod
}
/// Command definition
pub fn cmd_test() -> Command {
/* 1. Create a new cmd */
let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
/* 2. Define exec callback */
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
if let ServerMessage::Privmsg(msg) = message {
let _= bot.client.say_in_reply_to(
&msg, "test return".to_string()).await;
}
Result::Err("Not Valid message type".to_string())
}
/* 3. Set Command flags */
cmd.set_exec_fn(execution_async(execbody));
cmd.set_admin_only(false);
cmd.set_min_badge(Badge::Vip);
cmd
}
}