Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 119 additions & 119 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,129 +1,129 @@
<img width="20%" src="assets/icon-no-bg.png" align="right" alt="Icon">
<br>

# Project for PvR: Discord MusicBot in Rust
- Author: Pavel Mikula (MIK0486)
- Took approximately 40 hours

## Project Theme
This project is a Discord bot developed in Rust, designed to play music in Discord voice channels. It uses libraries like `serenity` and `poise` for handling the Discord API, `songbird` for managing audio playback, `yt-dlp` for YouTube track streaming, and integrates with the Spotify Web API for Spotify URLs. The bot supports a full music queue, a local audio library, timed reminders, and several utility commands.

## Project Requirements
- Rust: The primary programming language for bot logic
- Serenity + Poise: Discord API wrapper and command framework for Rust
- Songbird: Voice and audio playback library for Discord
- yt-dlp: For downloading and streaming audio from YouTube
- SQLite (via sqlx): Persistent storage for guild settings and reminders
- Spotify Web API: For resolving Spotify track / playlist URLs

## Installation
### Prerequisites
- Installed rust from [rust-lang.org](https://www.rust-lang.org/tools/install)
- Installed yt-dlp from [github.com/yt-dlp/yt-dlp](https://github.com/yt-dlp/yt-dlp)
- `yt-dlp` should be in the system PATH
- Installed `ffmpeg` (in the system PATH) — used by yt-dlp for post-processing **and** by the bot's loudness analyzer to measure each track's integrated LUFS for cross-track volume normalization
- YouTube Data API token from [Google Cloud Console](https://developers.google.com/youtube/registering_an_application)
- Discord bot token from [Discord Developer Portal](https://discord.com/developers/applications)
- (Optional) Spotify Client ID & Secret from [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) — required for Spotify URL support
- Installed CMAKE. Required for the [audiopus_sys](https://github.com/Lakelezz/audiopus_sys) library

### Installation
1. Clone this repository
```bash
git clone https://github.com/Firestone82/RustyTunes.git
cd RustyTunes
```
2. Create a `.env` file in the root directory
```bash
cp .env.example .env

# Edit the .env file with your Discord bot token, YouTube API key,
# and (optionally) Spotify client id and secret.
```
3. Setup database
```bash
cargo install sqlx-cli
sqlx database create
sqlx migrate run
```
4. Install dependencies
```bash
# yt-dlp
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod a+rx /usr/local/bin/yt-dlp

# ffmpeg (required by yt-dlp and by the loudness normalizer)
sudo apt-get install ffmpeg

# CMAKE
sudo apt-get install cmake
```
5. Build and run the bot
```bash
cargo build --release
cargo run
```

## Usage
All commands are available as both **prefix commands** (default prefix `!`) and **slash commands** (`/`). Use `!help` or `/help` in Discord to list every command, or `!help <command>` for details on a specific one.
<img width="18%" src="assets/icon-no-bg.png" align="right" alt="Icon">

# RustyTunes

> **VŠB-TUO** — School project · Programming in Rust (PvR)

![Rust](https://img.shields.io/badge/Rust-2021-orange) ![Discord](https://img.shields.io/badge/Discord-Bot-5865F2)

A feature-rich Discord music bot written in Rust. Supports YouTube, Spotify, local audio files, and direct URLs. Includes a persistent queue, per-guild volume memory, loudness normalization, timed reminders, and various utility commands.

## Features

### Audio Sources
- Play tracks and playlists from **YouTube** (direct URL or text search)
- Play tracks and playlists from **Spotify** (URL — resolved to YouTube for playback)
- Play files from a **local audio library** stored on the bot host
- Stream user-uploaded **Discord attachments** (audio files)
- Stream audio from **arbitrary direct URLs**
- YouTube (direct URL or text search)
- Spotify (URL — resolved to YouTube for playback)
- Local audio library stored on the bot host
- Discord attachment uploads (audio files)
- Arbitrary direct URLs

### Playback Controls
- `play <query|url>` — play a track or playlist from YouTube or Spotify (appends to queue)
- `playtop <query|url>` — same as `play` but inserts at the front of the queue
- `pause` / `resume` — pause and resume the current track
- `skip [amount]` — skip the current track (or multiple at once)
- `stop` — stop playback and clear the active track
- `playing` — show the currently playing track
- `volume [1-100]` — set the playback volume; append `!` (e.g. `volume 200!`) to opt into the extended 1–500 overdrive range
- `normalize [on|off]` — toggle session-only cross-track loudness normalization (off by default, applies to every source once enabled, resets on restart)
- `silent [on|off]` — suppress NowPlaying announcements for the session
- `join` / `leave` — manually summon or dismiss the bot from your voice channel
| Command | Description |
|---------|-------------|
| `play <query\|url>` | Play a track or playlist, append to queue |
| `playtop <query\|url>` | Same, but insert at front of queue |
| `pause` / `resume` | Pause and resume the current track |
| `skip [amount]` | Skip current track (or N tracks) |
| `stop` | Stop playback and clear the active track |
| `playing` | Show the currently playing track |
| `volume [1-100]` | Set volume; append `!` for overdrive (1–500) |
| `normalize [on\|off]` | Toggle cross-track loudness normalization (EBU R128) |
| `silent [on\|off]` | Suppress Now Playing announcements |
| `join` / `leave` | Summon or dismiss from voice channel |

### Queue Management
- `queue` — paginated queue listing (10 tracks per page) with navigation buttons
- `clear` — remove every track from the queue
- `remove <index>` — remove a specific track from the queue by its 1-based index
- `shuffle` — shuffle the current queue
- `history` — show the last 10 played tracks with buttons to instantly replay any of them
| Command | Description |
|---------|-------------|
| `queue` | Paginated queue (10 tracks/page) with navigation |
| `clear` | Remove all tracks from the queue |
| `remove <index>` | Remove a specific track by 1-based index |
| `shuffle` | Shuffle the current queue |
| `history` | Last 10 played tracks with replay buttons |

### Local Audio Library
The `local` command groups subcommands for managing audio files saved on the bot host:
- `local download <url> [name]` — download an audio file from a URL into the library
- `local upload [name]` — save an uploaded Discord attachment into the library
- `local list` — list all saved local tracks
- `local play [name]` — play a saved track by name (with autocomplete and an interactive picker)
- `local rename <track> <new name>` — rename a saved track
- `local remove <track>` — delete a saved track from the library

### Reminders / Notifications
The `notify` command (alias `remind`) lets users schedule timed reminders persisted to the database:
- `notify me <when> <message>` — schedule a reminder for yourself
- `notify you <user> <when> <message>` — schedule a reminder for another user
- `notify list` — list your pending reminders
- `notify remove <id>` — cancel a pending reminder

### Utility Commands
- `wakeup <user> [count]` — drags a user briefly between voice channels to grab their attention (also available as a right-click **WakeUp!** user context menu action)
- `rename <user> [new name]` — set another member's nickname (respects role hierarchy)
- `uwu <text>` — uwuify the given text
- `uwu_me <text>` — uwuify text and post it impersonating the author via webhook
- `help [command]` — built-in help listing and per-command detail

### Quality-of-Life Behaviour
- **Auto-leave**: bot automatically leaves the voice channel when it's left alone
- **Auto-cleanup**: when the bot is kicked, dragged out, or otherwise loses its voice connection, playback state and the queue are cleaned up automatically
- **Per-guild volume persistence**: the last-set volume is remembered between sessions in SQLite
- **Cross-track loudness normalization**: opt-in via `!normalize`. When enabled, each cached track's integrated loudness is measured once (EBU R128 via ffmpeg) and a static gain is applied so songs sit at roughly the same perceived loudness without crushing in-song dynamics. Off by default, takes effect immediately on the current track, resets on restart. The target loudness (`NORMALIZE_TARGET_LUFS`, default -10) and gain clamps (`NORMALIZE_MIN_GAIN_DB`, `NORMALIZE_MAX_GAIN_DB`) are tunable via env vars — higher target = louder output
- **Per-source cache layout**: downloaded audio is filed under `cache/youtube/` or `cache/spotify/` (legacy flat-cached files still play)
- **Slash + prefix parity**: every command works both ways
- **Graceful shutdown**: handles SIGINT/SIGTERM (and Ctrl+C on Windows) to disconnect cleanly
- **Structured logging**: powered by `tracing` with environment-controlled filtering
| Command | Description |
|---------|-------------|
| `local download <url> [name]` | Download audio from URL into library |
| `local upload [name]` | Save a Discord attachment into library |
| `local list` | List all saved tracks |
| `local play [name]` | Play a saved track (with autocomplete) |
| `local rename <track> <name>` | Rename a saved track |
| `local remove <track>` | Delete a saved track |

### Reminders
| Command | Description |
|---------|-------------|
| `notify me <when> <msg>` | Schedule a reminder for yourself |
| `notify you <user> <when> <msg>` | Schedule a reminder for another user |
| `notify list` | List your pending reminders |
| `notify remove <id>` | Cancel a pending reminder |

### Utilities
| Command | Description |
|---------|-------------|
| `wakeup <user> [count]` | Drag a user between voice channels to get attention |
| `rename <user> [name]` | Set a member's nickname |
| `uwu <text>` | Uwuify text |
| `help [command]` | Show command list or per-command help |

All commands are available as both prefix commands (default `!`) and slash commands (`/`).

### Quality-of-Life
- Auto-leave when alone in channel
- Per-guild volume persistence (SQLite)
- Cross-track loudness normalization (opt-in, EBU R128 via ffmpeg)
- Slash + prefix parity
- Graceful SIGINT/SIGTERM shutdown
- Structured logging via `tracing`

## Requirements

- Rust stable toolchain — [rustup.rs](https://rustup.rs/)
- [`yt-dlp`](https://github.com/yt-dlp/yt-dlp) in system PATH
- [`ffmpeg`](https://ffmpeg.org/) in system PATH
- CMake (required by `audiopus_sys`)
- Discord bot token — [discord.com/developers](https://discord.com/developers/applications)
- YouTube Data API v3 key — [Google Cloud Console](https://console.cloud.google.com/)
- *(Optional)* Spotify Client ID & Secret — [Spotify Developer Dashboard](https://developer.spotify.com/dashboard)

## Setup

1. Install system dependencies:
```bash
# Debian/Ubuntu
sudo apt-get install ffmpeg cmake
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp \
-o /usr/local/bin/yt-dlp && sudo chmod a+rx /usr/local/bin/yt-dlp
```

2. Clone the repository:
```bash
git clone https://github.com/Firestone82/RustyTunes.git
cd RustyTunes
```

3. Copy `.env.example` to `.env` and fill in your Discord token, YouTube API key, and optional Spotify credentials.

4. Set up the database:
```bash
cargo install sqlx-cli
sqlx database create

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Install sqlx-cli before invoking sqlx

This setup step now calls sqlx database create on a clean checkout without ever installing the sqlx CLI. A standard Rust install only provides Cargo/Rust tools, so users following the README hit sqlx: command not found before they can create the SQLite database or run migrations; the previous instructions included cargo install sqlx-cli before these commands.

Useful? React with 👍 / 👎.

sqlx migrate run
```

5. Build and run:
```bash
cargo build --release
cargo run --release
```

### Discord bot setup

1. Create an application at [discord.com/developers/applications](https://discord.com/developers/applications).
2. Under **Bot**, enable **Message Content Intent** and **Server Members Intent**.
3. Copy the bot token into `.env` as `DISCORD_TOKEN`.
4. Invite the bot via the OAuth2 URL generator with `bot` + `applications.commands` scopes and `Connect`, `Speak`, and `Send Messages` permissions.

## License

This project was created as a school assignment at VŠB-TUO.
Loading