Files
netneutrality/docs/events.md
T

4.2 KiB

Event Handling & Routing

Package: me.bobert.netneutrality.platform.event 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 a Context to be routed onward (command or task flow).
  • addGenericListener(Class<T>, Consumer<T>) — registers a factory that runs a side-effecting consumer and returns null, 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:

  1. Ignores messages authored by bots and webhook messages (returns null).
  2. Reads the configured prefix (NETNEUTRALITY_BOT_PREFIX, default $).
  3. If the trimmed message starts with the prefix → MessageCommandContext (command flow).
  4. 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 — resolves GuildService.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 CommandContext goes to CommandProcessor.dispatch(), a TaskContext goes to TaskManager.getInstance().handle(context).
  • null contexts (bot messages, consumer-only events) are simply not routed anywhere.

Extending the event system

To handle a new event type:

  1. Add an addListener(SomeEvent.class, SomeContext::new) (or addGenericListener(...) for a consumer-only side effect) to the static {} block.
  2. If it should run the command or task flow, make the produced context extend CommandContext or TaskContext respectively.