2026-05-12 23:37:18 -04:00
2024-07-14 22:13:31 -04:00
2025-03-09 20:08:43 +00:00
2024-07-14 11:53:22 -04:00

Net Neutrality Discord Bot

A custom Discord bot written in Java. It uses JDA for Discord, a Lavalink node for music playback, MariaDB/MySQL for persistence, and exposes an HTTP API built with Javalin.

This is a personal project. No support is offered for building or running it.

Table of Contents

Requirements

  • Docker and Docker Compose (recommended), or
  • JDK 25 and Maven (the included mvnw wrapper works) for local runs
  • A MariaDB/MySQL database
  • A Lavalink node, version 4.2.0 or newer
  • A Discord bot token

Configuration

All settings are provided as environment variables. A template with every supported variable is available in bot.properties.example.

Key variables:

Variable Description
NETNEUTRALITY_BOT_TOKEN Discord bot token
NETNEUTRALITY_BOT_PREFIX Command prefix
NETNEUTRALITY_BOT_OWNER Discord user ID of the bot owner
NETNEUTRALITY_API_PORT Port for the HTTP API (default 9000)
MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE Database connection settings
NETNEUTRALITY_LAVALINK_URI, NETNEUTRALITY_LAVALINK_PASSWORD Lavalink connection settings
NETNEUTRALITY_SENTRY_DSN Optional Sentry DSN for error reporting
GITEA_HOST, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO Optional Gitea settings for issue reporting
NETNEUTRALITY_WEBHOOK_URL, NETNEUTRALITY_WEBHOOK_SECRET Optional frontend webhook URL and HMAC secret (see below)

Webhook events

When NETNEUTRALITY_WEBHOOK_URL is set, the bot POSTs JSON music-state events (TRACK_START, TRACK_END, QUEUE_UPDATED, PLAYER_PAUSED, PLAYER_RESUMED, VOLUME_CHANGED, VOICE_CONNECTED, VOICE_DISCONNECTED) to it asynchronously. Each event has type, guildId, timestamp, a per-guild seq and a payload. Requests carry an X-Signature: sha256=<hex HMAC-SHA256 of the body> header computed with NETNEUTRALITY_WEBHOOK_SECRET. Failed deliveries are retried 3 times with exponential backoff and then dropped, so the frontend should still do a full resync on load and periodically. The polling endpoints remain available.

PLAYER_SEEKED is sent after a seek with payload { position } (milliseconds). VOTE_SKIP_UPDATED is sent after a vote that doesn't skip the track yet, with payload { userId, votes, threshold } (threshold is the percentage of listeners needed). A vote that does skip produces the usual TRACK_END / TRACK_START / QUEUE_UPDATED events instead.

TRACK_START / TRACK_END payloads are { identifier, title, author, length, uri, artworkUrl } (plus reason on TRACK_END). uri and artworkUrl may be null when the source doesn't provide them.

VOICE_CONNECTED / VOICE_DISCONNECTED are sent for the bot itself and for any member joining or leaving the voice channel the bot is currently in. Their payload is { channelId, userId, username, nickname, bot, self }, where nickname is the member's effective display name in the guild, bot marks bot accounts and self marks this bot's own voice changes.

Running with Docker Compose

Docker Compose is the preferred way to run the bot. It starts the bot, the database, Lavalink, and the supporting YouTube cipher and PO token services.

  1. Copy bot.properties.example and fill in the values (see Configuration).
  2. Build and start the stack:
docker compose build
docker compose up -d
  1. View logs:
docker compose logs -f netneutrality-bot

Logs are also written to the logs directory.

Running Locally

  1. Copy bot.properties.example to bot.properties and fill in the values.
  2. Make sure a database and a Lavalink node are reachable at the configured addresses.
  3. Build the project:
./mvnw package
  1. Run the bot:
java -jar target/netneutrality.jar

Dependencies are copied to target/lib during packaging and referenced by the jar manifest.

Testing

./mvnw test

Database tests use Testcontainers, so Docker must be running. Checkstyle runs during the test phase.

License

See the LICENSE file.

S
Languages
Java 99.1%
Shell 0.5%
PowerShell 0.3%