From a84614fbdd175fd5247a5210522d4ea610a97c5c Mon Sep 17 00:00:00 2001 From: Oliver Cordingley Date: Sat, 16 Jan 2021 14:19:37 +0000 Subject: [PATCH 1/6] closeall sets all open tickets to closed * currently does not update channels after closing. --- src/commands/closeall.js | 204 +++++++++++++++++++++++++++++++++++++++ user/config.js | 3 + 2 files changed, 207 insertions(+) create mode 100644 src/commands/closeall.js diff --git a/src/commands/closeall.js b/src/commands/closeall.js new file mode 100644 index 0000000..2b88666 --- /dev/null +++ b/src/commands/closeall.js @@ -0,0 +1,204 @@ +/** + * @name DiscordTickets + * @author eartharoid + * @license GNU-GPLv3 + * + */ + +const Logger = require('leekslazylogger'); +const log = new Logger(); +const { + MessageEmbed +} = require('discord.js'); +const fs = require('fs'); +const { join } = require('path'); +const config = require(join(__dirname, '../../user/', require('../').config)); + +// Similar to 'close' command, however it iterates through them. + +module.exports = { + name: 'closeall', + description: 'Closes all currently open tickets.', + usage: '', + aliases: ['ca'], + example: 'closeall', + args: false, + disabled: !config.commands.new.enabled, + async execute(client, message, args, { + config, + Ticket + }) { + const guild = client.guilds.cache.get(config.guild); + + if (!message.member.roles.cache.has(config.staff_role)) + return message.channel.send( + new MessageEmbed() + .setColor(config.err_colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle('❌ **No permission**') + .setDescription(`You do not have permission to use this command as you are not a staff member.`) + .addField('Usage', `\`${config.prefix}${this.name}${" " + this.usage}\`\n`) + .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) + .setFooter(guild.name, guild.iconURL()) + ); + + let tickets; + let channel; + + channel = message.channel; + tickets = await Ticket.findAll({ + where: { + open: true, + } + }); + + if (tickets.length == 0) + return message.channel.send( + new MessageEmbed() + .setColor(config.err_colour) + .setAuthor(message.author.username, message.author.display) + .setTitle('❌ **No open tickets**') + .setDescription('There are no open tickets to close.') + .setFooter(guild.name, guild.iconURL()) + ); + + log.info(`Found ${tickets.length} open tickets`); + + if (config.commands.close.confirmation) { + let success; + let pre = `Transcript versions may be available using the \`${config.prefix}\``; + + let confirm = await message.channel.send( + new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle('❔ Are you sure?') + .setDescription(`${pre}\n**React with ✅ to confirm.**`) + .setFooter(guild.name + ' | Expires in 15 seconds', guild.iconURL()) + ); + + await confirm.react('✅'); + + const collector = confirm.createReactionCollector( + (reaction, user) => reaction.emoji.name === '✅' && user.id === message.author.id, { + time: 15000, + }); + + collector.on('collect', async () => { + if (channel.id != message.channel.id) { + channel.send( + new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`**\`${tickets.length}\` tickets closed**`) + .setDescription(`**\`${tickets.length}\`** tickets closed by ${message.author}`) + .setFooter(guild.name, guild.iconURL()) + ); + } + + confirm.reactions.removeAll(); + confirm.edit( + new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`✅ ** \`${tickets.length}\` tickets closed**`) + .setDescription('The channels will be automatically deleted in a few seconds, once the contents have been archived.') + .setFooter(guild.name, guild.iconURL()) + ); + + if (channel.id !== message.channel.id) + message.delete({ + timeout: 5000, + }).then(() => confirm.delete()); + + success = true; + closeAll(); + }) + + collector.on('end', () => { + if (!success) { + confirm.reactions.removeAll(); + confirm.edit( + new MessageEmbed() + .setColor(config.err_colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle('❌ **Expired**') + .setDescription('You took too long to react; confirmation failed.') + .setFooter(guild.name, guild.iconURL())); + + message.delete({ + timeout: 10000 + }).then(() => confirm.delete()); + } + }); + } else { + closeAll(); + } + + async function closeAll() { + let users = []; + + if (config.transcripts.text.enabled || config.transcripts.web.enabled) { + + // LOOP START + tickets.forEach(async ticket => { + let user = await client.users.fetch(ticket.creator); + let paths = { + text: join(__dirname, `../../user/transcripts/text/${ticket.get('channel')}.txt`), + log: join(__dirname, `../../user/transcripts/raw/${ticket.get('channel')}.log`), + json: join(__dirname, `../../user/transcripts/raw/entities/${ticket.get('channel')}.json`) + }; + + if (user) { + let dm; + try { + dm = user.dmChannel || await user.createDM(); + } catch (e) { + log.warn(`Could not create DM channel with ${user.tag}`); + } + + let res = {}; + const embed = new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username) + .setTitle(`${tickets.length} tickets`) + .setFooter(guild.name, guild.iconURL()); + + if (fs.existsSync(paths.text)) { + embed.addField('Text Transcript', 'See attachment'); + res.files = [{ + attachment: paths.text, + name: `ticket-${ticket.id}-${ticket.get('channel')}.txt` + }]; + } + + if (fs.existsSync(paths.log) && fs.existsSync(paths.json)) { + let data = JSON.parse(fs.readFileSync(paths.json)); + for (user in data.entities.users) users.push(user); + embed.addField('Web archive', await archive.export(Ticket, channel)); + } + + res.embed = embed; + + try { + if (config.commands.close.send_transcripts) dm.send(res); + if (config.transcripts.channel.length > 1) client.channels.cache.get(config.transcripts.channel).send(res); + } catch (e) { + message.channel.send('❌ Couldn\'t send DM or transcript log message'); + } + } + + }); + } + + await Ticket.destroy({ + where: { + open: true, + }, + }); + } + + // TODO: possibly make users allow to close all of their issues? + + }, +}; \ No newline at end of file diff --git a/user/config.js b/user/config.js index 7e182e5..e5083ab 100644 --- a/user/config.js +++ b/user/config.js @@ -76,6 +76,9 @@ module.exports = { new: { enabled: true }, + closeall: { + enabled: true, + }, }, transcripts: { From 2cd943985eec9a3e90de081d37a56aba2b847d4b Mon Sep 17 00:00:00 2001 From: Oliver Cordingley Date: Sat, 16 Jan 2021 14:50:52 +0000 Subject: [PATCH 2/6] linted code --- src/commands/closeall.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/commands/closeall.js b/src/commands/closeall.js index 2b88666..6b346b6 100644 --- a/src/commands/closeall.js +++ b/src/commands/closeall.js @@ -13,6 +13,7 @@ const { const fs = require('fs'); const { join } = require('path'); const config = require(join(__dirname, '../../user/', require('../').config)); +const archive = require('../modules/archive'); // Similar to 'close' command, however it iterates through them. @@ -36,8 +37,8 @@ module.exports = { .setColor(config.err_colour) .setAuthor(message.author.username, message.author.displayAvatarURL()) .setTitle('❌ **No permission**') - .setDescription(`You do not have permission to use this command as you are not a staff member.`) - .addField('Usage', `\`${config.prefix}${this.name}${" " + this.usage}\`\n`) + .setDescription('You do not have permission to use this command as you are not a staff member.') + .addField('Usage', `\`${config.prefix}${this.name}${' ' + this.usage}\`\n`) .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); @@ -82,7 +83,7 @@ module.exports = { const collector = confirm.createReactionCollector( (reaction, user) => reaction.emoji.name === '✅' && user.id === message.author.id, { time: 15000, - }); + }); collector.on('collect', async () => { if (channel.id != message.channel.id) { @@ -113,7 +114,7 @@ module.exports = { success = true; closeAll(); - }) + }); collector.on('end', () => { if (!success) { @@ -190,12 +191,6 @@ module.exports = { }); } - - await Ticket.destroy({ - where: { - open: true, - }, - }); } // TODO: possibly make users allow to close all of their issues? From bf12ac8e7a463d2db0ff769a644be6d44d526038 Mon Sep 17 00:00:00 2001 From: Oliver Cordingley Date: Sat, 16 Jan 2021 15:44:35 +0000 Subject: [PATCH 3/6] fixed closing (db & discord channel) in closeall --- src/commands/closeall.js | 64 +++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/src/commands/closeall.js b/src/commands/closeall.js index 6b346b6..3de93db 100644 --- a/src/commands/closeall.js +++ b/src/commands/closeall.js @@ -43,11 +43,7 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) ); - let tickets; - let channel; - - channel = message.channel; - tickets = await Ticket.findAll({ + let tickets = await Ticket.findAll({ where: { open: true, } @@ -86,16 +82,14 @@ module.exports = { }); collector.on('collect', async () => { - if (channel.id != message.channel.id) { - channel.send( - new MessageEmbed() - .setColor(config.colour) - .setAuthor(message.author.username, message.author.displayAvatarURL()) - .setTitle(`**\`${tickets.length}\` tickets closed**`) - .setDescription(`**\`${tickets.length}\`** tickets closed by ${message.author}`) - .setFooter(guild.name, guild.iconURL()) - ); - } + message.channel.send( + new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`**\`${tickets.length}\` tickets closed**`) + .setDescription(`**\`${tickets.length}\`** tickets closed by ${message.author}`) + .setFooter(guild.name, guild.iconURL()) + ); confirm.reactions.removeAll(); confirm.edit( @@ -107,10 +101,9 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) ); - if (channel.id !== message.channel.id) - message.delete({ - timeout: 5000, - }).then(() => confirm.delete()); + message.delete({ + timeout: 5000, + }).then(() => confirm.delete()); success = true; closeAll(); @@ -143,11 +136,17 @@ module.exports = { // LOOP START tickets.forEach(async ticket => { - let user = await client.users.fetch(ticket.creator); + let { + channel, + id, + creator + } = ticket; + + let user = await client.users.fetch(creator); let paths = { - text: join(__dirname, `../../user/transcripts/text/${ticket.get('channel')}.txt`), - log: join(__dirname, `../../user/transcripts/raw/${ticket.get('channel')}.log`), - json: join(__dirname, `../../user/transcripts/raw/entities/${ticket.get('channel')}.json`) + text: join(__dirname, `../../user/transcripts/text/${channel}.txt`), + log: join(__dirname, `../../user/transcripts/raw/${channel}.log`), + json: join(__dirname, `../../user/transcripts/raw/entities/${channel}.json`) }; if (user) { @@ -169,13 +168,13 @@ module.exports = { embed.addField('Text Transcript', 'See attachment'); res.files = [{ attachment: paths.text, - name: `ticket-${ticket.id}-${ticket.get('channel')}.txt` + name: `ticket-${id}-${channel}.txt` }]; } if (fs.existsSync(paths.log) && fs.existsSync(paths.json)) { let data = JSON.parse(fs.readFileSync(paths.json)); - for (user in data.entities.users) users.push(user); + for (u in data.entities.users) users.push(u); embed.addField('Web archive', await archive.export(Ticket, channel)); } @@ -189,11 +188,22 @@ module.exports = { } } + await Ticket.update({ + open: false, + }, { + where: { + id, + } + }); + + client.channels.fetch(channel) + .then(c => c.delete() + .then(o => log.info(`Deleted channel with name: \'#${o.name}\' <${o.id}>`)) + .catch(e => log.error(e))) + .catch(e => log.error(e)); }); } } - - // TODO: possibly make users allow to close all of their issues? }, }; \ No newline at end of file From 3d2b3c12cbca69997bb6a7d205e7d1d4dcd382e3 Mon Sep 17 00:00:00 2001 From: Oliver Cordingley Date: Sat, 16 Jan 2021 15:50:15 +0000 Subject: [PATCH 4/6] fixed eslint problems in closeall.js --- src/commands/closeall.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/closeall.js b/src/commands/closeall.js index 3de93db..9adafde 100644 --- a/src/commands/closeall.js +++ b/src/commands/closeall.js @@ -174,7 +174,7 @@ module.exports = { if (fs.existsSync(paths.log) && fs.existsSync(paths.json)) { let data = JSON.parse(fs.readFileSync(paths.json)); - for (u in data.entities.users) users.push(u); + data.entities.users.forEach(u => users.push(u)); embed.addField('Web archive', await archive.export(Ticket, channel)); } @@ -198,8 +198,8 @@ module.exports = { client.channels.fetch(channel) .then(c => c.delete() - .then(o => log.info(`Deleted channel with name: \'#${o.name}\' <${o.id}>`)) - .catch(e => log.error(e))) + .then(o => log.info(`Deleted channel with name: '#${o.name}' <${o.id}>`)) + .catch(e => log.error(e))) .catch(e => log.error(e)); }); } From c12d5ba047e68dc7e7d8924c8aa80374d4b31a29 Mon Sep 17 00:00:00 2001 From: Oliver Cordingley Date: Sat, 16 Jan 2021 17:06:05 +0000 Subject: [PATCH 5/6] improved logging for closeall --- src/commands/closeall.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/commands/closeall.js b/src/commands/closeall.js index 9adafde..2cc3450 100644 --- a/src/commands/closeall.js +++ b/src/commands/closeall.js @@ -7,15 +7,13 @@ const Logger = require('leekslazylogger'); const log = new Logger(); -const { - MessageEmbed -} = require('discord.js'); +const { MessageEmbed } = require('discord.js'); const fs = require('fs'); const { join } = require('path'); const config = require(join(__dirname, '../../user/', require('../').config)); const archive = require('../modules/archive'); -// Similar to 'close' command, however it iterates through them. +// A slight modification to the 'close' command to allow multiple tickets to be closed at once module.exports = { name: 'closeall', @@ -196,12 +194,29 @@ module.exports = { } }); + log.info(log.format(`${message.author.tag} closed ticket &7${id}&f`)); + client.channels.fetch(channel) .then(c => c.delete() .then(o => log.info(`Deleted channel with name: '#${o.name}' <${o.id}>`)) .catch(e => log.error(e))) .catch(e => log.error(e)); }); + + if (config.logs.discord.enabled) { + let embed = new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`${tickets.length} ticket${tickets.length > 1 ? 's' : ''} closed (${config.prefix}closeall)`) + .addField('Closed by', message.author, true) + .setFooter(guild.name, guild.iconURL()) + .setTimestamp(); + + if (users.length > 1) + embed.addField('Members', users.map(u => `<@${u}>`).join('\n')); + + client.channels.cache.get(config.logs.discord.channel).send(embed); + } } } From d725324053cfe36726c0b73eed63df4679cca8a1 Mon Sep 17 00:00:00 2001 From: Oliver Cordingley Date: Sat, 16 Jan 2021 20:18:49 +0000 Subject: [PATCH 6/6] Fixed and tested issues pointed out by @Eartharoid --- src/commands/closeall.js | 63 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/commands/closeall.js b/src/commands/closeall.js index 2cc3450..f6fe8b8 100644 --- a/src/commands/closeall.js +++ b/src/commands/closeall.js @@ -22,7 +22,7 @@ module.exports = { aliases: ['ca'], example: 'closeall', args: false, - disabled: !config.commands.new.enabled, + disabled: !config.commands.closeall.enabled, async execute(client, message, args, { config, Ticket @@ -41,13 +41,13 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) ); - let tickets = await Ticket.findAll({ + let tickets = await Ticket.findAndCountAll({ where: { open: true, - } + }, }); - if (tickets.length == 0) + if (tickets.count === 0) return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -57,17 +57,19 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) ); - log.info(`Found ${tickets.length} open tickets`); + log.info(`Found ${tickets.count} open tickets`); if (config.commands.close.confirmation) { let success; - let pre = `Transcript versions may be available using the \`${config.prefix}\``; + let pre = config.transcripts.text.enabled || config.transcripts.web.enabled + ? `You will be able to view an archived version of each ticket later with \`${config.prefix}transcript \`` + : ''; let confirm = await message.channel.send( new MessageEmbed() .setColor(config.colour) .setAuthor(message.author.username, message.author.displayAvatarURL()) - .setTitle('❔ Are you sure?') + .setTitle(`❔ Are you sure you want to close **${tickets.count}** tickets?`) .setDescription(`${pre}\n**React with ✅ to confirm.**`) .setFooter(guild.name + ' | Expires in 15 seconds', guild.iconURL()) ); @@ -84,8 +86,8 @@ module.exports = { new MessageEmbed() .setColor(config.colour) .setAuthor(message.author.username, message.author.displayAvatarURL()) - .setTitle(`**\`${tickets.length}\` tickets closed**`) - .setDescription(`**\`${tickets.length}\`** tickets closed by ${message.author}`) + .setTitle(`**\`${tickets.count}\` tickets closed**`) + .setDescription(`**\`${tickets.count}\`** tickets closed by ${message.author}`) .setFooter(guild.name, guild.iconURL()) ); @@ -94,7 +96,7 @@ module.exports = { new MessageEmbed() .setColor(config.colour) .setAuthor(message.author.username, message.author.displayAvatarURL()) - .setTitle(`✅ ** \`${tickets.length}\` tickets closed**`) + .setTitle(`✅ ** \`${tickets.count}\` tickets closed**`) .setDescription('The channels will be automatically deleted in a few seconds, once the contents have been archived.') .setFooter(guild.name, guild.iconURL()) ); @@ -127,13 +129,12 @@ module.exports = { closeAll(); } + async function closeAll() { - let users = []; + tickets.rows.forEach(async ticket => { + let users = []; - if (config.transcripts.text.enabled || config.transcripts.web.enabled) { - - // LOOP START - tickets.forEach(async ticket => { + if (config.transcripts.text.enabled || config.transcripts.web.enabled) { let { channel, id, @@ -159,9 +160,9 @@ module.exports = { const embed = new MessageEmbed() .setColor(config.colour) .setAuthor(message.author.username) - .setTitle(`${tickets.length} tickets`) + .setTitle(`Ticket ${id}`) .setFooter(guild.name, guild.iconURL()); - + if (fs.existsSync(paths.text)) { embed.addField('Text Transcript', 'See attachment'); res.files = [{ @@ -201,23 +202,23 @@ module.exports = { .then(o => log.info(`Deleted channel with name: '#${o.name}' <${o.id}>`)) .catch(e => log.error(e))) .catch(e => log.error(e)); - }); - if (config.logs.discord.enabled) { - let embed = new MessageEmbed() - .setColor(config.colour) - .setAuthor(message.author.username, message.author.displayAvatarURL()) - .setTitle(`${tickets.length} ticket${tickets.length > 1 ? 's' : ''} closed (${config.prefix}closeall)`) - .addField('Closed by', message.author, true) - .setFooter(guild.name, guild.iconURL()) - .setTimestamp(); + if (config.logs.discord.enabled) { + let embed = new MessageEmbed() + .setColor(config.colour) + .setAuthor(message.author.username, message.author.displayAvatarURL()) + .setTitle(`${tickets.count} ticket${tickets.count > 1 ? 's' : ''} closed (${config.prefix}closeall)`) + .addField('Closed by', message.author, true) + .setFooter(guild.name, guild.iconURL()) + .setTimestamp(); - if (users.length > 1) - embed.addField('Members', users.map(u => `<@${u}>`).join('\n')); - - client.channels.cache.get(config.logs.discord.channel).send(embed); + if (users.length > 1) + embed.addField('Members', users.map(u => `<@${u}>`).join('\n')); + + client.channels.cache.get(config.logs.discord.channel).send(embed); + } } - } + }); } },