DiscordTickets/src/commands/close.js
Isaac 95f76716ed
feat: #206 convert to interactions (buttons and application commands) (#238)
* feat: make command handler slash command-ready

Only `help` and `settings` commands work so far

* feat(commands): finish new settings command

* fix(settings): convert `roles` and `ping` to an array

* fix(commands): make `add` a slash command

* fix(commands): make `blacklist` a slash command

* fix(commands): remove URLs from `help` command

* Add weblate badge and overview image

* Update sponsors

* sqlite things

* imrpovements

* update eslint rules

* (⚠ untested) close command

* fix: default locale for getting command option names

* Update README.md

* Update README.md

* Update README.md

* update new command to slash commands and fix close command

* fixes and improvements

* fix: closing a ticket when the creator has left

* Revert "fix: closing a ticket when the creator has left"

This reverts commit afc40ae17077782e344fd8cee03a089966c2347e.

* fix: closing a ticket when the creator has left

* fix: localisation issues in settings command

* fix: delete category channel

* New button and select panels + updated message panels

Includes new options for panel embed message image and thumbnail

Co-Authored-By: Puneet Gopinath <baalkrshna@gmail.com>
Co-Authored-By: thevisuales <6569806+thevisuales@users.noreply.github.com>

* Finish converting to buttons, added close button

Co-Authored-By: Puneet Gopinath <baalkrshna@gmail.com>
Co-Authored-By: thevisuales <6569806+thevisuales@users.noreply.github.com>

* fully convert to slash commands

* re-add "... has created a ticket" message

* locales

* fix add and remove commands

* fix remove command

* fix stats command

* eslint

Co-authored-by: Puneet Gopinath <baalkrshna@gmail.com>
Co-authored-by: thevisuales <6569806+thevisuales@users.noreply.github.com>
2021-09-24 15:32:36 +01:00

298 lines
9.7 KiB
JavaScript

const Command = require('../modules/commands/command');
const {
Interaction, // eslint-disable-line no-unused-vars
MessageActionRow,
MessageButton,
MessageEmbed
} = require('discord.js');
const { Op } = require('sequelize');
const ms = require('ms');
module.exports = class CloseCommand extends Command {
constructor(client) {
const i18n = client.i18n.getLocale(client.config.locale);
super(client, {
description: i18n('commands.close.description'),
internal: true,
name: i18n('commands.close.name'),
options: [
{
description: i18n('commands.close.options.reason.description'),
name: i18n('commands.close.options.reason.name'),
required: false,
type: Command.option_types.STRING
},
{
description: i18n('commands.close.options.ticket.description'),
name: i18n('commands.close.options.ticket.name'),
required: false,
type: Command.option_types.INTEGER
},
{
description: i18n('commands.close.options.time.description'),
name: i18n('commands.close.options.time.name'),
required: false,
type: Command.option_types.STRING
}
]
});
}
/**
* @param {Interaction} interaction
* @returns {Promise<void|any>}
*/
async execute(interaction) {
const settings = await this.client.utils.getSettings(interaction.guild.id);
const default_i18n = this.client.i18n.getLocale(this.client.config.defaults.locale); // command properties could be in a different locale
const i18n = this.client.i18n.getLocale(settings.locale);
const reason = interaction.options.getString(default_i18n('commands.close.options.reason.name'));
const ticket = interaction.options.getInteger(default_i18n('commands.close.options.ticket.name'));
const time = interaction.options.getString(default_i18n('commands.close.options.time.name'));
if (time) {
let period;
try {
period = ms(time);
} catch {
return await interaction.reply({
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setTitle(i18n('commands.close.response.invalid_time.title'))
.setDescription(i18n('commands.close.response.invalid_time.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
const tickets = await this.client.db.models.Ticket.findAndCountAll({
where: {
guild: interaction.guild.id,
last_message: { [Op.lte]: new Date(Date.now() - period) },
open: true
}
});
if (tickets.count === 0) {
return await interaction.reply({
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setTitle(i18n('commands.close.response.no_tickets.title'))
.setDescription(i18n('commands.close.response.no_tickets.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
} else {
await interaction.reply({
components: [
new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId(`confirm_close_multiple:${interaction.id}`)
.setLabel(i18n('commands.close.response.confirm_multiple.buttons.confirm', tickets.count, tickets.count))
.setEmoji('✅')
.setStyle('SUCCESS')
)
.addComponents(
new MessageButton()
.setCustomId(`cancel_close_multiple:${interaction.id}`)
.setLabel(i18n('commands.close.response.confirm_multiple.buttons.cancel'))
.setEmoji('❌')
.setStyle('SECONDARY')
)
],
embeds: [
new MessageEmbed()
.setColor(settings.colour)
.setTitle(i18n('commands.close.response.confirm_multiple.title'))
.setDescription(i18n('commands.close.response.confirm_multiple.description', tickets.count, tickets.count))
.setFooter(this.client.utils.footer(settings.footer, i18n('collector_expires_in', 30)), interaction.guild.iconURL())
],
ephemeral: true
});
const filter = i => i.user.id === interaction.user.id && i.customId.includes(interaction.id);
const collector = interaction.channel.createMessageComponentCollector({
filter,
time: 30000
});
collector.on('collect', async i => {
await i.deferUpdate();
if (i.customId === `confirm_close_multiple:${interaction.id}`) {
for (const ticket of tickets.rows) {
await this.client.tickets.close(ticket.id, interaction.user.id, interaction.guild.id, reason);
}
await i.editReply({
components: [],
embeds: [
new MessageEmbed()
.setColor(settings.success_colour)
.setTitle(i18n('commands.close.response.closed_multiple.title', tickets.count, tickets.count))
.setDescription(i18n('commands.close.response.closed_multiple.description', tickets.count, tickets.count))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
} else {
await i.editReply({
components: [],
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setTitle(i18n('commands.close.response.canceled.title'))
.setDescription(i18n('commands.close.response.canceled.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
collector.stop();
});
collector.on('end', async collected => {
if (collected.size === 0) {
await interaction.editReply({
components: [],
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setAuthor(interaction.user.username, interaction.user.displayAvatarURL())
.setTitle(i18n('commands.close.response.confirmation_timeout.title'))
.setDescription(i18n('commands.close.response.confirmation_timeout.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
});
}
} else {
let t_row;
if (ticket) {
t_row = await this.client.tickets.resolve(ticket, interaction.guild.id);
if (!t_row) {
return await interaction.reply({
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setTitle(i18n('commands.close.response.unresolvable.title'))
.setDescription(i18n('commands.close.response.unresolvable.description', ticket))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
} else {
t_row = await this.client.db.models.Ticket.findOne({ where: { id: interaction.channel.id } });
if (!t_row) {
return await interaction.reply({
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setTitle(i18n('commands.close.response.not_a_ticket.title'))
.setDescription(i18n('commands.close.response.not_a_ticket.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
}
await interaction.reply({
components: [
new MessageActionRow()
.addComponents(
new MessageButton()
.setCustomId(`confirm_close:${interaction.id}`)
.setLabel(i18n('commands.close.response.confirm.buttons.confirm'))
.setEmoji('✅')
.setStyle('SUCCESS')
)
.addComponents(
new MessageButton()
.setCustomId(`cancel_close:${interaction.id}`)
.setLabel(i18n('commands.close.response.confirm.buttons.cancel'))
.setEmoji('❌')
.setStyle('SECONDARY')
)
],
embeds: [
new MessageEmbed()
.setColor(settings.colour)
.setTitle(i18n('commands.close.response.confirm.title'))
.setDescription(settings.log_messages ? i18n('commands.close.response.confirm.description_with_archive') : i18n('commands.close.response.confirm.description'))
.setFooter(this.client.utils.footer(settings.footer, i18n('collector_expires_in', 30)), interaction.guild.iconURL())
],
ephemeral: true
});
const filter = i => i.user.id === interaction.user.id && i.customId.includes(interaction.id);
const collector = interaction.channel.createMessageComponentCollector({
filter,
time: 30000
});
collector.on('collect', async i => {
await i.deferUpdate();
if (i.customId === `confirm_close:${interaction.id}`) {
await this.client.tickets.close(t_row.id, interaction.user.id, interaction.guild.id, reason);
await i.editReply({
components: [],
embeds: [
new MessageEmbed()
.setColor(settings.success_colour)
.setTitle(i18n('commands.close.response.closed.title', t_row.number))
.setDescription(i18n('commands.close.response.closed.description', t_row.number))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
} else {
await i.editReply({
components: [],
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setTitle(i18n('commands.close.response.canceled.title'))
.setDescription(i18n('commands.close.response.canceled.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
collector.stop();
});
collector.on('end', async collected => {
if (collected.size === 0) {
await interaction.editReply({
components: [],
embeds: [
new MessageEmbed()
.setColor(settings.error_colour)
.setAuthor(interaction.user.username, interaction.user.displayAvatarURL())
.setTitle(i18n('commands.close.response.confirmation_timeout.title'))
.setDescription(i18n('commands.close.response.confirmation_timeout.description'))
.setFooter(settings.footer, interaction.guild.iconURL())
],
ephemeral: true
});
}
});
}
}
};