From 77216ba43d6d8565a530d16281e0e199a0da2bf7 Mon Sep 17 00:00:00 2001 From: Isaac Date: Sat, 29 Oct 2022 00:33:19 +0100 Subject: [PATCH] feat: add ticket claiming --- src/buttons/claim.js | 12 ++- src/buttons/unclaim.js | 12 ++- src/commands/slash/claim.js | 9 +- src/commands/slash/release.js | 11 ++- src/i18n/en-GB.yml | 4 +- src/lib/tickets/manager.js | 171 +++++++++++++++++++++++++++++++++- 6 files changed, 211 insertions(+), 8 deletions(-) diff --git a/src/buttons/claim.js b/src/buttons/claim.js index 2461e4b..c57e5df 100644 --- a/src/buttons/claim.js +++ b/src/buttons/claim.js @@ -8,5 +8,15 @@ module.exports = class ClaimButton extends Button { }); } - async run (id, interaction) {} + /** + * @param {*} id + * @param {import("discord.js").ChatInputCommandInteraction} interaction + */ + async run(id, interaction) { + /** @type {import("client")} */ + const client = this.client; + + await interaction.deferReply({ ephemeral: false }); + await client.tickets.claim(interaction); + } }; \ No newline at end of file diff --git a/src/buttons/unclaim.js b/src/buttons/unclaim.js index 50ed4b5..e6c7f4e 100644 --- a/src/buttons/unclaim.js +++ b/src/buttons/unclaim.js @@ -8,5 +8,15 @@ module.exports = class UnclaimButton extends Button { }); } - async run(id, interaction) { } + /** + * @param {*} id + * @param {import("discord.js").ChatInputCommandInteraction} interaction + */ + async run(id, interaction) { + /** @type {import("client")} */ + const client = this.client; + + await interaction.deferReply({ ephemeral: false }); + await client.tickets.release(interaction); + } }; \ No newline at end of file diff --git a/src/commands/slash/claim.js b/src/commands/slash/claim.js index 05fedfe..0e457fa 100644 --- a/src/commands/slash/claim.js +++ b/src/commands/slash/claim.js @@ -13,7 +13,14 @@ module.exports = class ClaimSlashCommand extends SlashCommand { }); } + /** + * @param {import("discord.js").ChatInputCommandInteraction} interaction + */ async run(interaction) { - // tickets/manager.js + /** @type {import("client")} */ + const client = this.client; + + await interaction.deferReply({ ephemeral: false }); + await client.tickets.claim(interaction); } }; \ No newline at end of file diff --git a/src/commands/slash/release.js b/src/commands/slash/release.js index b1f43d5..b9430c7 100644 --- a/src/commands/slash/release.js +++ b/src/commands/slash/release.js @@ -13,5 +13,14 @@ module.exports = class ReleaseSlashCommand extends SlashCommand { }); } - async run(interaction) { } + /** + * @param {import("discord.js").ChatInputCommandInteraction} interaction + */ + async run(interaction) { + /** @type {import("client")} */ + const client = this.client; + + await interaction.deferReply({ ephemeral: false }); + await client.tickets.release(interaction); + } }; \ No newline at end of file diff --git a/src/i18n/en-GB.yml b/src/i18n/en-GB.yml index a915d69..0d3b138 100644 --- a/src/i18n/en-GB.yml +++ b/src/i18n/en-GB.yml @@ -151,7 +151,6 @@ commands: description: Release (unclaim) a ticket name: release remove: - removed: ⬅️ {removed} has been removed by {by}. description: Remove a member from a ticket name: remove not_staff: @@ -164,6 +163,7 @@ commands: ticket: description: The ticket to remove the member from name: ticket + removed: ⬅️ {removed} has been removed by {by}. success: description: "{member} has been removed from {ticket}." title: ✅ Added @@ -332,6 +332,8 @@ modals: ticket: answers: no_value: "*No response*" + claimed: 🙌 {user} has claimed this ticket. + released: ♻️ {user} has released this ticket. created: description: "Your ticket channel has been created: {channel}." title: ✅ Ticket created diff --git a/src/lib/tickets/manager.js b/src/lib/tickets/manager.js index 3a46cbb..4e00194 100644 --- a/src/lib/tickets/manager.js +++ b/src/lib/tickets/manager.js @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ /* eslint-disable max-lines */ const TicketArchiver = require('./archiver'); const { @@ -344,15 +345,15 @@ module.exports = class TicketManager { id: guild.roles.everyone, }, { - allow: allow, + allow, id: this.client.user.id, }, { - allow: allow, + allow, id: creator.id, }, ...category.staffRoles.map(id => ({ - allow: allow, + allow, id, })), ], @@ -635,6 +636,170 @@ module.exports = class TicketManager { } } + /** + * @param {import("discord.js").ChatInputCommandInteraction|import("discord.js").ButtonInteraction} interaction + */ + async claim(interaction) { + const ticket = await this.client.prisma.ticket.findUnique({ + include: { + _count: { select: { questionAnswers: true } }, + category: true, + guild: true, + }, + where: { id: interaction.channel.id }, + }); + const getMessage = this.client.i18n.getLocale(ticket.guild.locale); + + await interaction.channel.permissionOverwrites.edit(interaction.user, { 'ViewChannel': true }, `Ticket claimed by ${interaction.user.tag}`); + + for (const role of ticket.category.staffRoles) await interaction.channel.permissionOverwrites.edit(role, { 'ViewChannel': false }, `Ticket claimed by ${interaction.user.tag}`); + + await this.client.prisma.ticket.update({ + data: { + claimedBy: { + connectOrCreate: { + create: { id: interaction.user.id }, + where: { id: interaction.user.id }, + }, + }, + }, + where: { id: interaction.channel.id }, + }); + + const openingMessage = await interaction.channel.messages.fetch(ticket.openingMessageId); + + if (openingMessage && openingMessage.components.length !== 0) { + const components = new ActionRowBuilder(); + + if (ticket.topic || ticket._count.questionAnswers !== 0) { + components.addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ action: 'edit' })) + .setStyle(ButtonStyle.Secondary) + .setEmoji(getMessage('buttons.edit.emoji')) + .setLabel(getMessage('buttons.edit.text')), + ); + } + + if (ticket.guild.claimButton && ticket.category.claiming) { + components.addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ action: 'unclaim' })) + .setStyle(ButtonStyle.Secondary) + .setEmoji(getMessage('buttons.unclaim.emoji')) + .setLabel(getMessage('buttons.unclaim.text')), + ); + } + + if (ticket.guild.closeButton) { + components.addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ action: 'close' })) + .setStyle(ButtonStyle.Danger) + .setEmoji(getMessage('buttons.close.emoji')) + .setLabel(getMessage('buttons.close.text')), + ); + } + + await openingMessage.edit({ components: [components] }); + } + + await interaction.editReply({ + embeds: [ + new ExtendedEmbedBuilder() + .setColor(ticket.guild.primaryColour) + .setDescription(getMessage('ticket.claimed', { user: interaction.user.toString() })), + ], + }); + + logTicketEvent(this.client, { + action: 'claim', + target: { + id: ticket.id, + name: interaction.channel.toString(), + }, + userId: interaction.user.id, + }); + } + + /** + * @param {import("discord.js").ChatInputCommandInteraction|import("discord.js").ButtonInteraction} interaction + */ + async release(interaction) { + const ticket = await this.client.prisma.ticket.findUnique({ + include: { + category: true, + guild: true, + }, + where: { id: interaction.channel.id }, + }); + const getMessage = this.client.i18n.getLocale(ticket.guild.locale); + + await interaction.channel.permissionOverwrites.delete(interaction.user, `Ticket released by ${interaction.user.tag}`); + + for (const role of ticket.category.staffRoles) await interaction.channel.permissionOverwrites.edit(role, { 'ViewChannel': true }, `Ticket released by ${interaction.user.tag}`); + + await this.client.prisma.ticket.update({ + data: { claimedBy: { disconnect: true } }, + where: { id: interaction.channel.id }, + }); + + const openingMessage = await interaction.channel.messages.fetch(ticket.openingMessageId); + + if (openingMessage && openingMessage.components.length !== 0) { + const components = new ActionRowBuilder(); + + if (ticket.topic || ticket._count.questionAnswers !== 0) { + components.addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ action: 'edit' })) + .setStyle(ButtonStyle.Secondary) + .setEmoji(getMessage('buttons.edit.emoji')) + .setLabel(getMessage('buttons.edit.text')), + ); + } + + if (ticket.guild.claimButton && ticket.category.claiming) { + components.addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ action: 'claim' })) + .setStyle(ButtonStyle.Secondary) + .setEmoji(getMessage('buttons.claim.emoji')) + .setLabel(getMessage('buttons.claim.text')), + ); + } + + if (ticket.guild.closeButton) { + components.addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ action: 'close' })) + .setStyle(ButtonStyle.Danger) + .setEmoji(getMessage('buttons.close.emoji')) + .setLabel(getMessage('buttons.close.text')), + ); + } + + await openingMessage.edit({ components: [components] }); + } + + await interaction.editReply({ + embeds: [ + new ExtendedEmbedBuilder() + .setColor(ticket.guild.primaryColour) + .setDescription(getMessage('ticket.released', { user: interaction.user.toString() })), + ], + }); + + logTicketEvent(this.client, { + action: 'unclaim', + target: { + id: ticket.id, + name: interaction.channel.toString(), + }, + userId: interaction.user.id, + }); + } + /** * @param {import("discord.js").ChatInputCommandInteraction|import("discord.js").ButtonInteraction} interaction