Files
netneutrality/docs/web-api.md
T
anthorob d97c4c6da2 **Add autoplay support for seamless music playback**
Introduced autoplay functionality to keep music playing when the queue ends. Added new `/autoplay` API endpoints, command handling, and service implementations. Enhanced `TrackScheduler`, queue operations, and testing to support autoplay behavior. Integrated YouTube radio mix as the initial provider.
2026-10-31 16:13:04 -04:00

5.3 KiB
Raw Blame History

HTTP Web API

Package: me.bobert.netneutrality.http (outbound client) and me.bobert.netneutrality.http.server (server). Server: embedded Javalin 7 with the OpenAPI and Swagger UI plugins.

The bot exposes an internal HTTP API for health monitoring, control, guild introspection, and music control — intended for an external dashboard/companion, not for end users in Discord.

Server startup

WebApi is a Lifecycle component (priority 70) registered in App.buildContext(). It receives SettingsProvider, GuildServiceProvider, MusicService, and suppliers for the live JDA / shutdown callback via its constructor. On start() it:

  • reads the port from NETNEUTRALITY_API_PORT (default 9000),
  • creates a Javalin app (Javalin 7 requires routes and exception/error handlers to be registered upfront inside Javalin.create),
  • registers the OpenApiPlugin (title "Net Neutrality API", X-API-Key security scheme) and the SwaggerPlugin,
  • installs the ApiKeyFilter as a before handler,
  • registers the route groups (below), the exception handlers and the 404 handler, and
  • starts the server on the port.

On stop() the Javalin app is stopped.

Authentication

ApiKeyFilter compares the X-API-Key request header with the NETNEUTRALITY_API_KEY setting. A missing/invalid key — or no configured key at all — yields 401 ("Missing or invalid API key"). Public paths that skip the check: /, and anything starting with /openapi, /swagger or /webjars (Swagger UI and its assets).

Error handling

  • HttpResponseException (e.g. BadRequestResponse, NotFoundResponse) → {"status":"error","message":"..."} with the exception's status.
  • Any other exception → logged, 500 with {"status":"error"}.
  • Unknown paths → 404 with {"status":"error","message":"Not found"}.

Route groups

Each group is a *Routes class with a static register(RoutesConfig, ...) method; handlers are annotated with @OpenApi so they appear in the generated OpenAPI document. Request/response shapes live in the http.server.dto package.

Class Endpoints
WebApi GET / — HTML liveness message.
GuildRoutes GET /guilds (optional user_id query restricts to mutual guilds); GET /guilds/owner; GET /guilds/{guildId}/owner; /channels; /roles; /members/{userId}, /members/{userId}/access, /members/{userId}/roles, /members/{userId}/admin; /voice-channels, /voice-channels/{channelId}; GET /guilds/{guildId}/config; PUT /guilds/{guildId}/debug, /logging, /log-channel.
ActionRoutes GET /actions/status — JDA health + gateway ping; POST /actions/shutdown — graceful shutdown.
SettingsRoutes GET /settings/catalog — every known setting with default and description; GET /settings/current/{guildId} — current guild settings; PUT /settings/current/{guildId}/{name} — update a setting; PUT /settings/reload/{guildId} — invalidate & reload the settings cache.
StatsRoutes GET /stats/bot; GET /stats/guilds/{guildId}.
MusicRoutes See below.

Music endpoints

Mirror the slash commands (see music.md); announcements are also posted to the guild's music channel when a context is attached.

Method & path Purpose
GET /music/sources Supported music sources.
GET /music/{guildId}/status Connection, playback, current track, position, queue size.
GET /music/{guildId}/queue Queued tracks.
POST /music/{guildId}/search Resolve a query (optional searchEngine) into candidates; returns a search_id. Nothing is queued.
POST /music/{guildId}/queue Queue a candidate by searchId + index (or all with queueAll=true) for userId; the bot must already be in a voice channel.
DELETE /music/{guildId}/queue Clear the queue, or remove one track with ?position= (1-based).
PATCH /music/{guildId}/queue/reorder Move a track (fromPosition → toPosition).
POST /music/{guildId}/skip, /stop, /pause, /resume, /shuffle, /leave Playback controls.
GET / PUT /music/{guildId}/volume Read / set volume (0–100).
GET / PUT /music/{guildId}/autoplay Read / set autoplay ({"enabled": true}). In-memory, resets when the player is destroyed.
PUT /music/{guildId}/seek Seek to positionMs.
GET /music/{guildId}/vote-skip Vote-skip status.
POST /music/{guildId}/vote-skip/{userId} Cast a vote on behalf of a member.
GET /music/{guildId}/dj/{userId} Whether the member is currently allowed to act as DJ.

Search results are held in PendingSearchResults and expire after a TTL (returned as search_id_ttl_seconds).

HTTP client — WebModule

WebModule (package me.bobert.netneutrality.http) wraps an OkHttpClient for the bot's own outbound HTTP calls (e.g. checking for updates via GitLab). Responses are cached in ResponseCache, a LinkedHashMap-based LRU limited to 50 entries.

Notes

  • All routes except the public ones require the API key. Even so, the API is expected to be bound to a trusted/internal network (e.g. within the Docker network); /actions/shutdown is destructive, so the port should not be exposed publicly.