A production-grade Twitch IRC bot that enforces League of Legends rank requirements in chat using AWS ECS Fargate for secure hosting and Cloudflare Workers for business logic.
EloWardBot creates a funnel to drive Twitch viewers to connect their League of Legends accounts to EloWard:
- Always present in chat (Standby mode) to respond instantly to
!elowardcommands - Enforces rank requirements when enabled, timing out users without connected LoL accounts
- Supports chat commands (
!eloward on/off/mode minrank gold4) for streamers and moderators - Dashboard integration with 1-3 second configuration updates
- Secure and scalable with no exposed ports and HMAC-protected communications
ββββββββββββββββ CLOUDFLARE (Control Plane) βββββββββββββββββββ
β β
β π Dashboard π Bot Worker π Rank Worker β
β β’ React UI β’ Token Management β’ LoL Lookups β
β β’ Pages Functions β’ Config Updates β’ D1 Queries β
β β’ Auth Flow β’ HMAC Security β’ Cache Logic β
β β’ Redis Publisher β’ Fresh Data β
β β
β πΎ KV Storage ποΈ D1 Database β‘ Upstash β
β β’ Bot Tokens β’ bot_channels β’ Redis Pub/Subβ
β β’ User Sessions β’ lol_ranks β’ Global CDN β
β β’ Audit Logs β’ TLS + Auth β
β β
βββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββ
β HMAC + Redis Pub/Sub (1-3s updates)
β
βββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββ
β AWS (Data Plane) β
β β
β π³ ECS Fargate π Multi-Region Ready β
β β’ Containerized IRC Bot β’ us-east-1 (primary) β
β β’ No Inbound Ports β’ eu-west-1 (future) β
β β’ Auto-restart/Scale β’ preferred_region in D1 β
β β’ Outbound Only β’ Logical region assignment (na/eu)β
β β
β π€ IRC Connection β
β β’ Persistent to irc.chat.twitch.tv β
β β’ Always Joined (Standby β Enforcing) β
β β’ Token Refresh + Error Recovery β
β β’ Chat Commands + Message Processing β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Always-Joined Design: Bot joins channels immediately after OAuth Connect and stays joined:
- Standby Mode: Passive presence, only responds to
!elowardcommands - Enforcing Mode: Active moderation, timeouts users without sufficient rank
- Instant Switching:
!eloward ontakes effect in 1-3 seconds via Redis pub/sub - Leave Only: On permission revoke or disconnect - otherwise maintains presence
This solves the "chicken and egg" problem where users couldn't enable the bot because it wasn't in their channel.
Note: Auto-follow functionality was removed due to Twitch's deprecation of programmatic follow/unfollow API endpoints on July 27, 2021. Users must manually follow channels through the Twitch interface.
File: bot.js
Purpose: Secure, always-on IRC presence with instant config updates
Key Responsibilities:
- Maintain persistent connection to
irc.chat.twitch.tv - Process all chat messages (fast prefix check in Standby)
- Handle
!elowardcommands from broadcasters/mods - Make local caching decisions (config + rank lookups)
- Execute timeouts via Twitch Helix API (bot calls Helix directly)
- Subscribe to Redis for instant config updates
- HMAC-signed calls to Workers only on cache misses
State Machine:
// On OAuth Connect
NotConnected β Standby (joins channel, passive)
// Via Dashboard or Chat Commands
Standby β Enforcing (instant via Redis)
// Only on revoke/disconnect
Standby/Enforcing β NotConnected (leaves channel)File: Backend/workers/elowardbot/bot-worker.ts
Purpose: Central control plane for bot operations
Key Responsibilities:
- OAuth token management and automatic refresh
- Process bot configuration updates from Dashboard
- Serve read-through endpoints for config/ranks to bot
- Publish config updates to Redis for instant propagation
- HMAC request validation from bot instances
- Light rate limiting to protect D1
Critical Endpoints:
GET /token // Bot token sync
POST /bot/config:get // HMAC, read config for a channel (D1)
POST /bot/config:update // HMAC, write config (dashboard or chat), publish Redis
POST /rank:get // HMAC, read lol_ranks for a user (D1)
GET /channels // Active channel list for opsFile: EloWardSite/src/pages/Dashboard.js
Flow: Dashboard β Pages Functions β Bot Worker β D1 β Redis Pub/Sub β Bot (1-3s)
Features:
- Real-time bot enable/disable toggle
- Configuration: timeout duration, reason template, enforcement mode
- Minimum rank settings (Iron β Challenger)
- OAuth Connect flow for broadcaster permissions
Syntax: !eloward <command> [args]
Permissions: Broadcaster + Moderators only
Enforcement Ignore List: Bot ignores broadcaster/mods (and optionally VIPs/subs if configured) for enforcement
Supported Commands:
!eloward on # Enable enforcement
!eloward off # Disable (standby mode)
!eloward mode hasrank # Require any connected rank
!eloward mode minrank gold 4 # Require Gold 4 or higher
!eloward timeout 30 # Set timeout duration (seconds)Processing Flow: Chat Command β Bot (validates mod/broadcaster) β Worker (/bot/config:update) β D1 β Redis β All Bots (1-3s)
Target: ~200-400ms decision time per message
Caching Strategy:
- β Positive cache only: Config (1-2s TTL), Rank (30-60s TTL)
- β No negative caching: New users see effects immediately
- π Cache invalidation: Via Redis pub/sub on updates
Decision Flow:
1. Check local config cache (1-2s TTL) β Cache hit = instant decision
2. On miss: HMAC call to /bot/config:get β D1 β Update cache
3. Check local rank cache (30-60s TTL) β Cache hit = instant decision
4. On miss: HMAC call to /rank:get β D1 β Update cache
5. Apply decision locally: timeout via Helix API or allow (fail-open on errors)Target: 1-3 seconds end-to-end
Flow:
Dashboard/Chat β Worker (/bot/config:update) β D1 Write β Redis Publish β Bot Cache Invalidate β Effect
Redis Message Format:
{
"type": "config_update",
"channel_login": "streamername",
"fields": {"bot_enabled": true, "enforcement_mode": "minrank", "min_rank": "GOLD4"},
"version": 1737849600,
"updated_at": "2025-01-25T12:00:00Z"
}Version Handling: Bot ignores messages if version <= cached_version to prevent race conditions.
JOIN Rate Limits & Anti-Spam:
- Max 15 channels per 10 seconds per connection (conservative approach)
- 667ms delay between JOINs to prevent Twitch spam detection
- Progressive startup with detailed logging to track join progress
- Always-on presence model: joins ALL channels regardless of enabled status
Connection Strategy:
- Two IRC connections (75-80 channels each) for resilience
- Fast
!elowardprefix check in Standby mode (minimal CPU) - Exponential backoff + jitter on reconnection to prevent storms
Required Helix Scopes:
moderator:manage:banned_users- For timeout/ban actions via/moderation/banschannel:moderate- For mod/broadcaster context- If we also delete messages, add
moderator:manage:chat_messages - Respect Twitch Helix rate buckets (per-user/app); on
429, use exponential backoff + jitter
Note: user:edit:follows scope was removed as Twitch deprecated programmatic follow/unfollow API on July 27, 2021.
Self-Healing:
- Every 60-120s: lightweight reconcile sweep (check
updated_atfor stale cache) - Sweep checks only channel
updated_at/versionto avoid heavy reads; full config fetch only when stale - Prevents missed Redis pub/sub messages during brief disconnects
- β No open ports on bot instances
- β Outbound only: Twitch IRC, CF Workers HTTPS, Upstash Redis TLS
- β ECS Fargate: Managed, patched, isolated containers
// Bot β Worker requests are HMAC-SHA256 signed
const signature = hmac_sha256(secret, timestamp + method + path + body);
headers['X-HMAC-Signature'] = signature;
headers['X-Timestamp'] = timestamp; // Allow Β±60s clock skew; reject outside windowBenefits:
- Prevents replay attacks (timestamp window)
- Works with open source (secrets in env only)
- Standard cryptographic approach
- Lightweight validation
- Bot Tokens: CF KV (encrypted at rest)
- HMAC Keys: Environment variables only
- Database: D1 with CF security model
- Redis: Upstash TLS + auth token
- No inbound ports β Port scan/DDoS resistant
- HMAC (60s window) β Replay attack resistant
- Worker rate limits β D1 protection from abuse
- message_decision_ms_p95: < 400ms (local cache hit performance)
- config_propagation_ms_p95: < 3s (Redis pub β cache invalidation)
- helix_timeout_failure_rate: < 2% (API success rate)
# Worker Health
curl -s https://eloward-bot.unleashai.workers.dev/irc/health
# Expected Response (Control Plane Health)
{
"worker_status": "healthy",
"architecture": "fargate_redis",
"enabled_channels": 5,
"d1_status": "operational",
"redis_status": "connected",
"timestamp": "2025-01-15T10:30:00Z"
}
# IRC socket health is observed via bot logs/metrics; the Worker reports only control-plane status# ECS Logs via CloudWatch
aws logs tail /ecs/eloward-bot --follow
# Key Log Patterns
β
[INFO] Redis notification received: config_update channel:streamer
β
[INFO] Message decision: allow user:player channel:streamer (45ms)
β‘ [INFO] Config updated: enforcement_mode=minrank (RedisβCache)
π [WARN] Rank cache miss: player (fetching fresh)- ECS Fargate:
us-east-1(closest to CF edge) - Logical Region:
nain D1 - Target: US/Canada streamers
- ECS Fargate:
eu-west-1 - Logical Region:
euin D1 - Target: EU streamers
-- D1 uses logical regions
ALTER TABLE bot_channels ADD COLUMN preferred_region TEXT DEFAULT 'na';
-- Assignment Logic (in Worker)
const region = request.cf.country in ['US','CA'] ? 'na' : 'eu';- AWS Account with ECS permissions
- Cloudflare account with Workers/D1 access
- Upstash Redis database
- Twitch Developer Application
# Deploy Bot Worker
cd Backend/workers/elowardbot
wrangler deploy
# Configure Environment Variables
wrangler secret put TWITCH_CLIENT_ID
wrangler secret put TWITCH_CLIENT_SECRET
wrangler secret put BOT_WRITE_KEY
wrangler secret put UPSTASH_REDIS_URL
wrangler secret put UPSTASH_REDIS_TOKEN-- bot_channels: add columns for region assignment and change tracking
ALTER TABLE bot_channels ADD COLUMN preferred_region TEXT DEFAULT 'na';
ALTER TABLE bot_channels ADD COLUMN updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'));
ALTER TABLE bot_channels ADD COLUMN version INTEGER NOT NULL DEFAULT (strftime('%s','now'));
-- Performance indexes
CREATE INDEX idx_bot_channels_enabled ON bot_channels(bot_enabled);
CREATE INDEX idx_lol_ranks_user_login ON lol_ranks(user_login);
-- updated_at and version are set by the Worker on every write (monotonic epoch ms preferred)
-- No DB triggers used# Create Redis instance at console.upstash.com
# Enable TLS, get connection details
# Workers use REST; the bot uses the Redis protocol (TLS)
# CF Workers (REST API):
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-rest-token
# Bot (Redis Protocol):
UPSTASH_REDIS_URL=rediss://your-host:6380
UPSTASH_REDIS_PASSWORD=your-password# Build and push container
docker build -t eloward-bot .
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker tag eloward-bot:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/eloward-bot:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/eloward-bot:latest
# Deploy ECS Service
aws ecs create-service --cluster eloward --service-name eloward-bot \
--task-definition eloward-bot:1 --desired-count 1# ECS Task Environment Variables
CF_WORKER_URL=https://eloward-bot.unleashai.workers.dev
HMAC_SECRET=your-shared-secret-here
UPSTASH_REDIS_URL=rediss://your-host:6380
UPSTASH_REDIS_PASSWORD=your-password
AWS_REGION=us-east-1# Install dependencies
npm install
# Set up environment
cp .env.example .env
# Configure CF_WORKER_URL and other secrets
# Run locally (connects to production CF Worker)
npm start# 1. Test bot logic locally
npm start
# 2. Deploy Worker changes
cd Backend/workers/elowardbot && wrangler deploy
# 3. Build and deploy container
docker build -t eloward-bot . && ./deploy-fargate.sh
# 4. Monitor logs
aws logs tail /ecs/eloward-bot --follow- Channels: 150+ in Standby, 30 actively Enforcing
- Messages: 750 msgs/sec aggregate (5 msgs/sec Γ 150 channels)
- Bandwidth: ~1.2 Mbps inbound (totally manageable)
- Memory: < 100MB (minimal per-channel state)
- CPU: < 5% utilization at peak
- IRC Connections: 2 concurrent connections by default for resilience; both use JOIN throttling (β€20/10s)
# Horizontal scaling (if needed)
# Option 1: Increase ECS desired count
aws ecs update-service --cluster eloward --service eloward-bot --desired-count 2
# Option 2: Add second region
# Deploy eu-west-1 task, use preferred_region for assignment- IRC Connection Sharding: 50-100 channels per connection
- Rate Limited Joins: 15-20 channels per 10s window
- Fast Command Parsing:
startsWith('!eloward')early exit - Connection Pooling: Reuse HTTP connections to CF Workers
Bot Not Joining Channels:
# Check ECS task status
aws ecs describe-services --cluster eloward --services eloward-bot
# Check logs
aws logs tail /ecs/eloward-bot --since 5m
# Check Redis connectivity
redis-cli -u $UPSTASH_REDIS_URL pingConfig Updates Not Propagating:
# Check Redis pub/sub
redis-cli -u $UPSTASH_REDIS_URL monitor
# Check Worker logs
wrangler tail eloward-bot
# Test HMAC endpoint directly
curl -X POST https://eloward-bot.unleashai.workers.dev/bot/config:update \
-H "X-HMAC-Signature: $(generate_hmac)" \
-H "Content-Type: application/json" \
-d '{"channel_login":"streamer","fields":{"bot_enabled":true}}'Slow Message Decisions:
# Check cache hit rates in logs
aws logs filter-log-events --log-group-name /ecs/eloward-bot \
--filter-pattern "cache_miss"
# Check Worker response times
wrangler tail eloward-bot | grep "config:update\|rank:get"Disable All Timeouts:
-- Emergency stop via D1 console
UPDATE bot_channels SET bot_enabled = 0 WHERE bot_enabled = 1;Rollback Deployment:
# Revert to previous ECS task definition
aws ecs update-service --cluster eloward --service eloward-bot \
--task-definition eloward-bot:previous-version- β
!eloward ontakes effect in < 3 seconds - β New account links stop timeouts on next message
- β Dashboard toggles propagate in < 2 seconds
- β Chat commands work for all mods/broadcaster
- β Message decisions in < 400ms p95
- β 99.9%+ IRC connection uptime
- β < 1% timeout API failure rate
- β Zero security incidents (no exposed ports)
- β Drives account connections to EloWard
- β Increases premium badge engagement
- β Creates positive stream experience
- β Scales to support growth
Apache 2.0 + Commons Clause - see LICENSE file for details.
- Issues: GitHub Issues tracker
- Documentation: This README + inline code comments
- Monitoring: CloudWatch dashboards + Upstash analytics
- Emergency: Bot can be disabled instantly via D1 console
EloWardBot: Production-grade Twitch chat moderation that drives user engagement and account connections through smart rank enforcement.