Customizable Twitch chat bot written in rust # Quick Start Run a Simple bot with Built in functionality 1. Generate a twitch access token - Get a Bot Chat Token here - https://twitchtokengenerator.com - More Info - https://dev.twitch.tv/docs/authentication 2. Define an `.env` file with the following ``` login_name=BOTNAME access_token=ACCESS_TOKEN bot_channels=BOTNAME prefix=` bot_admins=ADMIN ``` 3. Build & run ``` cargo run -p forcebot_core ``` # Features ## Built In Chat Commands - `quiet on` / `quiet off` - Moderators & Broadcasters can quiet the bot - `enable $module$` / `disable $module$` - Moderators & Broadcasters can enable or disable `Modules` of bot functionality through chat `Commands` ## Custom Modules can be coded to load additional functionality Developers an create Modules that add more bot functionality The main `forcebot_core` Binary crate includes the following Custom `Modules` - `debug` - outputs to console messages from the channel where it was enabled. Toggle debug with the Commands `debug on` or `debug off` - `guest_badge` - Temporary badges can be issued to chatters - `besty` - Tomfoolery - `pyramid` - for detecting & handling pyramids ## `forcebot_core` Bot Library - `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` ## Workspaces 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 to build and run built-in bots. No coding required! ## New Empty Bot Run an empty simple bot that logs into chat and has minimum built in functions ``` cargo run -p new_empty_bot ``` ## Full Featured Forcebot Run a forcebot with fun catered customizations ``` cargo run -p forcebot_core ``` ## Simple Debug Listener Run a bot that listens to all messages and output to console ``` 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 -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 -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 -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 ``` # Example Code ## New Bot Uses Env defined variables to create and run the bot ```rust use forcebot_core::Bot; #[tokio::main] pub async fn main() { /* 1. Create the bot using env */ let bot = Bot::new().await; /* 2. Run the bot */ bot.run().await; } ``` ## Customize by Loading Custom Modules 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_core::{custom_mods::{debug, guest_badge, pyramid}, Bot}; #[tokio::main] pub async fn main() { /* Create the bot using env */ let bot = Bot::new().await; /* 1. Load the module into the bot */ bot.load_module(funbot_objs::create_module()).await; /* 2. Load Custom Modules */ bot.load_module(guest_badge::create_module()).await; bot.load_module(pyramid::create_module()).await; bot.load_module(debug::create_module()).await; /* 3. Run the bot */ bot.run().await; } ``` ## Create your own Custom Modules Create a custom `Module` by : 1. Defining Functions that create the Custom Bot Objects (eg `Command`) 2. Define a function that creates a `Module` with the Custom Bot Objects loaded ```rust use forcebot_core::Bot; #[tokio::main] pub async fn main() { /* Create the bot using env */ let bot = Bot::new().await; /* 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.chat.lock().await.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 } } ``` ## Simple Debug Listener Bot with a simple listener that listens for all messages and prints in output ```rust use std::sync::Arc; use forcebot_core::{execution_async, Bot, Listener}; use twitch_irc::message::ServerMessage; #[tokio::main] pub async fn main() { /* 1. Create the bot using env */ let bot = Bot::new().await; /* 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); /* outputs message to debug */ Result::Ok("Success".to_string()) } /* 2d. Set and Store the execution body using `execution_async()` */ listener.set_exec_fn(execution_async(execbody)); /* 3. Load the listener into the bot */ bot.load_listener(listener).await; /* 4. Run the bot */ bot.run().await; } ``` ## Moderator Reactor Example listener listens for a moderator badge and reply in chat ```rust use std::sync::Arc; use forcebot_core::Bot; use forcebot_core::execution_async; use forcebot_core::Listener; use twitch_irc::message::ServerMessage; #[tokio::main] pub async fn main() { /* Create the bot using env */ let bot = Bot::new().await; /* 1. Create a new blank Listener */ let mut listener = Listener::new(); /* 2. Set a trigger condition function for listener */ listener.set_trigger_cond_fn( |_:Arc<Bot>,message:ServerMessage| if let ServerMessage::Privmsg(msg) = message { for badge in msg.badges { if matches!(badge, x if x.name == "moderator") { // dbg!("moderator found"); return true; } } false } else { false } ); /* 3. Define an async fn callback execution */ async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> { if let ServerMessage::Privmsg(msg) = message { let _ = bot.chat.lock().await.say_in_reply_to(&msg, "pepeKneel".to_string()).await ; return Result::Ok("Success".to_string()) ; } Result::Err("Not Valid message type".to_string()) } /* 4. Set and Store the execution body using `execution_async()` */ listener.set_exec_fn(execution_async(execbody)); /* 5. Load the listener into the bot */ bot.load_listener(listener).await; /* Run the bot */ bot.run().await; } ``` ## Simple Test Command ```rust use std::sync::Arc; 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() { /* Create the bot using env */ let bot = Bot::new().await; /* 1. Create a new blank cmd */ let mut cmd = Command::new(vec!["test".to_string()],"".to_string()); /* 2. Define an async fn callback execution */ async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> { if let ServerMessage::Privmsg(msg) = message { let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await; return Result::Ok("Success".to_string()) ; } Result::Err("Not Valid message type".to_string()) } /* 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); /* 6. Load the cmd into the bot */ bot.load_command(cmd).await; /* Run the bot */ bot.run().await; } ``` # Crate Rust API Documentation Create `forcebot_rs_v2` Rust Crate documentation Documentation - Clean Build & Open in Default Browser ``` cargo clean && cargo doc --open ```