An open-source, multi-tenant Twitch bot platform. Run one deployment and serve unlimited channels — every streamer gets a fully isolated bot: custom commands, a visual pipeline engine, moderation, channel-point rewards, timers, an economy, text-to-speech, song requests, OBS overlays, and integrations (Spotify, Discord, YouTube).
You run NomNomzBot through its dashboard app, which gives you a guided setup — it connects your Twitch and bot accounts and collects everything the bot needs through proper input fields, so you never edit a config file. The bot itself is self-hostable from day one (point the dashboard at your own server — zero external infrastructure or oversight), and the same codebase powers an optional hosted (SaaS) offering. Under the hood it speaks Twitch over Helix and EventSub (WebSocket) — chat included, no IRC — and exposes a versioned REST API plus real-time SignalR feeds.
- NomNomzBot
Pre-1.0, under active development. Honest snapshot:
- Backend (
server/) — the live, supported component. Built and covered by tests across identity/auth, the Twitch Helix + EventSub integration, moderation, channel-point rewards, the pipeline engine, timers, the economy, TTS, roles & permissions, server-side chat decoration (emotes, badges, cheermotes), webhooks, and the sandboxed custom-code action. - Public surfaces (overlays + song-request) — delivered by the widget system: widgets are compiled from source at build time, served by the bot, and CDN-cached for SaaS (not static files). In development.
- Dashboard (
app/) — the Kotlin Multiplatform + Compose client (one codebase for desktop and web) is built and functional: guided setup, Twitch device-code login, and management pages for the core modules, all against the live API. The web build ships inside every backend artifact; pre-release polish is ongoing. Every operation is also available through the self-describing REST API (/scalar).
It has not yet been validated in a long-running production deployment, so treat it as early software.
What a connected channel gets:
- Commands — custom and built-in chat commands, each backed by a visual pipeline of actions and
conditions (send/reply, timeout/ban, shoutout, set-variable, wait, play music, and more), with 90+
template variables (
{{user.name}},{{stream.uptime}},{{args.1}}, …). - Event responses & timers — automatic reactions to follows, subs, gifts, cheers, raids, and redemptions; scheduled recurring messages.
- Moderation — bans and timeouts, AutoMod settings, blocked-term lists, Shield Mode, and a mod-action log — all mirrored from the real Twitch API.
- Channel points & live ops — custom reward management, polls, and predictions.
- Economy & games — a per-channel currency with an atomic ledger, a store, cross-channel savings jars, leaderboards, and fun-money games (with an optional, off-by-default 18+ gate).
- Music & song requests — viewer song requests (Spotify / YouTube) with a fair, rank-based queue and trust scoring, plus a public request page that needs no login.
- Rich chat — server-side message decoration: Twitch and BTTV / FFZ / 7TV emotes, badge and cheermote images, coloured mentions, and opt-in link previews, emitted ready-to-render to the dashboard.
- Text-to-speech — Azure Cognitive Services and ElevenLabs voices.
- Overlays & widgets — OBS browser-source alerts and a now-playing bar, pushed in real time over SignalR.
- Integrations — Spotify, Discord, and YouTube via OAuth, requested progressively as you enable them.
- Multi-tenant & roles — one deployment, unlimited channels, each isolated; a three-plane authorization
model (community standing, management roles, and per-action
!permitdelegation). - Extensibility — inbound and outbound webhooks, and a hardened, sandboxed custom-code action.
┌─ server/ — .NET 10 backend (the bot)
│ PostgreSQL + Redis · Twitch Helix + EventSub (WebSocket) · SignalR · Serilog
│ Versioned REST API (v1) · interactive docs at /scalar
│ Public surfaces (OBS overlays, song-request) — compiled widgets served by the bot, CDN-cached for SaaS
│
└─ app/ — Kotlin Multiplatform + Compose dashboard (desktop + web/Wasm; the web build is served by the bot)
The backend is profile-agnostic and direct-connect: the dashboard just needs the backend URL and talks
REST + SignalR straight to it. Self-host points it at localhost or your own exposed URL; the hosted build
points it at the SaaS API. There is no central broker.
| Layer | Technology |
|---|---|
| Runtime | .NET 10, C# 14 |
| Framework | ASP.NET Core (Asp.Versioning) |
| ORM | EF Core 10 + Npgsql → PostgreSQL 16 |
| Cache / pub-sub | Redis 7 |
| Real-time | ASP.NET SignalR (WebSocket) |
| Auth | JWT + Twitch Device Code Flow (secret-free login) · OAuth code flow for integrations |
| Twitch | Helix API, EventSub (WebSocket) — chat reads via EventSub, sends via Helix; no IRC |
| Logging | Serilog |
| Dashboard | Kotlin Multiplatform + Compose (desktop + web) |
| API docs | OpenAPI + Scalar |
There are two pieces: the dashboard, which you set the bot up and run it from, and the backend, the server the dashboard talks to. On the hosted offering the backend is run for you, so you only need the dashboard. To self-host, run your own backend and point the dashboard at it.
Quickest path: one deploy script per OS, covering every scenario — the zero-dependency single-file binary, the Docker stack, and SaaS mode (a restricted option — hosting NomNomzBot for others is against the project license, reserved to NoMercy Labs), each with the bundled web dashboard or the standalone desktop app. DEPLOY.md explains which to choose.
- Linux / macOS:
./deploy.sh <scenario> [--app]- Windows (PowerShell):
.\deploy.ps1 <scenario> [-App]
The dashboard is the normal way to run NomNomzBot — no config files, no API calls by hand. You point it
at a backend URL (localhost or your own server for self-host, the hosted API for SaaS) and a setup
wizard walks you through everything, with a labelled input for each value the bot needs:
- Connect your Twitch account — sign in and authorize. Scopes are requested progressively, only as you enable the features that need them.
- Connect your bot account — authorize the account the bot posts chat from. This is an additional connection on top of your account (like linking Spotify), not a second login.
- Enter your app credentials (self-host) — paste the Twitch Client ID / Secret from your Twitch developer app into the wizard's fields. On the hosted offering this is already configured for you.
- Basics — bot prefix, default language, timezone.
- Integrations (optional) — Spotify, Discord, YouTube; enable any now or later from settings.
When you finish, you land on the dashboard home with the bot live in your channel.
Where to get it: the web dashboard ships inside every backend build — just open the bot's URL in a browser. The desktop app builds with the deploy script's app flag (see DEPLOY.md):
- Linux / macOS:
./deploy.sh <scenario> --app- Windows (PowerShell):
.\deploy.ps1 <scenario> -AppThe same guided flow is also available through the self-describing API:
GET /api/v1/system/setup/wizardreturns each step and the endpoint to call, runnable from the interactive docs at/scalar.
Skip this section if you're using the hosted offering. To self-host, run the backend — the dashboard then connects to it.
Prerequisites: Docker, and a registered Twitch application (Twitch Developer
Console) for its Client ID / Secret. Redirect URIs are computed at
runtime from App:BaseUrl, so register exactly one callback — {App:BaseUrl}/api/v1/auth/twitch/callback
(locally, http://localhost:5080/api/v1/auth/twitch/callback).
Linux / macOS
./deploy.sh dockerWindows (PowerShell)
.\deploy.ps1 dockerThe script creates .env with generated secrets, asks for your Twitch credentials (press Enter to
skip and enter them in the dashboard wizard instead), starts PostgreSQL, Redis, and the API, and waits
until the stack reports ready. Prefer raw compose? Copy .env.example to .env, then
docker compose up -d --build from the repo root — same result on every OS. The zero-Docker
single-file path and SaaS mode are covered in DEPLOY.md. On first start the API waits for Postgres and Redis, runs migrations, seeds
reference data (TTS voices, permission presets), and connects the Twitch EventSub WebSocket. Point your
dashboard at http://localhost:5080 (or your exposed URL) and run the setup above.
For local backend development without Docker (dotnet run), see Development.
| URL | Purpose |
|---|---|
http://localhost:5080 |
API (point the dashboard here) |
http://localhost:5080/scalar |
Interactive API docs (Scalar) |
http://localhost:5080/health |
Health status (JSON) |
http://localhost:5080/health/live |
Liveness probe |
http://localhost:5080/health/ready |
Readiness probe (DB + Redis) |
http://localhost:5080/health/version |
Deployed build version |
http://localhost:8082 |
Adminer (Postgres browser) — dev override only |
The root quickstart compose includes Adminer bound to loopback only. The production compose (
server/docker-compose.yml) excludes it — there it is a dev-overlay extra (docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d adminer).
Day to day you drive the bot from the dashboard. Everything the dashboard does is also available through
the REST API — the interactive Scalar docs at /scalar let you call every endpoint from the browser.
The onboarding flow (the dashboard wizard, or GET /api/v1/system/setup/wizard today) is:
- Twitch application — your Client ID / Secret (entered in the wizard, or set in the backend env).
- Streamer account — authorize your own Twitch account (
GET /api/v1/auth/twitch→ approve on Twitch → back to the callback). Scopes are requested progressively as you enable features. - Bot account — authorize the account the bot posts chat from — an additional connection, not a separate login.
- Basics — bot prefix, default language, timezone.
- Integrations (optional) — Spotify, Discord, YouTube; can be added later.
Once a streamer account is configured, the credential-setup endpoints lock to admins.
Everything the dashboard surfaces is a versioned endpoint under /api/v1/ (browse them in /scalar):
- Commands, pipelines, event responses, timers
- Moderation, channel-point rewards, polls/predictions, stream info
- Economy (currency, store, jars, leaderboards, games)
- TTS config and voices, music/song-request settings
- Roles & permissions, integrations, webhooks
Responses use StatusResponseDto<T> or PaginatedResponse<T>; errors are RFC 7807 problem details.
Viewer/OBS-facing surfaces are delivered by the widget system — widgets are compiled from source at build time, served by the bot, and CDN-cached for SaaS (not static files):
- Song requests — viewers open a token-based request page and queue tracks with no login.
- OBS overlays — add your channel's overlay URL as a browser source; alerts and a now-playing bar are
pushed live over the
OverlayHub(SignalR).
This is in development; the compiled-widget pipeline (build → serve → CDN cache) is being specced.
The dashboard collects most settings for you; these are the backend's own knobs:
- Docker / deploy —
.envat the repo root (the deploy script creates it;.env.exampledocuments every variable). Config keys map to env vars with__, e.g.Twitch:ClientId→TWITCH_CLIENT_ID;docker-compose.ymlalso maps the friendlyAPI_BASE_URLontoApp__BaseUrl. - Local dev (
dotnet run) —server/src/NomNomzBot.Api/appsettings.json(defaults) +appsettings.Development.json(your secrets). Everything else falls back to the defaults. - Twitch redirect URI is computed at runtime from
App:BaseUrl— register only{App:BaseUrl}/api/v1/auth/twitch/callback.
Optional integrations activate when their credentials are present: Spotify, Discord, YouTube,
Azure:Tts, and ElevenLabs. See server/.env.example for the full, commented reference.
The API speaks plain HTTP inside the container (ASPNETCORE_URLS=http://+:5000); TLS is terminated by a
reverse proxy in front of it. Running the API port directly on the public internet without TLS is not
supported — OAuth tokens and JWTs would travel in cleartext. Two supported paths:
-
Cloudflare Tunnel — the bundled
cloudflaredservice (setCLOUDFLARE_TUNNEL_TOKEN) gives a public HTTPS hostname with no inbound ports opened. -
Reverse proxy — put Caddy or nginx in front (
reverse_proxy localhost:5080). Caddy provisions Let's Encrypt certificates automatically:api.example.com { reverse_proxy localhost:5080 }
The Postgres/Redis ports bind to 127.0.0.1 and Adminer is excluded from the production compose, so only
the proxy reaches the API. Set App:BaseUrl to your public HTTPS URL — host-header filtering and the
Twitch OAuth redirect URI are both derived from it. If the proxy reaches the API from a non-loopback
address (e.g. a containerised sidecar), set TRUSTED_PROXY_NETWORKS so the real client IP is trusted.
nomnomzbot/
├── server/ .NET 10 backend (Domain → Application → Infrastructure → Api), tests, Docker
└── app/ Kotlin Multiplatform + Compose dashboard (in active development)
The backend follows Clean Architecture (dependencies flow inward) with the Result<T> pattern, soft
deletes, per-request multi-tenancy, and auto-discovered DI.
cd server
docker compose up -d postgres redis # just the infrastructure
cd src/NomNomzBot.Api
dotnet run # auto-migrates and seeds on first startcd server
dotnet build NomNomzBot.slnx
dotnet testThe build treats warnings as errors, and the code is formatted with CSharpier
(dotnet csharpier format .) — both are commit gates.
Contributions are welcome. A few conventions keep the tree healthy:
- Work in small, validated vertical slices; keep the working tree clean.
- Match the surrounding style; explicit types (no
var), file-scoped namespaces,Result<T>over exceptions, async all the way. - Before committing, run
dotnet csharpier format .and ensuredotnet build+dotnet testare green (warnings are errors). Conventional commit messages (feat:,fix:,chore:) are preferred. - The default branch is
master.
Found a vulnerability? Please report it privately via a
GitHub Security Advisory rather than a
public issue. Security fixes ship as GitHub releases tagged [SECURITY] and, where warranted, as
advisories — watch this repository's releases. Check the build you are running with
GET /health/version.
AGPL-3.0-or-later. Copyright © NoMercy Labs.