diff --git a/src/commands.ts b/src/commands.ts index 3d201e2..4e2f1dc 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -3,20 +3,26 @@ import path from 'path'; import { AutocompleteInteraction, + BaseInteraction, ChatInputCommandInteraction, + GuildMember, + Interaction, Message, + MessageFlags, + PermissionFlagsBits, REST, Routes, SlashCommandOptionsOnlyBuilder, VoiceState } from 'discord.js'; import { Logger } from './utils/log'; -import { Bot } from './bot'; import { config } from './utils/config'; +import { Bot } from './bot'; export interface Command { name?: string; requiresAdmin: boolean; + ownerOnly?: boolean; execute?: (interaction: ChatInputCommandInteraction) => Promise; autocomplete?: (interaction: AutocompleteInteraction) => Promise; messageListener?: (msg: Message) => Promise; @@ -58,6 +64,18 @@ export class CommandManager { this.initialized = true; await this.populateCommands(); this.registerSlashCommands(); + + const bot = Bot.get; + + bot.on('interactionCreate', (interaction: Interaction) => { + this.onInteraction(interaction); + }); + bot.on('messageCreate', (message: Message) => { + this.onMessage(message); + }); + bot.on('voiceStateUpdate', (oldState: VoiceState, newState: VoiceState) => { + this.onVoiceStateUpdate(oldState, newState); + }); } public getCategories(): Array { @@ -194,4 +212,112 @@ export class CommandManager { ); } } + + /* + interaction handling + */ + private async executeCommandInteraction( + interaction: ChatInputCommandInteraction + ): Promise { + const cmdName = interaction.commandName; + const command = this.get(cmdName); + if (!command) + return this.log.error( + 'Attempted to execute non-existing command (%s)', + cmdName + ); + + if (command.requiresAdmin) { + const member = interaction.member as GuildMember; + if (!member.permissions.has(PermissionFlagsBits.Administrator)) { + await interaction.reply({ + content: + "You don't have the permissions required to execute this command.", + flags: MessageFlags.Ephemeral + }); + return; + } + } + + if (command.ownerOnly) { + const member = interaction.member as GuildMember; + if (member.id != config.owner_id) { + await interaction.reply({ + content: 'This command is restricted.', + flags: MessageFlags.Ephemeral + }); + return; + } + } + + if (command.execute) { + try { + await command.execute(interaction); + } catch (error) { + this.log.error( + 'Error occurred while executing command %s: %s', + cmdName, + error + ); + } + } else this.log.error('Command is missing execute method: %s', cmdName); + } + + private async autocompleteCommandInteraction( + interaction: AutocompleteInteraction + ): Promise { + const cmdName = interaction.commandName; + const command = this.get(cmdName); + + if (!command) + return this.log.error( + 'Attempted to execute unexisting command (%s)', + cmdName + ); + + if (command.autocomplete) { + try { + await command.autocomplete(interaction); + } catch (error) { + this.log.error( + 'Error occurred while autocompleting command %s: %s', + cmdName, + error + ); + } + } else + this.log.error('Command is missing autocomplete method: %s', cmdName); + } + + /* + event listeners + */ + private async onInteraction(interaction: BaseInteraction): Promise { + /* + cmd execution + */ + if (interaction.isChatInputCommand()) + return this.executeCommandInteraction(interaction); + + if (interaction.isAutocomplete()) + return this.autocompleteCommandInteraction(interaction); + + return; + } + + private async onMessage(message: Message): Promise { + if (message.author.bot) return; + + for (const cmd of this.getAll()) + if (cmd.messageListener) await cmd.messageListener(message); + } + + private async onVoiceStateUpdate( + oldState: VoiceState, + newState: VoiceState + ): Promise { + for (const cmd of this.getAll()) + if (cmd.voiceStateListener) + await cmd.voiceStateListener(oldState, newState); + } }