8.5 KiB
Contexts
Packages: me.bobert.netneutrality.framework.context (framework),
me.bobert.netneutrality.framework.context (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 API for reading who/where the event came from and for replying to it.
Contexts are created either by the EventsHandler when Discord triggers an event, or by
another context further down a flow (for example, a reply produces an AsyncContext
that can be edited later). A context can be handled by one of three flows depending on its type and
the event it wraps: the command processor, the task manager, or a simple
lambda. The id of a context only matters for tasks — the task manager uses getId() to locate
the Task that should run (see tasks.md).
Type hierarchy
Context (interface)
├── InteractionContext (interface) ── reply/defer/ack via IReplyCallback + InteractionHook
├── NoMessageContext (interface) ── defaults for events without a triggering message
│ └── MessageContext (interface) ── adds a triggering Message (prefix commands, replies)
│
└── AbstractContext (abstract, implements Context) ── stores JDA/Guild/Channel/Member/User
├── CommandContext (abstract) ── command flow; holds CommandOptions + subcommand metadata
│ ├── SlashCommandContext
│ ├── MessageCommandContext
│ ├── MessageMenuContext
│ └── UserMenuContext
├── TaskContext (abstract) ── task flow; abstract getId(); contextData state map
│ ├── ButtonTaskContext
│ ├── EmojiTaskContext
│ ├── MessageTaskContext
│ └── ModalTaskContext
├── AsyncContext ── wraps the result of a previous reply (edit/follow-up)
└── MusicChannelContext ── synthetic context targeting a guild's music channel
Core framework types
Context (interface)
The root contract for every context. Notable methods:
getJDA(),getGuild(),getChannel(),getGuildUser()(Member),getDiscordUser()(User)reply(...)— unified send; overloads support plain text, embeds, message components, anephemeralflag, and an async callback that receives anAsyncContextfor follow-up edits.defer(ephemeral, consumer)— signals "thinking…" and later completes the response.ack(ephemeral)— minimal acknowledgement of the event.getInteractionHook()— the JDAInteractionHookwhen the underlying event is an interaction.
AbstractContext (abstract class)
Base implementation that stores the common JDA entities (JDA, Guild, MessageChannel, Member,
User) and exposes them via the Context getters. Its constructor also sets the Sentry user
(id + effective name) so any error captured while handling this context is attributed to the right
Discord user:
if (user != null) {
io.sentry.protocol.User sentryUser = new io.sentry.protocol.User();
sentryUser.setId(user.getId());
sentryUser.setUsername(user.getEffectiveName());
Sentry.setUser(sentryUser);
}
InteractionContext (interface)
Behavioral mix-in for events that are JDA Interactions (slash commands, buttons, modals, context
menus). It implements reply/defer/ack in terms of IReplyCallback and InteractionHook, so
these contexts can reply ephemerally, defer, and edit the original response.
NoMessageContext / MessageContext (interfaces)
NoMessageContext— provides sensible defaults for events that have no triggering message (e.g. reactions, interactions).MessageContext extends NoMessageContext— adds access to a triggeringMessage, used by prefix commands and by replies that drive step tasks.
Command contexts
CommandContext
Abstract base for the command flow. It carries the parsed
CommandOptions and the active subcommand / subcommand-group metadata,
and exposes type-safe option access, e.g. getOption(name, resolver). Concrete subclasses adapt a
specific JDA event to this API.
| Class | Wraps | Notes |
|---|---|---|
SlashCommandContext |
SlashCommandInteractionEvent |
Options already parsed by Discord; maps OptionMapping → SlashCommandOption. Interaction-based replies. |
MessageCommandContext |
MessageReceivedEvent (prefix) |
Contains the manual parser that turns the raw message string into CommandOptions (mentions, integers, booleans, …). Message-based replies. |
MessageMenuContext |
MessageContextInteractionEvent |
"Apps → …" action on a message; exposes the target Message. |
UserMenuContext |
UserContextInteractionEvent |
"Apps → …" action on a user; exposes the target User. |
Task contexts
TaskContext
Abstract base for the task flow. Two things distinguish it:
getId()(abstract) — the id used byTaskManagerto find the matchingTask. How the id is derived depends on the concrete subtype (see the table below and tasks.md).contextDatamap — a small state bag used to carry values between steps of a multi-step flow.
| Class | Wraps | getId() derived from |
|---|---|---|
ButtonTaskContext |
ButtonInteractionEvent |
getComponentId() of the clicked button |
ModalTaskContext |
ModalInteractionEvent |
modal customId |
EmojiTaskContext |
MessageReactionAddEvent |
composite guildId:messageId |
MessageTaskContext |
MessageReceivedEvent (no prefix) |
the id of the message being replied to (drives step tasks) |
Reply mechanism & AsyncContext
AsyncContext
me.bobert.netneutrality.framework.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:
context.reply("Initial response", async -> {
// 'async' is an AsyncContext for the message just sent
async.edit("Updated response");
});
Under the hood:
- Interactions — edits/follow-ups go through
hook.editOriginal()/hook.sendMessage(). - Messages — edits/follow-ups go through
message.editMessage()/message.reply().
This is what makes the command and task code path-agnostic: authors write against Context /
AsyncContext and never branch on "was this a slash command or a text message?".
MusicChannelContext
A synthetic AbstractContext that targets a guild's configured music channel rather than the
channel an event came from. The music subsystem uses it to post now-playing / queue messages into the
correct channel even when playback state changes without a fresh user interaction. See
music.md.
End-to-end summary
| Event | Context | Flow | Async result wraps |
|---|---|---|---|
SlashCommandInteractionEvent |
SlashCommandContext |
Command | InteractionHook |
MessageReceivedEvent (prefix) |
MessageCommandContext |
Command | Message |
ButtonInteractionEvent |
ButtonTaskContext |
Task | InteractionHook |
MessageReactionAddEvent |
EmojiTaskContext |
Task | Message |
ModalInteractionEvent |
ModalTaskContext |
Task | InteractionHook |