update docs
This commit is contained in:
1 parent
d3211c6702
commit
389af563b9
11 files changed
+181
-69
No files matched your search
+1
-1
@@ -17,7 +17,7 @@ subsystem, how they fit together, and the end-to-end flows that tie them.
|
||||
| [tasks.md](tasks.md) | Tasks — button/emoji/modal/step/built-in/persistable/scheduled tasks |
|
||||
| [persistence.md](persistence.md) | Database — Hibernate/JPA, Flyway, entities, repositories, services |
|
||||
| [music.md](music.md) | Music — Lavalink node & client, queue, event dispatch |
|
||||
| [web-api.md](web-api.md) | HTTP Web API — internal control & introspection server |
|
||||
| [web-api.md](web-api.md) | HTTP Web API — Javalin server for control, introspection & music |
|
||||
| [threading.md](threading.md) | Threading & scheduling infrastructure |
|
||||
| [utilities-and-bootstrap.md](utilities-and-bootstrap.md) | Utilities, configuration, logging, exceptions |
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ infrastructure.
|
||||
| Logging | Log4j2 + `log4j-slf4j2-impl` | 2.26.0 |
|
||||
| Testing | JUnit Jupiter 6 + Mockito 5 | — |
|
||||
|
||||
The HTTP **server** for the internal API uses the JDK built-in
|
||||
`com.sun.net.httpserver.HttpServer` (no external framework); see [web-api.md](web-api.md).
|
||||
The HTTP **server** for the internal API uses embedded Javalin (with OpenAPI/Swagger plugins); see
|
||||
[web-api.md](web-api.md).
|
||||
|
||||
## Startup lifecycle
|
||||
|
||||
@@ -157,5 +157,4 @@ block the others. Effectively this means:
|
||||
| `…http` | Internal web API and HTTP client module |
|
||||
| `…music` | Music domain (service, manager, queue, scheduler, track loading) |
|
||||
| `…utils` | `Settings` / `SettingsProvider`, `ChannelMessagePurger`, `MusicUtils` |
|
||||
| `…shared.util` | Cross-cutting helpers |
|
||||
| `…exceptions` | Custom exception hierarchy |
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# Commands
|
||||
|
||||
**Packages:** `me.bobert.netneutrality.framework.command` (framework),
|
||||
`me.bobert.netneutrality.feature.*.command` (concrete commands)
|
||||
**Packages:** `me.bobert.netneutrality.core.command` (framework),
|
||||
`me.bobert.netneutrality.command.*` (concrete commands)
|
||||
|
||||
A **Command** is what a user executes on Discord. It receives a
|
||||
[`CommandContext`](contexts.md#commandcontext) and runs logic based on it. There are two primary
|
||||
@@ -129,7 +129,7 @@ At runtime, an incoming command event becomes a `CommandContext` in the
|
||||
|
||||
## Command catalog
|
||||
|
||||
Concrete commands live under `me.bobert.netneutrality.feature.*.command`, grouped by feature package:
|
||||
Concrete commands live under `me.bobert.netneutrality.command.*`, grouped by feature package:
|
||||
|
||||
| Command | Package | Type | Description |
|
||||
|---|---|---|---|
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# Contexts
|
||||
|
||||
**Packages:** `me.bobert.netneutrality.framework.context` (framework),
|
||||
`me.bobert.netneutrality.framework.context` (concrete implementations)
|
||||
**Packages:** `me.bobert.netneutrality.core.context` (framework),
|
||||
`me.bobert.netneutrality.context.command` / `me.bobert.netneutrality.context.tasks` (concrete implementations)
|
||||
|
||||
A **Context** is a high-level abstraction over a Discord event. It hides the differences between the
|
||||
many JDA event types (slash command, message, button, modal, reaction, …) behind a single, uniform
|
||||
@@ -117,7 +117,7 @@ Abstract base for the [task flow](tasks.md). Two things distinguish it:
|
||||
|
||||
### `AsyncContext`
|
||||
|
||||
`me.bobert.netneutrality.framework.context.AsyncContext` wraps the **result** of a previous reply — either an
|
||||
`me.bobert.netneutrality.core.context.AsyncContext` wraps the **result** of a previous reply — either an
|
||||
`InteractionHook` (for interactions) or a `Message` (for message replies) — so follow-up actions are
|
||||
expressed the same way regardless of the original trigger:
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Event Handling & Routing
|
||||
|
||||
**Package:** `me.bobert.netneutrality.platform.event`
|
||||
**Package:** `me.bobert.netneutrality.hooks`
|
||||
**Primary class:** `EventsHandler`
|
||||
|
||||
`EventsHandler` is the single JDA `EventListener` registered on the `JDA` instance
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Persistence & Database
|
||||
|
||||
**Packages:** `me.bobert.netneutrality.platform.persistence` (session/migration),
|
||||
**Packages:** `me.bobert.netneutrality.db` (session/migration),
|
||||
`me.bobert.netneutrality.db` (entities, repositories, services).
|
||||
**Resources:** `hibernate.cfg.xml`, `db/migration/V1__initial_schema.sql`,
|
||||
`db/migration/V2__task_type_id.sql`.
|
||||
@@ -45,7 +45,7 @@ Repositories obtain sessions through the factory; transactional access uses help
|
||||
|
||||
## Entity catalog
|
||||
|
||||
Entities live in `me.bobert.netneutrality.data.entity` and are prefixed with `O`. `OGuild` is the hub
|
||||
Entities live in `me.bobert.netneutrality.db.entities` and are prefixed with `O`. `OGuild` is the hub
|
||||
of a star-shaped model; most other tables reference `guild_id` with `ON DELETE CASCADE`.
|
||||
|
||||
| Entity | Table | Key | Relationships / notes |
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# Tasks
|
||||
|
||||
**Packages:** `me.bobert.netneutrality.framework.task` (framework),
|
||||
`me.bobert.netneutrality.feature.*.task` (concrete tasks)
|
||||
**Packages:** `me.bobert.netneutrality.core.tasks` (framework),
|
||||
`me.bobert.netneutrality.tasks.*` (concrete tasks)
|
||||
|
||||
Tasks are one of the more advanced concepts in the bot. A **Task** is a unit of logic that is looked
|
||||
up and executed by **id** when a matching Discord event arrives, as opposed to a
|
||||
@@ -60,7 +60,7 @@ AbstractTask (implements Task)
|
||||
|
||||
## `TaskManager`
|
||||
|
||||
`me.bobert.netneutrality.framework.task.TaskManager` is the registry, lookup, and execution
|
||||
`me.bobert.netneutrality.core.tasks.TaskManager` is the registry, lookup, and execution
|
||||
coordinator.
|
||||
|
||||
### Registration — `insert`
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
# Threading & Scheduling
|
||||
|
||||
**Package:** `me.bobert.netneutrality.platform.concurrency`
|
||||
**Packages:** `me.bobert.netneutrality.core.thread` and `me.bobert.netneutrality.core.scheduled`
|
||||
|
||||
This subsystem centralizes asynchronous execution and recurring jobs.
|
||||
|
||||
@@ -21,11 +21,11 @@ This subsystem centralizes asynchronous execution and recurring jobs.
|
||||
|
||||
## `BaseThreadFactory`
|
||||
|
||||
Names created threads (e.g. `web-api-1`) so they are identifiable in logs and stack dumps, and installs
|
||||
Names created threads (e.g. `pool-1`) so they are identifiable in logs and stack dumps, and installs
|
||||
an [`ExecutorException`](utilities-and-bootstrap.md#exceptions) as the thread's
|
||||
`UncaughtExceptionHandler` so crashes on worker threads are still routed through
|
||||
[`LogHandler`](utilities-and-bootstrap.md#loghandler). It is used both by `ThreadManager` and by the
|
||||
[Web API](web-api.md) server pool.
|
||||
[`LogHandler`](utilities-and-bootstrap.md#loghandler). It is used by `ThreadManager`; the
|
||||
[Web API](web-api.md) runs on Javalin's own thread pool instead.
|
||||
|
||||
## `ScheduledTaskManager`
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ error tracking, the shared utility helpers, and the custom exception hierarchy.
|
||||
| `App` | The application: holds the process instance and the `JDA` handle, builds the `ApplicationContext`, runs `context.start()` / `context.stop()`, and is the JAR main class (`main()`). See [architecture.md](architecture.md). |
|
||||
| `bootstrap.ApplicationContext` | Hand-rolled container: `register(T)`, `get(Class<T>)`, ordered `start()` / `stop()` for `Lifecycle` components. |
|
||||
| `bootstrap.Lifecycle` | Optional component contract: `start() throws Exception`, default `stop()`, default `priority()`. |
|
||||
| `core.BotState` | Work-in-progress global state helper: exposes a `SAFE` flag and `dump()`, which serializes per-guild/music state to `state.json`. Bound to `GuildServiceProvider` / `MusicService` during bootstrap. |
|
||||
| `bootstrap.BotState` | Work-in-progress global state helper: exposes a `SAFE` flag and `dump()`, which serializes per-guild/music state to `state.json`. Bound to `GuildServiceProvider` / `MusicService` during bootstrap. |
|
||||
|
||||
The full startup and shutdown sequences are documented in
|
||||
[architecture.md](architecture.md#startup-lifecycle).
|
||||
@@ -37,13 +37,13 @@ resolve via `context.get(type)` is preferred; otherwise the no-arg constructor i
|
||||
|
||||
| Interface | Typical implementor |
|
||||
|-----------------------------------------|---------------------------------------|
|
||||
| `platform.config.SettingsProvider` | `utils.Settings` |
|
||||
| `platform.persistence.SessionProvider` | `core.db.SessionManager` |
|
||||
| `platform.concurrency.ExecutorProvider` | `core.thread.ThreadManager` |
|
||||
| `framework.command.CommandRegistry` | `core.command.CommandManager` |
|
||||
| `framework.task.TaskManager` | `core.tasks.TaskManagerImpl` |
|
||||
| `feature.music.MusicService` | `music.MusicServiceImpl` |
|
||||
| `data.service.GuildServiceProvider` | `db.service.GuildServiceProviderImpl` |
|
||||
| `utils.SettingsProvider` | `utils.Settings` |
|
||||
| `db.SessionProvider` | `db.SessionManager` |
|
||||
| `core.thread.ExecutorProvider` | `core.thread.ThreadManager` |
|
||||
| `core.command.CommandRegistry` | `core.command.CommandManager` |
|
||||
| `core.tasks.TaskManager` | `core.tasks.TaskManagerImpl` |
|
||||
| `music.MusicService` | `music.MusicServiceImpl` |
|
||||
| `db.service.GuildServiceProvider` | `db.service.GuildServiceProviderImpl` |
|
||||
|
||||
There are **no** `getInstance()` / `instance()` shims on the managers above. The only intentional
|
||||
static access points left are:
|
||||
@@ -60,8 +60,8 @@ static access points left are:
|
||||
|
||||
### `Settings` / `SettingsProvider`
|
||||
|
||||
**Class:** `me.bobert.netneutrality.platform.config.Settings` implements
|
||||
`me.bobert.netneutrality.platform.config.SettingsProvider`.
|
||||
**Class:** `me.bobert.netneutrality.utils.Settings` implements
|
||||
`me.bobert.netneutrality.utils.SettingsProvider`.
|
||||
|
||||
`Settings` is the single source of truth for configuration. It loads values from the `bot.properties`
|
||||
file and overlays **environment variables** on top (environment variables win).
|
||||
@@ -90,7 +90,7 @@ An example configuration file is `bot.properties.example`. Key configuration val
|
||||
|
||||
## Utility helpers
|
||||
|
||||
**Package:** `me.bobert.netneutrality.shared.util` (helpers) / `me.bobert.netneutrality.bootstrap` / `me.bobert.netneutrality.platform.*`
|
||||
**Package:** `me.bobert.netneutrality.utils` (helpers) / `me.bobert.netneutrality.bootstrap` / `me.bobert.netneutrality.exceptions`
|
||||
|
||||
| Class | Purpose |
|
||||
|---|---|
|
||||
@@ -125,7 +125,7 @@ singletons.
|
||||
|
||||
### `LogHandler`
|
||||
|
||||
**Class:** `me.bobert.netneutrality.platform.logging.LogHandler`.
|
||||
**Class:** `me.bobert.netneutrality.core.LogHandler`.
|
||||
|
||||
`LogHandler` is the unified logging/error entry point. It routes messages and exceptions to:
|
||||
|
||||
|
||||
+70
-31
@@ -1,52 +1,91 @@
|
||||
# HTTP Web API
|
||||
|
||||
**Package:** `me.bobert.netneutrality.platform.web` (client module) and
|
||||
`me.bobert.netneutrality.platform.web.server` (server).
|
||||
**Server:** JDK built-in `com.sun.net.httpserver.HttpServer` (no external web framework).
|
||||
**Package:** `me.bobert.netneutrality.http` (outbound client) and
|
||||
`me.bobert.netneutrality.http.server` (server).
|
||||
**Server:** embedded [Javalin](https://javalin.io) 7 with the OpenAPI and Swagger UI plugins.
|
||||
|
||||
The bot exposes a small internal HTTP API for health monitoring, control, and guild introspection —
|
||||
intended for an external dashboard/companion, not for end users in Discord.
|
||||
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`](utilities-and-bootstrap.md#applicationcontext--lifecycle) component
|
||||
(priority 70) registered in [`App.buildContext()`](architecture.md#startup-lifecycle).
|
||||
It receives `SettingsProvider`, `GuildServiceProvider`, and suppliers for the live `JDA` /
|
||||
shutdown callback via its constructor. On `start()` it:
|
||||
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 an `HttpServer` bound to that port,
|
||||
- installs an `Executor` backed by a fixed thread pool of 10 threads named via
|
||||
[`BaseThreadFactory`](threading.md),
|
||||
- registers the handler contexts (below), and
|
||||
- starts the server.
|
||||
- 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 server and its executor are shut down gracefully.
|
||||
On `stop()` the Javalin app is stopped.
|
||||
|
||||
## Handler model
|
||||
## Authentication
|
||||
|
||||
`AbstractHandler` (implements `HttpHandler`) is the base for all handlers and provides helpers such as
|
||||
`sendJsonResponse(...)` and `sendStringResponse(...)`. Concrete handlers extend it and are mounted at
|
||||
a path prefix.
|
||||
`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).
|
||||
|
||||
| Handler | Base path | Endpoints |
|
||||
|---|---|---|
|
||||
| `RootHandler` | `/` | Simple HTML "OK" landing response. |
|
||||
| `GuildHandler` | `/guilds` | `/guilds/owner` — guild ids + owner ids (JSON); `/guilds/{guildId}/channels` — text channels (JSON); `/guilds/{guildId}/roles` — roles (JSON). |
|
||||
| `ActionHandler` | `/actions` | `/actions/status` — JDA health + gateway ping; `/actions/shutdown` — graceful shutdown. |
|
||||
| `SettingsHandler` | `/settings` | `/settings/catalog` — every known setting with its default value and description; `/settings/current/{guildId}` — current guild settings; `/settings/reload/{guildId}` — invalidate & reload the settings cache. |
|
||||
## Error handling
|
||||
|
||||
The action/guild handlers read live state from the [`JDA`](architecture.md) instance and from the
|
||||
[persistence services](persistence.md#service-layer) (e.g. `GuildSettingService` for the settings
|
||||
endpoints).
|
||||
- `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](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). |
|
||||
| `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` wraps an `OkHttpClient` for the bot's own outbound HTTP calls (e.g. checking for updates
|
||||
via GitLab). It keeps a small `LinkedHashMap`-based response cache limited to 50 entries.
|
||||
`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
|
||||
|
||||
- There is no authentication layer documented on these endpoints; 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.
|
||||
- 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.
|
||||
Reference in new issue
Block a user