Remove deprecated classes and static singleton-based architecture
This commit is contained in:
1 parent
54a27883d1
commit
b8786c8dc9
133 files changed
+4984
-1038
No files matched your search
@@ -0,0 +1,164 @@
|
||||
# Utilities, Bootstrap, Logging & Exceptions
|
||||
|
||||
This document covers the cross-cutting glue of the bot: the entry points, configuration, logging /
|
||||
error tracking, the shared utility helpers, and the custom exception hierarchy.
|
||||
|
||||
## Bootstrap
|
||||
|
||||
**Package:** `me.bobert.netneutrality` / `me.bobert.netneutrality.bootstrap`
|
||||
|
||||
| Class | Role |
|
||||
|---|---|
|
||||
| `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()`. |
|
||||
| `NetNeutralityBot` | Currently an **empty placeholder** class under `framework` (reserved name; no logic yet). |
|
||||
| `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. |
|
||||
|
||||
The full startup and shutdown sequences are documented in
|
||||
[architecture.md](architecture.md#startup-lifecycle).
|
||||
|
||||
### `ApplicationContext` + `Lifecycle`
|
||||
|
||||
Domain services are constructed once in `App.buildContext()`, registered with the context,
|
||||
and resolved later by type:
|
||||
|
||||
- `register(component)` — indexes the concrete class; if the component implements `Lifecycle` it
|
||||
participates in ordered start/stop.
|
||||
- `get(Class<T>)` — returns the first registered instance assignable to `T` (interfaces work).
|
||||
- `start()` — sorts `Lifecycle` components by ascending `priority()` and starts them.
|
||||
- `stop()` — stops them in the exact reverse order; individual failures are logged and swallowed so
|
||||
remaining components still tear down.
|
||||
|
||||
Constructor injection for reflectively loaded commands/tasks is handled by
|
||||
[`ReflectionHelper`](#reflectionhelper): the richest public constructor whose parameter types all
|
||||
resolve via `context.get(type)` is preferred; otherwise the no-arg constructor is used.
|
||||
|
||||
**Interfaces used for injection** (implemented by the concrete managers registered above):
|
||||
|
||||
| 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` |
|
||||
|
||||
There are **no** `getInstance()` / `instance()` shims on the managers above. The only intentional
|
||||
static access points left are:
|
||||
|
||||
- `Settings.getInstance()` — early bootstrap only (`main()`, Sentry, token validation) before the
|
||||
context exists; the same instance is registered as `SettingsProvider` for everything else.
|
||||
- `App.getInstance()` — application root used by a couple of shutdown entry points
|
||||
(`ShutdownCommand`). Prefer constructor injection of the needed collaborators where possible.
|
||||
- `JpaUtils.bind(SessionProvider)` / `LogHandler.bind(GuildServiceProvider)` /
|
||||
`ChannelMessagePurger.bind(ExecutorProvider)` — single well-defined static bind points for deep
|
||||
utility graphs that are not constructor-injected (repositories, static log helpers).
|
||||
|
||||
## Configuration
|
||||
|
||||
### `Settings` / `SettingsProvider`
|
||||
|
||||
**Class:** `me.bobert.netneutrality.platform.config.Settings` implements
|
||||
`me.bobert.netneutrality.platform.config.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).
|
||||
|
||||
- **Early bootstrap:** `Settings.getInstance()` (process-wide, created once before the context).
|
||||
- **Runtime injection:** depend on `SettingsProvider` and let `ApplicationContext` supply the same
|
||||
instance. Prefer this path in commands, tasks, handlers, and services.
|
||||
- `Settings.getVersion()` / `Settings.getDiscordInvite()` — thin static helpers used by the banner /
|
||||
invite strings; they delegate to the bootstrap instance.
|
||||
|
||||
An example configuration file is `bot.properties.example`. Key configuration values:
|
||||
|
||||
| Key | Purpose |
|
||||
|---|---|
|
||||
| `NETNEUTRALITY_BOT_ENV` | `dev` / `prod`; controls debug logging and the Sentry environment. |
|
||||
| `NETNEUTRALITY_BOT_TOKEN` | Discord bot token (**required**). |
|
||||
| `NETNEUTRALITY_BOT_PREFIX` | Message-command prefix (default `$`). |
|
||||
| `NETNEUTRALITY_BOT_OWNER` | Bot owner user id (used for owner-only commands / permissions). |
|
||||
| `NETNEUTRALITY_BOT_INVITE` | Invite link surfaced by help/info. |
|
||||
| `NETNEUTRALITY_API_PORT` | Port for the [Web API](web-api.md) (default `9000`). |
|
||||
| `MYSQL_*` | Database connection settings (see [persistence.md](persistence.md)). |
|
||||
| `NETNEUTRALITY_SENTRY_DSN` | Enables Sentry when present. |
|
||||
| `GITLAB_HOST` / `GITLAB_TOKEN` / `GITLAB_PROJECT_ID` | GitLab issue reporting ([`ReportIssueTask`](tasks.md#worked-example--gitlab-bug-report-modal)). |
|
||||
| `NETNEUTRALITY_PO_TOKEN` / `NETNEUTRALITY_VISITOR_DATA` / `NETNEUTRALITY_CIPHER_*` | YouTube playback auth/cipher settings for the [music](music.md) source. |
|
||||
|
||||
## Utility helpers
|
||||
|
||||
**Package:** `me.bobert.netneutrality.shared.util` (helpers) / `me.bobert.netneutrality.bootstrap` / `me.bobert.netneutrality.platform.*`
|
||||
|
||||
| Class | Purpose |
|
||||
|---|---|
|
||||
| `Settings` | Configuration (see above). |
|
||||
| `ReflectionHelper` | Classpath scanning / reflective instantiation (see below). |
|
||||
| `ContextUtils` | Boilerplate helpers for building/handling [contexts](contexts.md). |
|
||||
| `GuildUtils` | Guild-related helpers (lookups, permission checks). |
|
||||
| `JpaUtils` | Session/transaction helpers for the [persistence](persistence.md) layer. Bound once to a `SessionProvider` during bootstrap (`JpaUtils.bind`). |
|
||||
| `MusicUtils` | Voice-channel connectivity and DJ/permission checks for [music](music.md). Callers pass the guild `MusicManager` (no static music lookup). |
|
||||
| `MiscUtils` | Assorted small helpers. |
|
||||
| `ChannelMessagePurger` | Bulk message deletion (used by the purge command). Bound once to an `ExecutorProvider` during bootstrap. |
|
||||
| `MessageFunction` | Standardized message/embed formatting. Help text builders take a `CommandRegistry` rather than a static command manager. |
|
||||
| `UserFunction` | Standardized user/string formatting. |
|
||||
|
||||
### `ReflectionHelper`
|
||||
|
||||
Built on the `org.reflections` library, `ReflectionHelper` discovers and instantiates implementation
|
||||
classes in specific packages. It is the mechanism behind the bot's "drop a class in the right package
|
||||
and it's picked up" model, used to auto-load:
|
||||
|
||||
- [`Command`](commands.md) implementations (`CommandManager` / `CommandRegistry.start()`),
|
||||
- [`BuiltInTask`](tasks.md#32-built-in-tasks) and [`ScheduledTask`](tasks.md#34-scheduled-tasks)
|
||||
implementations (`TaskManager.start()`).
|
||||
|
||||
When given an `ApplicationContext`, `newInstance(Class, ApplicationContext)` selects the public
|
||||
constructor with the most parameters that can all be resolved via `context.get(parameterType)`. If
|
||||
no such constructor exists it falls back to the no-arg constructor. That is how music/admin/react
|
||||
commands receive `MusicService`, `GuildServiceProvider`, `TaskManager`, etc. without static
|
||||
singletons.
|
||||
|
||||
## Logging & error tracking
|
||||
|
||||
### `LogHandler`
|
||||
|
||||
**Class:** `me.bobert.netneutrality.platform.logging.LogHandler`.
|
||||
|
||||
`LogHandler` is the unified logging/error entry point. It routes messages and exceptions to:
|
||||
|
||||
1. **Log4j2** (configured by `log4j2.properties`; SLF4J is bridged via `log4j-slf4j2-impl`),
|
||||
2. **Sentry** (when `NETNEUTRALITY_SENTRY_DSN` is configured), and
|
||||
3. optionally a per-guild **Discord debug channel** (e.g. `sendMessageToDebugChannel(guild, msg)`),
|
||||
gated by [`GuildService.isDebugEnabled()`](persistence.md#service-layer) /
|
||||
[`OLogging`](persistence.md#entity-catalog) configuration.
|
||||
|
||||
The Sentry **user** context is attached automatically when a
|
||||
[`AbstractContext`](contexts.md#abstractcontext-abstract-class) is created, so captured errors are
|
||||
attributed to the Discord user that triggered them.
|
||||
|
||||
## Exception hierarchy
|
||||
|
||||
**Package:** `me.bobert.netneutrality.exceptions`
|
||||
|
||||
```
|
||||
RuntimeException
|
||||
└── ContextException ── carries a Context (guild/channel/user)
|
||||
├── CommandExecutionException ── thrown during command processing
|
||||
└── TaskExecutionException ── thrown during task execution
|
||||
|
||||
GracefulContextError ── user-facing, non-logged failure → ephemeral reply
|
||||
ExecutorException ── Thread.UncaughtExceptionHandler → routes crashes to LogHandler
|
||||
```
|
||||
|
||||
- **`ContextException`** — base runtime exception that carries the [`Context`](contexts.md) in which
|
||||
it occurred, so downstream handling can respond in the right channel and attribute the error.
|
||||
- **`CommandExecutionException`** / **`TaskExecutionException`** — specializations thrown by the
|
||||
[command](commands.md) and [task](tasks.md) flows respectively.
|
||||
- **`GracefulContextError`** — signals an expected, user-facing problem (e.g. bad input); when caught
|
||||
it produces an ephemeral message to the user instead of a full error log/Sentry event.
|
||||
- **`ExecutorException`** — an `UncaughtExceptionHandler` installed by
|
||||
[`BaseThreadFactory`](threading.md#basethreadfactory) so exceptions escaping worker threads are still
|
||||
captured by [`LogHandler`](#loghandler).
|
||||
Reference in new issue
Block a user