Customizable Rust Twitch Bot
Find a file
2025-02-06 09:41:54 -05:00
forcebot_core cargo-fmt 2025-02-06 09:41:54 -05:00
moderator_reactor cargo-fmt 2025-02-06 09:41:54 -05:00
new_empty_bot cargo-fmt 2025-02-06 09:41:54 -05:00
simple_command_bot cargo-fmt 2025-02-06 09:41:54 -05:00
simple_debug_listener cargo-fmt 2025-02-06 09:41:54 -05:00
simple_module_example cargo-fmt 2025-02-06 09:41:54 -05:00
.gitignore custom pyramid 2025-01-31 13:25:09 -05:00
Cargo.lock reorg workspaces 2025-02-02 17:01:54 -05:00
Cargo.toml reorg workspaces 2025-02-02 17:01:54 -05:00
readme.md enh pyramid + chat module 2025-02-06 09:35:39 -05:00

Customizable Twitch chat bot written in rust

Quick Start

Run a Simple bot with Built in functionality

  1. Generate a twitch access token

  2. Define an .env file with the following

login_name=BOTNAME
access_token=ACCESS_TOKEN
bot_channels=BOTNAME
prefix=`
bot_admins=ADMIN
  1. 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

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
  1. 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"
  1. Copy main.rs from the new_empty_bot package into your package

  2. Optionally, customize your main() to load modules before starting the bot

  3. 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

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

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


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


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


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

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