import { AnySelectMenuInteraction, AutocompleteInteraction, ButtonInteraction, ChatInputCommandInteraction, inlineCode, Interaction, InteractionReplyOptions, MessageFlags, ModalSubmitInteraction, } from "discord.js"; import Commands from "./Commands/Commands"; import {Logger} from "log4js"; import {UserError} from "./UserError"; import {Container} from "../Container/Container"; import {EventHandler} from "../Events/EventHandler"; import {ModalInteractionEvent} from "../Events/EventClasses/ModalInteractionEvent"; import {ComponentInteractionEvent} from "../Events/EventClasses/ComponentInteractionEvent"; import {log} from "node:util"; import {PermissionError} from "./PermissionError"; import {EmbedLibrary} from "./EmbedLibrary"; enum InteractionRoutingType { Unrouted, Command, AutoComplete, ModalSubmit, ButtonSubmit, MenuSubmit, } export class InteractionRouter { constructor( public readonly commands: Commands, public readonly logger: Logger, private readonly events: EventHandler ) { } async route(interaction: Interaction) { const interactionType = this.findInteractionType(interaction); switch (interactionType) { case InteractionRoutingType.Unrouted: this.logger.debug("Unroutable interaction found...") break; case InteractionRoutingType.Command: await this.handleCommand(interaction); break; case InteractionRoutingType.AutoComplete: await this.handleAutocomplete(interaction) break; case InteractionRoutingType.ModalSubmit: this.events.dispatch(new ModalInteractionEvent(interaction)); break; case InteractionRoutingType.ButtonSubmit: case InteractionRoutingType.MenuSubmit: this.events.dispatch(new ComponentInteractionEvent(interaction)) } } private findInteractionType(interaction: Interaction): InteractionRoutingType { if (interaction.isChatInputCommand()) { return InteractionRoutingType.Command; } if (interaction.isAutocomplete()) { return InteractionRoutingType.AutoComplete; } if (interaction.isModalSubmit()) { return InteractionRoutingType.ModalSubmit; } if (interaction.isButton()) { return InteractionRoutingType.ButtonSubmit; } if (interaction.isAnySelectMenu()) { return InteractionRoutingType.MenuSubmit; } return InteractionRoutingType.Unrouted; } private async handleCommand(interaction: ChatInputCommandInteraction) { try { const command = this.commands.getCommand(interaction.commandName); if (!command) { throw new UserError(`Requested command not found.`); } if (!('execute' in command)) { throw new UserError(`Requested command is not setup for a chat command.`); } this.logger.debug(`Found chat command ${interaction.commandName}: running...`); await command.execute?.call(command, interaction); } catch (e: any) { let logErrorMessage = true; if ("shouldLog" in e) { logErrorMessage = e.shouldLog; } if (logErrorMessage) { this.logger.error(e) } const responseOptions: InteractionReplyOptions = { embeds: [ EmbedLibrary.error(e) ], flags: MessageFlags.Ephemeral } if (interaction.replied || interaction.deferred) { await interaction.followUp(responseOptions); } else { await interaction.reply(responseOptions); } } } private async handleAutocomplete(interaction: AutocompleteInteraction) { const command = this.commands.getCommand(interaction.commandName); if (!command) { return null; } if (!('handleAutocomplete' in command)) { return null; } Container.get("logger").debug(`Found command ${interaction.commandName} for autocomplete: handling...`); try { await command.handleAutocomplete?.call(command, interaction); } catch (e: unknown) { Container.get('logger').error(e); } } }