a discord music bot you can just talk to. play songs, albums and playlists from spotify links or plain names, with slash commands or by mentioning the bot in english.
- spotify in, music out: paste a spotify track, album or playlist link, or just type a song name
- natural language control: mention the bot (
@Dusic play some daft punk) and an llm picks the right action through function calling - slash commands with spotify-powered autocomplete on
/play - per-server queues: albums and playlists are queued track by track
- parallel downloads: a pool of worker processes fetches audio ahead of playback and caches it on disk
- follows you around: if the bot is moved to another voice channel, playback moves with it; if it gets disconnected, the queue is cleared
- error reporting: errors (with stack traces) are posted to a discord webhook
flowchart LR
U[user] -->|/play or @mention| B[discord bot]
B -->|@mention| AI[openai chat<br/>tool calling]
AI -->|play / pause / skip ...| B
B --> S[spotify web api<br/>track · album · playlist · search]
S --> Y[ytmusicapi<br/>best youtube match]
Y --> D[download workers<br/>pytube · multiprocessing]
D --> C[(music/ cache)]
C --> Q[guild queue]
Q --> F[ffmpeg + opus] --> V[voice channel]
/play(or the llm) resolves the request against the spotify web api: a direct link, or the top search result- each track is matched to a youtube music result with
ytmusicapi - download workers grab the audio with
pytubeintomusic/, skipping files already cached - a queue loop per server streams the next ready track into the voice channel through ffmpeg
| command | description |
|---|---|
/play <name or url> |
play a song, album or playlist (spotify url or search text) |
/pause |
pause the current song |
/resume |
resume playback |
/skip |
skip the current song |
/clear |
clear the queue and stop playback |
/leave |
leave the voice channel |
every command also works in plain english by mentioning the bot, e.g. @Dusic skip this one or @Dusic pause.
| area | tech |
|---|---|
| bot | python 3.12, discord.py (slash commands, cogs, voice) |
| ai | openai gpt-3.5-turbo with function calling |
| metadata | spotify web api (client credentials) via aiohttp |
| matching | ytmusicapi |
| audio | pytube downloads, ffmpeg + libopus playback, PyNaCl |
| config | python-dotenv |
- python 3.12 and uv
ffmpegon yourPATH- the libopus shared library (e.g.
libopus.so.0on linux) - a discord application with a bot token
- spotify api credentials and an openai api key
git clone https://github.com/kjhq/Dusic.git
cd Dusic
uv venv
uv pip install -r requirements.txt
mkdir -p music # audio cache; must exist before first start
# create .env (see configuration), then:
uv run main.pyset these in a .env file in the project root:
| variable | purpose |
|---|---|
DISCORD_TOKEN |
discord bot token |
SPOTIFY_CLIENT_ID |
spotify app client id |
SPOTIFY_CLIENT_SECRET |
spotify app client secret |
OPENAI_API_KEY |
openai key for natural language control |
OPUS_PATH |
path to the libopus shared library |
other knobs (download worker count, cache folder, error webhook) are constants in modules/helper/config.py.
Dusic/
├── main.py # bot entrypoint: loads cogs, opus, download workers
├── requirements.txt
└── modules/
├── ai.py # @mention handler, openai tool calling
├── queue.py # per-guild queue + playback loop
├── download.py # multiprocessing download workers (pytube)
├── discord_voice.py # voice connect / play / stop
├── discord_voice_afk.py # handles disconnects and channel moves
├── error_handler.py # logs errors to a discord webhook
├── commands/
│ ├── music_commands.py # slash commands
│ └── play.py # play flow + autocomplete
├── search/
│ ├── spotify.py # spotify web api client
│ └── youtube.py # youtube music matching
└── helper/ # config, embeds, logger, types
bugs and feature requests: open an issue. code: fork and open a pr.