diff --git a/src/commands/new.js b/src/commands/new.js index 72efd93..89e4744 100644 --- a/src/commands/new.js +++ b/src/commands/new.js @@ -18,7 +18,7 @@ module.exports = class NewCommand extends Command { name: i18n('commands.new.args.topic.name'), description: i18n('commands.new.args.topic.description'), example: i18n('commands.new.args.topic.example'), - required: true, + required: false, } ] }); diff --git a/src/index.js b/src/index.js index 3fbc0a6..cc633e9 100644 --- a/src/index.js +++ b/src/index.js @@ -66,7 +66,7 @@ const { selectPresence } = require('./utils/discord'); const I18n = require('@eartharoid/i18n'); const CommandManager = require('./modules/commands/manager'); const PluginManager = require('./modules/plugins/manager'); -const TicketManager = require('./modules/tickets'); +const TicketManager = require('./modules/tickets/manager'); require('./modules/structures')(); // load extended structures before creating the client diff --git a/src/listeners/message.js b/src/listeners/message.js index cb0630e..9773eb5 100644 --- a/src/listeners/message.js +++ b/src/listeners/message.js @@ -4,32 +4,9 @@ module.exports = { let settings = await message.guild?.settings; - // message collection for t_row archiving if (settings?.log_messages) { if (message.system) return; - - let t_row = await client.db.models.Ticket.findOne({ - where: { - id: message.channel.id - } - }); - - if (t_row) { - let embeds = []; - for (let embed in message.embeds) embeds.push({ ...message.embeds[embed] }); - - await client.db.models.Message.create({ - id: message.id, - ticket: t_row.id, - author: message.author.id, - data: { - content: message.content, - // time: message.createdTimestamp, - embeds, - attachments: [...message.attachments.values()] - } - }); - } + client.tickets.archives.addMessage(message); } client.commands.handle(message); diff --git a/src/listeners/messageDelete.js b/src/listeners/messageDelete.js index 4302acc..1fed7fc 100644 --- a/src/listeners/messageDelete.js +++ b/src/listeners/messageDelete.js @@ -6,17 +6,7 @@ module.exports = { if (settings?.log_messages) { if (message.system) return; - - let msg = await client.db.models.Message.findOne({ - where: { - id: message.id - } - }); - - if (msg) { - msg.deleted = true; - await msg.save(); // save changes to message row - } + client.tickets.archives.deleteMessage(message); } } }; \ No newline at end of file diff --git a/src/listeners/messageUpdate.js b/src/listeners/messageUpdate.js index b81d768..6f2cd21 100644 --- a/src/listeners/messageUpdate.js +++ b/src/listeners/messageUpdate.js @@ -14,30 +14,7 @@ module.exports = { if (settings?.log_messages) { if (newm.system) return; - - let m_row = await client.db.models.Message.findOne({ - where: { - id: newm.id - } - }); - - if (m_row) { - let embeds = []; - for (let embed in newm.embeds) embeds.push({ ...newm.embeds[embed] }); - - m_row.data = { - content: newm.content, - // time: newm.editedTimestamp, - embeds: newm.embeds.map(embed => { - return { ...newm.embeds[embed] }; - }), - attachments: [...newm.attachments.values()] - }; - - if (newm.editedTimestamp) m_row.edited = true; - - await m_row.save(); // save changes - } + client.tickets.archives.updateMessage(newm); } } }; \ No newline at end of file diff --git a/src/locales/en-GB.json b/src/locales/en-GB.json index c6647d0..5f25ab1 100644 --- a/src/locales/en-GB.json +++ b/src/locales/en-GB.json @@ -8,7 +8,7 @@ "example": "**Example:**" }, "description": "**Usage:**\n`%s`\n\n**Example:**\n`%s`\n\nRequired arguments are prefixed with `❗`.", - "named_args_description": "This command uses named arguments.\n\n", + "named_args": "This command uses named arguments.\n\n", "title": "`%s` command usage" }, "commands": { diff --git a/src/modules/commands/manager.js b/src/modules/commands/manager.js index 12aa220..8f4248b 100644 --- a/src/modules/commands/manager.js +++ b/src/modules/commands/manager.js @@ -99,7 +99,7 @@ module.exports = class CommandManager { let embed = new MessageEmbed() .setColor(settings.error_colour) .setTitle(i18n('cmd_usage.title', cmd_name)) - .setDescription(i18n('cmd_usage.named_args_description') + i18n('cmd_usage.description', usage, example)); + .setDescription(i18n('cmd_usage.named_args') + i18n('cmd_usage.description', usage, example)); cmd.args.forEach(a => addArgs(embed, a)); return message.channel.send(embed); } diff --git a/src/modules/tickets/archives.js b/src/modules/tickets/archives.js new file mode 100644 index 0000000..2574285 --- /dev/null +++ b/src/modules/tickets/archives.js @@ -0,0 +1,165 @@ +/** Manages ticket archiving */ +module.exports = class TicketArchives { + /** + * Create a TicketArchives instance + * @param {Client} client + */ + constructor(client) { + + /** The Discord Client */ + this.client = client; + + } + + async addMessage(message) { + let t_row = await this.client.db.models.Ticket.findOne({ + where: { + id: message.channel.id + } + }); + + if (t_row) { + let embeds = []; + for (let embed in message.embeds) embeds.push({ ...message.embeds[embed] }); + + await this.client.db.models.Message.create({ + id: message.id, + ticket: t_row.id, + author: message.author.id, + data: { + content: message.content, + // time: message.createdTimestamp, + embeds, + attachments: [...message.attachments.values()] + } + }); + + this.updateEntities(message); + } + } + + async updateMessage(message) { + let m_row = await this.client.db.models.Message.findOne({ + where: { + id: message.id + } + }); + + if (m_row) { + let embeds = []; + for (let embed in message.embeds) embeds.push({ ...message.embeds[embed] }); + + m_row.data = { + content: message.content, + // time: message.editedTimestamp, + embeds: message.embeds.map(embed => { + return { ...message.embeds[embed] }; + }), + attachments: [...message.attachments.values()] + }; + + if (message.editedTimestamp) { + m_row.edited = true; + this.updateEntities(message); + } + + await m_row.save(); // save changes + } + } + + async deleteMessage(message) { + let msg = await this.client.db.models.Message.findOne({ + where: { + id: message.id + } + }); + + if (msg) { + msg.deleted = true; + await msg.save(); // save changes to message row + } + } + + + async updateEntities(message) { + let m_row = await this.client.db.models.Message.findOne({ + where: { + id: message.id + } + }); + + if (m_row) { + // message author + let u_model_data = { + user: message.author.id, + ticket: message.channel.id + }; + let [u_row] = await this.client.db.models.UserEntity.findOrCreate({ + where: u_model_data, + defaults: u_model_data + }); + await u_row.update({ + avatar: message.author.displayAvatarURL(), + username: message.author.username, + discriminator: message.author.discriminator, + display_name: message.member.displayName, + colour: message.member.displayColor === 0 ? null : message.member.displayColor, + bot: message.author.bot + }); + + // mentioned members + message.mentions.members.forEach(async member => { + let m_model_data = { + user: member.user.id, + ticket: message.channel.id + }; + let [m_row] = await this.client.db.models.UserEntity.findOrCreate({ + where: m_model_data, + defaults: m_model_data + }); + await m_row.update({ + avatar: member.user.displayAvatarURL(), + username: member.user.username, + discriminator: member.user.discriminator, + display_name: member.displayName, + colour: member.displayColor === 0 ? null : member.displayColor, + bot: member.user.bot + }); + }); + + // mentioned channels + message.mentions.channels.forEach(async channel => { + let c_model_data = { + channel: channel.id, + ticket: message.channel.id + }; + let [c_row] = await this.client.db.models.ChannelEntity.findOrCreate({ + where: c_model_data, + defaults: c_model_data + }); + await c_row.update({ + name: channel.name + }); + }); + + // mentioned roles + message.mentions.roles.forEach(async role => { + let r_model_data = { + role: role.id, + ticket: message.channel.id + }; + let [r_row] = await this.client.db.models.RoleEntity.findOrCreate({ + where: r_model_data, + defaults: r_model_data + }); + await r_row.update({ + name: role.name, + colour: role.color === 0 ? 7506394 : role.color + }); + }); + } + + } + + +}; \ No newline at end of file diff --git a/src/modules/tickets.js b/src/modules/tickets/manager.js similarity index 62% rename from src/modules/tickets.js rename to src/modules/tickets/manager.js index a49d8d1..f14807b 100644 --- a/src/modules/tickets.js +++ b/src/modules/tickets/manager.js @@ -1,12 +1,11 @@ const EventEmitter = require('events'); -// eslint-disable-next-line no-unused-vars -const { Client } = require('discord.js'); +const TicketArchives = require('./archives'); /** Manages tickets */ module.exports = class TicketManager extends EventEmitter { /** * Create a TicketManager instance - * @param {Client} client + * @param {Client} client */ constructor(client) { super(); @@ -15,6 +14,8 @@ module.exports = class TicketManager extends EventEmitter { this.client = client; this.setMaxListeners(this.client.config.max_listeners); + + this.archives = new TicketArchives(this.client); } /** @@ -55,6 +56,13 @@ module.exports = class TicketManager extends EventEmitter { reason: `${member.tag} requested a new ticket channel` }); + await t_channel.updateOverwrite(creator_id, { + VIEW_CHANNEL: true, + READ_MESSAGE_HISTORY: true, + SEND_MESSAGES: true, + ATTACH_FILES: true + }, `Ticket channel created by ${member.user.tag}`); + let t_row = await this.client.db.models.Ticket.create({ id: t_channel.id, number, @@ -69,60 +77,54 @@ module.exports = class TicketManager extends EventEmitter { /** * Get a ticket - * @param {(string|number)} ticket - The channel ID, or the ticket number + * @param {(string|number)} ticket_id - The channel ID, or the ticket number * @param {string} guild_id - The ID of the ticket's guild (used if a ticket number is provided instead of ID) */ - async get(ticket, guild_id) { - let row = await this.resolveTicket(ticket, guild_id); - if (!row) throw new Error(`Could not find a ticket with ID ${ticket}`); + async get(ticket_id, guild_id) { + return await this.resolve(ticket_id, guild_id); } /** * Close a ticket - * @param {(string|number)} ticket - The channel ID, or the ticket number - * @param {(string|null)} closer_id - ID of the member who is closing the ticket, or null + * @param {(string|number)} ticket_id - The channel ID, or the ticket number + * @param {string?} closer_id - ID of the member who is closing the ticket, or null * @param {string} [guild_id] - The ID of the ticket's guild (used if a ticket number is provided instead of ID) */ - async close(ticket, closer_id, guild_id) { - let row = await this.resolveTicket(ticket, guild_id); - if (!row) throw new Error(`Could not find a ticket with ID ${ticket}`); + async close(ticket_id, closer_id, guild_id) { + let t_row = await this.resolve(ticket_id, guild_id); + if (!t_row) throw new Error(`Could not find a ticket with ID ${ticket_id}`); + ticket_id = t_row.id; - this.emit('beforeClose', ticket, closer_id); + this.emit('beforeClose', ticket_id, closer_id); - /** - * - * - * for each message of ticket, create entities - * - * - */ + // ... - this.emit('close', ticket, closer_id); + this.emit('close', ticket_id, closer_id); } /** * - * @param {(string|number)} ticket - ID or number of the ticket + * @param {(string|number)} ticket_id - ID or number of the ticket * @param {string} [guild_id] - The ID of the ticket's guild (used if a ticket number is provided instead of ID) */ - async resolve(ticket, guild_id) { - if (!this.client.channels.resolve(ticket)) { - let row = await this.client.db.models.Ticket.findOne({ + async resolve(ticket_id, guild_id) { + if (!this.client.channels.resolve(ticket_id)) { + let t_row = await this.client.db.models.Ticket.findOne({ where: { - number: ticket, + number: ticket_id, guild_id } }); - ticket = row?.id; + ticket_id = t_row?.id; } - let row = await this.client.db.models.Ticket.findOne({ + let t_row = await this.client.db.models.Ticket.findOne({ where: { - id: ticket + id: ticket_id } }); - return row; + return t_row; } }; \ No newline at end of file diff --git a/user/example.config.js b/user/example.config.js index 57a0d66..3aadcb8 100644 --- a/user/example.config.js +++ b/user/example.config.js @@ -16,11 +16,13 @@ * ############################################################################################### */ +const prefix = '-'; + module.exports = { debug: false, defaults: { colour: '#009999', // https://discord.js.org/#/docs/main/stable/typedef/ColorResolvable - command_prefix: 'tickets/', + command_prefix: prefix, log_messages: true, // transcripts/archives will be empty if false name_format: 'ticket-{number}', ticket_welcome: 'Hello {name}, thank you for creating a ticket. A member of staff will soon be available to assist you.\n\n__All messages in this channel are stored for future reference.__', @@ -36,9 +38,8 @@ module.exports = { presence: { presences: [ { - activity: '/new', - type: 'PLAYING', - status: 'online' + activity: `${prefix}new`, + type: 'PLAYING' }, { activity: 'with tickets',