From 8f51ff885c4000bbcf38c4a09c1d7dfb46792d0b Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 30 May 2023 00:25:25 +0100 Subject: [PATCH] feat: finish user `create` command (closes #291) --- src/buttons/create.js | 3 +- src/commands/user/create.js | 158 ++++++++++++++++++++++++++++++++++-- src/i18n/en-GB.yml | 9 ++ 3 files changed, 163 insertions(+), 7 deletions(-) diff --git a/src/buttons/create.js b/src/buttons/create.js index 2e7d02c..b5f5cf1 100644 --- a/src/buttons/create.js +++ b/src/buttons/create.js @@ -13,10 +13,11 @@ module.exports = class CreateButton extends Button { * @param {import("discord.js").ButtonInteraction} interaction */ async run(id, interaction) { + if (id.targetUser && id.targetUser !== interaction.user.id) return; await this.client.tickets.create({ categoryId: id.target, interaction, topic: id.topic, }); } -}; \ No newline at end of file +}; diff --git a/src/commands/user/create.js b/src/commands/user/create.js index 4cf06a0..7176d40 100644 --- a/src/commands/user/create.js +++ b/src/commands/user/create.js @@ -1,4 +1,16 @@ const { UserCommand } = require('@eartharoid/dbf'); +const { isStaff } = require('../../lib/users'); +const ExtendedEmbedBuilder = require('../../lib/embed'); +const ms = require('ms'); +const { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + ComponentType, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, +} = require('discord.js'); +const emoji = require('node-emoji'); module.exports = class CreateUserCommand extends UserCommand { constructor(client, options) { @@ -13,10 +25,144 @@ module.exports = class CreateUserCommand extends UserCommand { }); } - async run(/* interaction */) { - // TODO: isStaff? - // TODO: user->create - // select category - // send button + /** + * @param {import("discord.js").UserContextMenuCommandInteraction} interaction + */ + async run(interaction) { + /** @type {import("client")} */ + const client = this.client; + + await interaction.deferReply({ ephemeral: true }); + + const settings = await client.prisma.guild.findUnique({ + include: { categories:true }, + where: { id: interaction.guild.id }, + }); + const getMessage = client.i18n.getLocale(settings.locale); + + if (!await isStaff(interaction.guild, interaction.user.id)) { + return await interaction.editReply({ + embeds: [ + new ExtendedEmbedBuilder({ + iconURL: interaction.guild.iconURL(), + text: settings.footer, + }) + .setColor(settings.errorColour) + .setTitle(getMessage('commands.user.create.not_staff.title')) + .setDescription(getMessage('commands.user.create.not_staff.description')), + ], + }); + } + + const prompt = async categoryId => { + interaction.followUp({ + components: [ + new ActionRowBuilder() + .addComponents( + new ButtonBuilder() + .setCustomId(JSON.stringify({ + action: 'create', + target: categoryId, + targetUser: interaction.targetId, + })) + .setStyle(ButtonStyle.Primary) + .setEmoji(getMessage('buttons.create.emoji')) // emoji.get('ticket') + .setLabel(getMessage('buttons.create.text')), + ), + ], + content: interaction.targetUser.toString(), + embeds: [ + new ExtendedEmbedBuilder() + .setColor(settings.primaryColour) + .setAuthor({ + iconURL: interaction.member.displayAvatarURL(), + name: interaction.member.displayName, + }) + .setTitle(getMessage('commands.user.create.prompt.title')) + .setDescription(getMessage('commands.user.create.prompt.description')), + ], + ephemeral: false, + }); + }; + + if (settings.categories.length === 0) { + interaction.reply({ + components: [], + embeds: [ + new ExtendedEmbedBuilder() + .setColor(settings.errorColour) + .setTitle(getMessage('misc.no_categories.title')) + .setDescription(getMessage('misc.no_categories.description')), + ], + ephemeral: true, + }); + } else if (settings.categories.length === 1) { + await prompt(settings.categories[0].id); + } else { + const collectorTime = ms('15s'); + const confirmationM = await interaction.editReply({ + components: [ + new ActionRowBuilder() + .setComponents( + new StringSelectMenuBuilder() + .setCustomId(JSON.stringify({ + action: 'promptCreate', + user: interaction.targetId, + })) + .setPlaceholder(getMessage('menus.category.placeholder')) + .setOptions( + settings.categories.map(category => + new StringSelectMenuOptionBuilder() + .setValue(String(category.id)) + .setLabel(category.name) + .setDescription(category.description) + .setEmoji(emoji.hasEmoji(category.emoji) ? emoji.get(category.emoji) : { id: category.emoji }), + ), + ), + ), + ], + }); + + confirmationM.awaitMessageComponent({ + componentType: ComponentType.StringSelect, + filter: i => i.user.id === interaction.user.id, + time: collectorTime, + }) + .then(async i => { + const category = settings.categories.find(c => c.id === Number(i.values[0])); + await i.update({ + components: [], + embeds: [ + new ExtendedEmbedBuilder({ + iconURL: interaction.guild.iconURL(), + text: settings.footer, + }) + .setColor(settings.successColour) + .setTitle(getMessage('commands.user.create.sent.title')) + .setDescription(getMessage('commands.user.create.sent.description', { + category: category.name, + user: interaction.targetUser.toString(), + })), + ], + ephemeral: true, + }); + await prompt(category.id); + }) + .catch(async error => { + client.log.error(error); + await interaction.reply({ + components: [], + embeds: [ + new ExtendedEmbedBuilder({ + iconURL: interaction.guild.iconURL(), + text: settings.footer, + }) + .setColor(settings.errorColour) + .setTitle(getMessage('misc.expired.title')) + .setDescription(getMessage('misc.expired.description')), + ], + }); + }); + } } -}; \ No newline at end of file +}; diff --git a/src/i18n/en-GB.yml b/src/i18n/en-GB.yml index e16413e..803f574 100644 --- a/src/i18n/en-GB.yml +++ b/src/i18n/en-GB.yml @@ -247,6 +247,15 @@ commands: user: create: name: Create ticket for user + not_staff: + description: Only staff members can open tickets for other members. + title: ❌ Error + prompt: + description: Click the button below to create a ticket. + title: Please create a ticket + sent: + description: "{user} has been invited to create a ticket in **{category}**." + title: ✅ Prompt sent dm: closed: archived: Use the `/transcript` command in **{guild}** to view the archived messages.