diff --git a/src/autocomplete/ticket.js b/src/autocomplete/ticket.js index 6c7214b..60b5e8b 100644 --- a/src/autocomplete/ticket.js +++ b/src/autocomplete/ticket.js @@ -8,5 +8,37 @@ module.exports = class TicketCompleter extends Autocompleter { }); } - async run(value, comamnd, interaction) { } + /** + * @param {string} value + * @param {*} comamnd + * @param {import("discord.js").AutocompleteInteraction} interaction + */ + async run(value, comamnd, interaction) { + /** @type {import("client")} */ + const client = this.client; + const settings = await client.prisma.guild.findUnique({ where: { id: interaction.guild.id } }); + const tickets = await client.prisma.ticket.findMany({ + where: { + createdById: interaction.user.id, + guildId: interaction.guild.id, + open: false, + }, + }); + const options = value ? tickets.filter(t => + String(t.number).match(new RegExp(value, 'i')) || + t.topic?.match(new RegExp(value, 'i')) || + new Date(t.createdAt).toLocaleString(settings.locale, { dateStyle: 'short' })?.match(new RegExp(value, 'i')), + ) : tickets; + await interaction.respond( + options + .slice(0, 25) + .map(t => { + const date = new Date(t.createdAt).toLocaleString(settings.locale, { dateStyle: 'short' }); + return { + name: `#${t.number} - ${date} ${t.topic ? '| ' + t.topic.substring(0, 50) : ''}`, + value: t.id, + }; + }), + ); + } }; \ No newline at end of file diff --git a/src/commands/slash/close.js b/src/commands/slash/close.js index 74bad52..3705e54 100644 --- a/src/commands/slash/close.js +++ b/src/commands/slash/close.js @@ -10,21 +10,17 @@ module.exports = class CloseSlashCommand extends SlashCommand { client.i18n.locales.forEach(l => (nameLocalizations[l] = client.i18n.getMessage(l, 'commands.slash.close.name'))); let opts = [ - { - name: 'channel', - required: false, - type: ApplicationCommandOptionType.Channel, - }, - { - name: 'number', - required: false, - type: ApplicationCommandOptionType.Integer, - }, { name: 'reason', required: false, type: ApplicationCommandOptionType.String, }, + { + autocomplete: true, + name: 'ticket', + required: false, + type: ApplicationCommandOptionType.String, + }, { name: 'time', required: false, diff --git a/src/commands/slash/new.js b/src/commands/slash/new.js index 4b0aedc..ccf978d 100644 --- a/src/commands/slash/new.js +++ b/src/commands/slash/new.js @@ -50,6 +50,5 @@ module.exports = class NewSlashCommand extends SlashCommand { */ async run(interaction) { await useGuild(this.client, interaction, { referencesTicketId: interaction.options.getString('references', false) }); - } }; \ No newline at end of file diff --git a/src/commands/user/create.js b/src/commands/user/create.js index 8a9579b..648096d 100644 --- a/src/commands/user/create.js +++ b/src/commands/user/create.js @@ -13,5 +13,8 @@ module.exports = class CreateUserCommand extends UserCommand { }); } - async run(interaction) { } + async run(interaction) { + // select category + // send button + } }; \ No newline at end of file diff --git a/src/i18n/en-GB.yml b/src/i18n/en-GB.yml index 560edf7..df0515e 100644 --- a/src/i18n/en-GB.yml +++ b/src/i18n/en-GB.yml @@ -41,15 +41,12 @@ commands: description: Close a ticket name: close options: - channel: - description: The ticket channel to close - name: channel - number: - description: The number of the ticket to close - name: number reason: description: The reason for closing the ticket(s) name: reason + ticket: + description: The ticket to close + name: ticket time: description: Close all tickets that have been inactive for the specific time name: time diff --git a/src/lib/tickets/manager.js b/src/lib/tickets/manager.js index 74f125c..159ac08 100644 --- a/src/lib/tickets/manager.js +++ b/src/lib/tickets/manager.js @@ -386,6 +386,20 @@ module.exports = class TicketManager { if (category.image) await channel.send(category.image); + const statsCacheKey = `cache/category-stats/${categoryId}`; + let stats = await this.client.keyv.get(statsCacheKey); + if (!stats) { + const { tickets } = await this.client.prisma.category.findUnique({ + select: { tickets: { where: { open: false } } }, + where: { id: categoryId }, + }); + stats = { + avgResolutionTime: ms(tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / tickets.length), + avgResponseTime: ms(tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / tickets.length), + }; + this.client.keyv.set(statsCacheKey, stats, ms('1h')); + } + const embeds = [ new ExtendedEmbedBuilder() .setColor(category.guild.primaryColour) @@ -395,11 +409,14 @@ module.exports = class TicketManager { }) .setDescription( category.openingMessage - .replace(/{+\s?(user)?name\s?}+/gi, creator.user.toString()), - + .replace(/{+\s?(user)?name\s?}+/gi, creator.user.toString()) + .replace(/{+\s?avgResponseTime\s?}+/gi, stats.avgResponseTime) + .replace(/{+\s?avgResolutionTime\s?}+/gi, stats.avgResolutionTime), ), ]; + // TODO: !staff || workingHours + if (answers) { embeds.push( new ExtendedEmbedBuilder() diff --git a/src/listeners/client/messageDelete.js b/src/listeners/client/messageDelete.js index 11504a0..a6dd3dc 100644 --- a/src/listeners/client/messageDelete.js +++ b/src/listeners/client/messageDelete.js @@ -11,5 +11,6 @@ module.exports = class extends Listener { run(message) { // TODO: archive messages in tickets + // TODO: log channel } }; diff --git a/src/listeners/client/messageUpdate.js b/src/listeners/client/messageUpdate.js index 59a2a63..6792eb6 100644 --- a/src/listeners/client/messageUpdate.js +++ b/src/listeners/client/messageUpdate.js @@ -11,5 +11,6 @@ module.exports = class extends Listener { run(oldMessage, newMessage) { // TODO: archive messages in tickets + // TODO: log channel } }; diff --git a/src/routes/api/admin/guilds/[guild]/categories/index.js b/src/routes/api/admin/guilds/[guild]/categories/index.js index 95d1f81..254a8d5 100644 --- a/src/routes/api/admin/guilds/[guild]/categories/index.js +++ b/src/routes/api/admin/guilds/[guild]/categories/index.js @@ -21,7 +21,7 @@ module.exports.get = fastify => ({ name: true, requiredRoles: true, staffRoles: true, - tickets: true, + tickets: { where: { open: false } }, }, }, }, diff --git a/src/stdin/commands.js b/src/stdin/commands.js index 6b1242b..29e4a71 100644 --- a/src/stdin/commands.js +++ b/src/stdin/commands.js @@ -12,11 +12,8 @@ module.exports = class Commands extends StdinCommand { switch (args[0]) { case 'publish': { this.client.commands.publish() - .then(commands => { - if (!commands) return console.log('None published'); - console.log('Published %d commands', commands.size); - }) - .catch(console.error); + .then(commands => this.client.log.success('Published %d commands', commands?.size)) + .catch(this.client.log.error); break; } }