MainsBot

Current Version: v3.0.0

Release notes:

Bot Features

ㅤ• A killswitch

ㅤ• Roblox game/playtime command

ㅤ• Twitch Stuff

NPM

Run:

npm i pm2 tmi.js ws fs spotify-buddylist string-similarity nodemon dotenv node-fetch discord.js

sudo npm install pm2 -g
cd MainsBot && npm i pm2 tmi.js ws fs spotify-buddylist string-similarity nodemon dotenv node-fetch discord.js && cd..

GIT

Run:

git clone https://github.com/Mr-Cheeezz/MainsBot.git

START

Run:

cd MainsBot && pm2 start main.js && cd ..

Split runtime (recommended):

# bot only
node main.js --config config/myinstance.ini

# web only (optional)
node main-web.js --config config/myinstance.ini

Code layout

Multi-instance (INI)

If instance.data_dir is set, per-instance state lives under that directory (file backend) or in Postgres (postgres backend). Files under data_dir are still used to seed defaults on first run.

Web admin login (Twitch)

Config (INI keys in [web] or env vars):

Socket hosting (nginx)

Docker

Yes. A basic container path is now included:

Recommended Docker shape:

Use it like this:

cp docker-compose.example.yml docker-compose.yml
cp config/instance.example.ini config/docker.ini
docker compose up --build -d

For Docker, use TCP for the web service, not unix sockets. In config/docker.ini:

[modules]
web = 0

[web]
listen = tcp
host = 0.0.0.0
port = 8787

[state]
backend = postgres

[database]
url = postgresql://mainsbot:mainsbot@postgres:5432/mainsbot
schema = mainsbot_example

[redis]
url = redis://redis:6379/0

Then edit the rest of the INI normally:

The compose example defaults to config/docker.ini. Override it with:

MAINSBOT_INSTANCE_CONFIG=myinstance.ini docker compose up --build -d

Docker + unix sockets + nginx + multi-instance

If you want the container stack to mirror the split production layout:

Example:

cp docker-compose.sockets.example.yml docker-compose.yml
cp config/instance.example.ini config/example.ini
cp config/instance.example.ini config/second.ini
docker compose up --build -d

For each instance INI, use socket mode:

[modules]
web = 0

[web]
listen = socket
socket_path = /app/run/example/web.sock
overlay_socket_path = /app/run/example/overlay.sock

[state]
backend = postgres

[database]
url = postgresql://mainsbot:mainsbot@postgres:5432/mainsbot
schema = mainsbot_example

[redis]
url = redis://redis:6379/0

Inside the containers:

The nginx side example is:

That example shows two instances:

If you want TLS in Docker too, keep the same socket layout and replace the sample nginx server blocks with your normal listen 443 ssl setup.

systemd split services

State is stored in Postgres (JSONB) when enabled. This lets you run multiple instances without sharing JSON files.

Create DB (Debian/server)

Run as a Postgres superuser:

Then set these in your INI:

On Debian-like systems, a passwordless role can work because pg_hba.conf often allows peer auth for local users.

Create DB (Windows local testing)

Scoop psql is just the client. You also need a Postgres server running (local service, Docker, WSL, etc).

1) Check you can connect to a server:

2) Create the mainsbot role + DB (run as a superuser):

3) (Optional) Pre-create a schema/table:

Passwordless on Windows usually requires editing pg_hba.conf to allow trust/peer for local connections. If you don’t want to touch pg_hba.conf, set a password:

Note

You don’t have to run deploy/sql/10-schema.sql for the bot to work: the bot auto-creates the schema/table on startup based on [database].schema.

Modules (optional)

Enable/disable optional feature modules in your INI:

ENV

.env / exported environment variables are no longer the supported configuration path.

Use config/<instance>.ini (see “Multi-instance (INI)” above).

Legacy reference (do not use):

COOKIE = ''

BOT_TOKEN = '' // preferred
// BOT_OAUTH = '' // legacy fallback
BOT_NAME = ''
BOT_ID = 
CLIENT_ID = '' // preferred Twitch app client id
CLIENT_SECRET = '' // preferred Twitch app client secret

CHANNEL_NAME = ''
CHANNEL_NAME_DISPLAY = ''
CHANNEL_ID = 

STRAMER_TOKEN = '' // preferred streamer token name (supports STREAMER_TOKEN too)

// Optional: send bot chat/replies through Helix instead of IRC PRIVMSG
TWITCH_CHAT_USE_HELIX = true
TWITCH_CHAT_CLIENT_ID = '' // optional override; defaults to CLIENT_ID (or token store client_id)
TWITCH_CHAT_TOKEN = '' // optional override; defaults to BOT_TOKEN
TWITCH_CHAT_SENDER_ID = '' // defaults to BOT_ID
TWITCH_CHAT_BROADCASTER_ID = '' // defaults to CHANNEL_ID
TWITCH_CHAT_BROADCASTER_LOGIN = '' // defaults to CHANNEL_NAME
// token scopes for Helix chat should include user:write:chat (+ user:read:chat / user:bot as needed by your auth flow)
// All client.say/client.raw PRIVMSG sends attempt Helix first, with IRC fallback if Helix fails.

// OAuth web auth + token storage
TWITCH_AUTH_REDIRECT_URI = 'https://example.com/auth/callback'
TWITCH_AUTH_FORCE_VERIFY = true
// OAuth tokens are stored in Redis per instance.

// Roblox OAuth 2.0 token storage
ROBLOX_CLIENT_ID = ''
ROBLOX_CLIENT_SECRET = ''
ROBLOX_AUTH_REDIRECT_URI = 'https://example.com/auth/roblox/callback'
ROBLOX_AUTH_SCOPES = 'openid profile'

// Web admin login (protects /auth/*)
WEB_COOKIE_SECRET = ''
WEB_OWNER_USER_ID = ''
WEB_OWNER_LOGIN = ''
WEB_ALLOWED_USERS = '' // comma/space-separated Twitch logins

// Web server listen (prefer socket behind nginx)
WEB_SOCKET_PATH = '/run/mainsbot.sock'
WEB_HOST = '127.0.0.1'
WEB_PORT = 8787

// Optional Postgres state for SETTINGS/STREAMS/playtime
STATE_BACKEND = 'file' // or 'postgres'
DATABASE_URL = ''

// Public site URL (used in !commands and web UI labels)
WEB_PUBLIC_URL = 'https://example.com'

// Optional: reddit recap link for !reddit.on
REDDIT_RECAP_URL = 'https://reddit.com/r/your_subreddit'

WEB_ACCESS_TOKEN = ''

ADMIN_ID = 505216805

WAIT_REGISTER = 300000

COOLDOWN = 90000
MESSAGE_MEMORY = 5000

MAX_MESSAGE_LENGTH = 495
BASE_LENGTH_TIMEOUT = 15 
MAX_LENGTH_TIMEOUT = 300

BASE_SPAM_TIMEOUT = 30 
MAX_SPAM_TIMEOUT = 300 

MINIMUM_CHARACTERS = 0
MAXIMUM_SIMILARITY = 0
MINIMUM_MESSAGE_COUNT = 4

WAIT_UNTIL_FOC_OFF = 60000
WAIT_UNTIL_FOC_OFF_RAID = 300000
SPAM_LINK = 300000
JOIN_TIMER = 150000
SONG_TIMER = 4000

Auth routes: