diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 6afdb1be..6e76ee0f 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,7 @@ Update _ May 2024 +- feat: button handler (27/05/2024) - fix: add missing efb command to `/locate` (26/05/2024) - docs: improved CONTRIBUTING.md (26/05/2024) - ci: disable CDN workflows on forks (26/05/2024) diff --git a/src/commands/moderation/roleAssignment.ts b/src/commands/moderation/roleAssignment.ts index 10b0de5e..0ad0c832 100644 --- a/src/commands/moderation/roleAssignment.ts +++ b/src/commands/moderation/roleAssignment.ts @@ -54,7 +54,7 @@ export default slashCommand(data, async ({ interaction }) => { roleAssignmentIds.forEach((group) => { group.roles.forEach((role) => { const button = new ButtonBuilder() - .setCustomId(role.id) + .setCustomId(`roleAssignment_${role.id}`) .setLabel(role.label) .setStyle(ButtonStyle.Primary); diff --git a/src/events/buttonHandlers/buttonHandler.ts b/src/events/buttonHandlers/buttonHandler.ts new file mode 100644 index 00000000..d98abdad --- /dev/null +++ b/src/events/buttonHandlers/buttonHandler.ts @@ -0,0 +1,33 @@ +import { event, Events } from '../../lib'; +import { handleRollAssignment } from './functions/handleRollAssignment'; + +export default event(Events.InteractionCreate, async ({ log }, interaction) => { + if (!interaction.isButton()) return; + + log('Button Handler: Button pressed'); + + const { customId, component, user } = interaction; + + const buttonLabel = component?.label; + + try { + const [prefix, ...params] = interaction.customId.split('_'); + + switch (prefix) { + case 'roleAssignment': + const [roleID] = params; + await handleRollAssignment(interaction, roleID); + log(`Button Handler: Role assignment button pressed by ${user.tag} (${user.id}). roleID: ${roleID}`); + break; + default: + if (buttonLabel) { + log(`Button Handler: Custom ID not matched. Skipping...\nCustom ID: ${customId}, Label: ${buttonLabel}, User: ${user.tag}, User ID: ${user.id}`); + } else { + log(`Button Handler: Custom ID not matched. Skipping...\nCustom ID: ${customId}, Label: null, User: ${user.tag}, User ID: ${user.id}`); + } + return; + } + } catch (error) { + log('Button Handler: Error handling button press', error); + } +}); diff --git a/src/events/buttonHandlers/functions/handleRollAssignment.ts b/src/events/buttonHandlers/functions/handleRollAssignment.ts new file mode 100644 index 00000000..e2001374 --- /dev/null +++ b/src/events/buttonHandlers/functions/handleRollAssignment.ts @@ -0,0 +1,41 @@ +import { ButtonInteraction, GuildMember } from 'discord.js'; +import { constantsConfig, Logger } from '../../../lib'; + +export async function handleRollAssignment(interaction: ButtonInteraction, roleID: string) { + await interaction.deferReply({ ephemeral: true }); + + try { + // Find the role object based on the customId + let role = null; + for (const group of constantsConfig.roleAssignmentIds) { + role = group.roles.find((r) => r.id === roleID); + if (role) break; // Stop searching if the role is found in any group + } + + if (!role) { + Logger.error('Role Assignment: Role not found'); + interaction.editReply({ content: 'I couldn\'t find that role' }); + return; + } + + if (!interaction.member) { + Logger.error('Role Assignment: Interaction member is null'); + return; + } + + const member = interaction.member as GuildMember; + + const hasRole = member.roles.cache.has(role.id); + + if (hasRole) { + await member.roles.remove(role.id); + await interaction.editReply(`The role <@&${role.id}> has been removed.`); + } else { + await member.roles.add(role.id); + await interaction.editReply(`The role <@&${role.id}> has been added.`); + } + } catch (error) { + Logger.error(error); + await interaction.editReply({ content: 'Something went wrong, this role may no longer exist. Please try again. The error message has been logged.' }); + } +} diff --git a/src/events/index.ts b/src/events/index.ts index 27e678b8..8de11afd 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -5,19 +5,19 @@ import scamLogs from './logging/scamLogs'; import detectBan from './logging/detectBan'; import slashCommandHandler from './slashCommandHandler'; import contextInteractionHandler from './contextInteractionHandler'; -import roleAssignmentHandler from './roleAssignmentHandler'; import messageDelete from './logging/messageDelete'; import messageUpdate from './logging/messageUpdate'; import autocompleteHandler from './autocompleteHandler'; +import buttonHandler from './buttonHandlers/buttonHandler'; export default [ ready, scamLogs, detectBan, slashCommandHandler, - autocompleteHandler, contextInteractionHandler, - roleAssignmentHandler, messageDelete, messageUpdate, + autocompleteHandler, + buttonHandler, ] as Event[]; diff --git a/src/events/roleAssignmentHandler.ts b/src/events/roleAssignmentHandler.ts deleted file mode 100644 index 828d746a..00000000 --- a/src/events/roleAssignmentHandler.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { GuildMember } from 'discord.js'; -import { constantsConfig, event, Events, Logger } from '../lib'; - -export default event(Events.InteractionCreate, async ({ log }, interaction) => { - if (!interaction.isButton()) return; - - Logger.info('Role Assignment: Button pressed'); - - try { - const roleButtonCustomIds = constantsConfig.roleAssignmentIds.flatMap((group) => group.roles.map((role) => role.id)); - - const { customId, component, user } = interaction; - - // Check if the pressed button's custom ID is in the array of role-related custom IDs - if (!roleButtonCustomIds.includes(customId)) { - const buttonLabel = component?.label; - - if (buttonLabel) { - Logger.info(`Role Assignment: Custom ID not matched. Custom ID: ${customId}, Label: ${buttonLabel}, User: ${user.tag}, User ID: ${user.id}`); - } else { - Logger.info(`Role Assignment: Custom ID not matched. Custom ID: ${customId}, Label: null, User: ${user.tag}, User ID: ${user.id}`); - } - return; - } - - await interaction.deferReply({ ephemeral: true }); - - // Find the role object based on the customId - let role = null; - for (const group of constantsConfig.roleAssignmentIds) { - role = group.roles.find((r) => r.id === customId); - if (role) break; // Stop searching if the role is found in any group - } - if (!role) { - Logger.error('Role Assignment: Role not found'); - interaction.editReply({ content: 'I couldn\'t find that role' }); - return; - } - - if (!interaction.member) { - Logger.error('Role Assignment: Interaction member is null'); - return; - } - - const member = interaction.member as GuildMember; - - const hasRole = member.roles.cache.has(role.id); - - if (hasRole) { - await member.roles.remove(role.id); - await interaction.editReply(`The role <@&${role.id}> has been removed.`); - } else { - await member.roles.add(role.id); - await interaction.editReply(`The role <@&${role.id}> has been added.`); - } - } catch (error) { - log(error); - await interaction.editReply({ content: 'Something went wrong, this role may no longer exist. Please try again. The error message has been logged.' }); - } -}); diff --git a/src/lib/genericPagination.ts b/src/lib/genericPagination.ts index 54acb3fc..9490d091 100644 --- a/src/lib/genericPagination.ts +++ b/src/lib/genericPagination.ts @@ -4,12 +4,12 @@ export async function sendPaginatedEmbed(interaction: CommandInteraction, embeds let currentPage = 0; const nextButton = new ButtonBuilder() - .setCustomId('next_page') + .setCustomId('pagination_nextPage') .setLabel('Next') .setStyle(ButtonStyle.Primary); const prevButton = new ButtonBuilder() - .setCustomId('prev_page') + .setCustomId('pagination_prevPage') .setLabel('Previous') .setStyle(ButtonStyle.Primary); @@ -28,9 +28,9 @@ export async function sendPaginatedEmbed(interaction: CommandInteraction, embeds collector.on('collect', async (buttonInteraction: any) => { buttonInteraction.deferUpdate(); - if (buttonInteraction.customId === 'next_page') { + if (buttonInteraction.customId === 'pagination_nextPage') { currentPage++; - } else if (buttonInteraction.customId === 'prev_page') { + } else if (buttonInteraction.customId === 'pagination_prevPage') { currentPage--; } diff --git a/src/lib/infractionPagination.ts b/src/lib/infractionPagination.ts index fd86d0e5..b042e7b8 100644 --- a/src/lib/infractionPagination.ts +++ b/src/lib/infractionPagination.ts @@ -4,37 +4,37 @@ export async function sendPaginatedInfractionEmbeds(interaction: CommandInteract let currentPage = 0; const aboutButton = new ButtonBuilder() - .setCustomId('about') + .setCustomId('infractions_about') .setLabel('About') .setStyle(ButtonStyle.Success); const warnButton = new ButtonBuilder() - .setCustomId('warns') + .setCustomId('infractions_warns') .setLabel(`Warns (${infractionsLengths.warnsLength})`) .setStyle(ButtonStyle.Primary); const timeoutButton = new ButtonBuilder() - .setCustomId('timeouts') + .setCustomId('infractions_timeouts') .setLabel(`Timeouts (${infractionsLengths.timeoutsLength})`) .setStyle(ButtonStyle.Primary); const scamLogButton = new ButtonBuilder() - .setCustomId('scamlog') + .setCustomId('infractions_scamlog') .setLabel(`Scam Logs (${infractionsLengths.scamLogsLength})`) .setStyle(ButtonStyle.Primary); const banButton = new ButtonBuilder() - .setCustomId('bans') + .setCustomId('infractions_bans') .setLabel(`Bans (${infractionsLengths.bansLength})`) .setStyle(ButtonStyle.Primary); const unbanButton = new ButtonBuilder() - .setCustomId('unbans') + .setCustomId('infractions_unbans') .setLabel(`Unbans (${infractionsLengths.unbansLength})`) .setStyle(ButtonStyle.Primary); const noteButton = new ButtonBuilder() - .setCustomId('notes') + .setCustomId('infractions_notes') .setLabel(`Notes (${infractionsLengths.notesLength})`) .setStyle(ButtonStyle.Primary); @@ -48,19 +48,19 @@ export async function sendPaginatedInfractionEmbeds(interaction: CommandInteract collector.on('collect', async (interaction: any) => { interaction.deferUpdate(); - if (interaction.customId === 'about') { + if (interaction.customId === 'infractions_about') { currentPage = 0; - } else if (interaction.customId === 'warns') { + } else if (interaction.customId === 'infractions_warns') { currentPage = 1; - } else if (interaction.customId === 'timeouts') { + } else if (interaction.customId === 'infractions_timeouts') { currentPage = 2; - } else if (interaction.customId === 'scamlog') { + } else if (interaction.customId === 'infractions_scamlog') { currentPage = 3; - } else if (interaction.customId === 'bans') { + } else if (interaction.customId === 'infractions_bans') { currentPage = 4; - } else if (interaction.customId === 'unbans') { + } else if (interaction.customId === 'infractions_unbans') { currentPage = 5; - } else if (interaction.customId === 'notes') { + } else if (interaction.customId === 'infractions_notes') { currentPage = 6; }