4.2 KiB
4.2 KiB
Event Handling & Routing
Package: me.bobert.netneutrality.hooks
Primary class: EventsHandler
EventsHandler is the single JDA EventListener registered on the JDA instance
(jda.addEventListener(new EventsHandler()) in App.startup()). It is the funnel through
which every gateway event passes. Its job is to translate a raw JDA event into a
Context and route that context to the correct subsystem.
Listener registry
Listeners are held in a static, thread-safe map keyed by the JDA event class:
private static final Map<Class<? extends Event>, Function<? extends Event, Context>> listeners
= new ConcurrentHashMap<>();
Each entry is a factory function: given an event of that type, it returns a Context (or null).
The map is populated once in a static {} block.
addListener vs addGenericListener
addListener(Class<T>, Function<? super T, Context>)— registers a factory that produces aContextto be routed onward (command or task flow).addGenericListener(Class<T>, Consumer<T>)— registers a factory that runs a side-effecting consumer and returnsnull, so nothing is routed onward. This is the lambda flow for simple events.
Registered events
| JDA event | Produces | Flow |
|---|---|---|
MessageReceivedEvent (prefix match) |
MessageCommandContext |
Command |
MessageReceivedEvent (no prefix) |
MessageTaskContext |
Task |
SlashCommandInteractionEvent |
SlashCommandContext |
Command |
UserContextInteractionEvent |
UserMenuContext |
Command |
MessageContextInteractionEvent |
MessageMenuContext |
Command |
ButtonInteractionEvent |
ButtonTaskContext |
Task |
ModalInteractionEvent |
ModalTaskContext |
Task |
MessageReactionAddEvent |
EmojiTaskContext |
Task |
GuildJoinEvent |
— (consumer) | Lambda |
GuildLeaveEvent |
— (consumer) | Lambda |
Message routing detail
For MessageReceivedEvent the factory:
- Ignores messages authored by bots and webhook messages (returns
null). - Reads the configured prefix (
NETNEUTRALITY_BOT_PREFIX, default$). - If the trimmed message starts with the prefix →
MessageCommandContext(command flow). - Otherwise →
MessageTaskContext(task flow). This lets ordinary messages participate in the task system, e.g. as a reply that advances a step task.
Guild lifecycle (lambda flow)
GuildJoinEvent— resolvesGuildService.get(guild); if debug logging is enabled for that guild, sends a notice to the debug channel.GuildLeaveEvent—GuildService.get(guild).deleteGuild()removes the guild's stored data.
Dispatch algorithm
@Override
public void onEvent(@Nonnull GenericEvent event) {
handleEvent((Event) event);
}
private void handleEvent(Event event) {
Class<?> eventClass = event.getClass();
while (eventClass != null) {
if (listeners.containsKey(eventClass)) {
Context context = listeners.get(eventClass).apply(event);
if (context instanceof CommandContext c) handleCommand(c);
if (context instanceof TaskContext t) handleTask(t);
break;
}
eventClass = eventClass.getSuperclass(); // walk up the class hierarchy
}
}
Key points:
- Superclass walk — if the exact event class is not registered, the handler walks up the superclass chain until it finds a match. This means a listener registered for a base event type also catches subclasses.
- Routing by context type — the resulting object is routed purely by its runtime type:
a
CommandContextgoes toCommandProcessor.dispatch(), aTaskContextgoes toTaskManager.getInstance().handle(context). nullcontexts (bot messages, consumer-only events) are simply not routed anywhere.
Extending the event system
To handle a new event type:
- Add an
addListener(SomeEvent.class, SomeContext::new)(oraddGenericListener(...)for a consumer-only side effect) to thestatic {}block. - If it should run the command or task flow, make the produced context extend
CommandContextorTaskContextrespectively.