Discord bot that reads chat aloud with local Kokoro TTS.
Discord bot setup
- Create an application at Discord Developer Portal → Bot → reset/copy the token (
DISCORD_TOKEN). - Under Privileged Gateway Intents, enable Message Content Intent (required to read chat for TTS).
- Invite the bot with scopes
bot+applications.commands, and permissions at least: Connect, Speak, Use Voice Activity, Send Messages, Read Message History (and Add Reactions if you want the⚠️ fail cue). - Drop the token into your compose
.env/ environment and bring the stack up below.
# docker-compose.yml
services:
kokoro:
image: ghcr.io/remsky/kokoro-fastapi-cpu:latest
restart: unless-stopped
bot:
image: ghcr.io/jakehwll/vocaoid:latest
restart: unless-stopped
environment:
DISCORD_TOKEN: "${DISCORD_TOKEN}"
TTS_URL: http://kokoro:8880
DATABASE_PATH: /data/vocaoid.sqlite
volumes:
- vocaoid-data:/data
depends_on:
- kokoro
volumes:
vocaoid-data:export DISCORD_TOKEN=... # or put it in a .env next to the compose file
docker compose up -d
docker compose logs -f botFor local development (Bun + hot reload), see CONTRIBUTING.md.
| Command | What it does |
|---|---|
/join |
Join your VC; read your messages from this channel |
/leave |
Stop reading you (bot leaves VC when nobody remains) |
/autojoin |
Toggle auto-join; reads that VC’s text chat (whisper lane) |
/here |
Move your listen-channel here (no leave/rejoin) |
/stfu |
Stop current speech and clear the queue |
/say |
Speak one line in VC (doesn’t opt you into chat reading) |
/dict |
Server pronunciation dictionary (add / remove / list) |
/voice |
Dropdown picker — Preview / Save (your voice in this server) |
/speed |
Your TTS speed: slow / normal / fast |
/status |
Ephemeral: session + Kokoro up/down + last synth latency |
/statistics |
Ephemeral embed: uptime, synth counts, latency, joins, etc. |
- Multiple users can
/jointhe same session; the bot leaves when the VC empties - TTS pauses when someone talks, then resumes
- After a few quiet minutes it naps (stays in VC, deafens) and wakes on the next message with a chime
- If Kokoro dies: one quiet retry, then an error blip (+ “Kokoro's down.” /
⚠️ ) - Voice cues for join / leave / wake / error; presence shows live session count
What gets spoken
- Mentions → names · replies → “replying to Alice: …” · spoilers → “spoiler”
- Huge pastes → “long paste, N lines”
- Messages starting with
!are skipped /dictexact-token fixes per server (e.g.ghz→ gigahertz), max 50