Skip to content

Advanced Configuration

Ashton edited this page Feb 11, 2026 · 11 revisions

Advanced Topics & Technical Guide

Version: 1.5.11

← Back to USER_GUIDE | FAQ →


📑 Table of Contents

  1. Security & Privacy
  2. Performance & Scaling
  3. File Structure Reference
  4. Variable Reference
  5. Migration Guides
  6. Custom Integration Examples
  7. Backup & Recovery

Security & Privacy

Encryption Details

The bot uses AES-256-CBC encryption for API keys with the following implementation:

Auto-Encryption (Updated in 1.4.3): The bot automatically upgrades legacy keys to AES-256-CBC using a portable salt.

  • Plain text (abc-123) → Encrypted (AES:...)
  • Legacy Base64 (OBF:...) → Encrypted (AES:...)

Key Derivation (Portable):

Salt = GlobalConfig.Globals.EncryptionSalt (Random 32-byte hash)
Key = PBKDF2(Salt, Salt, 1000 iter) -> 32 bytes
IV  = First 16 bytes of Salt

Why Portable? Unlike previous versions that tied keys to your Windows User ID (DPAPI), the EncryptionSalt is stored in your giveaway_config.json.

  • Portable: You can copy your entire Giveaway Bot folder to another PC, and it will still work.
  • Secure: The salt is unique to your installation.
  • Resilient: Survives Windows reinstallations.

Security Warning:

  • Do NOT share your giveaway_config.json publicly, as it contains the salt used to decrypt your keys.
  • Always exclude giveaway_config.json from public git repositories (add to .gitignore).

Data Retention Policy

What's Stored:

Data Type Location Retention Purpose
Entry Records dumps/Main/*.txt Manual deletion Winner verification
Logs logs/General/*.log LogRetentionDays (default 90) Debugging
State Files state/*.json Until profile deleted Active giveaway data
Config config/*.json Permanent Bot settings

GDPR Compliance:

  • No PII beyond username/UserID (public Twitch data)
  • Local storage only (except Wheel API if enabled)
  • Users can request data deletion (manual file removal)

API Key Security Best Practices

  1. Never share encrypted blobs (AES:...) - they contain your key

  2. Limit API key scope - use Wheel of Names' read-only keys if possible

  3. Rotate keys - change periodically (update variable + re-encrypt)

  4. Monitor logs - watch for [Security] warnings

  5. Monitor logs - watch for [Security] warnings


Game Name Support (New in v1.5.11)

You can allow users to specify which game or item they want when entering. This is useful for "Key Dump" giveaways where you have multiple different keys to give away.

Configuration

In your profile config:

"RequireGameName": false, // If true, users MUST type "!enter <Name>"
"GameNameRequiredMessage": "Please specify a game! Usage: !enter <Game Name>"

How it works

  1. User types !enter Elden Ring
  2. Bot records "Elden Ring" as their GameName selection.
  3. When you draw a winner, their selection is shown in:
    • The winner announcement message
    • The dumps/..._Winners_GameNames.txt file

Dumps

The bot now generates specific files for game names to make distributing keys easier:

  • dumps/Main/Winners_GameNames.txt: Contains only the Game Names of the winners (in order).
  • dumps/Main/Entries_GameNames.txt: Contains only the Game Names of all entrants.

Performance & Scaling

Recommended Settings by Stream Size

Small Streams (<100 viewers)

{
  "MaxEntriesPerMinute": 60,
  "StateSyncIntervalSeconds": 30,
  "LogLevel": "INFO",
  "DumpEntriesOnEntry": false,
  "ExposeVariables": true
}

Why: Balanced performance, minimal overhead

Medium Streams (100-1,000 viewers)

{
  "MaxEntriesPerMinute": 200,
  "StateSyncIntervalSeconds": 60,
  "LogLevel": "WARN",
  "DumpEntriesOnEntry": true,
  "DumpEntriesOnEntryThrottle": 30,
  "ExposeVariables": true
}

Why: Higher rate limits, less frequent disk writes

Large Streams (1,000+ viewers)

{
  "MaxEntriesPerMinute": 500,
  "StateSyncIntervalSeconds": 120,
  "LogLevel": "ERROR",
  "DumpEntriesOnEntry": false,
  "ExposeVariables": false,
  "EnableEntropyCheck": true,
  "MinAccountAgeDays": 30
}

Why: Aggressive rate limiting, bot protection, minimal I/O

Performance Bottlenecks

Disk I/O (Slow HDD):

  • Symptom: Streamer.bot freezes when entries spike
  • Solution 1: Set StatePersistenceMode: GlobalVar (risk: data loss on crash)
  • Solution 2: Move Streamer.bot folder to SSD
  • Solution 3: Increase StateSyncIntervalSeconds to 120+

CPU (Regex Validation):

  • Symptom: High CPU when UsernamePattern or ExternalListeners enabled
  • Solution: Simplify regex patterns, avoid backtracking (e.g., .* is slow)
  • Test patterns with !giveaway regex test for performance

Memory (Large Entry Counts):

  • Symptom: Streamer.bot memory usage grows unbounded
  • Solution: Close/archive old giveaways regularly
  • Estimate: ~100 bytes per entry → 10,000 entries ≈ 1 MB (negligible)

Smart Sync Optimization (New in v1.5.11)

The bot now uses an intelligent "Diff & Sync" system (SetGlobalVarIfChanged). It only sends updates to Streamer.bot when variables actually change.

  • Impact: Reduces IPC log spam by 99% during idle periods.
  • Benefit: You can leave ExposeVariables: true enabled even on lower-end systems with minimal performance penalty.

Optimization Checklist

  • Use Mirror RunMode (best for stability + performance)
  • Set LogLevel to INFO or higher (avoid TRACE/DEBUG in production)
  • Set LogLevel to INFO or higher. NOTE: TRACE level will output high-frequency variable sync logs (spammy).
  • Disable DumpEntriesOnEntry for streams >500 viewers
  • Increase StateSyncIntervalSeconds if on slow HDD
  • Enable EnableEntropyCheck if seeing bot/fake accounts
  • Use Wheel API Animate mode (lower overhead than interactive)

Remote Control & Automation

As of v1.3.3, you can control the bot programmatically by setting specific Global Variables in Streamer.bot. This allows you to start/end giveaways from your own Actions, Stream Deck, or other integration logic without simulating chat commands.

How to use Integration Control

To trigger an action, use the Core -> Global Variables -> Set Global Variable sub-action in Streamer.bot.

Goal Variable Name Set Value to... Effect
Start Giveaway Giveaway <Profile> Is Active True Opens the giveaway (if closed). Triggers HandleStart.
End Giveaway Giveaway <Profile> Is Active False Closes the giveaway (if open). Triggers HandleEnd.
Change Timer Giveaway <Profile> Timer Duration "5m" Updates auto-close timer and announces new time.

Important

System Override: Variable-based triggers bypass the usual "Moderator" permission check, allowing automated systems to control the bot.


File Structure Reference

C:\Users\<You>\Streamer.bot\data\Giveaway Bot\
│
├── config\
│   └── giveaway_config.json         # Main configuration
│       ├── Globals (Bot-wide settings)
│       └── Profiles
│           ├── Main
│           ├── Weekly
│           └── ...
│
├── dumps\                            # Entry/Winner exports
│   ├── Main\
│   │   ├── 20260128_Entries.txt              # Final snapshot (!end)
│   │   ├── 20260128_Entries_GameNames.txt    # (v1.5.3) Just GameNames/Codes
│   │   ├── 20260128_Entries_Incremental.txt # Real-time (if enabled)
│   │   ├── 20260128_Winners.txt               # Winner log
│   │   └── 20260128_Winners_GameNames.txt     # (v1.5.3) Just Winner GameNames
│   └── Weekly\
│       └── ...
│
├── logs\                             # Debug & error logs
│   └── General\
│       ├── 2026-01-28.log            # Daily rotation
│       ├── 2026-01-27.log
│       └── ...                       # Auto-pruned after LogRetentionDays
│
└── state\                            # Active giveaway data
    ├── Main.json                     # Real-time entry list
    └── Weekly.json

File Formats:

giveaway_config.json: Standard JSON, editable in any text editor state/Main.json: JSON with UTF-8 encoding, contains active entries dumps/*.txt: Plain text, one entry per line:

[14:23:45] UserName (123456) - Tickets: 3

logs/*.log: Plaintext with ISO 8601 timestamps:

[2026-01-28 14:23:45] [INFO] [GiveawayManager] Giveaway started

Variable Reference

Profile-Specific Variables

(Exposed when ExposeVariables: true in profile)

Variable Name Type Update Frequency Example
Giveaway <Profile> Is Active Boolean Immediate (on start/end) true
Giveaway <Profile> Entry Count Integer Per entry (+1) 47
Giveaway <Profile> Ticket Count Integer Per entry (incl. sub luck) 68
Giveaway <Profile> Winner Name String On draw CoolViewer123
Giveaway <Profile> Winner User Id String On draw 987654321
Giveaway <Profile> Last Entry String Per entry NewUser456
Giveaway <Profile> Draw Time DateTime On draw 2026-01-28 14:30:00
Giveaway <Profile> Winner Count Integer Per Draw 3
Giveaway <Profile> Cumulative Entries Integer Per entry 150
Giveaway <Profile> Sub Entry Count Integer Per entry 12
Giveaway <Profile> Timer Duration String On config change 10m
Giveaway <Profile> Msg <Key> String On config change Winner is {0}!
Giveaway <Profile> Max Entries Per Minute Integer On config change 100
Giveaway <Profile> Require Subscriber Boolean On config change true
Giveaway <Profile> Sub Luck Multiplier Decimal On config change 1.5

Configuration Variables

Variable Name Default Description
Giveaway Global Run Mode Mirror Config sync mode
Giveaway Global Log Level INFO Minimum log severity
Giveaway Global Log Max File Size MB 10 Max size of a single log file
Giveaway Global Log Size Cap MB 100 Total log directory size cap
Giveaway Global Log Prune Probability 100 1-in-N chance to prune logs on startup
Giveaway Global Wheel Api Key (empty) Wheel API key (Auto-Encrypts if entered as plain text)
Giveaway Global Wheel Api Key Status Missing Current status of the API Key
Giveaway Global Enabled Platforms Twitch,YouTube Comma-separated list of active platforms
Giveaway Global Security Toasts True Toggle for security-related notifications

Global Metrics

(Always available, useful for observability and debugging)

Variable Name Type Description
Giveaway Global Metrics Entries Total Integer Lifetime entries accepted
Giveaway Global Metrics Entries Rejected Integer Spam/bots/regex-mismatches blocked
Giveaway Global Metrics Winners Total Integer Total winners drawn
Giveaway Global Metrics Entries Processed Integer Total entry commands handled
Giveaway Global Metrics Entry Processing Avg Ms Integer Avg time to process an entry (performance)
Giveaway Global Metrics File IO Errors Integer Disk write failures (check permissions)
Giveaway Global Metrics Config Reloads Integer Number of times config was reloaded
Giveaway Global Metrics Loop Detected Integer Anti-loop protection triggers fired
Giveaway Global Metrics Api Errors Integer Total API failures (Wheel + General)
Giveaway Global Metrics System Errors Integer Internal exceptions rooted in bot logic

Wheel of Names Metrics:

Variable Name Type Description
Giveaway Global Metrics Wheel Api Calls Integer Total calls to Wheel of Names API
Giveaway Global Metrics Wheel Api Total Ms Integer Total time spent waiting for Wheel API
Giveaway Global Metrics Wheel Api Avg Ms Integer Avg latency for Wheel API calls
Giveaway Global Metrics Wheel Api Errors Integer API failures (5xx, Network)
Giveaway Global Metrics Wheel Api Invalid Keys Integer Auth failures (403)
Giveaway Global Metrics Wheel Api Timeouts Integer Requests that took too long

Internal State Variables

(Read-Only, used for debugging and sync)

Variable Name Description
Giveaway Global Config Full JSON content of current config
Giveaway Global Config Last Write Time Timestamp of last config file write
Giveaway Global Last Config Errors Error message if config failed to load
Giveaway Global Backup Count Number of config backups found
Giveaway Global Instructions Header instructions from config file
Giveaway Global Trigger Help Trigger prefix help text

OBS Usage Example:

Text Source: "Entries: %GiveawayBot_Main_EntryCount% | Winner: %GiveawayBot_Main_WinnerName%"

Migration Guides

From Nightbot/Moobot Giveaways

Old System:

  • Nightbot stores list of users
  • Manual !winner command picks random from list

Migration Steps:

  1. Export old entries (if possible) from Nightbot dashboard

  2. Manually add to state/Main.json (or start fresh)

  3. Configure external listener:

    "AllowedExternalBots": ["Nightbot"],
    "ExternalListeners": [
      { "Pattern": "(?i)giveaway.*open", "Action": "Open" },
      { "Pattern": "(?i)giveaway.*close", "Action": "Close" }
    ]
  4. Test: Have Nightbot say "GIVEAWAY OPEN" → Bot should auto-start

Benefits:

  • Wheel integration
  • Sub luck
  • Anti-bot protection
  • OBS variables

From StreamElements

Old System:

  • StreamElements Giveaway module
  • Web dashboard management

Migration:

  1. No direct import - SE doesn't export entry data
  2. Fresh start recommended
  3. Replicate settings manually:
    • SE "Subscriber Luck" → SubLuckMultiplier
    • SE "Account Age" → MinAccountAgeDays

From Excel Sheets / Manual Tracking

Old System:

  • Copy/paste usernames to Excel
  • Use =RAND() to pick winner

Migration:

  1. If you have old entry list, convert to JSON:

    [
      {
        "UserId": "123456",
        "UserName": "OldWinner",
        "EntryTime": "2026-01-15T10:00:00",
        "TicketCount": 1
      }
    ]
  2. Paste into state/Main.json under "Entries": {}

  3. Run !giveaway system test to validate


Custom Integration Examples

Discord Webhook on Winner

Edit HandleDrawWinner method in GiveawayBot.cs to add:

// After winner selection
var webhookUrl = "https://discord.com/api/webhooks/YOUR_WEBHOOK";
var payload = new {
    content = $"🎉 Giveaway Winner: **{winnerEntry.UserName}**!"
};
var json = JsonConvert.SerializeObject(payload);
using (var client = new System.Net.WebClient())
{
    client.Headers.Add("Content-Type", "application/json");
    client.UploadString(webhookUrl, json);
}

Note: Requires System.Net reference (already included in Streamer.bot).

OBS Advanced - Custom Overlay

Create HTML file in OBS Browser Source:

<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        font-family: Arial;
        background: transparent;
      }
      #count {
        font-size: 48px;
        color: #ffd700;
      }
    </style>
  </head>
  <body>
    <div id="count">Entries: 0</div>
    <script>
      // Poll Streamer.bot variable (requires OBS WebSocket)
      setInterval(() => {
        // Use Streamer.bot API to fetch variable
        fetch(
          "http://localhost:7474/GetGlobalVar?name=Giveaway Main Entry Count",
        )
          .then((r) => r.json())
          .then((data) => {
            document.getElementById("count").textContent =
              `Entries: ${data.value}`;
          });
      }, 1000);
    </script>
  </body>
</html>

Requirements:

  • Streamer.bot HTTP Server enabled
  • CORS configured
  • ExposeVariables: true

Backup & Recovery

Manual Backup (Recommended Monthly)

What to backup:

  1. Config: config/giveaway_config.json
  2. Active State: state/*.json (if mid-giveaway)
  3. Historical Data (optional): dumps/ folder

Backup command (PowerShell):

$source = "$env:APPDATA\Streamer.bot\data\Giveaway Bot"
$dest = "C:\Backups\GiveawayBot_$(Get-Date -Format 'yyyyMMdd').zip"
Compress-Archive -Path $source -DestinationPath $dest

Disaster Recovery Scenarios

Lost Config File

  1. Run !giveaway config gen to create default
  2. Restore from backup if available
  3. Reconfigure settings via !giveaway profile config commands

Corrupted State File

  1. Check logs: logs/General/YYYY-MM-DD.log for errors
  2. If state/Main.json is corrupt, delete it
  3. Bot will create fresh state (loses active entries)
  4. Restore from backup if critical

Streamer.bot Wipe

  1. Config: Restore config/ folder
  2. API Keys: Re-enter WheelOfNamesApiKey (plain text)
  3. Global Variables: Manually recreate in Streamer.bot UI
  4. Run !giveaway system test to verify

Automated Backup (Advanced)

Create Windows Task Scheduler job:

Trigger: Daily at 3 AM Action: PowerShell script:

$source = "$env:APPDATA\Streamer.bot\data\Giveaway Bot\config"
$dest = "C:\Backups\GiveawayConfig_$(Get-Date -Format 'yyyyMMdd').json"
Copy-Item "$source\giveaway_config.json" -Destination $dest

# Prune old backups (>30 days)
Get-ChildItem "C:\Backups\GiveawayConfig_*.json" |
  Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
  Remove-Item

← Back to USER_GUIDE | FAQ → | Quick Reference →

Clone this wiki locally