-
Notifications
You must be signed in to change notification settings - Fork 4
Modals #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Modals #74
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ export interface Config { | |
| slashCommandPath: string; | ||
| cmCommandPath: string; | ||
| buttonPath: string; | ||
| modalPath: string; | ||
| eventPath: string; | ||
| endpointPath: string; | ||
| sentryDNS: string; | ||
|
|
@@ -87,17 +88,18 @@ export default class Bot extends Client { | |
| this, | ||
| config.slashCommandPath, | ||
| config.cmCommandPath, | ||
| config.buttonPath | ||
| config.buttonPath, | ||
| config.modalPath, | ||
| ), | ||
| error: new ErrorManager(this), | ||
| database: new DatabaseManager(this, config), | ||
| firestore: new FirestoreManager(this), | ||
| verification: new VerificationManager(this), | ||
| event: new EventManager(this, config.eventPath), | ||
| indicator: new IndicatorManager(this), | ||
| database: new DatabaseManager(this, config), | ||
| scheduler: new ScheduleManager(this), | ||
| circle: new CircleManager(this), | ||
| rero: new ReactionRoleManager(this), | ||
| firestore: new FirestoreManager(this), | ||
| resolve: new ResolveManager(this), | ||
| express: new ExpressManager(this, config.endpointPath), | ||
| points: new PointsManager(this), | ||
|
|
@@ -110,9 +112,9 @@ export default class Bot extends Client { | |
| async start(): Promise<void> { | ||
| await this.login(this.config.token); | ||
| this.logger.info("Initializing managers..."); | ||
| Object.entries(this.managers).forEach(([k, v]) => { | ||
| v.init(); | ||
| }); | ||
| for (const manager of Object.values(this.managers)) { | ||
| await manager.init(); | ||
| } | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some managers depend on others to start up first (error handling, databases, etc. should be first)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should probably leave a comment here |
||
| this.logger.info("Bot started."); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import { | ||
| ApplicationCommandPermissionData, | ||
| ModalSubmitInteraction, | ||
| Interaction, | ||
| } from "discord.js"; | ||
| import { SlashCommandBuilder } from "@discordjs/builders"; | ||
| import CustomInteraction, { | ||
| InteractionConfig, | ||
| InteractionContext, | ||
| } from "./interaction"; | ||
|
|
||
| export interface ModalInteractionConfig extends InteractionConfig {} | ||
|
|
||
| export interface ModalInteractionContext extends InteractionContext { | ||
| interaction: ModalSubmitInteraction; | ||
| } | ||
|
|
||
| /** | ||
| * No need to register modals, but we do need a way to handle the callbacks | ||
| */ | ||
| export default abstract class CustomModalInteraction extends CustomInteraction { | ||
| protected constructor(config: ModalInteractionConfig) { | ||
| super(config); | ||
| } | ||
|
|
||
| /** | ||
| * Match to customId, either by direct comparison or regex or something else. | ||
| * If it matches, expect handleInteraction to be called afterwards. | ||
| * @param customId | ||
| */ | ||
| public abstract matchCustomId(customId: string): boolean; | ||
|
|
||
| /** | ||
| * Perform actions for handling the interaction | ||
| */ | ||
| public abstract handleInteraction(context: ModalInteractionContext): any; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import CustomButtonInteraction, { | ||
| ButtonInteractionContext | ||
| } from "../../api/interaction/button"; | ||
|
|
||
| export default class VerificationButton extends CustomButtonInteraction { | ||
| public constructor() { | ||
| super({ | ||
| name: "verification-button", | ||
| }); | ||
| } | ||
|
|
||
| public matchCustomId(customId: string) { | ||
| return customId === "verification-button"; | ||
| } | ||
|
|
||
| public async handleInteraction({ | ||
| bot, | ||
| interaction, | ||
| }: ButtonInteractionContext): Promise<void> { | ||
| await bot.managers.verification.handleVerificationRequest(interaction); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import CustomModalInteraction, { | ||
| ModalInteractionContext, | ||
| } from "../../api/interaction/modal"; | ||
|
|
||
| export default class VerificationModal extends CustomModalInteraction { | ||
| public constructor() { | ||
| super({ | ||
| name: "verification-modal", | ||
| }); | ||
| } | ||
|
|
||
| public matchCustomId(customId: string) { | ||
| return customId === "verification-modal"; | ||
| } | ||
|
|
||
| public async handleInteraction({ | ||
| bot, | ||
| interaction, | ||
| }: ModalInteractionContext): Promise<void> { | ||
| // Forward to circle handler | ||
| await bot.managers.verification.handleVerificationSubmit(interaction); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,7 @@ | ||
| import { | ||
| ButtonInteraction, | ||
| Collection, | ||
| CommandInteraction, | ||
| ContextMenuInteraction, | ||
| GuildApplicationCommandPermissionData, | ||
| Interaction, | ||
| } from "discord.js"; | ||
| import path from "path"; | ||
| import Bot from "../../api/bot"; | ||
| import Manager from "../../api/manager"; | ||
| import BaseInteraction from "../../api/interaction/interaction"; | ||
|
|
@@ -15,28 +10,32 @@ import { Routes } from "discord-api-types/v9"; | |
| import SlashCommand from "../../api/interaction/slashcommand"; | ||
| import CustomButtonInteraction from "../../api/interaction/button"; | ||
| import ContextMenuCommand from "../../api/interaction/contextmenucommand"; | ||
| import { ApplicationCommandType } from "discord-api-types"; | ||
| import CustomModalInteraction from "../../api/interaction/modal"; | ||
|
|
||
| export default class InteractionManager extends Manager { | ||
| // private readonly interactionPath = process.cwd() + "/dist/interaction/"; | ||
| private slashCommandPath: string; | ||
| private cmCommandPath: string; | ||
| private buttonPath: string; | ||
| private modalPath: string; | ||
|
|
||
| private slashCommands: Map<string, SlashCommand> = new Map(); | ||
| private cmCommands: Map<string, ContextMenuCommand> = new Map(); | ||
| private buttons: Map<string, CustomButtonInteraction> = new Map(); | ||
| private modals: Map<string, CustomModalInteraction> = new Map(); | ||
|
|
||
| constructor( | ||
| bot: Bot, | ||
| slashCommandPath: string, | ||
| cmCommandPath: string, | ||
| buttonPath: string | ||
| buttonPath: string, | ||
| modalPath: string, | ||
| ) { | ||
| super(bot); | ||
| this.slashCommandPath = slashCommandPath; | ||
| this.cmCommandPath = cmCommandPath; | ||
| this.buttonPath = buttonPath; | ||
| this.modalPath = modalPath; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -45,7 +44,7 @@ export default class InteractionManager extends Manager { | |
| public init() { | ||
| // this.loadInteractionHandlers(); | ||
| this.registerSlashAndContextMenuCommands(); | ||
| this.registerButtons(); | ||
| this.registerButtonsAndModals(); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -65,6 +64,10 @@ export default class InteractionManager extends Manager { | |
| handler = [...this.buttons.values()].find((x) => | ||
| x.matchCustomId(interaction.customId) | ||
| ) as BaseInteraction; | ||
| } else if (interaction.isModalSubmit()) { | ||
| handler = [...this.modals.values()].find((x) => | ||
| x.matchCustomId(interaction.customId) | ||
| ) as BaseInteraction; | ||
| } else return; | ||
|
|
||
| // Return if not found | ||
|
|
@@ -74,9 +77,10 @@ export default class InteractionManager extends Manager { | |
| try { | ||
| await handler.handleInteraction({ bot: this.bot, interaction }); | ||
| } catch (e: any) { | ||
| await interaction.reply( | ||
| "Command execution failed. Please contact a bot maintainer..." | ||
| ); | ||
| await interaction.reply({ | ||
| content: "Command execution failed. Please contact a bot maintainer...", | ||
| ephemeral: true | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made this ephemeral to hide failed executions from public, in case someones trying to use an anon slash command or smth. |
||
| }); | ||
| // Don't throw and let the bot handle this as an unhandled rejection. Instead, | ||
| // take initiative to handle it as an error so we can see the trace. | ||
| await this.bot.managers.error.handleErr(e); | ||
|
|
@@ -161,16 +165,22 @@ export default class InteractionManager extends Manager { | |
| } | ||
| } | ||
|
|
||
| private async registerButtons() { | ||
| private async registerButtonsAndModals() { | ||
| try { | ||
| // Dynamically load source files | ||
| this.buttons = new Map( | ||
| DynamicLoader.loadClasses(this.buttonPath).map((sc) => [sc.name, sc]) | ||
| ); | ||
|
|
||
| for (const btn of this.buttons.keys()) { | ||
| this.bot.logger.info(`Loaded button '${btn}'`); | ||
| } | ||
|
|
||
| this.modals = new Map( | ||
| DynamicLoader.loadClasses(this.modalPath).map((sc) => [sc.name, sc]) | ||
| ); | ||
| for (const mdl of this.modals.keys()) { | ||
| this.bot.logger.info(`Loaded modal '${mdl}'`); | ||
| } | ||
| } catch (error: any) { | ||
| await this.bot.managers.error.handleErr(error); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was using an alpha version of this earlier, but stable version works now