Scripting Guide

Scripting Best Practices

Write reliable, secure, and maintainable scripts with these proven patterns and recommendations.

Overview

Following best practices ensures your scripts are reliable during streams, easy to maintain, and don't introduce security vulnerabilities. This guide covers the most important patterns to follow.

Quick Checklist

  • Always handle errors gracefully
  • Validate all user input
  • Use appropriate logging
  • Cache expensive operations
  • Never trust user-provided data
  • Test before going live

Error Handling

Proper error handling prevents script failures from disrupting your stream. Always wrap your code in try-catch blocks and validate inputs.

Robust error handling
def Execute():
    try:
        # Always validate required arguments
        user = args.get("user")
        if not user:
            CPH.LogWarn("No user provided")
            return False

        # Parse numbers safely
        amount_str = args.get("amount", "0")
        try:
            amount = int(amount_str)
        except ValueError:
            CPH.SendMessage("Please provide a valid number!")
            return False

        # Main logic
        process_user(user, amount)
        return True

    except Exception as e:
        CPH.LogError(f"Script failed: {e}")
        return False

def process_user(user, amount):
    # Separated logic for clarity
    points = CPH.GetUserVar(user, "points", 0)
    CPH.SetUserVar(user, "points", points + amount)

Do

  • Validate required arguments exist
  • Parse numbers with error handling
  • Log errors for debugging
  • Return False on failure
  • Provide user-friendly error messages

Avoid

  • Assuming arguments always exist
  • Unhandled exceptions
  • Silent failures
  • Exposing technical errors to chat
  • Continuing after critical errors

Performance

Scripts run during your stream, so performance matters. Avoid blocking operations and cache expensive data.

Performance optimizations
# BAD: Fetching data in every call
def Execute():
    all_users = fetch_all_users()  # Expensive!
    for user in all_users:
        if user["name"] == args["user"]:
            return process(user)
    return False

# GOOD: Cache expensive operations
_user_cache = {}
_cache_time = 0

def Execute():
    global _user_cache, _cache_time

    import time
    current_time = time.time()

    # Refresh cache every 5 minutes
    if current_time - _cache_time > 300:
        _user_cache = fetch_all_users_as_dict()
        _cache_time = current_time

    user = args.get("user")
    if user in _user_cache:
        return process(_user_cache[user])
    return False

Performance Tips

Cache Data

Cache API responses and expensive calculations. Refresh periodically.

Avoid Loops

Use built-in functions instead of iterating when possible.

String Building

Use join/concat methods instead of repeated concatenation.

Early Returns

Return early from functions when conditions aren't met.

Security

Never trust user input. Chat messages can contain malicious content designed to exploit your scripts.

Secure input handling
# BAD: Using user input directly in commands
def Execute():
    filename = args["message"]
    content = CPH.ReadFile(filename)  # Path traversal risk!
    CPH.SendMessage(content)

# GOOD: Validate and sanitize input
import os

ALLOWED_DIR = "/path/to/allowed/files"

def Execute():
    filename = args.get("message", "")

    # Remove path traversal attempts
    safe_name = os.path.basename(filename)

    # Validate file extension
    if not safe_name.endswith(('.txt', '.json')):
        CPH.SendMessage("Invalid file type!")
        return False

    # Build safe path
    safe_path = os.path.join(ALLOWED_DIR, safe_name)

    # Verify path is still within allowed directory
    if not os.path.abspath(safe_path).startswith(ALLOWED_DIR):
        CPH.LogWarn("Path traversal attempt blocked")
        return False

    if os.path.exists(safe_path):
        content = CPH.ReadFile(safe_path)
        CPH.SendMessage(content[:500])  # Limit output

    return True

Security Essentials

  • Never execute user input as code - No eval(), loadstring(), etc.
  • Sanitize file paths - Prevent path traversal attacks
  • Validate URLs - Only allow known domains for HTTP requests
  • Limit output - Don't dump large amounts of data to chat
  • Check permissions - Verify user roles before sensitive operations

Code Organization

Well-organized code is easier to maintain, debug, and extend.

Organized script structure
"""
Points Management Script
Handles adding, removing, and checking user points.
"""

# Constants at the top
POINTS_PER_MINUTE = 10
MAX_POINTS = 1000000
MIN_BET = 100

def Execute():
    """Main entry point - route to appropriate handler."""
    command = args.get("command", "check")

    handlers = {
        "check": handle_check,
        "add": handle_add,
        "remove": handle_remove,
        "gamble": handle_gamble
    }

    handler = handlers.get(command, handle_unknown)
    return handler()

def handle_check():
    """Show user's current points."""
    user = get_user()
    points = get_points(user)
    CPH.SendMessage(f"{user} has {points:,} points")
    return True

def handle_add():
    """Add points to a user (mod only)."""
    if not is_mod():
        CPH.SendMessage("Only mods can add points!")
        return False
    # ... rest of logic
    return True

# Helper functions
def get_user():
    return args.get("user", "anonymous")

def get_points(user):
    return CPH.GetUserVar(user, "points", 0)

def is_mod():
    return args.get("isModerator", False)

Organization Principles

Constants at Top

Define configuration values at the top of your script for easy modification.

Single Responsibility

Each function should do one thing well. Break complex logic into smaller functions.

Descriptive Names

Use clear, descriptive names for functions and variables. Avoid abbreviations.

Document Intent

Add comments explaining why, not what. The code shows what happens.

Testing

Test your scripts before going live. Use debug modes and mock data to verify behavior.

Testing patterns
# Add test mode to your scripts
DEBUG_MODE = False  # Set True during development

def Execute():
    if DEBUG_MODE:
        # Log all incoming arguments
        CPH.LogInfo("=== DEBUG START ===")
        for key, value in args.items():
            CPH.LogInfo(f"  {key}: {value} ({type(value).__name__})")
        CPH.LogInfo("=== DEBUG END ===")

    # Main logic
    result = process_command()

    if DEBUG_MODE:
        CPH.LogInfo(f"Result: {result}")

    return result

def process_command():
    # Your logic here
    return True

# Test with mock data
def test_locally():
    """Run this function to test without FaustBot."""
    global args
    args = {
        "user": "testuser",
        "message": "!points check",
        "isModerator": True
    }

    # Mock CPH for local testing
    class MockCPH:
        @staticmethod
        def LogInfo(msg): print(f"[INFO] {msg}")
        @staticmethod
        def SendMessage(msg): print(f"[CHAT] {msg}")
        @staticmethod
        def GetUserVar(u, k, d=None): return d
        @staticmethod
        def SetUserVar(u, k, v): print(f"[VAR] {u}.{k} = {v}")

    global CPH
    CPH = MockCPH()

    Execute()

Testing Checklist

  • Test with missing/empty arguments
  • Test with invalid data types
  • Test edge cases (zero, negative, very large values)
  • Test with special characters in strings
  • Test permission checks with different user roles
  • Use FaustBot's Test Action feature with mock events

Common Mistakes

Avoid these frequently encountered issues in FaustBot scripts.

1. Not Returning a Boolean

The Execute() function must return True or False. Forgetting to return stops the action.

2. Infinite Loops

Scripts that run too long will be terminated. Avoid infinite loops and limit iterations in while loops.

3. Blocking Operations

Long HTTP requests or file operations block the action queue. Use timeouts and consider async patterns where available.

4. Global State Bugs

Be careful with global variables. They persist between script runs and can cause unexpected behavior.

5. Rate Limit Violations

Don't send too many messages or API requests. Respect platform rate limits to avoid bans.

6. Hardcoded Values

Use variables and constants instead of magic numbers. Makes scripts easier to configure and maintain.