Skip to content

xMagnetoFX/Last-Music-Bot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Last Music Bot logo

Last Music Bot

A self-hosted Discord music bot with a polished local dashboard, Discord-native control panels, persistent libraries, and Docker-first deployment.

Built for servers that want modern playback controls, strong persistence, and full ownership over the experience.

Platform: Self-Hosted Runtime: Docker Compose Language: TypeScript Playback: Lavalink v4 Data: SQLite UI: Web + Discord Panels Autoplay: Spotify Ready

Quick Start · Features · Control Surfaces · Configuration · Development

Note

The bundled Docker Compose setup publishes the dashboard locally at http://127.0.0.1:3000 by default.

Why Last Music Bot

Last Music Bot combines the familiarity of a Discord music bot with the convenience of a dedicated local dashboard and the polish of Discord-native control panels. Playback is scoped per guild, library data is stored per user, and state persists across restarts so the bot feels consistent instead of disposable.

It is designed for personal servers and small communities that want:

  • A web dashboard without relying on a hosted third-party service
  • Rich queue and library management beyond basic slash commands
  • Private, native Discord controls for day-to-day playback
  • Persistent settings, history, playlists, and restore behavior

Feature Set

Guild-Aware Playback

Queue actions, transport controls, connection state, and server settings stay scoped to the selected guild so one server does not interfere with another.

Local Web Dashboard

Use Discord OAuth to sign in to a responsive dashboard with search, queue management, server switching, playlists, liked music, history, and settings.

Discord-Native Control

Control playback without leaving Discord through an invoker-only /control panel and a dedicated /admin panel for server configuration.

Persistent Libraries

Store liked tracks, listening history, playlists, and user settings in SQLite with user-level isolation and persistent access across sessions.

Restore and Defaults

Resume from saved snapshots, honor saved guild defaults, remember preferred voice channels, and keep runtime playback state durable across restarts.

Spotify Login + Import

Connect Spotify for private or collaborative playlist import, import supported public playlists without login when Spotify exposes them, and keep autoplay metadata available when credentials are configured.

Control Surfaces

Surface Best For What It Covers
Web dashboard Full-session management Search, queue editing, transport controls, liked tracks, history, playlists, per-user settings, guild settings
/control Rich in-Discord control Playback, queue browsing, search, liked/history access, playlists, save actions, volume
/admin Server configuration Command channel lock, preferred voice channel, announcements, autoplay, guild-level settings
Slash commands Fast direct actions Play, connect, pause/resume, skip, disconnect

Architecture

flowchart LR
    U["Discord Users"] --> D["Slash Commands / Discord Panels"]
    B["Browser Dashboard"] --> API["Express API"]
    D --> BOT["Last Music Bot"]
    API --> BOT
    BOT --> LAV["Lavalink v4"]
    BOT --> DB["SQLite Stores"]
    BOT -. optional .-> SP["Spotify Catalog"]
Loading

Project layout:

  • apps/bot - bot runtime, REST API, player orchestration, stores, Discord commands, and panel controllers
  • dashboard - static frontend served by the bot
  • lavalink - Lavalink configuration used by Docker Compose
  • data - mounted persistence directory for SQLite databases

Core technologies:

  • discord.js
  • express
  • lavalink-client
  • Lavalink v4
  • SQLite
  • Docker Compose
  • TypeScript

Quick Start

1. Prepare the environment

Copy .env.example to .env.

Fill in the required values:

  • DISCORD_TOKEN
  • LAVALINK_PASSWORD
  • SESSION_SIGNING_KEY and DATA_ENCRYPTION_KEY
  • DISCORD_CLIENT_SECRET if you want dashboard login

Optional but recommended:

  • SPOTIFY_CLIENT_ID
  • SPOTIFY_CLIENT_SECRET
  • BACKUP_ROOT=/app/backups

2. Configure Discord OAuth

In the Discord Developer Portal, add this redirect URI:

http://127.0.0.1:3000/api/auth/callback

If you change WEB_URL, your redirect URI must match it exactly:

${WEB_URL}/api/auth/callback

3. Configure Spotify OAuth

Spotify login is optional, but it is required for importing playlists you own or collaborate on.

In the Spotify Developer Dashboard, add this redirect URI:

${WEB_URL}/api/spotify/callback

Public playlist import can still work without login when Spotify exposes the playlist to the Web API, but some Spotify-managed or editorial playlists may still fail even when the URL is valid.

4. Start the stack

docker compose up --build

The Compose stack now starts Lavalink, the bot, and the backup sidecar together. Lavalink still becomes healthy first, and the bot healthcheck remains available through docker compose ps.

5. Open the dashboard

http://127.0.0.1:3000

Sign in with Discord, choose a server, and start playback from the dashboard or from Discord commands.

Configuration

The bot reads configuration from .env. The table below covers the primary runtime options.

Variable Required Description
DISCORD_TOKEN Yes Discord bot token
PROXY_MODE No Proxy handling mode for client IP and rate limiting, defaults to direct for localhost use
DISCORD_CLIENT_SECRET Yes for dashboard login Used for Discord OAuth on the web dashboard
DISCORD_CLIENT_ID No Bot client ID; auto-detected if left blank
DISCORD_GUILD_ID No Preferred startup guild; auto-detected if left blank
DISCORD_ALLOWED_VOICE_CHANNEL_IDS No Comma-separated voice-channel allowlist
LAVALINK_HOST Yes Lavalink host, defaults to lavalink in Docker
LAVALINK_PORT Yes Lavalink port, defaults to 2333
LAVALINK_PASSWORD Yes Shared Lavalink password
SPOTIFY_CLIENT_ID No Enables Spotify login, playlist import, and Spotify-backed autoplay metadata
SPOTIFY_CLIENT_SECRET No Enables Spotify login, playlist import, and Spotify-backed autoplay metadata
SPOTIFY_MARKET No Two-letter market code used for Spotify catalog access when app credentials are used, defaults to US
WEB_HOST No Dashboard bind host
WEB_PORT No Dashboard port
WEB_URL Yes for OAuth correctness Public dashboard URL used for callbacks and cookies
SQLITE_PATH No Player snapshot database path
LIBRARY_SQLITE_PATH No Library and settings database path
SESSION_SIGNING_KEY Yes Primary session-signing secret for dashboard authentication
SESSION_SIGNING_KEY_PREVIOUS No Temporary previous signing key used during rotation for OAuth/session compatibility
DATA_ENCRYPTION_KEY Yes Primary encryption secret for stored linked-account tokens
DATA_ENCRYPTION_KEY_PREVIOUS No Temporary previous encryption key used while rotating stored linked-account token encryption
JWT_SECRET Deprecated fallback Legacy session/encryption secret kept for compatibility during rollout
BACKUP_ROOT No Backup destination inside the container, defaults to /app/backups
BACKUP_INTERVAL_MINUTES No Scheduled backup interval for the backup sidecar, defaults to 60
BACKUP_HOURLY_RETENTION No Number of hourly snapshots to retain, defaults to 48
BACKUP_DAILY_RETENTION No Number of daily snapshots to retain, defaults to 14
AUDIT_LOG_RETENTION_DAYS No Number of daily JSONL audit log files to retain, defaults to 30

Configuration Notes

  • WEB_URL must match the exact browser URL used for dashboard access.
  • If DISCORD_CLIENT_ID is blank, the bot resolves it automatically from the logged-in bot user.
  • If DISCORD_GUILD_ID is blank, the bot auto-selects a startup guild from the servers it has joined.
  • If no voice allowlist is configured, the bot discovers available voice channels automatically.
  • In production, SESSION_SIGNING_KEY and DATA_ENCRYPTION_KEY should never be left at sample values.
  • Set SESSION_SIGNING_KEY_PREVIOUS and DATA_ENCRYPTION_KEY_PREVIOUS temporarily when rotating active secrets, then remove them after the migration window.
  • JWT_SECRET can be left unset after migration, but it can remain temporarily to preserve older sessions and linked Spotify credentials while you rotate secrets.
  • Switching from legacy JWT dashboard cookies to database-backed sessions causes a one-time dashboard re-login after deployment.
  • Backups cover runtime user data only. They do not include .env or other secret files.

Backups and Restore

Backups are written locally to ./backups on the host:

  • hourly snapshots: ./backups/hourly/<timestamp-utc>/
  • daily snapshots: ./backups/daily/<date-utc>/
  • quick status pointer: ./backups/latest.json

Each backup contains:

  • library.db
  • player.db
  • recommendation-track-index.json when present
  • manifest.json with checksums and source metadata

The backup sidecar creates snapshots every hour by default, keeps the newest 48 hourly backups, and keeps the newest 14 daily backups.

Manual one-shot backup:

docker compose run --rm backup node dist/scripts/backupData.js manual

Restore a specific backup:

docker compose stop bot
docker compose run --rm backup node dist/scripts/restoreData.js <backup-id>
docker compose up -d bot

The restore flow verifies the manifest first, creates a pre-restore safety snapshot, restores the tracked data files, and removes stale SQLite -wal and -shm files before the bot comes back up.

Audit Logs

Sensitive actions are written as append-only JSONL audit records under ./data/audit/:

  • Discord login success, cancel, failure, logout, and no-shared-guild denial
  • Spotify connect, disconnect, and playlist import
  • Playback control mutations and queue edits
  • Guild settings changes
  • Playlist create/delete/import
  • Backup and restore operations

Audit logs rotate daily as YYYY-MM-DD.jsonl and are retained for 30 days by default.

Secret Rotation

Generate fresh secrets and a rollout checklist:

cd apps/bot
npm run build
npm run secrets:generate

Recommended rotation flow:

  1. Move the current SESSION_SIGNING_KEY into SESSION_SIGNING_KEY_PREVIOUS.
  2. Move the current DATA_ENCRYPTION_KEY into DATA_ENCRYPTION_KEY_PREVIOUS.
  3. Set new values for SESSION_SIGNING_KEY and DATA_ENCRYPTION_KEY.
  4. Keep JWT_SECRET only as a temporary legacy fallback if you still need it.
  5. Restart the stack, expect a one-time dashboard re-login, then remove the previous-key values after the migration window.

Discord Commands

Command Description
/play Search for a song or URL and add it to the queue
/connect Join the voice channel the user is currently in
/pause Toggle pause and resume for the current track
/skip Skip the current track
/disconnect Stop playback, leave voice, and clear the active session
/control Open the private music control panel
/admin Open the server admin panel

Dashboard and API

The dashboard is served directly by the bot and communicates with the local REST API.

Main API groups:

  • Auth: /api/auth/login, /api/auth/callback, /api/auth/me, /api/auth/logout
  • Health: /api/health
  • Spotify auth: /api/spotify/login, /api/spotify/callback, /api/spotify/status, /api/spotify/disconnect
  • Playback: /api/state, /api/connect, /api/disconnect, /api/queue, /api/play, /api/pause, /api/resume, /api/skip, /api/stop
  • Queue editing: /api/queue/:index, /api/queue/:index/move-up, /api/queue/:index/move-down
  • Library: /api/liked, /api/history, /api/playlists, /api/playlists/import/spotify
  • Settings: /api/settings
  • Guilds: /api/guilds, /api/guilds/:guildId/settings, /api/guilds/:guildId/channels

Guild-scoped playback routes accept an optional guildId query parameter so the dashboard can explicitly target the selected server.

Spotify

Spotify support now covers two separate flows:

  • Dashboard login and playlist import
  • Autoplay enrichment and related-track lookup

Playlist import behavior in this repo:

  • Public Spotify playlist URLs are supported only when Spotify exposes the playlist through app credentials.
  • Private or collaborative playlists require linking a Spotify account through the dashboard.
  • Imported playlists become local Last Music Bot playlists and playback still runs through Lavalink-resolved tracks.
  • Re-import is append-only in v1: it adds newly seen occurrences but does not delete or reorder existing local items.

Autoplay

Autoplay is a guild-level toggle that keeps playback moving after the manual queue runs out.

Current behavior:

  • Uses Spotify metadata when Spotify credentials are configured
  • Preserves the effective requester on autoplayed tracks
  • Applies per-session no-repeat safeguards
  • Avoids retry loops when discovery fails
  • Stops cleanly when no fresh candidate is available

Autoplay is intentionally conservative when discovery is incomplete, favoring a clean stop over unstable fallback behavior.

Persistence

The Docker setup mounts the data directory so state survives container rebuilds.

Stored data includes:

  • player.db - player snapshots and playback restore state
  • library.db - liked tracks, history, playlists, user settings, and guild settings

The bot restores saved session state on startup and prefers Lavalink session resume when available.

Healthchecks

The bundled Docker deployment includes container healthchecks:

  • Lavalink is considered healthy when its local HTTP endpoint responds on port 2333
  • The bot is considered healthy when /api/health reports both Discord and Lavalink as ready
  • The bundled bot healthcheck uses a dedicated short-lived bearer token instead of a dashboard session cookie

Use docker compose ps to inspect health, and docker compose logs -f bot lavalink if one service stays in starting or unhealthy.

Development

If you want to work on the bot outside Docker, install the bot app dependencies first:

cd apps/bot
npm install

Useful commands:

npm run dev
npm test
npm run build

If you run locally without Docker, make sure Lavalink is already reachable at the configured host, port, and password.

Testing

The bot includes automated coverage for:

  • Guild-aware API routing
  • Discord command behavior
  • Admin panel and control panel interactions
  • Player persistence and restore logic
  • Spotify catalog integration
  • Autoplay and no-repeat behavior
  • Dashboard smoke flows for playlist create/import, Spotify connect/disconnect, and mobile navigation

Run the suite from apps/bot:

npm test
npm run test:e2e
npm run test:smoke

Recommended dashboard smoke checks after UI or auth changes:

  • Create a playlist from search results, open it, and remove the imported item
  • Open the dedicated New Playlist modal and confirm it lands in the playlist detail view
  • Open Import Spotify Playlist, verify invalid URLs are rejected, then reconnect Spotify and finish an import
  • Verify the mobile nav drawer and playlist/import modals still behave correctly at widths at or below 768px

Project Structure

.
├─ apps/
│  └─ bot/
├─ dashboard/
├─ lavalink/
├─ data/
├─ docker-compose.yml
└─ .env.example

Operational Notes

  • The bundled Compose setup binds the dashboard to 127.0.0.1 by default.
  • Global slash-command registration can take a short time to propagate after startup.
  • The bot must be invited to at least one server before auto-detection can succeed.
  • Spotify credentials are optional, but recommended if you want related-track autoplay.

Summary

Last Music Bot is a self-hosted, full-stack Discord music bot project that blends traditional slash-command playback with a dedicated local dashboard and polished Discord-native control panels. It is built for people who want a bot they can actually own, customize, and keep running with persistent state rather than starting from scratch every session.

About

Discord music bot with Spotify-powered autoplay and related track recommendations through Lavalink/LavaSrc for private server playback control

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors