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,161 @@
|
||||
# Architecture Overview
|
||||
|
||||
Net Neutrality is a Java 25 Discord bot built on top of [JDA](https://github.com/discord-jda/JDA).
|
||||
It is packaged as a single fat JAR (via the Maven Shade plugin) whose entry point is
|
||||
`me.bobert.netneutrality.App`.
|
||||
|
||||
## Big picture
|
||||
|
||||
Discord sends **events** over the gateway. A single JDA `EventListener`
|
||||
(`me.bobert.netneutrality.hooks.EventsHandler`) receives every event and turns it into a
|
||||
**Context** — a high-level abstraction over that event. Each context is then routed to one of three
|
||||
execution paths:
|
||||
|
||||
```
|
||||
┌──────────────────────────────┐
|
||||
Discord Gateway ───────▶│ EventsHandler │
|
||||
(JDA events) │ event class → Context factory│
|
||||
└───────────────┬──────────────┘
|
||||
│ builds a Context
|
||||
┌─────────────────────────┼───────────────────────────┐
|
||||
▼ ▼ ▼
|
||||
CommandContext TaskContext (no Context)
|
||||
│ │ lambda /
|
||||
▼ ▼ consumer runs
|
||||
CommandProcessor TaskManager.handle() directly
|
||||
.dispatch() (lookup task by id) (e.g. GuildJoin)
|
||||
│ │
|
||||
▼ ▼
|
||||
Command.execute() Task.execute()
|
||||
```
|
||||
|
||||
1. **Command flow** — the context is a `CommandContext`; it is handed to `CommandProcessor` which
|
||||
resolves and runs a `Command`. See [commands.md](commands.md).
|
||||
2. **Task flow** — the context is a `TaskContext`; it is handed to `TaskManager` which looks up a
|
||||
`Task` by its **id** and runs it. See [tasks.md](tasks.md).
|
||||
3. **Lambda flow** — for simple events (e.g. `GuildJoinEvent`, `GuildLeaveEvent`) a consumer runs
|
||||
directly and no context is routed onward. See [events.md](events.md).
|
||||
|
||||
Around this core are the supporting subsystems: [persistence](persistence.md) (Hibernate + Flyway),
|
||||
[music](music.md) (Lavaplayer), the [web API](web-api.md), and the [threading/scheduling](threading.md)
|
||||
infrastructure.
|
||||
|
||||
## Technology stack
|
||||
|
||||
| Concern | Library | Version |
|
||||
|---|---|---|
|
||||
| Discord API | `net.dv8tion:JDA` | 6.4.1 |
|
||||
| Voice / DAVE (E2EE) | `club.minnced:jdave-*` | 0.1.8 |
|
||||
| Audio engine | `dev.arbjerg:lavaplayer` | 2.2.6 |
|
||||
| YouTube source | `dev.lavalink.youtube:v2` | 1.18.2 |
|
||||
| ORM | `org.hibernate.orm:hibernate-core` | 7.1.9.Final |
|
||||
| Connection pool | `hibernate-hikaricp` (HikariCP) | 7.1.9.Final |
|
||||
| JDBC driver | `com.mysql:mysql-connector-j` | 9.7.0 |
|
||||
| DB migrations | `org.flywaydb:flyway-core` / `flyway-mysql` | 11.20.3 |
|
||||
| Error tracking | `io.sentry:sentry` | 8.41.0 |
|
||||
| GitLab integration | `org.gitlab4j:gitlab4j-api` | 6.2.0 |
|
||||
| HTTP client | `com.squareup.okhttp3:okhttp-jvm` | 5.3.2 |
|
||||
| JSON | `org.json:json` + `com.google.code.gson:gson` | — |
|
||||
| Classpath scanning | `org.reflections:reflections` | 0.10.2 |
|
||||
| Emoji parsing | `net.fellbaum:jemoji` | 1.7.6 |
|
||||
| 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).
|
||||
|
||||
## Startup lifecycle
|
||||
|
||||
The entry point is `App.main()` followed by the `App()` constructor which calls
|
||||
`startup()`. Bootstrap is driven by a hand-rolled
|
||||
[`ApplicationContext`](utilities-and-bootstrap.md#applicationcontext--lifecycle) that owns wired
|
||||
instances and starts every [`Lifecycle`](utilities-and-bootstrap.md#applicationcontext--lifecycle)
|
||||
component in ascending priority order.
|
||||
|
||||
1. **Print banner / read env** — reads `NETNEUTRALITY_BOT_ENV` (default `dev`) via the early-bootstrap
|
||||
`Settings.getInstance()` singleton (the only remaining process-wide settings access point; it is
|
||||
also registered into the context as `SettingsProvider` for constructor injection).
|
||||
2. **Sentry** — if `NETNEUTRALITY_SENTRY_DSN` is set, initialize the Sentry SDK
|
||||
(environment, release = `VERSION`, traces sample rate 1.0).
|
||||
3. **Log levels** — in `dev`, raise JDA / Lavalink-YouTube / sedmelluq logging to `DEBUG`.
|
||||
4. `startup()`:
|
||||
1. Validate `NETNEUTRALITY_BOT_TOKEN` is present (throws if missing).
|
||||
2. `buildContext()` — register domain services and lifecycle adapters (see priorities below).
|
||||
3. `context.start()` — start every `Lifecycle` component in ascending priority order.
|
||||
4. Register a JVM shutdown hook calling `shutdown()` → `context.stop()`.
|
||||
|
||||
### Lifecycle priorities (startup order)
|
||||
|
||||
| Priority | Component | What `start()` does |
|
||||
|---|---|---|
|
||||
| 10 | `SessionManager` (`SessionProvider`) | Build Hibernate `SessionFactory` + run Flyway migrations. Binds itself into `JpaUtils`. |
|
||||
| 20 | `ThreadManager` (`ExecutorProvider`) | No-op start (executors are lazy). Bound into `ChannelMessagePurger`. |
|
||||
| 30 | `CommandManager` (`CommandRegistry`) | Reflectively discover/register `Command` implementations with context-aware constructor injection. |
|
||||
| 30 | `TaskManagerImpl` (`TaskManager`) | Reflectively load built-in & scheduled tasks (DI) and reload persistable tasks from the DB. |
|
||||
| 40 | `MusicServiceImpl` (`MusicService`) | Initialise the shared Lavaplayer `AudioPlayerManager`. |
|
||||
| 55 | `ScheduledTaskManager` | No-op start (tasks are scheduled by `TaskManager` during its own start). |
|
||||
| 60 | JDA adapter | Build `JDA` with gateway intents and await ready. |
|
||||
| 65 | Post-JDA adapter | Register slash commands, attach `EventsHandler` (injected providers), set presence. |
|
||||
| 70 | `WebApi` | Start the internal HTTP server (`NETNEUTRALITY_API_PORT`, default 9000). |
|
||||
|
||||
Commands and tasks that need shared services declare them as constructor parameters
|
||||
(`MusicService`, `TaskManager`, `GuildServiceProvider`, `SettingsProvider`, `CommandRegistry`, …).
|
||||
`ReflectionHelper` picks the richest public constructor whose parameter types all resolve from the
|
||||
context (falling back to a no-arg constructor when none match).
|
||||
|
||||
### Gateway intents
|
||||
|
||||
`initJDA()` enables: `GUILD_MEMBERS`, `GUILD_MESSAGE_REACTIONS`, `GUILD_VOICE_STATES`,
|
||||
`MESSAGE_CONTENT`. It also installs the DAVE (end-to-end encrypted voice) session factory via
|
||||
`AudioModuleConfig().withDaveSessionFactory(new JDaveSessionFactory())`.
|
||||
|
||||
## Shutdown lifecycle
|
||||
|
||||
`App.shutdown()` (invoked by the JVM shutdown hook, `ShutdownCommand`, or the HTTP
|
||||
`/actions/shutdown` endpoint) calls `context.stop()`, which stops every registered `Lifecycle`
|
||||
component in the **exact reverse** of start order. Failures in one component are logged and do not
|
||||
block the others. Effectively this means:
|
||||
|
||||
1. `WebApi.stop()` — stop the HTTP server and its executor.
|
||||
2. Post-JDA adapter — no-op.
|
||||
3. JDA adapter — `jda.shutdown()`.
|
||||
4. `ScheduledTaskManager.stop()` — cancel all scheduled tasks.
|
||||
5. `MusicServiceImpl.stop()` — disconnect players / leave voice channels.
|
||||
6. `CommandManager` / `TaskManagerImpl` — no-op stop today.
|
||||
7. `ThreadManager.stop()` — shut down the shared executors.
|
||||
8. `SessionManager.stop()` — release the Hibernate registry / connection pool.
|
||||
|
||||
## Configuration & build
|
||||
|
||||
- **Config sources** — `Settings` loads `bot.properties` and overlays environment variables (env
|
||||
vars win). See [utilities-and-bootstrap.md](utilities-and-bootstrap.md#settings). An example file
|
||||
lives at `bot.properties.example`.
|
||||
- **Build** — `./mvnw package` produces `target/netneutrality.jar` (Shade fat JAR, main class
|
||||
`me.bobert.netneutrality.App`). Checkstyle (`checkstyle.xml`) runs in the `test` phase
|
||||
and fails the build on violations.
|
||||
- **Run** — the preferred method is Docker Compose (`docker compose build && docker compose up -d`),
|
||||
which also provisions the MariaDB database. See `README.md` and `docker-compose.yml`.
|
||||
|
||||
## Package map
|
||||
|
||||
| Package | Contents |
|
||||
|---|---|
|
||||
| `me.bobert.netneutrality` | Entry point (`App`) |
|
||||
| `…bootstrap` | `ApplicationContext`, `Lifecycle`, `BotState`, `ReflectionHelper` |
|
||||
| `…core.command` | Command engine (registry, processor, manager, options, impl bases) |
|
||||
| `…core.context` | Context interfaces, abstract bases (`CommandContext`, `TaskContext`, etc.) |
|
||||
| `…core.tasks` | Task engine (manager, subtypes, traits, persistence, generic buttons) |
|
||||
| `…core.thread` | Executors & scheduling (`ThreadManager`, `ScheduledTaskManager`, `BaseThreadFactory`) |
|
||||
| `…core` | `LogHandler`, `NetNeutralityBot` |
|
||||
| `…command.*` | Concrete command implementations (`admin`, `basic`, `bot`, `crypto`, `custom`, `git`, `music`, `react`) |
|
||||
| `…context.command` / `…context.tasks` | Concrete context implementations (slash, message, button, modal, emoji, etc.) |
|
||||
| `…context` | `MusicChannelContext` |
|
||||
| `…tasks.*` | Concrete task implementations (`git`, `help`, `music`, `reaction`) |
|
||||
| `…db` | Session/migration, `Repository`, `JpaUtils`, `GSettings` |
|
||||
| `…db.entities` / `…db.repository` / `…db.service` | Persistence model, repositories, services |
|
||||
| `…hooks` | `EventsHandler` |
|
||||
| `…http` | Internal web API and HTTP client module |
|
||||
| `…music` | Music domain (service, manager, queue, sources, tracks) |
|
||||
| `…utils` | `Settings` / `SettingsProvider`, `ChannelMessagePurger`, `MusicUtils` |
|
||||
| `…shared.util` | Cross-cutting helpers |
|
||||
| `…exceptions` | Custom exception hierarchy |
|
||||
Reference in new issue
Block a user