From 5bc0239cdf818507f3ecd838f1dead1081d7ac4e Mon Sep 17 00:00:00 2001 From: Marius Begby Date: Tue, 29 Aug 2023 15:42:29 +0200 Subject: [PATCH] fix: update validation in interactions --- src/handlers/interactionComponentHandler.ts | 5 ++ src/interactions/commands/player/filters.ts | 23 ++++---- src/interactions/commands/player/leave.ts | 30 ++++------ src/interactions/commands/player/loop.ts | 18 +++--- src/interactions/commands/player/lyrics.ts | 24 ++++---- .../commands/player/nowplaying.ts | 25 ++++----- src/interactions/commands/player/pause.ts | 25 ++++----- src/interactions/commands/player/play.ts | 18 +++--- src/interactions/commands/player/queue.ts | 55 ++++--------------- src/interactions/commands/player/remove.ts | 22 ++++---- src/interactions/commands/player/seek.ts | 25 ++++----- src/interactions/commands/player/shuffle.ts | 23 ++++---- src/interactions/commands/player/skip.ts | 23 ++++---- src/interactions/commands/player/stop.ts | 30 ++++------ src/interactions/commands/player/volume.ts | 18 +++--- src/types/utilTypes.ts | 4 +- src/utils/validation/permissionValidator.ts | 15 ++++- 17 files changed, 164 insertions(+), 219 deletions(-) diff --git a/src/handlers/interactionComponentHandler.ts b/src/handlers/interactionComponentHandler.ts index 8a117115..bf4109ab 100644 --- a/src/handlers/interactionComponentHandler.ts +++ b/src/handlers/interactionComponentHandler.ts @@ -1,5 +1,6 @@ import { MessageComponentInteraction } from 'discord.js'; import loggerModule from '../services/logger'; +import { cannotSendMessageInChannel } from '../utils/validation/permissionValidator'; const logger = loggerModule.child({ source: 'interactionComponentHandler.js', @@ -16,6 +17,10 @@ export const handleComponent = async (interaction: MessageComponentInteraction, logger.debug(`Parsed componentId: ${componentId}`); + if (await cannotSendMessageInChannel({ interaction, executionId })) { + return; + } + const componentModule = await import(`../interactions/components/${componentId}.js`); const { default: component } = componentModule; diff --git a/src/interactions/commands/player/filters.ts b/src/interactions/commands/player/filters.ts index 50050d05..8131c7eb 100644 --- a/src/interactions/commands/player/filters.ts +++ b/src/interactions/commands/player/filters.ts @@ -39,22 +39,19 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueNoCurrentTrack({ interaction, queue, executionId }) + ]; - if (await queueNoCurrentTrack({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } const filterOptions: StringSelectMenuOptionBuilder[] = []; diff --git a/src/interactions/commands/player/leave.ts b/src/interactions/commands/player/leave.ts index 0ea3a535..0c5af313 100644 --- a/src/interactions/commands/player/leave.ts +++ b/src/interactions/commands/player/leave.ts @@ -6,6 +6,7 @@ import loggerModule from '../../../services/logger'; import { CustomSlashCommandInteraction } from '../../../types/interactionTypes'; import { EmbedOptions } from '../../../types/configTypes'; import { notInSameVoiceChannel, notInVoiceChannel } from '../../../utils/validation/voiceChannelValidator'; +import { queueDoesNotExist } from '../../../utils/validation/queueValidator'; const embedOptions: EmbedOptions = config.get('embedOptions'); @@ -27,29 +28,18 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (!queue) { - logger.debug('There is already no queue.'); - - logger.debug('Responding with warning embed.'); - return await interaction.editReply({ - embeds: [ - new EmbedBuilder() - .setDescription( - `**${embedOptions.icons.warning} Oops!**\n_Hmm.._ It seems I am not in a voice channel!` - ) - .setColor(embedOptions.colors.warning) - ] - }); - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }) + ]; - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } if (!queue.deleted) { diff --git a/src/interactions/commands/player/loop.ts b/src/interactions/commands/player/loop.ts index d4c3aee9..287200a3 100644 --- a/src/interactions/commands/player/loop.ts +++ b/src/interactions/commands/player/loop.ts @@ -41,18 +41,18 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }) + ]; - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } const loopModesFormatted = new Map([ diff --git a/src/interactions/commands/player/lyrics.ts b/src/interactions/commands/player/lyrics.ts index bd3dec80..23a91724 100644 --- a/src/interactions/commands/player/lyrics.ts +++ b/src/interactions/commands/player/lyrics.ts @@ -47,21 +47,19 @@ const command: CustomSlashCommandInteraction = { let geniusSearchQuery = ''; if (!query) { - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueNoCurrentTrack({ interaction, queue, executionId }) + ]; + + for (const validator of validators) { + if (await validator()) { + return; + } } - if (await queueNoCurrentTrack({ interaction, queue, executionId })) { - return; - } geniusSearchQuery = queue.currentTrack!.title.slice(0, 50); logger.debug( diff --git a/src/interactions/commands/player/nowplaying.ts b/src/interactions/commands/player/nowplaying.ts index eba6e86f..1003e293 100644 --- a/src/interactions/commands/player/nowplaying.ts +++ b/src/interactions/commands/player/nowplaying.ts @@ -37,22 +37,19 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; - } - - if (await queueNoCurrentTrack({ interaction, queue, executionId })) { - return; + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueNoCurrentTrack({ interaction, queue, executionId }) + ]; + + for (const validator of validators) { + if (await validator()) { + return; + } } const sourceStringsFormatted = new Map([ diff --git a/src/interactions/commands/player/pause.ts b/src/interactions/commands/player/pause.ts index 9d564bdf..c850ca92 100644 --- a/src/interactions/commands/player/pause.ts +++ b/src/interactions/commands/player/pause.ts @@ -28,22 +28,19 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; - } - - if (await queueNoCurrentTrack({ interaction, queue, executionId })) { - return; + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueNoCurrentTrack({ interaction, queue, executionId }) + ]; + + for (const validator of validators) { + if (await validator()) { + return; + } } const currentTrack = queue.currentTrack!; diff --git a/src/interactions/commands/player/play.ts b/src/interactions/commands/player/play.ts index 7b926820..dcb01769 100644 --- a/src/interactions/commands/player/play.ts +++ b/src/interactions/commands/player/play.ts @@ -43,18 +43,20 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - - if (await notInVoiceChannel({ interaction, executionId })) { + let queue: GuildQueue = useQueue(interaction.guild!.id)!; + if (queue && (await notInSameVoiceChannel({ interaction, queue, executionId }))) { return; } - if (await cannotJoinVoiceOrTalk({ interaction, executionId })) { - return; - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => cannotJoinVoiceOrTalk({ interaction, executionId }) + ]; - let queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (queue && (await notInSameVoiceChannel({ interaction, queue, executionId }))) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } const player = useMainPlayer()!; diff --git a/src/interactions/commands/player/queue.ts b/src/interactions/commands/player/queue.ts index 2dab6a75..672280d6 100644 --- a/src/interactions/commands/player/queue.ts +++ b/src/interactions/commands/player/queue.ts @@ -6,6 +6,7 @@ import loggerModule from '../../../services/logger'; import { CustomSlashCommandInteraction } from '../../../types/interactionTypes'; import { EmbedOptions, PlayerOptions } from '../../../types/configTypes'; import { notInSameVoiceChannel, notInVoiceChannel } from '../../../utils/validation/voiceChannelValidator'; +import { queueDoesNotExist } from '../../../utils/validation/queueValidator'; const embedOptions: EmbedOptions = config.get('embedOptions'); const playerOptions: PlayerOptions = config.get('playerOptions'); @@ -29,57 +30,23 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }) + ]; + + for (const validator of validators) { + if (await validator()) { + return; + } } const pageIndex = (interaction.options.getNumber('page') || 1) - 1; let queueString = ''; - if (!queue) { - if (pageIndex >= 1) { - logger.debug(`There is no queue and user tried to access page ${pageIndex + 1}.`); - - logger.debug('Responding with warning embed.'); - return await interaction.editReply({ - embeds: [ - new EmbedBuilder() - .setDescription( - `**${embedOptions.icons.warning} Oops!**\nPage **\`${ - pageIndex + 1 - }\`** is not a valid page number.\n\nThe queue is currently empty, first add some tracks with **\`/play\`**!` - ) - .setColor(embedOptions.colors.warning) - ] - }); - } - - logger.debug('There is no queue, displaying empty queue.'); - queueString = 'The queue is empty, add some tracks with **`/play`**!'; - - logger.debug('Responding with info embed.'); - return await interaction.editReply({ - embeds: [ - new EmbedBuilder() - .setAuthor({ - name: interaction.guild!.name, - iconURL: interaction.guild!.iconURL() || '' - }) - .setDescription(`**${embedOptions.icons.queue} Tracks in queue**\n${queueString}`) - .setColor(embedOptions.colors.info) - .setFooter({ - text: 'Page 1 of 1 (0 tracks)' - }) - ] - }); - } - const queueLength = queue.tracks.data.length; const totalPages = Math.ceil(queueLength / 10) || 1; diff --git a/src/interactions/commands/player/remove.ts b/src/interactions/commands/player/remove.ts index f5a50712..5bab22f1 100644 --- a/src/interactions/commands/player/remove.ts +++ b/src/interactions/commands/player/remove.ts @@ -1,5 +1,5 @@ import config from 'config'; -import { NodeResolvable, useQueue } from 'discord-player'; +import { GuildQueue, useQueue } from 'discord-player'; import { EmbedBuilder, GuildMember, SlashCommandBuilder } from 'discord.js'; import loggerModule from '../../../services/logger'; @@ -35,18 +35,18 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return Promise.resolve(); - } - - const queue: NodeResolvable = useQueue(interaction.guild!.id)!; + const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return Promise.resolve(); - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }) + ]; - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return Promise.resolve(); + for (const validator of validators) { + if (await validator()) { + return; + } } const removeTrackNumber = interaction.options.getNumber('tracknumber')!; diff --git a/src/interactions/commands/player/seek.ts b/src/interactions/commands/player/seek.ts index 8815c91a..86270a14 100644 --- a/src/interactions/commands/player/seek.ts +++ b/src/interactions/commands/player/seek.ts @@ -34,22 +34,19 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; - } - - if (await queueNoCurrentTrack({ interaction, queue, executionId })) { - return; + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueNoCurrentTrack({ interaction, queue, executionId }) + ]; + + for (const validator of validators) { + if (await validator()) { + return; + } } const durationInput = interaction.options.getString('duration'); diff --git a/src/interactions/commands/player/shuffle.ts b/src/interactions/commands/player/shuffle.ts index fd42828e..56c0af77 100644 --- a/src/interactions/commands/player/shuffle.ts +++ b/src/interactions/commands/player/shuffle.ts @@ -28,22 +28,19 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueIsEmpty({ interaction, queue, executionId }) + ]; - if (await queueIsEmpty({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } queue.tracks.shuffle(); diff --git a/src/interactions/commands/player/skip.ts b/src/interactions/commands/player/skip.ts index 64e12689..511f8684 100644 --- a/src/interactions/commands/player/skip.ts +++ b/src/interactions/commands/player/skip.ts @@ -31,22 +31,19 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } - - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }), + () => queueNoCurrentTrack({ interaction, queue, executionId }) + ]; - if (await queueNoCurrentTrack({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } const skipToTrack = interaction.options.getNumber('tracknumber'); diff --git a/src/interactions/commands/player/stop.ts b/src/interactions/commands/player/stop.ts index 36e4d0e8..a7971671 100644 --- a/src/interactions/commands/player/stop.ts +++ b/src/interactions/commands/player/stop.ts @@ -6,6 +6,7 @@ import loggerModule from '../../../services/logger'; import { CustomSlashCommandInteraction } from '../../../types/interactionTypes'; import { EmbedOptions } from '../../../types/configTypes'; import { notInSameVoiceChannel, notInVoiceChannel } from '../../../utils/validation/voiceChannelValidator'; +import { queueDoesNotExist } from '../../../utils/validation/queueValidator'; const embedOptions: EmbedOptions = config.get('embedOptions'); @@ -27,29 +28,18 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (!queue) { - logger.debug('There is no queue.'); - - logger.debug('Responding with warning embed.'); - return await interaction.editReply({ - embeds: [ - new EmbedBuilder() - .setDescription( - `**${embedOptions.icons.warning} Oops!**\n_Hmm.._ It seems I am not in a voice channel!` - ) - .setColor(embedOptions.colors.warning) - ] - }); - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }) + ]; - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } if (!queue.deleted) { diff --git a/src/interactions/commands/player/volume.ts b/src/interactions/commands/player/volume.ts index d5f59e68..2ebb1894 100644 --- a/src/interactions/commands/player/volume.ts +++ b/src/interactions/commands/player/volume.ts @@ -35,18 +35,18 @@ const command: CustomSlashCommandInteraction = { guildId: interaction.guild?.id }); - if (await notInVoiceChannel({ interaction, executionId })) { - return; - } - const queue: GuildQueue = useQueue(interaction.guild!.id)!; - if (await queueDoesNotExist({ interaction, queue, executionId })) { - return; - } + const validators = [ + () => notInVoiceChannel({ interaction, executionId }), + () => notInSameVoiceChannel({ interaction, queue, executionId }), + () => queueDoesNotExist({ interaction, queue, executionId }) + ]; - if (await notInSameVoiceChannel({ interaction, queue, executionId })) { - return; + for (const validator of validators) { + if (await validator()) { + return; + } } const volume = interaction.options.getNumber('percentage'); diff --git a/src/types/utilTypes.ts b/src/types/utilTypes.ts index 64d3b6a1..9965040f 100644 --- a/src/types/utilTypes.ts +++ b/src/types/utilTypes.ts @@ -1,5 +1,5 @@ import { GuildQueue, Player } from 'discord-player'; -import { ChatInputCommandInteraction } from 'discord.js'; +import { ChatInputCommandInteraction, MessageComponentInteraction } from 'discord.js'; import { ExtendedClient } from './clientTypes'; @@ -68,7 +68,7 @@ export interface CannotJoinVoiceOrTalkParams { } export interface CannotSendMessageInChannelParams { - interaction: ChatInputCommandInteraction; + interaction: ChatInputCommandInteraction | MessageComponentInteraction; executionId: string; } diff --git a/src/utils/validation/permissionValidator.ts b/src/utils/validation/permissionValidator.ts index 3ffcef2c..3921c2f5 100644 --- a/src/utils/validation/permissionValidator.ts +++ b/src/utils/validation/permissionValidator.ts @@ -1,5 +1,13 @@ import config from 'config'; -import { EmbedBuilder, GuildMember, TextChannel, VoiceChannel } from 'discord.js'; +import { + ChatInputCommandInteraction, + EmbedBuilder, + GuildMember, + InteractionType, + MessageComponentInteraction, + TextChannel, + VoiceChannel +} from 'discord.js'; import loggerModule from '../../services/logger'; import { EmbedOptions } from '../../types/configTypes'; @@ -50,10 +58,13 @@ export const cannotSendMessageInChannel = async ({ interaction, executionId }: C const channel = interaction.channel; + const interactionIdentifier = + interaction.type === InteractionType.ApplicationCommand ? interaction.commandName : interaction.customId; + // only checks if channel is viewable, as bot will have permission to send interaction replies if channel is viewable if (channel instanceof TextChannel && !channel.viewable) { logger.info( - `User tried to use command '${interaction.commandName}' but the bot had no permission to send reply in text channel.` + `User tried to use interaction '${interactionIdentifier}' but the bot has permission to send reply in text channel.` ); try {