Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

renderbot

A Discord bot that answers "can I use this track?" It takes a Spotify or YouTube link, a YouTube search, or an uploaded audio file, runs it through the looney-checks copyright engine, and renders the result as a Components V2 message: who owns the track, what clearance it needs, and where the claims come from.

Research assistance, not legal advice.

Commands

Slash Prefix What it does
/check query:<...> !check <...> Spotify URL, YouTube URL or 11-char video id, or a free-text search
/file audio:<attachment> !file (with attachment) Uploaded audio (mp3, flac, m4a, wav, ogg, opus, aac, wma)
/help !help Usage summary

A free-text query returns up to five YouTube candidates with pick buttons; the picked video is what gets checked. Searching is free, the check is not.

Every result carries a Refresh research button that re-runs the exact request with the engine's research cache bypassed. /check and /file accept a private flag to send the result as an ephemeral reply.

Requirements

  • Node.js >= 22.5 (the audit log uses the built-in node:sqlite)
  • The looney-checks engine, either downloaded prebuilt or run from source
  • An API key for the selected AI backend (OpenRouter by default)

Quick start

npm ci
cp .env.example .env          # fill in DISCORD_TOKEN, DISCORD_CLIENT_ID, OPENROUTER_API_KEY
npm run fetch-engine          # downloads the prebuilt engine for this OS/arch
npm start

npm run dev runs the bot under node --watch. There is no test suite; verify changes with node --check src/<file>.js and a manual run.

Engine

src/engine.js spawns one warm looney-checks server and keeps it running, since cold starts are slow. It waits for /health, restarts the child up to three times if it exits unexpectedly, and re-spawns after a broken pipe. Checks go through the async /jobs queue, which lets the bot show live progress; /check is used as a synchronous fallback.

Set LOONEY_URL to point at an already-running engine instead of spawning one. Setting LOONEY_BIN to a source checkout's venv binary (.../music-copyright-checker-server) is faster to iterate on than the prebuilt bundle:

LOONEY_BIN=/path/to/looney-checks/.venv/bin/music-copyright-checker-server

The engine caches research in SQLite for seven days (keyed by model, prompt version, and payload), so a repeated check returns in about a second with a cache hit. The Refresh button skips it.

Configuration

Everything comes from .env; .env.example documents each variable inline. The ones you are most likely to touch:

Variable Default Purpose
DISCORD_TOKEN / DISCORD_CLIENT_ID — Required bot credentials
DISCORD_GUILD_ID — Register slash commands instantly in one guild; blank registers globally
BOT_PREFIX ! Prefix for the legacy text commands
ALLOWED_GUILD_IDS — (all) Comma/space-separated guild ids the bot may serve; blank allows every server and DMs
ALLOW_DMS false Accept direct messages even when ALLOWED_GUILD_IDS restricts guilds
LOONEY_BIN macOS vendor path Engine executable; relative paths resolve from the project root
LOONEY_URL — Use an external engine instead of spawning one
LOONEY_PORT 8799 Port for the managed engine
LOONEY_AI_BACKEND openrouter openrouter, opencode-go, or opencode
LOONEY_MODEL openrouter/free Model passed to the engine
LOONEY_TIMEOUT 300 AI research timeout in seconds
QUOTA_DAILY_LIMIT 5 Checks per user per UTC day
QUOTA_BYPASS_USER_IDS — Comma/space-separated user ids that skip the limit
API_ENABLED false Serve the read-only public checks API
DASHBOARD_ENABLED false Serve the localhost admin audit dashboard

validateConfig() fails fast on a missing token, client id, or the API key required by the selected backend (skipped when LOONEY_URL is set).

State

  • Daily quota — data/renderbot.json, one counter per user, reset at 00:00 UTC. Reserved before the work starts and refunded if the result is not delivered. Bypass users get remaining: Infinity.
  • Audit log — data/requests.sqlite3, an append-only SQLite table of every check: user, guild, channel, request, status, duration, cache flag, and the answer JSON. Writes are best-effort and never fail a check.
  • Refresh contexts — in-memory, keyed by a random id in the button custom id, expiring after REQUEST_CONTEXT_TTL_MS (6 hours). Nothing is persisted.

Public API

With API_ENABLED=true, src/api.js serves a read-only HTTP API over completed checks, bound to 127.0.0.1:8800 by default. Put a tunnel or reverse proxy in front to publish it.

GET /health                                    -> status, completed_checks, uptime
GET /checks?limit&offset&source=url|file&cache=true|false&q=
GET /checks/:id

Responses are PII-free by construction: only id, created_at, command, source, request, from_cache, duration_ms, and answer are projected. There is no auth, so a fixed-window per-IP rate limiter is the only guard.

Admin dashboard

With DASHBOARD_ENABLED=true, src/dashboard.js serves a small HTML page over the full audit log — user, guild, channel, verdict, cache, duration — with sorting by date/user/guild and filters. It is not PII-free, so it binds to 127.0.0.1:8890 by default; set DASHBOARD_TOKEN to require a token, and reach it over an SSH tunnel:

ssh -L 8890:127.0.0.1:8890 opc@<host>   # then open http://127.0.0.1:8890
GET /                      -> the page
GET /api/summary           -> totals + by user/guild/day
GET /api/checks?...        -> rows: sort, dir, user_id, guild_id, status, source, q, limit, offset
GET /api/checks/:id        -> one row with the full answer JSON

Deployment

Runs anywhere Node runs. On a server, a systemd user service with linger keeps it alive across logouts:

systemctl --user status renderbot.service
journalctl --user -u renderbot.service -f
systemctl --user restart renderbot.service

Only one instance per DISCORD_TOKEN; two will both answer interactions and re-register commands.

Notes

  • Never commit .env, data/, or vendor/; they are git-ignored.
  • The engine is authoritative for copyright facts. The bot formats them and never invents or rewrites an answer.
  • Logging, progress edits, and engine output are best-effort side effects that degrade to console.error rather than failing a check.

About

Discord bot that checks songs, YouTube searches, and uploaded audio for copyright and licensing requirements via the looney-checks engine.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages