User Tools

Site Tools


examples

VoxAI Plugin Examples

This guide provides practical, copy-and-paste examples to help you get started with the VoxAI Plugin API. All code shown here should be placed inside the main register(app) function within your plugin's .py file.

For a complete list of all available functions, please see the API Reference.

Example 1: Basic Chat Command (!hello)

This is the “Hello World” of plugins. It registers a new chat command !hello that, when used, makes VoxAI send a chat message back to the user who typed it.

This example demonstrates:

  • app.register_command()
  • app.send_twitch_chat()
# This is the function that will be called when someone types !hello
def say_hello(args, user):
    # args = any text that came after the command (we ignore it here)
    # user = the username of the person who sent the command
    
    app.send_twitch_chat(f"@{user}, hello there! Welcome to the stream!")

# This tells VoxAI to listen for "!hello" and run our "say_hello" function
app.register_command("hello", say_hello)

Example 2: Streamer-Only Command (!shoutout)

This example demonstrates how to create a command that only the streamer can use. It uses the app.channel attribute to verify the user's identity, which is a core “Best Practice”. It also uses the args parameter to read text after the command.

This example demonstrates:

  • Checking user permissions against app.channel
  • Using the args parameter from a command
  • app.register_command()
def handle_shoutout(args, user):
    # BEST PRACTICE: Check if the user is the streamer (channel owner)
    # We always compare in .lower() case to be safe.
    if user.lower() != app.channel.lower():
        return  # Silently ignore if the user is not the streamer

    # 'args' will be the username, e.g., if the command was "!so some_streamer",
    # args will be "some_streamer"
    target_user = args.strip()
    
    if not target_user:
        app.send_twitch_chat(f"@{user}, you forgot to specify a user to shout out!")
        return

    # Clean the username just in case they added an @
    target_user = target_user.replace("@", "")

    app.send_twitch_chat(f"=== BIG SHOUTOUT === Go check out the amazing {target_user} over at https://twitch.tv/{target_user.lower()} ! They are fantastic!")

# Register the command. VoxAI handles the rest.
app.register_command("so", handle_shoutout)
app.register_command("shoutout", handle_shoutout) # You can register multiple aliases

Example 3: Handling a Twitch Event (Raid)

Plugins can listen for any Twitch event, not just chat commands. This example listens for the raid event and uses TTS to speak a custom welcome message.

This example demonstrates:

  • app.register_event_handler()
  • app.speak_text() (which is the easy-to-use TTS function)
  • app.add_chat() (to add a log message to the UI)
# This function's parameters MUST match the Event Reference guide
def on_raid(raider_name, viewer_count):
    # This function is automatically called by VoxAI when a raid occurs
    
    welcome_message = f"Welcome in to all {viewer_count} raiders from {raider_name}'s channel! Get comfortable, we're glad you're here!"
    
    # Make VoxAI speak the message aloud using the configured TTS service
    app.speak_text(welcome_message)
    
    # Also send a log message to the VoxAI chat window for the streamer to see
    app.add_chat(f"[MyPlugin] Fired custom thank you for {raider_name}'s raid.")

# Register the handler to listen for the 'raid' event
app.register_event_handler("raid", on_raid)

Example 4: AI Command with Threading (!ask)

This is a more advanced example showing the correct way to use blocking or long-running tasks like app.ask_ai(). You must run these tasks in a separate thread to prevent VoxAI from freezing.

This example requires an import: Add import threading to the very top of your plugin .py file.

This example demonstrates:

  • Using import threading (CRITICAL BEST PRACTICE)
  • app.ask_ai()
  • Handling a function in a new thread
# AT THE VERY TOP OF YOUR PLUGIN .py FILE, add this line:
import threading

# Then, inside your register(app) function, add the following logic:

def ask_ai_question(args, user):
    # args = the question the user asked (e.g., "what is the capital of France?")
    if not args:
        app.send_twitch_chat(f"@{user}, please ask a question after the command!")
        return
    
    # We can build a more detailed prompt for the AI
    prompt = f"A Twitch viewer named {user} has a question for you: {args}"
    
    # CRITICAL: AI calls and other network requests can take several seconds.
    # We MUST run them in a separate thread so they don't block the main VoxAI application.
    
    def do_ai_work():
        try:
            app.add_chat(f"[MyPlugin] Sending prompt to AI for {user}...")
            
            # This is the blocking call. It runs safely in the thread.
            response = app.ask_ai(prompt)
            
            # Send the response back to chat and speak it
            app.send_twitch_chat(f"@{user}, {response}")
            app.speak_text(response)
            
        except Exception as e:
            print(f"[MyPluginError] Failed to ask AI: {e}")
            app.send_twitch_chat(f"@{user}, I had a little brain-fart and couldn't think of an answer. Sorry!")

    # This is the final step: start the new thread.
    # setting daemon=True ensures the thread will close when VoxAI closes.
    threading.Thread(target=do_ai_work, daemon=True).start()

app.register_command("ask", ask_ai_question)

Example 5: Self-Contained Plugin with a Sound File

This demonstrates the “Best Practice” for creating a self-contained plugin. The plugin creates its own data folder to store an audio file, then registers a command to play it.

This example assumes you have created a folder named my_sound_plugin_data inside your plugins folder, and inside that folder, you have placed a file named alert.mp3.

File Structure:

plugins/
 |
 +- my_sound_plugin.py
 |
 +- my_sound_plugin_data/
     |
     +- alert.mp3

This example demonstrates:

  • app.play_sound_immediately()
  • Using os.path to create a self-contained plugin (BEST PRACTICE)
# AT THE VERY TOP OF YOUR PLUGIN .py FILE, add this line:
import os

# --- This setup code goes at the top of your register() function ---

# Best Practice: Store your assets in a dedicated subfolder.
# This gets the directory where your .py file is currently located.
plugin_dir = os.path.dirname(__file__)

# This creates a path to your data folder (e.g., "plugins/my_sound_plugin_data")
data_folder = os.path.join(plugin_dir, "my_sound_plugin_data")

# This creates the folder if it doesn't already exist.
os.makedirs(data_folder, exist_ok=True)

# Define the full path to your sound file
my_alert_sound = os.path.join(data_folder, "alert.mp3")


# --- Now, we define and register our command ---

def play_alert(args, user):
    # Always check if the file exists before trying to play it!
    if os.path.exists(my_alert_sound):
        # We use play_sound_immediately so it doesn't wait for TTS to finish
        app.play_sound_immediately(my_alert_sound)
        app.send_twitch_chat("WEE-WOO WEE-WOO! Alert has been triggered!")
    else:
        # This message helps the streamer debug if they installed it wrong.
        app.send_twitch_chat(f"@{user}, the streamer is missing the alert.mp3 file!")
        app.add_chat(f"[MyPlugin] ERROR: Could not find sound file at: {my_alert_sound}")

app.register_command("alert", play_alert)

Example 6: Creating a Plugin Menu Window

This example shows how to add your own menu item to the main “Plugins” menu in the VoxAI app. This is perfect for opening a settings or “about” window for your plugin.

This example requires imports for Tkinter. Add these to the top of your plugin file:

import tkinter as tk
import customtkinter as ctk

This example demonstrates:

  • app.add_plugin_menu_command()
  • Using app.master as the parent for a new tk.Toplevel window
  • Using win.grab_set() to make the window modal (stay on top)
# --- Place this logic inside your register() function ---

def open_my_about_window():
    # Create a new window. Always use app.master as the parent!
    win = tk.Toplevel(app.master)
    win.title("About My Plugin")
    win.geometry("350x200")

    # This tells the window to "grab" focus and stay on top of the main app.
    win.transient(app.master)
    win.grab_set()

    # We can use CustomTkinter widgets to match the style of VoxAI
    main_frame = ctk.CTkFrame(win, fg_color="#2b2b2b")
    main_frame.pack(fill="both", expand=True, padx=10, pady=10)
    
    ctk.CTkLabel(main_frame, text="My Awesome Plugin!", font=("Segoe UI", 20, "bold")).pack(pady=(10, 5))
    ctk.CTkLabel(main_frame, text="Version 1.0 - Made by Modder McModface").pack(pady=5)
    ctk.CTkButton(main_frame, text="Awesome!", command=win.destroy).pack(pady=20, side="bottom")

# This one line adds the button to the "Plugins" dropdown menu in the main VoxAI app.
app.add_plugin_menu_command("About My Awesome Plugin", open_my_about_window)

Example 7: Handling Power-ups (Gigantify)

VoxAI supports Twitch Power-ups like Gigantify, Message Effects, and On-Screen Celebrations. This example listens for these effects and tracks how many bits were spent on them.

This example demonstrates:

  • app.register_event_handler() for the new “powerup” event.
  • Extracting cost and type data.
def on_powerup(user, powerup_type, cost):
    # powerup_type will be "gigantify_an_emote", "message_effect", or "celebration"
    # cost is the integer amount of bits used (e.g. 50)
    
    clean_type = powerup_type.replace("_", " ").title() # e.g. "Gigantify An Emote"
    
    app.add_chat(f"[MyPlugin] {user} used {clean_type} for {cost} bits!")
    
    if cost > 0:
        # Example logic: Give the user 100 points for every bit spent
        new_points = cost * 100
        app.send_twitch_chat(f"Wow @{user}! Thanks for the Power-up! You earned {new_points} imaginary points!")

# Register the handler
app.register_event_handler("powerup", on_powerup)

Example 8: Saving & Loading Settings (Persistence)

Your plugin might need to remember things between sessions (like a custom API key, a toggle state, or a “High Score”). Since VoxAI's main settings file is reserved for the core app, Best Practice is to save a local JSON file inside your plugin's data folder.

This example creates a command `!setkey [value]` that saves a value to a file, and remembers it even after VoxAI restarts.

import json
import os

# 1. Setup paths
plugin_dir = os.path.dirname(__file__)
data_folder = os.path.join(plugin_dir, "my_settings_data")
settings_file = os.path.join(data_folder, "config.json")
os.makedirs(data_folder, exist_ok=True)

# 2. Global variable to hold our settings in memory
my_settings = {"api_key": "default"}

# 3. Helper functions to load/save
def load_settings():
    global my_settings
    if os.path.exists(settings_file):
        try:
            with open(settings_file, "r") as f:
                my_settings = json.load(f)
        except:
            print("[MyPlugin] Error loading settings, using defaults.")

def save_settings():
    with open(settings_file, "w") as f:
        json.dump(my_settings, f, indent=4)

# Load immediately when plugin starts
load_settings()

# 4. Command to change the setting
def set_api_key(args, user):
    if not args:
        app.send_twitch_chat(f"@{user}, please provide a key! usage: !setkey 12345")
        return
        
    # Update global variable
    my_settings["api_key"] = args.strip()
    
    # Save to disk
    save_settings()
    
    app.send_twitch_chat(f"@{user}, API Key updated and saved!")

# 5. Command to read the setting
def check_key(args, user):
    current_key = my_settings.get("api_key", "None")
    app.send_twitch_chat(f"The current saved key is: {current_key}")

app.register_command("setkey", set_api_key)
app.register_command("checkkey", check_key)

Example 9: Passive Chat Listener (Hooks)

Sometimes you want the bot to react to normal chat messages without a `!` command. You can do this using Chat Hooks.

Warning: Code in hooks runs for every single chat message. Keep it fast! If you need to do heavy work (like AI or API calls), spin up a thread.

def my_chat_hook(user, message):
    # Ignore messages from the bot itself to prevent infinite loops
    if user.lower() == app.channel.lower():
        return

    # Check for a keyword
    if "pizza" in message.lower():
        # React naturally without a command
        app.send_twitch_chat(f"Did someone say pizza? I love pizza, @{user}!")
        
    # Advanced: React to "Good Bot"
    if "good bot" in message.lower():
        app.speak_text(f"Thank you {user}, I try my best.")

# Register the hook
# Note: We append to the list, we don't use a register function.
app.plugin_chat_hooks.append(my_chat_hook)

Example 10: Periodic Timers (Loops)

If you want your plugin to do something automatically every X minutes (like a “Hydrate” reminder), you need a threaded loop.

Important: You must check `getattr(app, “_shutdown”, False)` in your loop. This ensures the thread stops running when you close VoxAI.

import threading
import time

def hydration_timer():
    interval_seconds = 1800 # 30 minutes
    
    # Loop forever until VoxAI shuts down
    while not getattr(app, "_shutdown", False):
        
        # Sleep in small chunks so we can exit quickly if the app closes
        for _ in range(interval_seconds):
            if getattr(app, "_shutdown", False): return
            time.sleep(1)
            
        # Time is up, do the thing!
        if app.is_channel_live(): # Only spam chat if we are actually live
            app.send_twitch_chat("🥤 Time to hydrate! Take a sip of water!")

# Start the timer in the background
threading.Thread(target=hydration_timer, daemon=True).start()

Example 11: Universal Bit Tracking (Goal Bar Logic)

VoxAI 1.1.8 introduced the “bits” event. This event fires for EVERY bit usage: standard cheers, power-ups (Gigantify), Extension usage, and Combos.

Use this event if you are building a Goal Bar, Leaderboard, or Total Counter. Do not use the legacy “cheer” or “powerup” events for accounting, or you might miss bits.

def on_any_bits(user, amount):
    # amount will be an integer
    try:
        bit_val = int(amount)
    except:
        bit_val = 0
        
    if bit_val > 0:
        # Example: Add to a global counter
        # global_bit_count += bit_val
        print(f"[MyPlugin] Tracking: {user} spent {bit_val} bits (Source: Universal).")
        
        # If this was a massive drop, maybe trigger a special alert?
        if bit_val >= 5000:
            app.send_twitch_chat(f"😱 HOLY MOLY! {user} just dropped {bit_val} bits!")

# Register for the universal "bits" event
app.register_event_handler("bits", on_any_bits)

Example 12: Audio Visualizer (Real-Time Volume)

VoxAI 1.1.7+ exposes the raw audio volume level while the bot is speaking. You can use this to animate avatars, create visualizers, or trigger lighting effects.

Note: This event fires very rapidly (approx. 50 times per second). Keep the logic inside very simple!

def on_audio_level(level):
    # level is a float, usually between 0.0 (Silent) and 1.0 (Loud)
    current_vol = float(level)
    
    # Example: Simple Threshold (Mouth Open/Closed)
    threshold = 0.05
    
    if current_vol > threshold:
        # Set your avatar image to Open
        pass 
    else:
        # Set your avatar image to Closed
        pass
        
    # Example: Print loudness (Only for testing, spams console!)
    # print(f"Volume: {current_vol:.3f}")

app.register_event_handler("audio_level", on_audio_level)
examples.txt · Last modified: by voxai_wiki