diff --git a/package.json b/package.json index 113fbb1..74d158f 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dotenv": "^8.2.0", "leeks.js": "^0.0.9", "leekslazylogger-fastify": "^0.1.0", + "node-emoji": "^1.10.0", "node-fetch": "^2.6.1", "semver": "^7.3.4", "sequelize": "^6.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b997a0d..1650f97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,7 @@ specifiers: leekslazylogger-fastify: ^0.1.0 mariadb: ^2.5.2 mysql2: ^2.2.5 + node-emoji: ^1.10.0 node-fetch: ^2.6.1 nodemon: ^2.0.7 pg: ^8.5.1 @@ -32,6 +33,7 @@ dependencies: dotenv: 8.2.0 leeks.js: 0.0.9 leekslazylogger-fastify: 0.1.0 + node-emoji: 1.10.0 node-fetch: 2.6.1 semver: 7.3.4 sequelize: 6.5.0_fb66e8c649bde1be622cc06164c9e22d @@ -1481,6 +1483,10 @@ packages: resolution: {integrity: sha1-soqmKIorn8ZRA1x3EfZathkDMaY=} dev: false + /lodash.toarray/4.4.0: + resolution: {integrity: sha1-JMS/zWsvuji/0FlNsRedjptlZWE=} + dev: false + /lodash/4.17.20: resolution: {integrity: sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==} @@ -1648,6 +1654,12 @@ packages: dev: false optional: true + /node-emoji/1.10.0: + resolution: {integrity: sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==} + dependencies: + lodash.toarray: 4.4.0 + dev: false + /node-fetch/2.6.1: resolution: {integrity: sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==} engines: {node: 4.x || >=6.0.0} diff --git a/src/commands/panel.js b/src/commands/panel.js index 0684840..f36c67c 100644 --- a/src/commands/panel.js +++ b/src/commands/panel.js @@ -1,5 +1,7 @@ const Command = require('../modules/commands/command'); const { MessageEmbed } = require('discord.js'); +const { some, wait } = require('../utils'); +const { emojify } = require('node-emoji'); module.exports = class PanelCommand extends Command { constructor(client) { @@ -40,12 +42,12 @@ module.exports = class PanelCommand extends Command { multiple: true, }, { - name: i18n('commands.panel.args.category.name'), - description: i18n('commands.panel.args.category.description'), - example: i18n('commands.panel.args.category.example'), + name: i18n('commands.panel.args.categories.name'), + description: i18n('commands.panel.args.categories.description'), + example: i18n('commands.panel.args.categories.example'), required: true, // for arg parsing - alias: i18n('commands.panel.args.category.alias'), + alias: i18n('commands.panel.args.categories.alias'), type: String, multiple: true, } @@ -55,30 +57,145 @@ module.exports = class PanelCommand extends Command { } async execute(message, args) { + // localised command and arg names are a pain const arg_title = this.args[0].name; const arg_description = this.args[1].name; const arg_emoji = this.args[2].name; - const arg_category = this.args[3].name; + const arg_categories = this.args[3].name; let settings = await message.guild.settings; const i18n = this.client.i18n.getLocale(settings.locale); - console.log(args) - message.channel.send(Object.keys(args).map(arg => `${arg}: \`${args[arg]}\``).join('\n')) - console.log(args.category) + if (!args[arg_emoji]) + args[arg_emoji] = []; + + args[arg_emoji] = args[arg_emoji].map(emoji => emojify(emoji.replace(/\\/g, ''))); - if (!args[arg_emoji]) { + const invalid_category = await some(args[arg_categories], async id => { + let cat_row = await this.client.db.models.Category.findOne({ + where: { + id: id, + guild: message.guild.id + } + }); + return !cat_row; + }); + + if (invalid_category) { + return await message.channel.send( + new MessageEmbed() + .setColor(settings.error_colour) + .setTitle(i18n('commands.panel.response.invalid_category.title')) + .setDescription(i18n('commands.panel.response.invalid_category.description')) + .setFooter(settings.footer, message.guild.iconURL()) + ); + } + + let panel_channel, + panel_message; + + let embed = new MessageEmbed() + .setColor(settings.colour) + .setFooter(settings.footer, message.guild.iconURL()); + + if (args[arg_title]) + embed.setTitle(args[arg_title]); + + if (args[arg_emoji].length === 0) { // reaction-less panel + panel_channel = await message.guild.channels.create('create-a-ticket', { + type: 'text', + rateLimitPerUser: 30, + permissionOverwrites: [ + { + id: message.guild.roles.everyone, + allow: ['VIEW_CHANNEL', 'SEND_MESSAGES', 'READ_MESSAGE_HISTORY'], + deny: ['ATTACH_FILES', 'EMBED_LINKS', 'ADD_REACTIONS'] + }, + { + id: this.client.user.id, + allow: ['EMBED_LINKS'] + } + ], + position: 1, + reason: `${message.author.tag} created a new reaction-less panel` + }); + + embed.setDescription(args[arg_description]); + panel_message = await panel_channel.send(embed); + + this.client.log.info(`${message.author.tag} has created a new reaction-less panel`); } else { - if (!args[arg_category] || args[arg_category].length !== args[arg_emoji.length]) { + if (args[arg_categories].length !== args[arg_emoji].length) { // send error + return await message.channel.send( + new MessageEmbed() + .setColor(settings.error_colour) + .setTitle(i18n('commands.panel.response.mismatch.title')) + .setDescription(i18n('commands.panel.response.mismatch.description')) + .setFooter(settings.footer, message.guild.iconURL()) + ); } else { + panel_channel = await message.guild.channels.create('create-a-ticket', { + type: 'text', + permissionOverwrites: [ + { + id: message.guild.roles.everyone, + allow: ['VIEW_CHANNEL', 'READ_MESSAGE_HISTORY'], + deny: ['SEND_MESSAGES', 'ADD_REACTIONS'] + }, + { + id: this.client.user.id, + allow: ['SEND_MESSAGES', 'EMBED_LINKS'] + } + ], + position: 1, + reason: `${message.author.tag} created a new panel` + }); + if (args[arg_emoji].length === 1) { // single category + embed.setDescription(args[arg_description]); + panel_message = await panel_channel.send(embed); + await panel_message.react(args[arg_emoji][0]); } else { // multi category + + let emoji_map = {}; + let description = ''; + + for (let i in args[arg_emoji]) { + emoji_map[args[arg_emoji][i]] = args[arg_categories][i]; + let cat_row = await this.client.db.models.Category.findOne({ + where: { + id: args[arg_categories][i], + guild: message.guild.id + } + }); + description += `\n> ${args[arg_emoji][i]} | ${cat_row.name}`; + } + + embed.setDescription(args[arg_description] + '\n' + description); + panel_message = await panel_channel.send(embed); + + for (let emoji of args[arg_emoji]) { + await panel_message.react(emoji); + await wait(1000); // 1 reaction per second rate-limit + } + } + + this.client.log.info(`${message.author.tag} has created a new panel`); } } + + message.channel.send(`✅ ${panel_channel}`); + + await this.client.db.models.Panel.create({ + categories: args[arg_categories], + channel: panel_channel.id, + guild: message.guild.id, + message: panel_message.id, + }); } }; \ No newline at end of file diff --git a/src/database/models/panel.model.js b/src/database/models/panel.model.js index 83080ed..870bd9a 100644 --- a/src/database/models/panel.model.js +++ b/src/database/models/panel.model.js @@ -6,6 +6,10 @@ module.exports = (client, sequelize) => { type: DataTypes.JSON, allowNull: false }, + channel: { + type: DataTypes.CHAR(19), + allowNull: false + }, guild: { type: DataTypes.CHAR(19), allowNull: false, @@ -17,10 +21,6 @@ module.exports = (client, sequelize) => { message: { type: DataTypes.CHAR(19), allowNull: false - }, - reactionless: { - type: DataTypes.BOOLEAN, - defaultValue: false } }, { tableName: DB_TABLE_PREFIX + 'panels' diff --git a/src/listeners/message.js b/src/listeners/message.js index 1b02c7e..b3653fa 100644 --- a/src/listeners/message.js +++ b/src/listeners/message.js @@ -23,6 +23,16 @@ module.exports = { first_response: new Date() }); } + } else { + let p_row = await client.db.models.Panel.findOne({ + where: { + channel: message.channel.id + } + }); + + if (p_row) { + // handle reaction-less panel + } } client.commands.handle(message); // pass the message to the command handler diff --git a/src/locales/en-GB.json b/src/locales/en-GB.json index c8bc1a5..a574326 100644 --- a/src/locales/en-GB.json +++ b/src/locales/en-GB.json @@ -157,11 +157,11 @@ "panel": { "aliases": {}, "args": { - "category": { + "categories": { "alias": "c", "description": "A category ID", "example": "451745464954650634", - "name": "category" + "name": "categories" }, "description": { "alias": "d", @@ -171,7 +171,7 @@ }, "emoji": { "alias": "e", - "description": "A **unicode** emoji character (escaped emoji)", + "description": "An emoji", "example": "🎫", "name": "emoji" }, @@ -184,7 +184,16 @@ }, "description": "Create a new ticket panel", "name": "panel", - "response": {} + "response": { + "invalid_category": { + "description": "One or more of the specified category IDs is invalid.", + "title": "❌ Invalid category" + }, + "mismatch": { + "description": "Please provide the name number of emojis and category IDs.", + "title": "❌ Invalid input" + } + } }, "settings": { "aliases": { diff --git a/src/utils/index.js b/src/utils/index.js index d544ab4..6bb6db0 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,4 +1,10 @@ module.exports = { int2hex: (int) => int.toString(16).toUpperCase(), + some: async (array, func) => { + for (let element of array) { + if (await func(element)) return true; + } + return false; + }, wait: (time) => new Promise(res => setTimeout(res, time)), }; \ No newline at end of file