diff --git a/src/commands/_settings.js b/src/commands/_settings.js index 13a7c81..c15d3da 100644 --- a/src/commands/_settings.js +++ b/src/commands/_settings.js @@ -44,6 +44,8 @@ module.exports = class SettingsCommand extends Command { }); category.name = c.name; category.roles = c.roles; + category.max_per_member = c.max_per_member; + category.name_format = c.name_format; category.save(); let cat_channel = await this.client.channels.fetch(c.id); @@ -67,6 +69,7 @@ module.exports = class SettingsCommand extends Command { let cat_channel = await guild.channels.create(c.name, { type: 'category', reason: `Tickets category created by ${member.user.tag}`, + position: 0, permissionOverwrites: [ ...[ { @@ -88,9 +91,11 @@ module.exports = class SettingsCommand extends Command { }); await this.client.db.models.Category.create({ id: cat_channel.id, + max_per_member: c.max_per_member, name: c.name, + name_format: c.name_format, guild: guild.id, - roles: c.roles + roles: c.roles, }); } @@ -119,7 +124,9 @@ module.exports = class SettingsCommand extends Command { data.categories = categories.map(c =>{ return { id: c.id, + max_per_member: c.max_per_member, name: c.name, + name_format: c.name_format, roles: c.roles }; }); diff --git a/src/commands/new.js b/src/commands/new.js index 6c8fa20..e303edf 100644 --- a/src/commands/new.js +++ b/src/commands/new.js @@ -9,6 +9,7 @@ module.exports = class NewCommand extends Command { internal: true, name: i18n('commands.new.name'), description: i18n('commands.new.description'), + // slash: false, options: [ { name: i18n('commands.new.options.category.name'), @@ -37,5 +38,8 @@ module.exports = class NewCommand extends Command { .setTitle(i18n('bot.version', require('../../package.json').version)) .setDescription(args.topic) ); + + // this.client.tickets.create(guild.id, member.id, args.category, args.topic); + this.client.tickets.create(guild.id, member.id, '825861413687787560'); } }; \ No newline at end of file diff --git a/src/database/index.js b/src/database/index.js index 09418ce..1e5b006 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -101,7 +101,7 @@ module.exports = async (log) => { name: { type: DataTypes.STRING, allowNull: false, - unique: 'name_guild' + unique: 'name-guild' }, guild: { type: DataTypes.CHAR(18), @@ -110,10 +110,19 @@ module.exports = async (log) => { model: Guild, key: 'id' }, - unique: 'name_guild' + unique: 'name-guild' }, roles: { type: DataTypes.JSON + }, + max_per_member: { + type: DataTypes.INTEGER, + defaultValue: 1 + }, + name_format: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: config.defaults.name_format } }, { tableName: DB_TABLE_PREFIX + 'categories' @@ -127,8 +136,8 @@ module.exports = async (log) => { }, number: { type: DataTypes.INTEGER, - autoIncrement: true, allowNull: false, + unique: 'number-guild' }, guild: { type: DataTypes.CHAR(18), @@ -136,7 +145,8 @@ module.exports = async (log) => { references: { model: Guild, key: 'id' - }, + }, + unique: 'number-guild' }, category: { type: DataTypes.CHAR(18), @@ -150,6 +160,10 @@ module.exports = async (log) => { type: DataTypes.CHAR(18), allowNull: false, }, + open: { + type: DataTypes.BOOLEAN, + defaultValue: true + } }, { tableName: DB_TABLE_PREFIX + 'tickets' }); @@ -193,12 +207,12 @@ module.exports = async (log) => { user: { type: DataTypes.CHAR(18), allowNull: false, - unique: 'id_ticket' + unique: 'id-ticket' }, ticket: { type: DataTypes.CHAR(18), allowNull: false, - unique: 'id_ticket', + unique: 'id-ticket', references: { model: Ticket, key: 'id' @@ -219,12 +233,12 @@ module.exports = async (log) => { channel: { type: DataTypes.CHAR(18), allowNull: false, - unique: 'id_ticket' + unique: 'id-ticket' }, ticket: { type: DataTypes.CHAR(18), allowNull: false, - unique: 'id_ticket', + unique: 'id-ticket', references: { model: Ticket, key: 'id' @@ -240,12 +254,12 @@ module.exports = async (log) => { role: { type: DataTypes.CHAR(18), allowNull: false, - unique: 'id_ticket' + unique: 'id-ticket' }, ticket: { type: DataTypes.CHAR(18), allowNull: false, - unique: 'id_ticket', + unique: 'id-ticket', references: { model: Ticket, key: 'id' diff --git a/src/locales/en-GB.json b/src/locales/en-GB.json index 3641d24..08a02dd 100644 --- a/src/locales/en-GB.json +++ b/src/locales/en-GB.json @@ -1,5 +1,4 @@ { - "admin_only": "❌ You must be an administrator to use this command.", "bot": { "version": "Discord Tickets v%s by eartharoid" }, @@ -28,5 +27,5 @@ }, "must_be_slash": "❌ This command must be invoked by a slash command interaction (`/%s`).", "no_perm": "❌ You do not have the permissions required to use this command:\n%s", - "support_only": "❌ You must be a member of staff to use this command." + "staff_only": "❌ You must be a member of staff to use this command." } \ No newline at end of file diff --git a/src/logger.js b/src/logger.js index e344add..efd05da 100644 --- a/src/logger.js +++ b/src/logger.js @@ -8,53 +8,54 @@ module.exports = new Logger({ splitFile: true, directory: path('./logs/'), keepFor: config.logs.keep_for, + timestamp: 'YYYY-MM-DD HH:mm:ss', levels: { _logger: { - format: '&7[{timestamp}]&r [LOGGER] {text}' + format: '&f&!7{timestamp}&r [LOGGER] {text}' }, basic: { - format: '[{timestamp}] {text}' + format: '&f&!7{timestamp} {text}' }, console: { - format: '[{timestamp}] [INFO] {text}' + format: '&f&!7{timestamp} [INFO] {text}' }, info: { - format: '&7[{timestamp}]&r &3[INFO] &b{text}' + format: '&f&!7{timestamp}&r &3[INFO] &b{text}' }, success: { - format: '&7[{timestamp}]&r &2[SUCCESS] &a{text}' + format: '&f&!7{timestamp}&r &2[SUCCESS] &a{text}' }, debug: { - format: '&7[{timestamp}]&r &1[DEBUG] &9{text}' + format: '&f&!7{timestamp}&r &1[DEBUG] &9{text}' }, notice: { - format: '&7[{timestamp}]&r &0&!6[NOTICE] {text}' + format: '&f&!7{timestamp}&r &0&!6[NOTICE] {text}' }, warn: { - format: '&7[{timestamp}]&r &6[WARN] &e{text}' + format: '&f&!7{timestamp}&r &6[WARN] &e{text}' }, error: { - format: '&7[{timestamp}]&r &4[ERROR] &c{text}' + format: '&f&!7{timestamp}&r &4[ERROR] &c{text}' }, commands: { type: 'info', - format: '&7[{timestamp}]&r &3[INFO] &d(COMMANDS)&r {text}' + format: '&f&!7{timestamp}&r &3[INFO] &d(COMMANDS)&r {text}' }, plugins: { type: 'info', - format: '&7[{timestamp}]&r &3[INFO] &d(PLUGINS)&r {text}' + format: '&f&!7{timestamp}&r &3[INFO] &d(PLUGINS)&r {text}' }, tickets: { type: 'info', - format: '&7[{timestamp}]&r &3[INFO] &d(TICKETS)&r {text}' + format: '&f&!7{timestamp}&r &3[INFO] &d(TICKETS)&r {text}' }, http: { type: 'info', - format: '&7[{timestamp}]&r &3[INFO] &d(HTTP)&r {text}' + format: '&f&!7{timestamp}&r &3[INFO] &d(HTTP)&r {text}' }, ws: { type: 'info', - format: '&7[{timestamp}]&r &3[INFO] &d(WS)&r {text}' + format: '&f&!7{timestamp}&r &3[INFO] &d(WS)&r {text}' } } }); \ No newline at end of file diff --git a/src/modules/commands/manager.js b/src/modules/commands/manager.js index 6aa3ac0..f399a8c 100644 --- a/src/modules/commands/manager.js +++ b/src/modules/commands/manager.js @@ -183,7 +183,7 @@ module.exports = class CommandManager { const cmd = this.commands.get(cmd_name); if (cmd.slash && !slash) { - this.client.log.commands(`Blocking command execution for the "${cmd_name}" command as it was invoked by a message, not a slash command interaction_or_message.`); + this.client.log.commands(`Blocking command execution for the "${cmd_name}" command as it was invoked by a message, not a slash command interaction.`); try { data.channel.send(i18n('must_be_slash', cmd_name)); // interaction_or_message.reply } catch (err) { diff --git a/src/modules/tickets.js b/src/modules/tickets.js index a2cbced..a49d8d1 100644 --- a/src/modules/tickets.js +++ b/src/modules/tickets.js @@ -19,56 +19,110 @@ module.exports = class TicketManager extends EventEmitter { /** * Create a new ticket - * @param {string} guild - ID of the guild to create the ticket in - * @param {string} creator - ID of the ticket creator (user) - * @param {string} category - ID of the ticket category + * @param {string} guild_id - ID of the guild to create the ticket in + * @param {string} creator_id - ID of the ticket creator (user) + * @param {string} category_id - ID of the ticket category * @param {string} [topic] - The ticket topic */ - async create(guild, creator, category, topic) { + async create(guild_id, creator_id, category_id, topic) { + if (!topic) topic = ''; + let cat_row = await this.client.db.models.Category.findOne({ + where: { + id: category_id + } + }); + + if (!cat_row) + throw new Error('Ticket category does not exist'); + + let number = (await this.client.db.models.Ticket.count({ + where: { + guild: guild_id + } + })) + 1; + + let guild = await this.client.guilds.cache.get(guild_id); + let member = await guild.members.fetch(creator_id); + let name = cat_row.name_format + .replace(/{+\s?(user)?name\s?}+/gi, member.displayName) + .replace(/{+\s?number\s?}+/gi, number); + + let t_channel = await guild.channels.create(name, { + type: 'text', + topic: `${member}${topic.length > 0 ? ` | ${topic}` : ''}`, + parent: category_id, + reason: `${member.tag} requested a new ticket channel` + }); + + let t_row = await this.client.db.models.Ticket.create({ + id: t_channel.id, + number, + guild: guild_id, + category: category_id, + creator: creator_id, + topic + }); + + this.emit('create', t_row.id, creator_id); } /** * Get a ticket - * @param {string} ticket - The channel ID, or the ticket number + * @param {(string|number)} ticket - 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) { - + 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}`); } /** * Close a ticket - * @param {string} ticket - The channel ID, or the ticket number - * @param {string} [closer] - ID of the member who is closing the 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} [guild_id] - The ID of the ticket's guild (used if a ticket number is provided instead of ID) */ - async close(ticket, closer) { + 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}`); + + this.emit('beforeClose', ticket, closer_id); + + /** + * + * + * for each message of ticket, create entities + * + * + */ + + this.emit('close', ticket, closer_id); + } + + /** + * + * @param {(string|number)} ticket - 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({ + let row = await this.client.db.models.Ticket.findOne({ where: { - number: ticket + number: ticket, + guild_id } }); - if (!row) throw new Error(`Could not find a ticket with number ${ticket}`); - ticket = row.id; + ticket = row?.id; } - - let row = await this.client.db.Models.Ticket.findOne({ + + let row = await this.client.db.models.Ticket.findOne({ where: { id: ticket } }); - if (!row) throw new Error(`Could not find a ticket with ID ${ticket}`); - - this.emit('beforeClose', ticket, closer); - - /** - * - * - * for each message in table, create entities - * - * - */ + return row; } }; \ No newline at end of file diff --git a/user/example.config.js b/user/example.config.js index 48dfc7d..7589201 100644 --- a/user/example.config.js +++ b/user/example.config.js @@ -27,6 +27,7 @@ module.exports = { defaults: { colour: '#009999', // https://discord.js.org/#/docs/main/stable/typedef/ColorResolvable log_messages: true, // transcripts/archives will be empty if false + name_format: 'ticket-{number}', prefix: '-', 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.__', },