diff --git a/.eslintrc.js b/.eslintrc.js index 9ba76e1..97d7070 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,9 @@ module.exports = { ], 'no-control-regex': [ 'off' + ], + 'no-console': [ + 'off' ] } }; \ No newline at end of file diff --git a/README.md b/README.md index 8ffd98c..0336ab3 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ DiscordTickets is a Discord bot for managing support ticket channels, to allow y - Highly customisable - Ticket "panel" / "widget" (react to embed to create ticket) - Simple commands -- SQLite (easy, default) **or** MySQL (recommend) storage +- Supports multiple databases: SQLite (easy, default), MySQL (recommend) storage, MariaDB, PostgresSQL and Microsoft SQL Server - Most (all of the best) features of the premium bots, for free - Self-hosted with your bot application, your logo and guild name -- Optional express.js server for web archvies: [DiscordTickets-Portal](https://github.com/eartharoid/DiscordTickets-Portal/) +- Optional express.js server for web archives: [DiscordTickets-Portal](https://github.com/eartharoid/DiscordTickets-Portal/) ## Screenshot @@ -31,4 +31,4 @@ For installation, configuration, usage, and other information, **go to the [wiki ## Donate -[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B214BHI) +[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/eartharoid) diff --git a/package.json b/package.json index e1e3b3b..7c6a81c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "sqlite3": "^5.0.0" }, "peerDependencies": { - "mysql2": "2.x" + "mysql2": "2.x", + "mariadb": "2.x", + "pg": "8.x", + "tedious": "9.x" }, "devDependencies": { "eslint": "^7.10.0", diff --git a/src/commands/add.js b/src/commands/add.js index dc500d7..4024851 100644 --- a/src/commands/add.js +++ b/src/commands/add.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { MessageEmbed } = require('discord.js'); @@ -18,7 +18,6 @@ module.exports = { example: 'add @member to #ticket-23', args: true, async execute(client, message, args, {config, Ticket}) { - const guild = client.guilds.cache.get(config.guild); const notTicket = new MessageEmbed() @@ -34,17 +33,14 @@ module.exports = { let channel = message.mentions.channels.first(); - if(!channel) { - + if (!channel) { channel = message.channel; ticket = await Ticket.findOne({ where: { channel: message.channel.id } }); - if(!ticket) - return message.channel.send(notTicket); + if (!ticket) return message.channel.send(notTicket); } else { - ticket = await Ticket.findOne({ where: { channel: channel.id } }); - if(!ticket) { + if (!ticket) { notTicket .setTitle(':x: **Channel is not a ticket**') .setDescription(`${channel} is not a ticket channel.`); @@ -52,7 +48,7 @@ module.exports = { } } - if(message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) + if (message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -63,12 +59,11 @@ module.exports = { .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); - - + } let member = guild.member(message.mentions.users.first() || guild.members.cache.get(args[0])); - - if(!member) + + if (!member) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -79,6 +74,7 @@ module.exports = { .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); + } try { channel.updateOverwrite(member.user, { @@ -88,7 +84,7 @@ module.exports = { READ_MESSAGE_HISTORY: true }); - if(channel.id !== message.channel.id) + if (channel.id !== message.channel.id) { channel.send( new MessageEmbed() .setColor(config.colour) @@ -97,9 +93,8 @@ module.exports = { .setDescription(`${member} has been added by ${message.author}`) .setFooter(guild.name, guild.iconURL()) ); + } - - message.channel.send( new MessageEmbed() .setColor(config.colour) @@ -108,7 +103,7 @@ module.exports = { .setDescription(`${member} has been added to <#${ticket.channel}>`) .setFooter(guild.name, guild.iconURL()) ); - + log.info(`${message.author.tag} added a user to a ticket (#${message.channel.id})`); } catch (error) { log.error(error); diff --git a/src/commands/close.js b/src/commands/close.js index 3bd2841..6b5a19e 100644 --- a/src/commands/close.js +++ b/src/commands/close.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -21,11 +21,7 @@ module.exports = { aliases: ['none'], example: 'close #ticket-17', args: false, - async execute(client, message, args, { - config, - Ticket - }) { - + async execute(client, message, args, { config, Ticket }) { const guild = client.guilds.cache.get(config.guild); const notTicket = new MessageEmbed() @@ -49,11 +45,8 @@ module.exports = { channel: channel.id } }); - if (!ticket) - return channel.send(notTicket); - + if (!ticket) return channel.send(notTicket); } else { - ticket = await Ticket.findOne({ where: { channel: channel.id @@ -102,7 +95,7 @@ module.exports = { }); collector.on('collect', async () => { - if (channel.id !== message.channel.id) + if (channel.id !== message.channel.id) { channel.send( new MessageEmbed() .setColor(config.colour) @@ -111,6 +104,7 @@ module.exports = { .setDescription(`Ticket closed by ${message.author}`) .setFooter(guild.name, guild.iconURL()) ); + } confirm.reactions.removeAll(); confirm.edit( @@ -149,16 +143,13 @@ module.exports = { }]; } - if ( - fs.existsSync(`user/transcripts/raw/${ticket.get('channel')}.log`) - && - fs.existsSync(`user/transcripts/raw/entities/${ticket.get('channel')}.json`) - ) + if (fs.existsSync(`user/transcripts/raw/${ticket.get('channel')}.log`) && fs.existsSync(`user/transcripts/raw/entities/${ticket.get('channel')}.json`)) { embed.addField('Web archive', await archive.export(Ticket, channel)); - - - if (embed.fields.length < 1) + } + + if (embed.fields.length < 1) { embed.setDescription(`No text transcripts or archive data exists for ticket ${ticket.id}`); + } res.embed = embed; @@ -166,7 +157,6 @@ module.exports = { } } - // update database success = true; ticket.update({ @@ -187,7 +177,7 @@ module.exports = { log.info(`${message.author.tag} closed a ticket (#ticket-${ticket.id})`); - if (config.logs.discord.enabled) + if (config.logs.discord.enabled) { client.channels.cache.get(config.logs.discord.channel).send( new MessageEmbed() .setColor(config.colour) @@ -198,6 +188,7 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) .setTimestamp() ); + } }); diff --git a/src/commands/delete.js b/src/commands/delete.js index d06a51e..3964721 100644 --- a/src/commands/delete.js +++ b/src/commands/delete.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -24,7 +24,6 @@ module.exports = { config, Ticket }) { - const guild = client.guilds.cache.get(config.guild); const notTicket = new MessageEmbed() @@ -48,11 +47,9 @@ module.exports = { channel: channel.id } }); - if (!ticket) - return channel.send(notTicket); + if (!ticket) return channel.send(notTicket); } else { - ticket = await Ticket.findOne({ where: { channel: channel.id @@ -65,7 +62,7 @@ module.exports = { return message.channel.send(notTicket); } - if (message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) + if (message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) { return channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -76,6 +73,7 @@ module.exports = { .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); + } } let success; @@ -124,14 +122,9 @@ module.exports = { raw = `user/transcripts/raw/${ticket.get('channel')}.log`, json = `user/transcripts/raw/entities/${ticket.get('channel')}.json`; - if (fs.existsSync(txt)) - fs.unlinkSync(txt); - - if (fs.existsSync(raw)) - fs.unlinkSync(raw); - - if (fs.existsSync(json)) - fs.unlinkSync(json); + if (fs.existsSync(txt)) fs.unlinkSync(txt); + if (fs.existsSync(raw)) fs.unlinkSync(raw); + if (fs.existsSync(json)) fs.unlinkSync(json); // update database success = true; @@ -147,7 +140,7 @@ module.exports = { log.info(`${message.author.tag} deleted a ticket (#ticket-${ticket.id})`); - if (config.logs.discord.enabled) + if (config.logs.discord.enabled) { client.channels.cache.get(config.logs.discord.channel).send( new MessageEmbed() .setColor(config.colour) @@ -158,6 +151,7 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) .setTimestamp() ); + } }); diff --git a/src/commands/help.js b/src/commands/help.js index a9af45b..9d06c38 100644 --- a/src/commands/help.js +++ b/src/commands/help.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -18,27 +18,23 @@ module.exports = { example: 'help new', args: false, execute(client, message, args, {config}) { - const guild = client.guilds.cache.get(config.guild); - + const commands = Array.from(client.commands.values()); if (!args.length) { let cmds = []; for (let command of commands) { - if (command.hide) - continue; - if (command.permission && !message.member.hasPermission(command.permission)) - continue; + if (command.hide) continue; + if (command.permission && !message.member.hasPermission(command.permission)) continue; let desc = command.description; - if (desc.length > 50) - desc = desc.substring(0, 50) + '...'; + if (desc.length > 50) desc = desc.substring(0, 50) + '...'; cmds.push(`**${config.prefix}${command.name}** **ยท** ${desc}`); } - + message.channel.send( new MessageEmbed() .setTitle('Commands') @@ -58,24 +54,22 @@ module.exports = { const name = args[0].toLowerCase(); const command = client.commands.get(name) || client.commands.find(c => c.aliases && c.aliases.includes(name)); - if (!command) + if (!command) return message.channel.send( new MessageEmbed() .setColor(config.err_colour) .setDescription(`:x: **Invalid command name** (\`${config.prefix}help\`)`) ); - + const cmd = new MessageEmbed() .setColor(config.colour) .setTitle(command.name); - if (command.long) { - cmd.setDescription(command.long); - } else { - cmd.setDescription(command.description); - } + if (command.long) cmd.setDescription(command.long); + else cmd.setDescription(command.description); + if (command.aliases) cmd.addField('Aliases', `\`${command.aliases.join(', ')}\``, true); if (command.usage) cmd.addField('Usage', `\`${config.prefix}${command.name} ${command.usage}\``, false); @@ -85,12 +79,9 @@ module.exports = { if (command.permission && !message.member.hasPermission(command.permission)) { cmd.addField('Required Permission', `\`${command.permission}\` :exclamation: You don't have permission to use this command`, true); - } else { - cmd.addField('Required Permission', `\`${command.permission || 'none'}\``, true); - } - - message.channel.send(cmd); + } else cmd.addField('Required Permission', `\`${command.permission || 'none'}\``, true); + message.channel.send(cmd); } // command ends here diff --git a/src/commands/new.js b/src/commands/new.js index 162d7aa..76ce804 100644 --- a/src/commands/new.js +++ b/src/commands/new.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -19,9 +19,8 @@ module.exports = { example: 'new my server won\'t start', args: false, async execute(client, message, args, {config, Ticket}) { - const guild = client.guilds.cache.get(config.guild); - + const supportRole = guild.roles.cache.get(config.staff_role); if (!supportRole) return message.channel.send( @@ -43,12 +42,12 @@ module.exports = { if (tickets.count >= config.tickets.max) { let ticketList = []; - for (let t in tickets.rows) { + for (let t in tickets.rows) { let desc = tickets.rows[t].topic.substring(0, 30); ticketList .push(`<#${tickets.rows[t].channel}>: \`${desc}${desc.length > 30 ? '...' : ''}\``); - } - + } + let m = await message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -63,7 +62,7 @@ module.exports = { await m.delete(); }, 15000); } - + let topic = args.join(' '); if (topic.length > 256) @@ -75,9 +74,7 @@ module.exports = { .setDescription('Please limit your ticket topic to less than 256 characters. A short sentence will do.') .setFooter(guild.name, guild.iconURL()) ); - else if (topic.length < 1) - topic = 'No topic given'; - + else if (topic.length < 1) topic = 'No topic given'; let ticket = await Ticket.create({ channel: '', @@ -100,7 +97,7 @@ module.exports = { { id: client.user, allow: ['VIEW_CHANNEL', 'SEND_MESSAGES', 'ATTACH_FILES', 'READ_MESSAGE_HISTORY'] - }, + }, { id: message.member, allow: ['VIEW_CHANNEL', 'SEND_MESSAGES', 'ATTACH_FILES', 'READ_MESSAGE_HISTORY'] @@ -134,7 +131,7 @@ module.exports = { await message.delete(); await m.delete(); }, 15000); - + // require('../modules/archive').create(client, c); // create files let ping; @@ -175,9 +172,8 @@ module.exports = { .setFooter(guild.name, guild.iconURL()) ); - if (config.tickets.pin) - await w.pin(); - // await w.pin().then(m => m.delete()); // oopsie, this deletes the pinned message + if (config.tickets.pin) await w.pin(); + // await w.pin().then(m => m.delete()); // oopsie, this deletes the pinned message if (config.logs.discord.enabled) client.channels.cache.get(config.logs.discord.channel).send( diff --git a/src/commands/panel.js b/src/commands/panel.js index 987f053..9bf6d93 100644 --- a/src/commands/panel.js +++ b/src/commands/panel.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -18,21 +18,21 @@ module.exports = { example: '', args: false, permission: 'MANAGE_GUILD', - async execute(client, message, args, {config, Setting}) { - + async execute(client, message, _args, {config, Setting}) { const guild = client.guilds.cache.get(config.guild); let msgID = await Setting.findOne({ where: { key: 'panel_msg_id' } }); let chanID = await Setting.findOne({ where: { key: 'panel_chan_id' } }); let panel; - if(!chanID) + if (!chanID) { chanID = await Setting.create({ key: 'panel_chan_id', value: message.channel.id, }); + } - if(!msgID) { + if (!msgID) { msgID = await Setting.create({ key: 'panel_msg_id', value: '', @@ -40,12 +40,12 @@ module.exports = { } else { try { panel = await client.channels.cache.get(chanID.get('value')).messages.fetch(msgID.get('value')); // get old panel message - if (panel) + if (panel) { panel.delete({ reason: 'Creating new panel/widget' }).then(() => log.info('Deleted old panel')).catch(e => log.warn(e)); // delete old panel + } } catch (e) { log.warn('Couldn\'t delete old panel'); } - } message.delete(); diff --git a/src/commands/remove.js b/src/commands/remove.js index 48b2de3..472daad 100644 --- a/src/commands/remove.js +++ b/src/commands/remove.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { MessageEmbed } = require('discord.js'); @@ -18,7 +18,6 @@ module.exports = { example: 'remove @member from #ticket-23', args: true, async execute(client, message, args, {config, Ticket}) { - const guild = client.guilds.cache.get(config.guild); const notTicket = new MessageEmbed() @@ -34,17 +33,17 @@ module.exports = { let channel = message.mentions.channels.first(); - if(!channel) { + if (!channel) { channel = message.channel; ticket = await Ticket.findOne({ where: { channel: message.channel.id } }); - if(!ticket) + if (!ticket) return message.channel.send(notTicket); } else { - + ticket = await Ticket.findOne({ where: { channel: channel.id } }); - if(!ticket) { + if (!ticket) { notTicket .setTitle(':x: **Channel is not a ticket**') .setDescription(`${channel} is not a ticket channel.`); @@ -52,7 +51,7 @@ module.exports = { } } - if(message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) + if (message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -63,12 +62,11 @@ module.exports = { .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); - - + } let member = guild.member(message.mentions.users.first() || guild.members.cache.get(args[0])); - - if(!member || member.id === message.author.id || member.id === guild.me.id) + + if (!member || member.id === message.author.id || member.id === guild.me.id) return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -88,7 +86,7 @@ module.exports = { READ_MESSAGE_HISTORY: false }); - if(channel.id !== message.channel.id) + if (channel.id !== message.channel.id) { channel.send( new MessageEmbed() .setColor(config.colour) @@ -97,9 +95,8 @@ module.exports = { .setDescription(`${member} has been removed by ${message.author}`) .setFooter(guild.name, guild.iconURL()) ); + } - - message.channel.send( new MessageEmbed() .setColor(config.colour) @@ -108,7 +105,7 @@ module.exports = { .setDescription(`${member} has been removed from <#${ticket.channel}>`) .setFooter(guild.name, guild.iconURL()) ); - + log.info(`${message.author.tag} removed a user from a ticket (#${message.channel.id})`); } catch (error) { log.error(error); diff --git a/src/commands/stats.js b/src/commands/stats.js index 762028f..7a3f53d 100644 --- a/src/commands/stats.js +++ b/src/commands/stats.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { MessageEmbed } = require('discord.js'); @@ -15,8 +15,7 @@ module.exports = { aliases: ['data', 'statistics'], example: '', args: false, - async execute(client, message, args, {config, Ticket}) { - + async execute(client, message, _args, {config, Ticket}) { const guild = client.guilds.cache.get(config.guild); let open = await Ticket.count({ where: { open: true } }); diff --git a/src/commands/tickets.js b/src/commands/tickets.js index d125865..b4c9f3f 100644 --- a/src/commands/tickets.js +++ b/src/commands/tickets.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { MessageEmbed } = require('discord.js'); @@ -17,11 +17,10 @@ module.exports = { example: '', args: false, async execute(client, message, args, {config, Ticket}) { - const guild = client.guilds.cache.get(config.guild); - + const supportRole = guild.roles.cache.get(config.staff_role); - if (!supportRole) + if (!supportRole) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -29,12 +28,13 @@ module.exports = { .setDescription(`${config.name} has not been set up correctly. Could not find a 'support team' role with the id \`${config.staff_role}\``) .setFooter(guild.name, guild.iconURL()) ); + } let context = 'self'; let user = message.mentions.users.first() || guild.members.cache.get(args[0]); - - if(user) { - if(!message.member.roles.cache.has(config.staff_role)) + + if (user) { + if (!message.member.roles.cache.has(config.staff_role)) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -45,12 +45,10 @@ module.exports = { .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); + } context = 'staff'; - } else { - user = message.author; - } - + } else user = message.author; let openTickets = await Ticket.findAndCountAll({ where: { @@ -74,33 +72,34 @@ module.exports = { .setTitle(`${context === 'self' ? 'Your' : user.username + '\'s'} tickets`) .setFooter(guild.name + ' | This message will be deleted in 60 seconds', guild.iconURL()); - if(config.transcripts.web.enabled) + if (config.transcripts.web.enabled) { embed.setDescription(`You can access all of your ticket archives on the [web portal](${config.transcripts.web.server}/${user.id}).`); - + } + let open = [], closed = []; - for (let t in openTickets.rows) { let desc = openTickets.rows[t].topic.substring(0, 30); open.push(`> <#${openTickets.rows[t].channel}>: \`${desc}${desc.length > 20 ? '...' : ''}\``); - } - + for (let t in closedTickets.rows) { let desc = closedTickets.rows[t].topic.substring(0, 30); let transcript = ''; let c = closedTickets.rows[t].channel; - if(fs.existsSync(`user/transcripts/text/${c}.txt`) || config.transcripts.web.enabled) + if (fs.existsSync(`user/transcripts/text/${c}.txt`) || config.transcripts.web.enabled) { transcript = `\n> Type \`${config.prefix}transcript ${closedTickets.rows[t].id}\` to view.`; + } closed.push(`> **#${closedTickets.rows[t].id}**: \`${desc}${desc.length > 20 ? '...' : ''}\`${transcript}`); - + } + let pre = context === 'self' ? 'You have' : user.username + ' has'; embed.addField('Open tickets', openTickets.count === 0 ? `${pre} no open tickets.` : open.join('\n\n'), false); embed.addField('Closed tickets', closedTickets.count === 0 ? `${pre} no old tickets` : closed.join('\n\n'), false); - + message.delete({timeout: 15000}); let channel; diff --git a/src/commands/topic.js b/src/commands/topic.js index 1b7261e..3c26382 100644 --- a/src/commands/topic.js +++ b/src/commands/topic.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { MessageEmbed } = require('discord.js'); @@ -16,7 +16,6 @@ module.exports = { example: 'topic need help error', args: true, async execute(client, message, args, {config, Ticket}) { - const guild = client.guilds.cache.get(config.guild); let ticket = await Ticket.findOne({ @@ -25,7 +24,7 @@ module.exports = { } }); - if (!ticket) + if (!ticket) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -36,10 +35,10 @@ module.exports = { .addField('Help', `Type \`${config.prefix}help ${this.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); - + } let topic = args.join(' '); - if (topic.length > 256) + if (topic.length > 256) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -47,10 +46,11 @@ module.exports = { .setTitle(':x: **Description too long**') .setDescription('Please limit your ticket topic to less than 256 characters. A short sentence will do.') .setFooter(guild.name, guild.iconURL()) - ); + ); + } message.channel.setTopic(`<@${ticket.creator}> | ` + topic); - + Ticket.update({ topic: topic }, { @@ -59,7 +59,6 @@ module.exports = { } }); - message.channel.send( new MessageEmbed() .setColor(config.colour) diff --git a/src/commands/transcript.js b/src/commands/transcript.js index 5c6be40..25b6ff3 100644 --- a/src/commands/transcript.js +++ b/src/commands/transcript.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const fs = require('fs'); @@ -19,7 +19,6 @@ module.exports = { example: 'transcript 57', args: true, async execute(client, message, args, {config, Ticket}) { - const guild = client.guilds.cache.get(config.guild); const id = args[0]; @@ -31,7 +30,7 @@ module.exports = { }); - if (!ticket) + if (!ticket) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -40,8 +39,9 @@ module.exports = { .setDescription('Couldn\'t find a closed ticket with that ID') .setFooter(guild.name, guild.iconURL()) ); + } - if (message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) + if (message.author.id !== ticket.creator && !message.member.roles.cache.has(config.staff_role)) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -50,6 +50,8 @@ module.exports = { .setDescription(`You don't have permission to view ticket ${id} as it does not belong to you and you are not staff.`) .setFooter(guild.name, guild.iconURL()) ); + } + let res = {}; const embed = new MessageEmbed() .setColor(config.colour) @@ -66,14 +68,12 @@ module.exports = { } ]; } - + const BASE_URL = config.transcripts.web.server; - if (config.transcripts.web.enabled) - embed.addField('Web archive', `${BASE_URL}/${ticket.creator}/${ticket.channel}`); + if (config.transcripts.web.enabled) embed.addField('Web archive', `${BASE_URL}/${ticket.creator}/${ticket.channel}`); - if (embed.fields.length < 1) - embed.setDescription(`No text transcripts or archive data exists for ticket ${id}`); + if (embed.fields.length < 1) embed.setDescription(`No text transcripts or archive data exists for ticket ${id}`); res.embed = embed; @@ -83,10 +83,9 @@ module.exports = { } catch (e) { channel = message.channel; } - + channel.send(res).then(m => { - if (channel.id === message.channel.id) - m.delete({timeout: 15000}); + if (channel.id === message.channel.id) m.delete({timeout: 15000}); }); message.delete({timeout: 1500}); } diff --git a/src/events/debug.js b/src/events/debug.js index 09e3463..1d085f4 100644 --- a/src/events/debug.js +++ b/src/events/debug.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -11,7 +11,7 @@ const log = new ChildLogger(); module.exports = { event: 'debug', - execute(client, [e]) { + execute(_client, [e]) { log.debug(e); } }; \ No newline at end of file diff --git a/src/events/error.js b/src/events/error.js index 2791d1d..f1c89de 100644 --- a/src/events/error.js +++ b/src/events/error.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -11,7 +11,7 @@ const log = new ChildLogger(); module.exports = { event: 'error', - execute(client, [e]) { + execute(_client, [e]) { log.error(e); } }; \ No newline at end of file diff --git a/src/events/message.js b/src/events/message.js index d115901..55e2040 100644 --- a/src/events/message.js +++ b/src/events/message.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { Collection, MessageEmbed } = require('discord.js'); @@ -23,19 +23,18 @@ module.exports = { I am the support bot for **${guild}**. Type \`${config.prefix}new\` on the server to create a new ticket.`); } // stop here if is DM - + /** * Ticket transcripts * (bots currently still allowed) */ - + let ticket = await Ticket.findOne({ where: { channel: message.channel.id } }); - if(ticket) - archive.add(message); // add message to archive + if (ticket) archive.add(message); // add message to archive if (message.author.bot || message.author.id === client.user.id) return; // goodbye bots - + /** * Command handler * (no bots / self) @@ -49,11 +48,10 @@ Type \`${config.prefix}new\` on the server to create a new ticket.`); const commandName = args.shift().toLowerCase(); const command = client.commands.get(commandName) || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName)); - + if (!command || commandName === 'none') return; // not an existing command - if (message.guild.id !== guild.id) - return message.reply(`This bot can only be used within the "${guild}" server`); // not in this server + if (message.guild.id !== guild.id) return message.reply(`This bot can only be used within the "${guild}" server`); // not in this server if (command.permission && !message.member.hasPermission(command.permission)) { log.console(`${message.author.tag} tried to use the '${command.name}' command without permission`); @@ -66,7 +64,7 @@ Type \`${config.prefix}new\` on the server to create a new ticket.`); ); } - if (command.args && !args.length) + if (command.args && !args.length) { return message.channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -74,9 +72,10 @@ Type \`${config.prefix}new\` on the server to create a new ticket.`); .addField('Help', `Type \`${config.prefix}help ${command.name}\` for more information`) .setFooter(guild.name, guild.iconURL()) ); + } if (!client.cooldowns.has(command.name)) client.cooldowns.set(command.name, new Collection()); - + const now = Date.now(); const timestamps = client.cooldowns.get(command.name); const cooldownAmount = (command.cooldown || config.cooldown) * 1000; diff --git a/src/events/messageDelete.js b/src/events/messageDelete.js index 9c102f0..cc304e7 100644 --- a/src/events/messageDelete.js +++ b/src/events/messageDelete.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -12,11 +12,10 @@ const fs = require('fs'); module.exports = { event: 'messageDelete', - async execute(client, [message], {config, Ticket}) { + async execute(_client, [message], {config, Ticket}) { + if (!config.transcripts.web.enabled) return; - if(!config.transcripts.web.enabled) return; - - if (message.partial) + if (message.partial) { try { await message.fetch(); } catch (err) { @@ -24,15 +23,15 @@ module.exports = { log.error(err.message); return; } + } let ticket = await Ticket.findOne({ where: { channel: message.channel.id } }); - if(!ticket) return; - + if (!ticket) return; + let path = `user/transcripts/raw/${message.channel.id}.log`; let embeds = []; - for (let embed in message.embeds) - embeds.push(message.embeds[embed].toJSON()); + for (let embed in message.embeds) embeds.push(message.embeds[embed].toJSON()); fs.appendFileSync(path, JSON.stringify({ id: message.id, @@ -44,6 +43,5 @@ module.exports = { edited: message.edits.length > 1, deleted: true // delete the message }) + '\n'); - } }; \ No newline at end of file diff --git a/src/events/messageReactionAdd.js b/src/events/messageReactionAdd.js index a1c212e..945a197 100644 --- a/src/events/messageReactionAdd.js +++ b/src/events/messageReactionAdd.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -14,28 +14,28 @@ const fs = require('fs'); module.exports = { event: 'messageReactionAdd', async execute(client, [r, u], {config, Ticket, Setting}) { - - if (r.partial) + if (r.partial) { try { await r.fetch(); } catch (err) { log.error(err); return; } + } let panelID = await Setting.findOne({ where: { key: 'panel_msg_id' } }); if (!panelID) return; - if(r.message.id !== panelID.get('value')) return; + if (r.message.id !== panelID.get('value')) return; - if(u.id === client.user.id) return; + if (u.id === client.user.id) return; if (r.emoji.name !== config.panel.reaction && r.emoji.id !== config.panel.reaction) return; let channel = r.message.channel; const supportRole = channel.guild.roles.cache.get(config.staff_role); - if (!supportRole) + if (!supportRole) { return channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -43,7 +43,7 @@ module.exports = { .setDescription(`${config.name} has not been set up correctly. Could not find a 'support team' role with the id \`${config.staff_role}\``) .setFooter(channel.guild.name, channel.guild.iconURL()) ); - + } // everything is cool @@ -56,7 +56,7 @@ module.exports = { }, limit: config.tickets.max }); - + if (tickets.count >= config.tickets.max) { let ticketList = []; for (let t in tickets.rows) { @@ -67,7 +67,6 @@ module.exports = { let dm = u.dmChannel || await u.createDM(); try { - return dm.send( new MessageEmbed() .setColor(config.err_colour) @@ -76,10 +75,7 @@ module.exports = { .setDescription(`Use \`${config.prefix}close\` in a server channel to close unneeded tickets.\n\n${ticketList.join(',\n')}`) .setFooter(channel.guild.name, channel.guild.iconURL()) ); - - } catch (e) { - let m = await channel.send( new MessageEmbed() .setColor(config.err_colour) @@ -88,11 +84,8 @@ module.exports = { .setDescription(`Use \`${config.prefix}close\` to close unneeded tickets.\n\n${ticketList.join(',\n')}`) .setFooter(channel.guild.name + ' | This message will be deleted in 15 seconds', channel.guild.iconURL()) ); - return m.delete({ timeout: 15000 }); } - - } let topic = 'No topic given (created via panel)'; @@ -130,7 +123,6 @@ module.exports = { ], reason: 'User requested a new support ticket channel' }).then(async c => { - Ticket.update({ channel: c.id }, { @@ -179,9 +171,8 @@ module.exports = { .setFooter(channel.guild.name, channel.guild.iconURL()) ); - if (config.tickets.pin) - await w.pin(); - // await w.pin().then(m => m.delete()); // oopsie, this deletes the pinned message + if (config.tickets.pin) await w.pin(); + // await w.pin().then(m => m.delete()); // oopsie, this deletes the pinned message if (config.logs.discord.enabled) client.channels.cache.get(config.logs.discord.channel).send( @@ -197,8 +188,6 @@ module.exports = { ); log.info(`${u.tag} created a new ticket (#${name}) via panel`); - - }).catch(log.error); } }; diff --git a/src/events/messageUpdate.js b/src/events/messageUpdate.js index fa1f2e1..598b651 100644 --- a/src/events/messageUpdate.js +++ b/src/events/messageUpdate.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -12,33 +12,33 @@ const fs = require('fs'); module.exports = { event: 'messageUpdate', - async execute(client, [o, n], {config, Ticket}) { + async execute(_client, [o, n], {config, Ticket}) { + if (!config.transcripts.web.enabled) return; - if(!config.transcripts.web.enabled) return; - - if (o.partial) + if (o.partial) { try { await o.fetch(); } catch (err) { log.error(err); return; } + } - if (n.partial) + if (n.partial) { try { await n.fetch(); } catch (err) { log.error(err); return; } + } let ticket = await Ticket.findOne({ where: { channel: n.channel.id } }); - if(!ticket) return; + if (!ticket) return; let path = `user/transcripts/raw/${n.channel.id}.log`; let embeds = []; - for (let embed in n.embeds) - embeds.push({ ...n.embeds[embed] }); + for (let embed in n.embeds) embeds.push({ ...n.embeds[embed] }); fs.appendFileSync(path, JSON.stringify({ id: n.id, @@ -49,7 +49,6 @@ module.exports = { attachments: [...n.attachments.values()], edited: true }) + '\n'); - } }; \ No newline at end of file diff --git a/src/events/rateLimit.js b/src/events/rateLimit.js index 3899ae9..b17e8fc 100644 --- a/src/events/rateLimit.js +++ b/src/events/rateLimit.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -11,7 +11,7 @@ const log = new ChildLogger(); module.exports = { event: 'rateLimit', - execute(client, [limit]) { + execute(_client, [limit]) { log.warn('Rate-limited! (Enable debug mode in config for details)'); log.debug(limit); } diff --git a/src/events/ready.js b/src/events/ready.js index d3c83c8..7203ebd 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -13,9 +13,8 @@ const config = require('../../user/' + require('../').config); module.exports = { event: 'ready', execute(client) { - log.success(`Authenticated as ${client.user.tag}`); - + const updatePresence = () => { let num = Math.floor(Math.random() * config.activities.length); client.user.setPresence({ @@ -26,17 +25,15 @@ module.exports = { }).catch(log.error); log.debug(`Updated presence: ${config.activity_types[num]} ${config.activities[num]}`); }; - + updatePresence(); setInterval(() => { updatePresence(); }, 60000); - - - if (client.guilds.cache.get(config.guild).member(client.user).hasPermission('ADMINISTRATOR', false)) + + + if (client.guilds.cache.get(config.guild).member(client.user).hasPermission('ADMINISTRATOR', false)) { log.success('\'ADMINISTRATOR\' permission has been granted'); - else - log.warn('Bot does not have \'ADMINISTRATOR\' permission'); - + } else log.warn('Bot does not have \'ADMINISTRATOR\' permission'); } }; diff --git a/src/events/warn.js b/src/events/warn.js index 77d7ac8..27e6c1b 100644 --- a/src/events/warn.js +++ b/src/events/warn.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -11,7 +11,7 @@ const log = new ChildLogger(); module.exports = { event: 'warn', - execute(client, [e]) { + execute(_client, [e]) { log.warn(e); } }; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 744d890..3068680 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const fs = require('fs'); const path = require('path'); @@ -40,7 +40,6 @@ log.multi(log); // required to allow other files to access the logger require('./modules/updater')(); // check for updates - /** * storage */ @@ -48,14 +47,40 @@ const { Sequelize, Model, DataTypes } = require('sequelize'); let sequelize; -if(config.storage.type === 'mysql') { +switch (config.storage.type) { +case 'mysql': log.info('Connecting to MySQL database...'); sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, { dialect: 'mysql', host: process.env.DB_HOST, logging: log.debug }); -} else { + break; +case 'mariadb': + log.info('Connecting to MariaDB database...'); + sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, { + dialect: 'mariadb', + host: process.env.DB_HOST, + logging: log.debug + }); + break; +case 'postgres': + log.info('Connecting to PostgresSQL database...'); + sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, { + dialect: 'postgres', + host: process.env.DB_HOST, + logging: log.debug + }); + break; +case 'microsoft': + log.info('Connecting to Microsoft SQL Server database...'); + sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASS, { + dialect: 'mssql', + host: process.env.DB_HOST, + logging: log.debug + }); + break; +default: log.info('Using SQLite storage'); sequelize = new Sequelize({ dialect: 'sqlite', @@ -90,7 +115,7 @@ Setting.sync(); /** * event loader */ -const events = fs.readdirSync('src/events').filter(file => file.endsWith('.js')); +const events = fs.readdirSync('src/events').filter(file => file.endsWith('.js')); for (const file of events) { const event = require(`./events/${file}`); client.events.set(event.event, event); @@ -102,12 +127,12 @@ for (const file of events) { /** * command loader */ -const commands = fs.readdirSync('src/commands').filter(file => file.endsWith('.js')); +const commands = fs.readdirSync('src/commands').filter(file => file.endsWith('.js')); for (const file of commands) { const command = require(`./commands/${file}`); client.commands.set(command.name, command); log.console(log.format(`> Loaded &7${config.prefix}${command.name}&f command`)); -} +} log.info(`Loaded ${events.length} events and ${commands.length} commands`); @@ -123,8 +148,7 @@ const clean = () => { total++; } } - if (total > 0) - log.info(`Deleted ${total} old text ${utils.plural('transcript', total)}`); + if (total > 0) log.info(`Deleted ${total} old text ${utils.plural('transcript', total)}`); }; if (config.transcripts.text.enabled) { diff --git a/src/modules/archive.js b/src/modules/archive.js index 8f995cd..f8f1c16 100644 --- a/src/modules/archive.js +++ b/src/modules/archive.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ @@ -17,7 +17,7 @@ const fetch = require('node-fetch'); module.exports.add = (message) => { - if(message.type !== 'DEFAULT') return; + if (message.type !== 'DEFAULT') return; if (config.transcripts.text.enabled) { // text transcripts let path = `user/transcripts/text/${message.channel.id}.txt`, @@ -33,8 +33,7 @@ module.exports.add = (message) => { json = `user/transcripts/raw/entities/${message.channel.id}.json`; let embeds = []; - for (let embed in message.embeds) - embeds.push({ ...message.embeds[embed] }); + for (let embed in message.embeds) embeds.push({ ...message.embeds[embed] }); // message fs.appendFileSync(raw, JSON.stringify({ @@ -100,15 +99,14 @@ module.exports.export = (Ticket, channel) => new Promise((resolve, reject) => { channel: channel.id } }); - + let raw = `user/transcripts/raw/${channel.id}.log`, json = `user/transcripts/raw/entities/${channel.id}.json`; - if (!config.transcripts.web.enabled || !fs.existsSync(raw) || !fs.existsSync(json)) - return reject(false); - + if (!config.transcripts.web.enabled || !fs.existsSync(raw) || !fs.existsSync(json)) return reject(false); + let data = JSON.parse(fs.readFileSync(json)); - + data.ticket = { id: ticket.id, name: channel.name, @@ -122,15 +120,12 @@ module.exports.export = (Ticket, channel) => new Promise((resolve, reject) => { lineReader.eachLine(raw, line => { let message = JSON.parse(line); let index = data.messages.findIndex(m => m.id === message.id); - if (index === -1) - data.messages.push(message); - else - data.messages[index] = message; + if (index === -1) data.messages.push(message); + else data.messages[index] = message; }, () => { let endpoint = config.transcripts.web.server; - if (endpoint[endpoint.length - 1] === '/') - endpoint = endpoint.slice(0, -1); + if (endpoint[endpoint.length - 1] === '/') endpoint = endpoint.slice(0, -1); endpoint += `/${data.ticket.creator}/${data.ticket.channel}/?key=${process.env.ARCHIVES_KEY}`; @@ -156,8 +151,6 @@ module.exports.export = (Ticket, channel) => new Promise((resolve, reject) => { log.warn(e); return resolve(e); }); - }); - + }); })(); - }); diff --git a/src/modules/banner.js b/src/modules/banner.js index 2397c84..e686532 100644 --- a/src/modules/banner.js +++ b/src/modules/banner.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const { version, homepage } = require('../../package.json'); diff --git a/src/modules/updater.js b/src/modules/updater.js index 987e443..ee91645 100644 --- a/src/modules/updater.js +++ b/src/modules/updater.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ const ChildLogger = require('leekslazylogger').ChildLogger; @@ -15,32 +15,27 @@ version = 'v' + version; const boxen = require('boxen'); const link = require('terminal-link'); -module.exports = () => { - if(!config.updater) - return; - - fetch('https://api.github.com/repos/eartharoid/DiscordTickets/releases') - .then(res => res.json()) - .then(json => { - const update = json[0]; - let notice = []; +module.exports = async () => { + if (!config.updater) return; + const json = await (await fetch('https://api.github.com/repos/eartharoid/DiscordTickets/releases')).json(); + const update = json[0]; + let notice = []; - if (version !== update.tag_name) { - log.notice(log.f(`There is an update available for Discord Tickets (${version} -> ${update.tag_name})`)); + if (version !== update.tag_name) { + log.notice(log.f(`There is an update available for Discord Tickets (${version} -> ${update.tag_name})`)); - notice.push(`&6You are currently using &c${version}&6, the latest is &a${update.tag_name}&6.`); - notice.push(`&6Download "&f${update.name}&6" from`); - notice.push(link('&6the GitHub releases page', 'https://github.com/eartharoid/DiscordTickets/releases/')); + notice.push(`&6You are currently using &c${version}&6, the latest is &a${update.tag_name}&6.`); + notice.push(`&6Download "&f${update.name}&6" from`); + notice.push(link('&6the GitHub releases page', 'https://github.com/eartharoid/DiscordTickets/releases/')); - console.log( - boxen(log.f(notice.join('\n')), { - padding: 1, - margin: 1, - align: 'center', - borderColor: 'yellow', - borderStyle: 'round' - }) - ); - } - }); + console.log( + boxen(log.f(notice.join('\n')), { + padding: 1, + margin: 1, + align: 'center', + borderColor: 'yellow', + borderStyle: 'round' + }) + ); + } }; \ No newline at end of file diff --git a/src/modules/utils.js b/src/modules/utils.js index d86de48..3eeecc1 100644 --- a/src/modules/utils.js +++ b/src/modules/utils.js @@ -1,9 +1,9 @@ /** - * + * * @name DiscordTickets * @author eartharoid * @license GNU-GPLv3 - * + * */ module.exports = { diff --git a/user/config.js b/user/config.js index ead0ac6..0b60aad 100644 --- a/user/config.js +++ b/user/config.js @@ -5,24 +5,24 @@ * | | | | | | / __| / __| / _ \ | '__| / _` | | | | | / __| | |/ / / _ \ | __| / __| * | |_| | | | \__ \ | (__ | (_) | | | | (_| | | | | | | (__ | < | __/ | |_ \__ \ * |____/ |_| |___/ \___| \___/ |_| \__,_| |_| |_| \___| |_|\_\ \___| \__| |___/ - * + * * --------------------- * Quick Start * --------------------- - * + * * > For detailed instructions, visit the GitHub repository and read the documentation: * https://github.com/eartharoid/DiscordTickets/wiki - * + * * > IMPORTANT: Also edit the TOKEN in 'user/.env' - * + * * --------------------- * Support * --------------------- - * + * * > Information: https://github.com/eartharoid/DiscordTickets/#readme * > Discord Support Server: https://go.eartharoid.me/discord * > Wiki: https://github.com/eartharoid/DiscordTickets/wiki - * + * * ############################################################################################### */ @@ -80,7 +80,7 @@ module.exports = { channel: '' // ID of your log channel } }, - + debug: false, updater: true }; diff --git a/user/images/how-can-i-help-1.gif b/user/images/how-can-i-help-1.gif index 703b5e8..185fde1 100644 Binary files a/user/images/how-can-i-help-1.gif and b/user/images/how-can-i-help-1.gif differ diff --git a/user/images/how-can-i-help-2.gif b/user/images/how-can-i-help-2.gif index ef8d279..181a57e 100644 Binary files a/user/images/how-can-i-help-2.gif and b/user/images/how-can-i-help-2.gif differ diff --git a/user/images/how-can-we-help.gif b/user/images/how-can-we-help.gif index 171836b..b57bd7d 100644 Binary files a/user/images/how-can-we-help.gif and b/user/images/how-can-we-help.gif differ