diff --git a/src/lib/stats.js b/src/lib/stats.js new file mode 100644 index 0000000..3aa9e7c --- /dev/null +++ b/src/lib/stats.js @@ -0,0 +1,2 @@ +module.exports.getAvgResolutionTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1) / tickets.length; +module.exports.getAvgResponseTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1) / tickets.length; diff --git a/src/lib/tickets/manager.js b/src/lib/tickets/manager.js index 2035027..480ce9b 100644 --- a/src/lib/tickets/manager.js +++ b/src/lib/tickets/manager.js @@ -20,6 +20,10 @@ const { isStaff } = require('../users'); const { Collection } = require('discord.js'); const spacetime = require('spacetime'); const Cryptr = require('cryptr'); +const { + getAvgResolutionTime, + getAvgResponseTime, +} = require('../stats'); const { decrypt, encrypt, @@ -404,10 +408,11 @@ module.exports = class TicketManager { if (category.image) await channel.send(category.image); + const needsStats = category.openingMessage.match(/{+\s?avgResponseTime\s?}+/i) || category.openingMessage.match(/{+\s?avgResolutionTime\s?}+/i); const statsCacheKey = `cache/category-stats/${categoryId}`; let stats = await this.client.keyv.get(statsCacheKey); - if (!stats) { - const tickets = await this.client.prisma.ticket.findMany({ + if (needsStats && !stats) { + const closedTickets = await this.client.prisma.ticket.findMany({ select: { closedAt: true, createdAt: true, @@ -420,8 +425,8 @@ module.exports = class TicketManager { }, }); stats = { - avgResolutionTime: ms(tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / tickets.length, { long: true }), - avgResponseTime: ms(tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / tickets.length, { long: true }), + avgResolutionTime: ms(getAvgResolutionTime(closedTickets), { long: true }), + avgResponseTime: ms(getAvgResponseTime(closedTickets), { long: true }), }; this.client.keyv.set(statsCacheKey, stats, ms('1h')); } diff --git a/src/listeners/client/ready.js b/src/listeners/client/ready.js index 81b48c2..53d5402 100644 --- a/src/listeners/client/ready.js +++ b/src/listeners/client/ready.js @@ -1,5 +1,9 @@ const { Listener } = require('@eartharoid/dbf'); const crypto = require('crypto'); +const { + getAvgResolutionTime, + getAvgResponseTime, +} = require('../../lib/stats'); const ms = require('ms'); const { version } = require('../../../package.json'); const { msToMins } = require('../../lib/misc'); @@ -58,8 +62,8 @@ module.exports = class extends Listener { }); const closedTickets = tickets.filter(t => t.firstResponseAt && t.closedAt); cached = { - avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), - avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), + avgResolutionTime: ms(getAvgResolutionTime(closedTickets)), + avgResponseTime: ms(getAvgResponseTime(closedTickets)), openTickets: tickets.length - closedTickets.length, totalTickets: tickets.length, }; diff --git a/src/routes/api/admin/guilds/[guild]/categories/index.js b/src/routes/api/admin/guilds/[guild]/categories/index.js index a171454..409baa2 100644 --- a/src/routes/api/admin/guilds/[guild]/categories/index.js +++ b/src/routes/api/admin/guilds/[guild]/categories/index.js @@ -6,6 +6,10 @@ const { ChannelType: { GuildCategory }, } = require('discord.js'); const ms = require('ms'); +const { + getAvgResolutionTime, + getAvgResponseTime, +} = require('../../../../../../lib/stats'); module.exports.get = fastify => ({ handler: async (req, res) => { @@ -36,8 +40,8 @@ module.exports.get = fastify => ({ c = { ...c, stats: { - avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), - avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), + avgResolutionTime: ms(getAvgResolutionTime(closedTickets)), + avgResponseTime: ms(getAvgResponseTime(closedTickets)), }, }; delete c.tickets; diff --git a/src/routes/api/admin/guilds/[guild]/index.js b/src/routes/api/admin/guilds/[guild]/index.js index 38e9b62..bb787fd 100644 --- a/src/routes/api/admin/guilds/[guild]/index.js +++ b/src/routes/api/admin/guilds/[guild]/index.js @@ -1,4 +1,8 @@ /* eslint-disable no-underscore-dangle */ +const { + getAvgResolutionTime, + getAvgResponseTime, +} = require('../../../../../lib/stats'); const ms = require('ms'); module.exports.get = fastify => ({ @@ -36,8 +40,8 @@ module.exports.get = fastify => ({ logo: guild.iconURL(), name: guild.name, stats: { - avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), - avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), + avgResolutionTime: ms(getAvgResolutionTime(closedTickets)), + avgResponseTime: ms(getAvgResponseTime(closedTickets)), categories: categories.map(c => ({ id: c.id, name: c.name, @@ -53,4 +57,4 @@ module.exports.get = fastify => ({ return cached; }, onRequest: [fastify.authenticate, fastify.isAdmin], -}); \ No newline at end of file +}); diff --git a/src/routes/api/client.js b/src/routes/api/client.js index 4c793fb..e18c1df 100644 --- a/src/routes/api/client.js +++ b/src/routes/api/client.js @@ -1,3 +1,6 @@ +const { + getAvgResolutionTime, getAvgResponseTime, +} = require('../../lib/stats'); const ms = require('ms'); module.exports.get = () => ({ @@ -25,8 +28,8 @@ module.exports.get = () => ({ stats: { activatedUsers: users.length, archivedMessages: users.reduce((total, user) => total + user.messageCount, 0), // don't count archivedMessage table rows, they can be deleted - avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), - avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), + avgResolutionTime: ms(getAvgResolutionTime(closedTickets)), + avgResponseTime: ms(getAvgResponseTime(closedTickets)), categories: await client.prisma.category.count(), guilds: client.guilds.cache.size, members: client.guilds.cache.reduce((t, g) => t + g.memberCount, 0), @@ -40,4 +43,4 @@ module.exports.get = () => ({ return cached; }, -}); \ No newline at end of file +});