**Add webhook event publishing for music state changes**

Introduced webhook event publishing to push music-state updates to a configured endpoint. Implemented `WebhookDispatcher` and `NoopEventPublisher` for dispatching events asynchronously or disabling them when no URL is set. Added handling of events like track start/end, queue updates, volume changes, and voice connection changes, enhancing integration with external systems.
This commit is contained in:
anthorob committed 2026-10-26 11:43:07 -04:00
1 parent 7443cbfb4b
commit b0664e8835
15 files changed
+513 -4

No files matched your search

+11
View File
@@ -37,6 +37,17 @@ Key variables:
| `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, bot, self }`, where `bot` marks bot accounts and `self` marks this bot's own voice changes.
## Running with Docker Compose