data/
SQLite data store and other runtime state live here.
Operations and runtime
Everything after first launch: config keys, data paths, logs, Docker, and bot-owner maintenance commands.
Python path
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python run.py
Docker path
docker compose up --build -d
Docker Compose mounts config.ini, data/, and logs/ from the host.
Persistence
SQLite and logs stay local. No cloud database required.
SQLite data store and other runtime state live here.
Rotating logs write to logs/nanobot.log.
Primary config file. Owner commands can reload and edit values.
Preflight checker validates Python version, deps, structure, config, and token format before launch.
Dependency base
discord.py >= 2.7.1, aiosqlite >= 0.22.1, aiohttp >= 3.14.1, psutil >= 7.2.2, Pillow >= 12.2.0, PyNaCl >= 1.6.2, yt-dlp >= 2026.6.9
Config reference
example_config.ini broken into sectionsThe descriptions below come from comments in the repository's sample config.
YOUR_BOT_TOKEN_HERE
Bot token from the Discord Developer Portal.
(blank)
Passphrase that encrypts both SQLite databases at rest via SQLCipher. Blank = no encryption. Requires pip install sqlcipher3-binary; the NANOBOT_DB_KEY env var overrides it. An existing plaintext database is encrypted on the next start. No key recovery — losing the key loses the data.
n!
Default command prefix (max 5 chars, no spaces).
(blank)
Discord user ID of the bot owner. Leave blank to use the application owner.
0
Port for a plain-HTTP health endpoint (GET /health) for containers and orchestrators. Returns 200 once ready, 503 while starting. 0 disables it. May share a port with vote_webhook_port — the health route and vote webhook then use one listener.
0.0.0.0
Bind address for the health endpoint. 0.0.0.0 = all interfaces (needed for orchestrators / panels); 127.0.0.1 = host-local only. The payload is unauthenticated, so lock it down if the port is reachable.
INFO
DEBUG / INFO / WARNING / ERROR / CRITICAL
false
true = log every raw HTTP request (very noisy).
true
Also write structured command-lifecycle events (start/ok/error with timings and a correlation id) to logs/events.jsonl, one JSON object per line. The human-readable log is unaffected.
0
Log any SQLite query slower than this many milliseconds, so a slow/contended database surfaces in the logs. 0 disables it (zero overhead). A starting point on a busy bot: 200.
(blank)
top.gg AUTH token.
(blank)
top.gg v1 API token used for commands sync.
(blank)
discordbotlist.com bot token.
(blank)
discord.bots.gg bot token.
5000
Local port the vote webhook listens on.
(blank)
Shared secret used by bot lists to authenticate webhooks.
(blank)
Groq API key (free at console.groq.com). Powers /eli5 and WYR generation. The GROQ_API_KEY environment variable takes priority if set.
500
FML pages fetched per daily scrape (~5-10 stories each).
500
Would-You-Rather API requests per rating per daily scrape.
400
nekos.best images fetched per endpoint per daily scrape.
400
Nekosia images fetched per tag per daily scrape.
604800
Age (seconds) before a cached URL is rechecked with HEAD. 604800 = 7 days.
1000
Max URLs to HEAD-check per 6-hour revalidation cycle.
You generate Would You Rather questions for a Discord bot. Return ONLY...
System prompt used when Groq generates fresh WYR questions daily.
(blank)
Path to a Netscape cookies file exported from a logged-in browser. Needed for age-restricted or login-only content.
50
Default playback volume (0–200).
180
Seconds before leaving after the channel empties or the queue runs out. When everyone leaves, playback pauses immediately and resumes automatically if someone returns within this window. Values below 30 are clamped to 30.
50
Percent of voice-channel listeners who must vote to skip (0–100). Requesters and Manage Server can always force-skip.
500
Maximum tracks allowed in a single server's queue.
(blank)
Path to a deno, node, or bun binary for yt-dlp's JavaScript interpreter. Leave blank to auto-detect.
true
Send audio as Opus (default, cheaper) or PCM (true = instant volume/speed changes).
true
Save queue to disk so it survives a restart. Bot reconnects and resumes on startup.
true
Download the next queued track while the current one plays for instant track switching. Live streams are never pre-downloaded.
true
Self-deafen when joining a voice channel.
1.0
Default playback speed (0.5–3.0).
ytsearch
Search service for non-URL queries: ytsearch, ytmsearch, or scsearch.
(blank)
HTTP/HTTPS proxy URL for yt-dlp. Example: http://user:pass@host:port.
false
Keep downloaded audio in data/music_cache/ for instant replays. Pair with cache limits below.
0
Max cache size in MB (0 = unlimited). Only used when music_save_videos is on.
0
Delete cached audio older than this many days (0 = never).
true
Save a per-server played-track history (for the n!history command).
true
Fill missing artist info via Apple's free iTunes Search API.
false
Skip sponsor/non-music segments using the crowd-sourced SponsorBlock database. Forces a download before playback; live streams are unaffected.
music_offtopic
Comma-separated SponsorBlock categories to remove. Valid: sponsor, intro, outro, selfpromo, preview, filler, interaction, music_offtopic, poi_highlight.
Owner only
Commands in cogs/admin/ and cogs/debug.py require bot ownership.
cachestatsShow cache DB statistics for FML, WYR, and image pools.
configDM-only: show, get, or set config values without restarting.
fmlpurgeWipe all cached FML stories and force a fresh scrape on next run.
healthProbe the bot's HTTP /health endpoint and show the response, verifying it is reachable from outside the bot process. Requires health_check_port in config.ini.
logsPrint the last N lines of the log file directly in Discord. Defaults to 20 lines.
reloadHot-reload one cog by name, or reload every cog if no argument given.
reloadconfigRe-read config.ini at runtime without restarting the bot.
restartGraceful shutdown then re-exec the process — equivalent to stop + start.
scrapeManually trigger the daily content cache scrape without waiting for the scheduler.
serversList all servers the bot is in, with member counts and IDs. Paginated.
setloglevelChange the active log level immediately and persist the new value to config.ini.
shutdownFlush logs and close the Discord connection before exiting the process.
syncPush slash command definitions to Discord — globally or scoped to a single guild.
unloadUnload a single cog by name without restarting the bot.
updatePull latest code from git and reload all cogs. Does not re-sync slash commands.
upgradeFull upgrade: git pull, pip install, spawn a new process, then shut down the old one.
shRun a shell command and see stdout + stderr in Discord. 60-second timeout by default — pass --disable-timeout for long jobs like downloads. Output truncated at 900 chars per stream.
pyEvaluate Python code inside the running bot process. Top-level await works. 60-second timeout by default — pass --disable-timeout to remove it. Output truncated at 900 chars.