Repository navigation
UI Interaction Handlers
The UI Interaction Handling system is a core component of Amina's architecture, responsible for processing all non-command interactions originating from Discord's UI components, such as buttons, select menus, and modals. This system provides a centralized routing mechanism that directs user actions to the appropriate business logic, enabling complex, multi-step, and interactive features.
The central entry point for this system is the interactionCreate event listener. It acts as a primary router, inspecting the customId of each incoming interaction to delegate it to a specific handler module. This modular approach keeps the interaction logic organized by feature (e.g., profile, roles, admin) and allows for the creation of sophisticated, stateful user workflows.
Relevant source files
- src/events/interactions/interactionCreate.ts
- src/helpers/componentHelper.ts
- src/structures/VirtualInteraction.ts
- src/handlers/dev/main-hub.ts
- src/handlers/roles/add-to-user/index.ts
- src/handlers/minaai/memories.ts
- src/commands/utility/profile.ts
- types/handlers.d.ts
- src/handlers/dev/minaai/handlers.ts
The interaction handling architecture is built around a customId-based routing system within the interactionCreate.ts event handler. When a user interacts with a UI component, Discord sends an interaction object with a unique customId. The system parses this ID to determine the feature, component type, and specific action to be performed.
This diagram illustrates the high-level flow from a user interaction to the execution of a specific handler.
flowchart TD
A[Interaction Received] --> B{Interaction Type?}
B -->|isButton| C[Parse customId]
B -->|isStringSelectMenu| D[Parse customId]
B -->|isUserSelectMenu| E[Parse customId]
B -->|isRoleSelectMenu| F[Parse customId]
B -->|isChannelSelectMenu| G[Parse customId]
B -->|isModalSubmit| H[Parse customId]
C --> I[Route to Handler]
D --> I
E --> I
F --> I
G --> I
H --> I
I --> J{Feature Prefix}
J -->|admin:| K[Admin Handlers]
J -->|dev:| L[Dev Handlers]
J -->|minaai:| M[Mina AI Handlers]
J -->|profile:| N[Profile Handlers]
J -->|roles:| O[Roles Handlers]
J -->|ticket:| P[Ticket Handlers]
Sources: src/events/interactions/interactionCreate.ts:14-490
A strict naming convention for customId is used to facilitate routing. The convention follows a feature:type:action pattern, often extended with a | delimiter to carry state.
-
Simple Routing:
feature:type:action-
feature: The name of the module (e.g.,profile,admin,dev). -
type: The component type (e.g.,btn,menu,modal). -
action: The specific action to perform (e.g.,back,confirm,edit). -
Example:
profile:btn:back
-
-
Stateful Routing:
feature:type:action|key1:value1|key2:value2- State is appended after a
|delimiter. this is used for passing data between interaction steps, such as user ids, page numbers, or selected options. -
Example:
minaai:btn:category|type:fact|mem_type:server|page:1
- State is appended after a
Sources: src/events/interactions/interactionCreate.ts:87, src/events/interactions/interactionCreate.ts:120, src/handlers/minaai/memories.ts:167
For stateful customIds, the parseCustomIdState helper function is used to separate the base action from the embedded state. This function splits the customId by the | delimiter, returning an object containing the base ID and a state object with key-value pairs.
This diagram shows the logic of the parseCustomIdState function.
flowchart TD
A[customId String] --> B{Contains '|' ?}
B -- No --> C[Return { base: customId, state: {} }]
B -- Yes --> D[Split by '|']
D --> E[First part is 'base']
D --> F[Remaining parts are 'state pairs']
F --> G[Loop through pairs]
G --> H[Split pair by ':']
H --> I[Assign key:value to state object]
I --> J[Return { base, state }]
Sources: src/helpers/componentHelper.ts:3-20
The system is composed of various handler modules, each dedicated to a specific feature. The interactionCreate.ts file dynamically imports and calls functions from these modules.
The following table lists the main prefixes used to route interactions to their respective handler modules.
| Prefix | Handler Module | Description |
|---|---|---|
profile: |
@handlers/profile |
Manages user profile creation, viewing, and editing. |
admin: |
@handlers/admin |
Handles server administration settings. |
minaai: |
@handlers/minaai |
Manages user-facing Mina AI settings and memory viewing. |
roles: |
@handlers/roles |
Handles role management features like cleanup and assignment. |
purge: |
@handlers/purge |
Manages the interactive message purge workflow. |
dev: |
@handlers/dev |
Provides access to developer-only tools and settings. |
ticket: |
@handlers/ticket |
Manages the ticket system UI for setup and management. |
report_modal_ |
@handlers/report |
Handles submission of bug, user, or server reports. |
Sources: src/events/interactions/interactionCreate.ts
The "Add Roles to User" feature is a prime example of a multi-step workflow that passes state through the customId.
- A user selects users from a
userselectmenu. - The selected user ids are base64-encoded and embedded into the
customidof the next component, aroleselectmenu. - The user selects roles.
- The role ids and the user ids (from the
customid) are used to generate a confirmation view with "confirm" and "cancel" buttons. thecustomidof the confirm button contains all necessary data. - Clicking "confirm" triggers the final
handleassignconfirmfunction, which decodes the data and performs the role assignments.
The sequence diagram below details this flow.
sequenceDiagram
participant User
participant InteractionCreate as "interactionCreate.ts"
participant RolesHandler as "add-to-user/index.ts"
participant DiscordAPI as "Discord API"
User->>InteractionCreate: Selects users from UserSelectMenu (`roles:user:select`)
InteractionCreate->>RolesHandler: handleUserSelect(interaction)
Note over RolesHandler: Encodes user IDs into new customId
RolesHandler->>DiscordAPI: update() with RoleSelectMenu (`roles:role:assign|{b64_users}`)
User->>InteractionCreate: Selects roles from RoleSelectMenu
InteractionCreate->>RolesHandler: handleRoleSelect(interaction)
Note over RolesHandler: Decodes user IDs, fetches roles, builds preview. Encodes users & roles into new customId.
RolesHandler->>DiscordAPI: editReply() with confirmation buttons (`roles:btn:assign_confirm|{b64_data}`)
User->>InteractionCreate: Clicks "Confirm" button
InteractionCreate->>RolesHandler: handleAssignConfirm(interaction)
Note over RolesHandler: Decodes all data, adds roles to members.
RolesHandler->>DiscordAPI: editReply() with success/fail message
Sources: src/handlers/roles/add-to-user/index.ts, src/events/interactions/interactionCreate.ts:223, src/events/interactions/interactionCreate.ts:800, src/events/interactions/interactionCreate.ts:805
The Developer Hub (/dev) provides a centralized menu for various developer-only functions. It demonstrates how a single command can open up a nested UI.
- A developer runs the
/dev hubcommand. -
showDevHubis called, presenting aStringSelectMenuwith categories like "Presence Management", "Command Reload", and "Mina AI". - The developer selects an option (e.g., "mina ai").
-
handleCategoryMenuinmain-hub.tsreceives the interaction withcustomIddev:menu:categoryand valueminaai. - It then calls the appropriate sub-handler,
showminaaimenufrom thedev/minaaimodule, to display the next level of the ui.
This flow allows for a clean separation of concerns, where main-hub.ts acts as a sub-router for all developer-related UI interactions.
Sources: src/handlers/dev/main-hub.ts, src/handlers/dev/minaai/handlers.ts
The project uses TypeScript declaration files (.d.ts) to define the structure and available actions for each handler module. This provides type safety and autocompletion for developers. For example, the AdminHandlers module defines the functions and action types available for the /admin command's UI.
| Type | Definition | Description |
|---|---|---|
AdminHandlers |
interface |
Defines the functions available in the admin handler, like handleAdminCategoryMenu. |
AdminMenuAction |
type |
A union of string literals for all possible admin menu values (e.g., 'settings', 'minaai', 'logs'). |
AdminChannelSelectType |
type |
A union of string literals for channel selection actions (e.g., 'updateschannel', 'freewill'). |
AdminRoleSelectType |
type |
A union of string literals for role selection actions (e.g., 'staffadd', 'staffremove'). |
Sources: types/handlers.d.ts:11-33
The UI Interaction Handling system is a robust and scalable solution for managing complex user interactions in Amina. By leveraging a customId-based routing convention and modular handlers, it separates concerns effectively, making the codebase easier to maintain and extend. The use of stateful customIds allows for sophisticated, multi-step workflows without relying on in-memory session management, ensuring that interactions are stateless and resilient. This architecture is fundamental to providing the rich, interactive experience that defines many of the bot's features.