BotOfTheSpecter Help

Run BotOfTheSpecter Yourself

Self-host BotOfTheSpecter and have complete control over your bot deployment

Complete Freedom & Control

To run the source code of BotOfTheSpecter on your own set of servers and not use our hosted system, you'll have complete freedom to host it yourself with more control over your data. BotOfTheSpecter runs on a full headless Linux server architecture.

Advanced Setup Required

Running SpecterSystems yourself requires technical knowledge of server administration, Python, PHP and Linux. This is recommended for experienced developers and system administrators only.

Server Architecture

Recommended Hosting: Linode

We recommend running the SpecterSystems on Linode. Our systems have been fully tested and optimized to work seamlessly on Linode's infrastructure.

Get $100 in free credit: Use our referral link to receive $100 of Linode credit to use within 60 days once you've entered a valid payment method to your Linode account.

Get $100 Linode Credit

Minimum Requirements: 4 Servers

The minimum server setup required to run SpecterSystems consists of 4 servers running on a headless Linux architecture:

Server 1: Web/Dashboard
  • OS: Linux (Ubuntu 24.04 LTS+)
  • CPU: 1+ core
  • RAM: 1GB minimum
  • Service: PHP/Apache2 Dashboard
Server 2: API
  • OS: Linux (Ubuntu 24.04 LTS+)
  • CPU: 1+ core
  • RAM: 1GB minimum
  • Service: FastAPI server
Server 3: WebSocket
  • OS: Linux (Ubuntu 24.04 LTS+)
  • CPU: 1+ core
  • RAM: 1GB minimum
  • Service: Python SocketIO server
Server 4: Database
  • OS: Linux (Ubuntu 24.04 LTS+)
  • CPU: 2+ cores
  • RAM: 4GB minimum
  • Service: MySQL

Recommended Setup: 5 Servers

For production deployments with improved reliability and scalability, a 5-server setup is recommended, adding a dedicated bot server (this is how SpecterSystems currently runs):

Server 5: Bot
  • OS: Linux (Ubuntu 24.04 LTS+)
  • CPU: 2+ cores
  • RAM: 4GB minimum
  • Service: Python bot process
Note: The 2+ cores and 4GB RAM configuration is optimized for running many bots through the service for multiple users. If you're only running a single bot for personal use, 1 core and 1GB RAM is sufficient.

Common Software Requirements (All Servers)

  • OS: Linux (Ubuntu 24.04 LTS or newer)
  • Python: 3.8+ (Bot, API, and WebSocket servers)
  • PHP: 8.0+ (Web/Dashboard server)
  • Apache2: (Web/Dashboard server)
  • MySQL: (Database server)
  • Git: For version control

Network & Services

  • Twitch API credentials (OAuth tokens)
  • Discord bot token (optional)
  • Spotify API credentials (optional)
  • OpenWeatherMap API key (optional)
  • SSL/TLS certificates for secure communication
  • Firewall configured for internal communication

Installation Steps

Prerequisites (All Servers)

Before deploying to individual servers, ensure each Linux server has:

# Update system packages (All Servers)
sudo apt update && sudo apt upgrade -y

# Install common dependencies (All Servers)
sudo apt install -y curl wget git build-essential openssl ssl-cert

# Create botofthespecter user (All Servers)
sudo useradd -m -s /bin/bash botofthespecter
sudo usermod -aG sudo botofthespecter

# For Servers 1, 2, 3, 5 - Install Python and pip
sudo apt install -y python3 python3-pip python3-venv

# For Server 1 Only - Install PHP and Apache2
sudo apt install -y php php-cli php-fpm php-curl php-json php-mysql php-ssh2 apache2 libapache2-mod-php

# For Server 4 Only - Install MySQL
sudo apt install -y mysql-server
        

Step 1: Clone the Repository (Servers 1, 2, 3, 5)

Clone the BotOfTheSpecter repository to a temporary directory on each server (except Server 4 - Database):

cd /tmp
git clone https://github.com/YourStreamingTools/BotOfTheSpecter.git botofthespecter-temp
cd botofthespecter-temp

Then move the appropriate files to their destinations based on your server type:

For Server 1 (Web/Dashboard):
sudo rm -rf /var/www/html
sudo cp -r /tmp/botofthespecter-temp/dashboard /var/www/
sudo cp -r /tmp/botofthespecter-temp/home /var/www/
sudo cp -r /tmp/botofthespecter-temp/html /var/www/
sudo cp -r /tmp/botofthespecter-temp/overlay /var/www/
sudo cp -r /tmp/botofthespecter-temp/roadmap /var/www/
sudo cp -r /tmp/botofthespecter-temp/tts /var/www/
sudo cp -r /tmp/botofthespecter-temp/walkons /var/www/
sudo cp -r /tmp/botofthespecter-temp/videoalerts /var/www/
sudo cp -r /tmp/botofthespecter-temp/soundalerts /var/www/
sudo cp -r /tmp/botofthespecter-temp/config /var/www/
sudo cp -r /tmp/botofthespecter-temp/cdn /var/www/
sudo chown -R www-data:www-data /var/www
For Server 2 (API):
sudo cp -r /tmp/botofthespecter-temp/api /home/botofthespecter/
sudo chown -R botofthespecter:botofthespecter /home/botofthespecter
For Server 3 (WebSocket):
sudo cp -r /tmp/botofthespecter-temp/websocket /home/botofthespecter/
sudo chown -R botofthespecter:botofthespecter /home/botofthespecter
For Server 5 (Bot):
sudo cp -r /tmp/botofthespecter-temp/bot /home/botofthespecter/
sudo chown -R botofthespecter:botofthespecter /home/botofthespecter
Clean up temporary directory (All Servers):
rm -rf /tmp/botofthespecter-temp

Step 2: Configure Database Server (Server 4 Only)

Note: Server 4 (Database) does not require any files from the repository. It only needs MySQL installed and configured.

Important: User-specific databases are created automatically on login, so you do not need to create them manually.

You only need to create the following databases manually:

Required Databases:
  • spam_patterns - For the bot to auto-ban users matching spam patterns
  • website - For the main website
  • specterdiscordbot - If running the Discord bot (optional)
  • roadmap - If running the roadmap site (optional)

Run the following SQL commands to set up the database(s). The user-specific databases are created automatically on login; the following are the manual databases and tables you should create for core features:

sudo mysql -u root -p
-- spam_pattern: stores spam regex/phrases for auto-ban
CREATE DATABASE IF NOT EXISTS spam_pattern;
USE spam_pattern;
CREATE TABLE IF NOT EXISTS spam_patterns (
    id INT NOT NULL AUTO_INCREMENT,
    spam_pattern TEXT NOT NULL,
    PRIMARY KEY (id)
);

-- roadmap: roadmap site schema
CREATE DATABASE IF NOT EXISTS roadmap;
USE roadmap;

-- roadmap_items: Main roadmap items
CREATE TABLE IF NOT EXISTS roadmap_items (
    id INT NOT NULL AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    category ENUM('REQUESTS', 'IN PROGRESS', 'BETA TESTING', 'COMPLETED', 'REJECTED') NOT NULL DEFAULT 'REQUESTS',
    subcategory ENUM('TWITCH BOT', 'DISCORD BOT', 'WEBSOCKET SERVER', 'API SERVER', 'WEBSITE', 'OTHER') NOT NULL,
    priority ENUM('LOW', 'MEDIUM', 'HIGH', 'CRITICAL') NOT NULL DEFAULT 'MEDIUM',
    website_type ENUM('DASHBOARD', 'OVERLAYS') DEFAULT NULL,
    completed_date DATE DEFAULT NULL,
    created_by VARCHAR(255) DEFAULT NULL,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_category (category),
    KEY idx_subcategory (subcategory),
    KEY idx_priority (priority),
    KEY idx_created_at (created_at)
);

-- roadmap_comments: Comments on roadmap items
CREATE TABLE IF NOT EXISTS roadmap_comments (
    id INT NOT NULL AUTO_INCREMENT,
    item_id INT NOT NULL,
    username VARCHAR(255) NOT NULL,
    comment TEXT NOT NULL,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_item_id (item_id),
    KEY idx_created_at (created_at),
    CONSTRAINT roadmap_comments_ibfk_1 FOREIGN KEY (item_id) REFERENCES roadmap_items (id) ON DELETE CASCADE
);

-- roadmap_attachments: File attachments for roadmap items
CREATE TABLE IF NOT EXISTS roadmap_attachments (
    id INT NOT NULL AUTO_INCREMENT,
    item_id INT NOT NULL,
    file_name VARCHAR(255) NOT NULL,
    file_path VARCHAR(255) NOT NULL,
    file_type VARCHAR(100) DEFAULT NULL,
    file_size INT DEFAULT NULL,
    is_image TINYINT(1) DEFAULT 0,
    uploaded_by VARCHAR(255) DEFAULT NULL,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_item_id (item_id),
    KEY idx_is_image (is_image),
    KEY idx_created_at (created_at),
    CONSTRAINT roadmap_attachments_ibfk_1 FOREIGN KEY (item_id) REFERENCES roadmap_items (id) ON DELETE CASCADE
);

-- specterdiscordbot: Discord bot
CREATE DATABASE IF NOT EXISTS specterdiscordbot;
USE specterdiscordbot;

-- channel_mappings
CREATE TABLE IF NOT EXISTS channel_mappings (
    channel_code VARCHAR(255) NOT NULL,
    guild_id VARCHAR(255) NOT NULL DEFAULT '',
    channel_id VARCHAR(255) NOT NULL DEFAULT '',
    channel_name VARCHAR(255) DEFAULT NULL,
    user_id VARCHAR(255) DEFAULT NULL,
    username VARCHAR(255) DEFAULT NULL,
    twitch_display_name VARCHAR(255) DEFAULT NULL,
    twitch_user_id VARCHAR(255) DEFAULT NULL,
    guild_name VARCHAR(255) DEFAULT NULL,
    stream_alert_channel_id VARCHAR(255) DEFAULT NULL,
    moderation_channel_id VARCHAR(255) DEFAULT NULL,
    alert_channel_id VARCHAR(255) DEFAULT NULL,
    online_text TEXT,
    offline_text TEXT,
    is_active TINYINT(1) DEFAULT 1,
    event_count INT DEFAULT 0,
    last_event_type VARCHAR(255) DEFAULT NULL,
    last_seen_at TIMESTAMP NULL DEFAULT NULL,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (channel_code),
    KEY idx_guild_id (guild_id),
    KEY idx_is_active (is_active),
    KEY idx_last_seen (last_seen_at)
);

-- live_notifications
CREATE TABLE IF NOT EXISTS live_notifications (
    guild_id VARCHAR(255) NOT NULL DEFAULT '',
    username VARCHAR(255) NOT NULL,
    stream_id VARCHAR(255) NOT NULL,
    started_at DATETIME NOT NULL,
    posted_at DATETIME NOT NULL,
    PRIMARY KEY (guild_id, username)
);

-- message_tracking_logs
CREATE TABLE IF NOT EXISTS message_tracking_logs (
    id BIGINT NOT NULL AUTO_INCREMENT,
    server_id BIGINT NOT NULL,
    channel_id BIGINT NOT NULL,
    message_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    username VARCHAR(255) DEFAULT NULL,
    action VARCHAR(50) DEFAULT NULL,
    original_content LONGTEXT,
    edited_content LONGTEXT,
    tracked_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_server (server_id),
    KEY idx_channel (channel_id),
    KEY idx_user (user_id),
    KEY idx_action (action),
    KEY idx_tracked_at (tracked_at)
);

-- online_streams
CREATE TABLE IF NOT EXISTS online_streams (
    username VARCHAR(255) NOT NULL,
    twitch_user_id VARCHAR(255) DEFAULT NULL,
    stream_id VARCHAR(255) DEFAULT NULL,
    started_at DATETIME DEFAULT NULL,
    last_checked DATETIME DEFAULT NULL,
    details TEXT,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (username),
    KEY idx_online_last_checked (last_checked),
    KEY idx_online_stream_id (stream_id)
);

-- role_history
CREATE TABLE IF NOT EXISTS role_history (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    user_id VARCHAR(255) NOT NULL,
    role_ids JSON NOT NULL COMMENT 'JSON array of role IDs',
    last_checked TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'Last time we checked if user is in server',
    PRIMARY KEY (id),
    UNIQUE KEY unique_server_user (server_id, user_id),
    KEY idx_server_id (server_id),
    KEY idx_user_id (user_id),
    KEY idx_last_checked (last_checked)
);

-- role_selection_messages
CREATE TABLE IF NOT EXISTS role_selection_messages (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    channel_id VARCHAR(255) NOT NULL,
    message_id VARCHAR(255) NOT NULL,
    message_text TEXT NOT NULL,
    mappings TEXT NOT NULL,
    role_mappings JSON NOT NULL,
    allow_multiple TINYINT(1) DEFAULT 0,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY server_id (server_id),
    KEY idx_server_id (server_id),
    KEY idx_message_id (message_id)
);

-- role_tracking_logs
CREATE TABLE IF NOT EXISTS role_tracking_logs (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    user_id VARCHAR(255) NOT NULL,
    username VARCHAR(255) DEFAULT NULL,
    action VARCHAR(50) NOT NULL COMMENT 'added or removed',
    role_id VARCHAR(255) NOT NULL,
    role_name VARCHAR(255) DEFAULT NULL,
    changed_by VARCHAR(255) DEFAULT NULL COMMENT 'User who made the change, if available',
    changed_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_server_id (server_id),
    KEY idx_user_id (user_id),
    KEY idx_changed_at (changed_at)
);

-- rules_messages
CREATE TABLE IF NOT EXISTS rules_messages (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    channel_id VARCHAR(255) NOT NULL,
    message_id VARCHAR(255) NOT NULL,
    title TEXT,
    rules_content TEXT,
    color VARCHAR(7) DEFAULT NULL,
    accept_role_id VARCHAR(255) DEFAULT NULL,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY unique_server (server_id),
    KEY idx_message_id (message_id)
);

-- server_management
CREATE TABLE IF NOT EXISTS server_management (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    welcomeMessage TINYINT(1) DEFAULT 0,
    autoRole TINYINT(1) DEFAULT 0,
    roleHistory TINYINT(1) DEFAULT 0,
    messageTracking TINYINT(1) DEFAULT 0,
    roleTracking TINYINT(1) DEFAULT 0,
    serverRoleManagement TINYINT(1) DEFAULT 0,
    userTracking TINYINT(1) DEFAULT 0,
    reactionRoles TINYINT(1) DEFAULT 0,
    rulesConfiguration TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Enable/disable rules configuration feature',
    streamSchedule TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Enable/disable stream schedule feature',
    stream_schedule_configuration TEXT COMMENT 'JSON configuration for stream schedule',
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    welcome_message_configuration_channel VARCHAR(255) DEFAULT NULL,
    welcome_message_configuration_message VARCHAR(50) DEFAULT NULL,
    welcome_message_configuration_default INT DEFAULT NULL,
    welcome_message_configuration_embed TINYINT(1) DEFAULT 0,
    welcome_message_configuration_colour VARCHAR(7) DEFAULT '#00d1b2',
    auto_role_assignment_configuration_role_id VARCHAR(255) DEFAULT NULL,
    role_history_configuration_setting INT DEFAULT NULL,
    role_history_configuration_option VARCHAR(255) DEFAULT NULL,
    message_tracking_configuration_channel VARCHAR(255) DEFAULT NULL,
    message_tracking_configuration_message_edits INT DEFAULT NULL,
    message_tracking_configuration_message_delete INT DEFAULT NULL,
    role_tracking_configuration_channel VARCHAR(255) DEFAULT NULL,
    role_tracking_configuration_role_added INT DEFAULT NULL,
    role_tracking_configuration_role_removed INT DEFAULT NULL,
    server_role_management_configuration_channel VARCHAR(255) DEFAULT NULL,
    server_role_management_configuration_role_created INT DEFAULT NULL,
    server_role_management_configuration_role_deleted INT DEFAULT NULL,
    user_tracking_configuration_channel VARCHAR(255) DEFAULT NULL,
    user_tracking_configuration_nickname INT DEFAULT NULL,
    user_tracking_configuration_avatar INT DEFAULT NULL,
    user_tracking_configuration_status INT DEFAULT NULL,
    reaction_roles_configuration JSON DEFAULT NULL,
    rules_configuration JSON DEFAULT NULL COMMENT 'JSON configuration for rules: channel_id, title, rules, color',
    role_history_configuration JSON DEFAULT NULL COMMENT 'JSON config for role history: {enabled: boolean, retention_days: int}',
    role_tracking_configuration JSON DEFAULT NULL COMMENT 'JSON config for role tracking: {enabled: boolean, log_channel_id: string, track_additions: boolean, track_removals: boolean}',
    server_role_management_configuration JSON DEFAULT NULL,
    message_tracking_configuration JSON DEFAULT NULL,
    user_tracking_configuration JSON DEFAULT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY unique_server (server_id),
    KEY idx_server_id (server_id),
    KEY idx_rules_configuration (rulesConfiguration)
);

-- server_role_management_logs
CREATE TABLE IF NOT EXISTS server_role_management_logs (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    role_id VARCHAR(255) NOT NULL,
    role_name VARCHAR(255) DEFAULT NULL,
    action VARCHAR(50) NOT NULL COMMENT 'created or deleted',
    changed_by VARCHAR(255) DEFAULT NULL COMMENT 'User who made the change, if available',
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_server_id (server_id),
    KEY idx_created_at (created_at)
);

-- stream_schedule_messages
CREATE TABLE IF NOT EXISTS stream_schedule_messages (
    id INT NOT NULL AUTO_INCREMENT,
    server_id VARCHAR(255) NOT NULL,
    channel_id VARCHAR(255) NOT NULL,
    message_id VARCHAR(255) NOT NULL,
    schedule_data JSON DEFAULT NULL,
    last_updated TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY unique_server_message (server_id, message_id),
    KEY idx_server_id (server_id),
    KEY idx_message_id (message_id)
);

-- ticket_comments
CREATE TABLE IF NOT EXISTS ticket_comments (
    comment_id INT NOT NULL AUTO_INCREMENT,
    guild_id VARCHAR(255) DEFAULT NULL,
    ticket_id INT NOT NULL,
    user_id VARCHAR(255) DEFAULT NULL,
    username VARCHAR(255) DEFAULT NULL,
    comment TEXT NOT NULL,
    timestamp TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (comment_id),
    KEY idx_ticket_id (ticket_id),
    CONSTRAINT ticket_comments_ibfk_1 FOREIGN KEY (ticket_id) REFERENCES tickets (ticket_id) ON DELETE CASCADE
);

-- ticket_history
CREATE TABLE IF NOT EXISTS ticket_history (
    history_id INT NOT NULL AUTO_INCREMENT,
    ticket_id INT NOT NULL,
    user_id VARCHAR(255) DEFAULT NULL,
    username VARCHAR(255) DEFAULT NULL,
    action VARCHAR(100) DEFAULT NULL,
    details TEXT,
    timestamp TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (history_id),
    KEY idx_ticket_id (ticket_id),
    CONSTRAINT ticket_history_ibfk_1 FOREIGN KEY (ticket_id) REFERENCES tickets (ticket_id) ON DELETE CASCADE
);

-- ticket_settings
CREATE TABLE IF NOT EXISTS ticket_settings (
    id INT NOT NULL AUTO_INCREMENT,
    guild_id VARCHAR(255) DEFAULT NULL,
    owner_id VARCHAR(255) DEFAULT NULL,
    info_channel_id VARCHAR(255) DEFAULT NULL,
    category_id VARCHAR(255) DEFAULT NULL,
    closed_category_id VARCHAR(255) DEFAULT NULL,
    support_role_id VARCHAR(255) DEFAULT NULL,
    mod_channel_id VARCHAR(255) DEFAULT NULL,
    enabled TINYINT(1) DEFAULT 0,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_guild_id (guild_id)
);

-- tickets
CREATE TABLE IF NOT EXISTS tickets (
    ticket_id INT NOT NULL AUTO_INCREMENT,
    guild_id VARCHAR(255) NOT NULL DEFAULT '',
    user_id VARCHAR(255) NOT NULL DEFAULT '',
    username VARCHAR(255) DEFAULT NULL,
    channel_id VARCHAR(255) DEFAULT NULL,
    issue TEXT,
    status ENUM('open','closed') NOT NULL DEFAULT 'open',
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    closed_at DATETIME DEFAULT NULL,
    closed_by BIGINT DEFAULT NULL,
    PRIMARY KEY (ticket_id),
    KEY idx_guild_id (guild_id),
    KEY idx_status (status)
);

-- website: core website tables (api metrics, users, tokens, settings)
CREATE DATABASE IF NOT EXISTS website;
USE website;

CREATE TABLE IF NOT EXISTS api_counts (
    id INT NOT NULL AUTO_INCREMENT,
    type VARCHAR(50) NOT NULL,
    count INT NOT NULL,
    reset_day INT NOT NULL,
    updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY type (type)
);

CREATE TABLE IF NOT EXISTS bot_messages (
    bot_system VARCHAR(255) NOT NULL,
    counted_since DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    messages_sent INT NOT NULL DEFAULT 0,
    last_updated DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (bot_system)
);

CREATE TABLE IF NOT EXISTS custom_bots (
    channel_id VARCHAR(255) NOT NULL,
    bot_username VARCHAR(255) NOT NULL,
    bot_channel_id VARCHAR(255) NOT NULL,
    access_token VARCHAR(255) NOT NULL,
    refresh_token VARCHAR(255) NOT NULL,
    token_expires DATETIME NOT NULL,
    is_verified INT NOT NULL DEFAULT 0,
    PRIMARY KEY (channel_id)
);

CREATE TABLE IF NOT EXISTS discord_users (
    user_id INT NOT NULL,
    discord_id VARCHAR(255) NOT NULL,
    access_token VARCHAR(255) DEFAULT NULL,
    refresh_token VARCHAR(255) DEFAULT NULL,
    reauth INT NOT NULL DEFAULT 0,
    discord_allowed_callers TINYINT NOT NULL DEFAULT 0,
    manual_ids INT NOT NULL DEFAULT 0,
    guild_id VARCHAR(255) DEFAULT NULL,
    live_channel_id VARCHAR(255) DEFAULT NULL,
    stream_alert_channel_id VARCHAR(255) DEFAULT NULL,
    moderation_channel_id VARCHAR(255) DEFAULT NULL,
    alert_channel_id VARCHAR(255) DEFAULT NULL,
    member_streams_id VARCHAR(255) DEFAULT NULL,
    stream_alert_everyone TINYINT NOT NULL DEFAULT 1,
    stream_alert_custom_role VARCHAR(255) DEFAULT NULL,
    online_text VARCHAR(20) NOT NULL DEFAULT 'Live on Twitch',
    offline_text VARCHAR(20) NOT NULL DEFAULT 'Not Live',
    auto_role_id VARCHAR(255) DEFAULT NULL,
    UNIQUE KEY user_id (user_id),
    CONSTRAINT discord_users_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS feedback (
    id INT NOT NULL AUTO_INCREMENT,
    twitch_user_id VARCHAR(64) DEFAULT NULL,
    display_name VARCHAR(255) DEFAULT NULL,
    message TEXT,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS languages (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    code VARCHAR(50) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS moderator_access (
    moderator_id VARCHAR(255) NOT NULL,
    broadcaster_id VARCHAR(255) NOT NULL,
    PRIMARY KEY (moderator_id, broadcaster_id),
    KEY broadcaster_id (broadcaster_id),
    CONSTRAINT moderator_access_ibfk_1 FOREIGN KEY (moderator_id) REFERENCES users (twitch_user_id) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT moderator_access_ibfk_2 FOREIGN KEY (broadcaster_id) REFERENCES users (twitch_user_id) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE IF NOT EXISTS restricted_users (
    id INT NOT NULL AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    twitch_user_id VARCHAR(50) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS spotify_tokens (
    id INT NOT NULL AUTO_INCREMENT,
    user_id INT NOT NULL,
    email TEXT,
    auth TINYINT NOT NULL DEFAULT 1,
    has_access INT NOT NULL DEFAULT 0,
    access_token VARCHAR(255) NOT NULL,
    refresh_token VARCHAR(255) NOT NULL,
    own_client TINYINT NOT NULL DEFAULT 0,
    client_id VARCHAR(255) DEFAULT NULL,
    client_secret VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (id),
    KEY user_id (user_id),
    CONSTRAINT spotify_tokens_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS streamelements_tokens (
    twitch_user_id VARCHAR(50) NOT NULL,
    access_token VARCHAR(255) NOT NULL,
    refresh_token VARCHAR(255) NOT NULL,
    jwt_token LONGTEXT,
    PRIMARY KEY (twitch_user_id)
);

CREATE TABLE IF NOT EXISTS streamlabs_tokens (
    twitch_user_id VARCHAR(255) NOT NULL DEFAULT '',
    access_token LONGTEXT NOT NULL,
    refresh_token LONGTEXT NOT NULL,
    socket_token LONGTEXT,
    expires_in INT NOT NULL DEFAULT 3600,
    created_at INT NOT NULL,
    PRIMARY KEY (twitch_user_id)
);

CREATE TABLE IF NOT EXISTS system_metrics (
    id INT NOT NULL AUTO_INCREMENT,
    server_name VARCHAR(255) NOT NULL,
    cpu_percent FLOAT NOT NULL,
    ram_percent FLOAT NOT NULL,
    ram_used FLOAT NOT NULL,
    ram_total FLOAT NOT NULL,
    disk_percent FLOAT NOT NULL,
    disk_used FLOAT NOT NULL,
    disk_total FLOAT NOT NULL,
    net_sent FLOAT NOT NULL,
    net_recv FLOAT NOT NULL,
    last_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY unique_server (server_name)
);

CREATE TABLE IF NOT EXISTS timezones (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS twitch_bot_access (
    twitch_user_id VARCHAR(255) NOT NULL,
    twitch_access_token VARCHAR(255) NOT NULL,
    PRIMARY KEY (twitch_user_id)
);

CREATE TABLE IF NOT EXISTS users (
    id INT NOT NULL AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    twitch_display_name VARCHAR(50) DEFAULT NULL,
    twitch_user_id VARCHAR(255) NOT NULL,
    access_token VARCHAR(255) DEFAULT NULL,
    refresh_token VARCHAR(255) DEFAULT NULL,
    api_key VARCHAR(32) NOT NULL,
    is_admin TINYINT(1) NOT NULL DEFAULT 0,
    beta_access TINYINT(1) NOT NULL DEFAULT 0,
    is_technical TINYINT(1) NOT NULL DEFAULT 0,
    signup_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    last_login TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    profile_image VARCHAR(255) NOT NULL DEFAULT 'https://cdn.botofthespecter.com/noimage.png',
    email VARCHAR(255) DEFAULT NULL,
    app_password VARCHAR(50) DEFAULT NULL,
    language VARCHAR(5) NOT NULL DEFAULT 'EN',
    PRIMARY KEY (id),
    UNIQUE KEY username (username),
    UNIQUE KEY api_key (api_key),
    KEY idx_twitch_user_id (twitch_user_id)
);

-- yourlinks: OPTIONAL - short links system (only if running your own short links domain)
CREATE DATABASE IF NOT EXISTS yourlinks;
USE yourlinks;

CREATE TABLE IF NOT EXISTS users (
    id INT NOT NULL AUTO_INCREMENT,
    twitch_id VARCHAR(50) NOT NULL,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(255) NOT NULL,
    display_name VARCHAR(255) NOT NULL,
    profile_image_url TEXT,
    custom_domain VARCHAR(255) DEFAULT NULL,
    domain_verified TINYINT(1) DEFAULT 0,
    domain_verification_token VARCHAR(64) DEFAULT NULL,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY twitch_id (twitch_id),
    UNIQUE KEY username (username),
    UNIQUE KEY custom_domain (custom_domain),
    KEY idx_twitch_id (twitch_id),
    KEY idx_username (username),
    KEY idx_custom_domain (custom_domain),
    KEY idx_domain_verified (domain_verified)
);

CREATE TABLE IF NOT EXISTS categories (
    id INT NOT NULL AUTO_INCREMENT,
    user_id INT NOT NULL,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    color VARCHAR(7) DEFAULT '#3273dc',
    icon VARCHAR(50) DEFAULT 'fas fa-tag',
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY unique_user_category (user_id, name),
    KEY idx_user_id (user_id),
    KEY idx_name (name),
    CONSTRAINT categories_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS links (
    id INT NOT NULL AUTO_INCREMENT,
    user_id INT NOT NULL,
    link_name VARCHAR(100) NOT NULL,
    original_url TEXT NOT NULL,
    title VARCHAR(255) DEFAULT NULL,
    category_id INT DEFAULT NULL,
    expires_at DATETIME DEFAULT NULL,
    expired_redirect_url TEXT,
    expiration_behavior ENUM('inactive','redirect','custom_page') DEFAULT 'inactive',
    expired_page_title VARCHAR(255) DEFAULT 'Link Expired',
    expired_page_message TEXT,
    deactivation_behavior ENUM('inactive','redirect','custom_page') DEFAULT 'inactive',
    deactivated_redirect_url TEXT,
    deactivated_page_title VARCHAR(255) DEFAULT 'Link Deactivated',
    deactivated_page_message TEXT,
    is_active TINYINT(1) DEFAULT 1,
    clicks INT DEFAULT 0,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    UNIQUE KEY unique_user_link (user_id, link_name),
    KEY idx_user_id (user_id),
    KEY idx_category_id (category_id),
    KEY idx_link_name (link_name),
    KEY idx_expires_at (expires_at),
    KEY idx_is_active (is_active),
    CONSTRAINT links_ibfk_1 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
    CONSTRAINT links_ibfk_2 FOREIGN KEY (category_id) REFERENCES categories (id) ON DELETE SET NULL
);

CREATE TABLE IF NOT EXISTS link_clicks (
    id INT NOT NULL AUTO_INCREMENT,
    link_id INT NOT NULL,
    ip_address VARCHAR(45) DEFAULT NULL,
    user_agent TEXT,
    referrer TEXT,
    is_expired TINYINT(1) DEFAULT 0,
    is_deactivated TINYINT(1) DEFAULT 0,
    created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    KEY idx_link_id (link_id),
    KEY idx_created_at (created_at),
    KEY idx_ip_address (ip_address),
    KEY idx_is_expired (is_expired),
    KEY idx_is_deactivated (is_deactivated),
    CONSTRAINT link_clicks_ibfk_1 FOREIGN KEY (link_id) REFERENCES links (id) ON DELETE CASCADE
);

Then create your database user with access to all databases:

CREATE USER 'your_username'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'your_username'@'%';
FLUSH PRIVILEGES;

⚠️ IMPORTANT: Per-User Databases Are Auto-Created

DO NOT manually create user databases! The system automatically creates a dedicated database for each user on their first login.

When a user logs in for the first time, the bot automatically:

  • Creates a new database named after their Twitch username
  • Sets up all necessary tables and initial configurations
  • Configures default settings and permissions

The example below shows what the system automatically generates for a user named "botofthespecter":

Example: Auto-Generated User Database (botofthespecter)

This database is created automatically by the system - you do not need to run these commands manually:

-- User-specific database (auto-created on first login)
CREATE DATABASE IF NOT EXISTS botofthespecter;
USE botofthespecter;

-- Core bot tables
CREATE TABLE IF NOT EXISTS bot_settings (
    id INT NOT NULL AUTO_INCREMENT,
    timezone VARCHAR(255) DEFAULT 'America/New_York',
    weather_location VARCHAR(255) DEFAULT NULL,
    weather_api_key VARCHAR(255) DEFAULT NULL,
    discord_server_id VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS everyone (
    username TEXT NOT NULL,
    status TEXT,
    PRIMARY KEY (username(255))
);

CREATE TABLE IF NOT EXISTS seen_users (
    id INT NOT NULL AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    display_name VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (id)
);

-- Command tables
CREATE TABLE IF NOT EXISTS builtin_commands (
    command TEXT NOT NULL,
    status TEXT,
    permission TEXT,
    PRIMARY KEY (command(255))
);

CREATE TABLE IF NOT EXISTS custom_commands (
    command TEXT NOT NULL,
    response TEXT,
    status TEXT,
    PRIMARY KEY (command(255))
);

CREATE TABLE IF NOT EXISTS custom_user_commands (
    command TEXT NOT NULL,
    response TEXT,
    status TEXT,
    PRIMARY KEY (command(255))
);

CREATE TABLE IF NOT EXISTS command_options (
    command TEXT NOT NULL,
    status TEXT,
    PRIMARY KEY (command(255))
);

-- Points and engagement
CREATE TABLE IF NOT EXISTS bot_points (
    user_id VARCHAR(50) NOT NULL,
    points INT DEFAULT 0,
    PRIMARY KEY (user_id)
);

CREATE TABLE IF NOT EXISTS message_counts (
    username TEXT NOT NULL,
    count INT DEFAULT 0,
    PRIMARY KEY (username(255))
);

CREATE TABLE IF NOT EXISTS seen_today (
    user_id TEXT NOT NULL,
    points_gained INT DEFAULT 0,
    PRIMARY KEY (user_id(255))
);

-- Interaction counts
CREATE TABLE IF NOT EXISTS hug_counts (
    username TEXT NOT NULL,
    hug_count INT DEFAULT 0,
    PRIMARY KEY (username(255))
);

CREATE TABLE IF NOT EXISTS kiss_counts (
    username TEXT NOT NULL,
    kiss_count INT DEFAULT 0,
    PRIMARY KEY (username(255))
);

CREATE TABLE IF NOT EXISTS highfive_counts (
    username VARCHAR(255) NOT NULL,
    highfive_count INT DEFAULT 0,
    PRIMARY KEY (username)
);

-- Stream data
CREATE TABLE IF NOT EXISTS followers_data (
    id INT NOT NULL AUTO_INCREMENT,
    follower_id VARCHAR(255) NOT NULL,
    follower_name VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS bits_data (
    id INT NOT NULL AUTO_INCREMENT,
    user_id VARCHAR(255) NOT NULL,
    bits_used INT DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS raid_data (
    id INT NOT NULL AUTO_INCREMENT,
    raider_name VARCHAR(255) NOT NULL,
    viewers INT DEFAULT 0,
    raided_at DATETIME DEFAULT NULL,
    PRIMARY KEY (id)
);

-- Channel rewards
CREATE TABLE IF NOT EXISTS channel_point_rewards (
    reward_id VARCHAR(255) NOT NULL,
    reward_title VARCHAR(255) DEFAULT NULL,
    status VARCHAR(50) DEFAULT 'enabled',
    PRIMARY KEY (reward_id)
);

CREATE TABLE IF NOT EXISTS reward_counts (
    id INT NOT NULL AUTO_INCREMENT,
    reward_id VARCHAR(255) NOT NULL,
    user VARCHAR(255) NOT NULL,
    count INT DEFAULT 0,
    PRIMARY KEY (id),
    UNIQUE KEY reward_id (reward_id, user)
);

CREATE TABLE IF NOT EXISTS reward_streaks (
    reward_id VARCHAR(255) NOT NULL,
    current_streak INT DEFAULT 0,
    highest_streak INT DEFAULT 0,
    PRIMARY KEY (reward_id)
);

-- Game features
CREATE TABLE IF NOT EXISTS game_deaths (
    game_name TEXT NOT NULL,
    death_count INT DEFAULT 0,
    PRIMARY KEY (game_name(255))
);

CREATE TABLE IF NOT EXISTS game_deaths_settings (
    game_name VARCHAR(255) NOT NULL,
    PRIMARY KEY (game_name)
);

CREATE TABLE IF NOT EXISTS per_stream_deaths (
    game_name VARCHAR(255) NOT NULL,
    death_count INT DEFAULT 0,
    PRIMARY KEY (game_name)
);

-- Quotes and categories
CREATE TABLE IF NOT EXISTS quotes (
    id INT NOT NULL AUTO_INCREMENT,
    quote TEXT,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS quote_category (
    id INT NOT NULL AUTO_INCREMENT,
    category_name VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS categories (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

-- User management
CREATE TABLE IF NOT EXISTS groups (
    id INT NOT NULL AUTO_INCREMENT,
    group_name VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS custom_counts (
    id INT NOT NULL AUTO_INCREMENT,
    counter_name VARCHAR(255) NOT NULL,
    count INT DEFAULT 0,
    PRIMARY KEY (id)
);

-- Timers and lurk
CREATE TABLE IF NOT EXISTS active_timers (
    user_id BIGINT NOT NULL,
    start_time DATETIME DEFAULT NULL,
    PRIMARY KEY (user_id)
);

CREATE TABLE IF NOT EXISTS lurk_times (
    user_id TEXT NOT NULL,
    total_lurk_time INT DEFAULT 0,
    PRIMARY KEY (user_id(255))
);

-- Protection and moderation
CREATE TABLE IF NOT EXISTS protection (
    url_blocking TEXT
);

CREATE TABLE IF NOT EXISTS link_whitelist (
    link TEXT NOT NULL,
    PRIMARY KEY (link(255))
);

CREATE TABLE IF NOT EXISTS link_blacklisting (
    link TEXT NOT NULL,
    PRIMARY KEY (link(255))
);

-- Polls and bingo
CREATE TABLE IF NOT EXISTS poll_results (
    poll_id TEXT,
    title TEXT,
    status TEXT,
    started_at DATETIME DEFAULT NULL,
    ended_at DATETIME DEFAULT NULL
);

CREATE TABLE IF NOT EXISTS bingo_games (
    game_id VARCHAR(255) NOT NULL,
    started_at DATETIME DEFAULT NULL,
    ended_at DATETIME DEFAULT NULL,
    status VARCHAR(50) DEFAULT 'active',
    PRIMARY KEY (game_id)
);

CREATE TABLE IF NOT EXISTS bingo_winners (
    id INT NOT NULL AUTO_INCREMENT,
    game_id VARCHAR(255) NOT NULL,
    username VARCHAR(255) NOT NULL,
    won_at DATETIME DEFAULT NULL,
    PRIMARY KEY (id),
    CONSTRAINT bingo_winners_ibfk_1 FOREIGN KEY (game_id) REFERENCES bingo_games (game_id) ON DELETE CASCADE
);

-- Profile and settings
CREATE TABLE IF NOT EXISTS profile (
    id INT NOT NULL AUTO_INCREMENT,
    bio TEXT,
    location VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS showobs (
    status TEXT
);

CREATE TABLE IF NOT EXISTS chat_history (
    author VARCHAR(255) NOT NULL,
    message TEXT,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Module settings
CREATE TABLE IF NOT EXISTS ad_notice_settings (
    id INT NOT NULL AUTO_INCREMENT,
    enabled TINYINT(1) DEFAULT 0,
    message TEXT,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS joke_settings (
    id INT NOT NULL AUTO_INCREMENT,
    enabled TINYINT(1) DEFAULT 0,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS auto_record_settings (
    id INT NOT NULL AUTO_INCREMENT,
    enabled TINYINT(1) DEFAULT 0,
    server_location VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY unique_server_location (server_location)
);

CREATE TABLE IF NOT EXISTS member_streams (
    id INT NOT NULL AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    enabled TINYINT(1) DEFAULT 1,
    PRIMARY KEY (id),
    UNIQUE KEY username (username)
);

Configure MySQL to accept connections from other servers by editing /etc/mysql/mysql.conf.d/mysqld.cnf:

bind-address = 0.0.0.0

Step 3: Set Up Python Environment (Servers 2, 3, & 5)

All application servers share the same repository path: /home/botofthespecter. Create the virtual environment in that directory and use the venv's pip/python directly so commands are deterministic and work the same on every server.

Recommended venv location: /home/botofthespecter/botofthespecter

# create the venv (run as the botofthespecter user)
python3 -m venv botofthespecter
# install all required packages from the single shared requirements file
/home/botofthespecter/botofthespecter/bin/pip install -r /home/botofthespecter/requirements.txt

Production notes:

  • Reference the virtualenv executables directly in systemd unit files. Example: Environment="PATH=/home/botofthespecter/botofthespecter/bin" and ExecStart=/home/botofthespecter/botofthespecter/bin/python /home/botofthespecter/api/api.py.
  • Always run the venv creation and package installs as the botofthespecter user to ensure correct ownership of files and installed packages.
  • Always run the venv creation and package installs as the botofthespecter user to ensure correct ownership of files and installed packages.
  • Step 4: Configure Environment Variables (All Servers)

    Create a .env file in /home/botofthespecter with your configuration. Replace the placeholders with your actual values:

    # SQL Data
    SQL_HOST=
    SQL_USER=
    SQL_PASSWORD=
    SQL_PORT=
    # API STUFF
    SHAZAM_API=
    WEATHER_API=
    STEAM_API=
    OPENAI_KEY=
    OPENAI_VECTOR_ID=
    STREAMELEMENTS_CLIENT_ID=
    STREAMELEMENTS_SECRET_KEY=
    HYPERATE_API_KEY=
    # Twitch Bot
    OAUTH_TOKEN=oauth:
    TWITCH_OAUTH_API_TOKEN=
    TWITCH_OAUTH_API_CLIENT_ID=
    CLIENT_ID=
    CLIENT_SECRET=
    TWITCH_GQL=
    TIMEZONE_API=
    EXCHANGE_RATE_API=
    SPOTIFY_CLIENT_ID=
    SPOTIFY_CLIENT_SECRET=
    BOT_ID=
    # Discord Bot
    DISCORD_TOKEN=
    DISCORD_PUBLIC_KEY=
    API_KEY=
    DISCORD_CLIENT_ID=
    DISCORD_CLIENT_SECRET=
    # Guided Bot
    GUIDED_BOT_USER_ID=
    GUIDED_BOT_TOKEN=
    # ADMINS
    ADMIN_KEY=
    # BACKUP SYSTEM
    USE_BACKUP_SYSTEM=False
    BACKUP_CLIENT_ID=
    BACKUP_SECRET_KEY=
    # SSH Settings
    SSH_USERNAME=
    SSH_PASSWORD=
    API-HOST=
    WEBSOCKET-HOST=
    BOT-SRV-HOST=
    SQL-HOST=
    WEB-HOST=
    BILLING-HOST=
    STREAM-AU-EAST-1-HOST=
    STREAM-US-EAST-1-HOST=
    STREAM-US-WEST-1-HOST=
    # STMP Email Settings
    SMTP_HOST=
    SMTP_PORT=465
    SMTP_FROM_NAME=
    SMTP_USERNAME=
    SMTP_PASSWORD=
    # S3 Bucket Settings for Exports Only
    S3_ENDPOINT_HOSTNAME=
    S3_CUSTOM_DOMAIN=
    S3_BUCKET_NAME=
    S3_ACCESS_KEY=
    S3_SECRET_KEY=
    S3_ALWAYS_UPLOAD=True

    Required Variables:

    • SQL_*: Database connection details (must match Server 4 configuration)
    • CLIENT_ID & CLIENT_SECRET: Your Twitch application credentials
    • OAUTH_TOKEN: Bot account OAuth token (get from https://twitchtokengenerator.com/)
    • API_KEY: Generate a secure random API key for internal service authentication

    Optional Variables:

    • WEATHER_API: For weather commands (get from OpenWeatherMap)
    • SPOTIFY_*: For Spotify integration (get from Spotify Developer Dashboard)
    • DISCORD_*: For Discord bot functionality (get from Discord Developer Portal)
    • OPENAI_KEY: For AI features (get from OpenAI)
    • S3_*: For user data exports to object storage (optional, exports can be local)
    • SMTP_*: For email notifications (optional)

    Server Host Variables:

    • API-HOST, WEBSOCKET-HOST, etc: Set these to the IP addresses or hostnames of your respective servers for inter-server communication

    Step 5: Set Up Python Environment (Server 3 - WebSocket)

    Install Python dependencies for the WebSocket server:

    cd /home/botofthespecter
    source /home/botofthespecter/botofthespecter/bin/activate
    /home/botofthespecter/botofthespecter/bin/pip install -r /home/botofthespecter/requirements.txt

    Step 6: Set Up Web Server (Server 1)

    Configure Apache2 to serve the PHP dashboard:

    sudo apt install -y apache2 libapache2-mod-php
    # Enable Apache2 modules
    sudo a2enmod rewrite
    sudo a2enmod php8.1
    # Create Apache2 configuration
    sudo nano /etc/apache2/sites-available/botofthespecter.conf

    Configure Apache (or your web server) however you prefer. You must serve the dashboard and related assets under your domain and the following example subdomains. We don't enforce a specific VirtualHost layout — pick the configuration that matches your infrastructure and SSL setup.

    Example DNS / subdomain names you should configure for your deployment:
    example.com
    dashboard.example.com
    overlay.example.com
    videoalert.example.com
    soundalert.example.com
    tts.example.com
    
    Tip: Use separate subdomains for static assets or features you want to scale independently.
    Ensure each subdomain points to the correct server (Server 1 for the dashboard/static sites) and that TLS is enabled (Let's Encrypt is recommended).

    Running the Services

    Server 1: Start the Web/Dashboard Server

    Ensure Apache2 and PHP are running:

    sudo systemctl enable apache2
    sudo systemctl start apache2
    sudo systemctl status apache2

    Server 2: Start the API Server

    From the API Server:

    cd /home/botofthespecter
    source /home/botofthespecter/botofthespecter/bin/activate
    # Run the API with TLS (replace the paths with your domain's Let's Encrypt certs)
    python -m uvicorn api.api:app --host 0.0.0.0 --port 443 \
      --ssl-keyfile=/etc/letsencrypt/live/api.example.com/privkey.pem \
      --ssl-certfile=/etc/letsencrypt/live/api.example.com/fullchain.pem

    Note: TLS is required for the API server. Update the cert paths above to match your domain (for example: /etc/letsencrypt/live/api.botofthespecter.com/privkey.pem).

    For production, create a systemd service similar to the bot service below.

    Server 3: Start the WebSocket Server

    From the WebSocket Server:

    cd /home/botofthespecter
    source /home/botofthespecter/botofthespecter/bin/activate
    python /home/botofthespecter/server.py

    For production, create a systemd service similar to the bot service below.

    Server 4: MySQL/MariaDB Database

    Ensure the database service is running:

    sudo systemctl enable mysql
    sudo systemctl start mysql
    sudo systemctl status mysql

    Server 5: Bot Service

    The bot is controlled and started from the dashboard (Server 1). No manual setup or startup is required on Server 5; it is ready once the Python environment and `.env` configuration from the earlier steps are complete.

    Inter-Server Networking

    Configure your servers to communicate securely with each other:

    • Internal Network: Use private IP addresses for inter-server communication
    • DNS/Hostnames: Set up DNS or /etc/hosts entries for server-to-server connections
    • Firewall Rules: Only allow necessary ports between servers
    • SSL/TLS: Encrypt communication between services

    Firewall Configuration Example

    # Server 1 (Web) - Allow HTTP/HTTPS and communication with other services
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow from 10.10.10.2:8001  # API Server
    sudo ufw allow from 10.10.10.3:8000  # WebSocket Server
    sudo ufw allow from 10.10.10.4:3306  # Database Server
    
    # Server 2 (API) - Allow inbound from Web and Bot servers
    sudo ufw allow from 10.10.10.1:any   # Web Server
    sudo ufw allow from 10.10.10.5:any   # Bot Server
    
    # Server 3 (WebSocket) - Allow inbound from Web and Bot servers
    sudo ufw allow from 10.10.10.1:any   # Web Server
    sudo ufw allow from 10.10.10.5:any   # Bot Server
    
    # Server 4 (Database) - Allow inbound from all services
    sudo ufw allow from 10.10.10.1:any   # Web Server
    sudo ufw allow from 10.10.10.2:any   # API Server
    sudo ufw allow from 10.10.10.5:any   # Bot Server
    
    # Server 5 (Bot) - Allow outbound to API, WebSocket, and Database
    sudo ufw allow to 10.10.10.2:8001    # API Server
    sudo ufw allow to 10.10.10.3:8000    # WebSocket Server
    sudo ufw allow to 10.10.10.4:3306    # Database Server

    Security Considerations

    • HTTPS/SSL: Always use SSL certificates for secure communication (Let's Encrypt is free)
    • Firewall: Configure firewalls to restrict database access to localhost only
    • Environment Variables: Never commit .env files to version control
    • Database Backups: Set up automated daily backups of your database
    • Updates: Keep dependencies updated to patch security vulnerabilities
    • Monitoring: Monitor system resources and bot logs for issues

    Troubleshooting

    Bot Not Connecting to Twitch

    • Verify your OAuth token is valid and not expired
    • Check that your Twitch Client ID and Secret are correct
    • Ensure the bot account has the proper permissions in your channel
    • Review logs in bot/logs/ for error messages

    Database Connection Errors

    • Verify MySQL/MariaDB is running
    • Check database credentials in .env and configuration files
    • Ensure the user has proper database permissions
    • Test connection: mysql -u botuser -p -h localhost botofthespecter

    API Server Not Responding

    • Verify FastAPI/Uvicorn is running correctly
    • Check that port 443 is not in use by another service
    • Review API logs for startup errors
    • Ensure all Python dependencies are installed

    WebSocket Connection Issues

    • Verify WebSocket server is running on port 8000
    • Check firewall rules allow WebSocket connections
    • Ensure the WebSocket URL is correctly configured in clients
    • Review WebSocket server logs for errors

    Maintenance

    Regular Tasks

    • Daily: Check logs for errors and unusual activity
    • Weekly: Verify all services are running and responsive
    • Monthly: Update dependencies and apply security patches
    • Quarterly: Review and optimize database performance

    Updating BotOfTheSpecter

    git pull origin main
    pip install -r bot/requirements.txt --upgrade
    pip install -r api/requirements.txt --upgrade

    Need Help?

    If you encounter issues while self-hosting BotOfTheSpecter: