diff --git a/src/lib/misc.js b/src/lib/misc.js index 3f23301..50538d3 100644 --- a/src/lib/misc.js +++ b/src/lib/misc.js @@ -1,6 +1,6 @@ const { createHash } = require('crypto'); module.exports.md5 = str => createHash('md5').update(str).digest('hex'); -module.exports.msToMins = ms => Number((ms / 1000 / 60).toFixed(2)); + module.exports.iconURL = guildLike => guildLike.icon ? guildLike.client.rest.cdn.icon(guildLike.id, guildLike.icon) : `https://api.dicebear.com/8.x/initials/png?seed=${encodeURIComponent(guildLike.name)}&size=96&backgroundType=gradientLinear&fontWeight=600`; diff --git a/src/lib/stats.js b/src/lib/stats.js index 010ef8c..8ad2cb8 100644 --- a/src/lib/stats.js +++ b/src/lib/stats.js @@ -1,10 +1,17 @@ /* eslint-disable no-underscore-dangle */ -const { version } = require('../../package.json'); const { - md5, - msToMins, -} = require('./misc'); + spawn, + Pool, + Worker, +} = require('threads'); +const { cpus } = require('node:os'); +const { version } = require('../../package.json'); +const { md5 } = require('./misc'); + +// ! ceiL: at least 1 +const poolSize = Math.ceil(cpus().length / 4); +const pool = Pool(() => spawn(new Worker('./workers/stats.js')), { size: poolSize }); module.exports.getAvgResolutionTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) || 1) / Math.max(tickets.length, 1); @@ -39,28 +46,9 @@ module.exports.sendToHouston = async client => { database: process.env.DB_PROVIDER, guilds: guilds .filter(guild => client.guilds.cache.has(guild.id)) - .map(guild => { - const closedTickets = guild.tickets.filter(t => t.firstResponseAt && t.closedAt); - return { - avg_resolution_time: msToMins(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), - avg_response_time: msToMins(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), - categories: guild.categories.length, - features: { - auto_close: msToMins(guild.autoClose), - claiming: guild.categories.filter(c => c.claiming).length, - feedback: guild.categories.filter(c => c.enableFeedback).length, - logs: !!guild.logChannel, - questions: guild.categories.filter(c => c._count.questions).length, - tags: guild.tags.length, - tags_regex: guild.tags.filter(t => t.regex).length, - topic: guild.categories.filter(c => c.requireTopic).length, - }, - id: md5(guild.id), - locale: guild.locale, - members: client.guilds.cache.get(guild.id).memberCount, - messages, // * global not guild, don't count archivedMessage table rows, they can be deleted - tickets: guild.tickets.length, - }; + .map(async guild => { + guild.members = client.guilds.cache.get(guild.id).memberCount; + return pool.queue(worker => worker.aggregateGuildForHouston(guild, messages)); }), id: md5(client.user.id), node: process.version, diff --git a/src/lib/workers/stats.js b/src/lib/workers/stats.js new file mode 100644 index 0000000..353d129 --- /dev/null +++ b/src/lib/workers/stats.js @@ -0,0 +1,40 @@ +/* eslint-disable no-underscore-dangle */ + +const { expose } = require('threads/worker'); +const { createHash } = require('crypto'); + +const md5 = str => createHash('md5').update(str).digest('hex'); + +const msToMins = ms => Number((ms / 1000 / 60).toFixed(2)); + +const getAvgResolutionTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) || 1) / Math.max(tickets.length, 1); + +const getAvgResponseTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) || 1) / Math.max(tickets.length, 1); + +expose({ + aggregateGuildForHouston(guild, messages) { + const closedTickets = guild.tickets.filter(t => t.firstResponseAt && t.closedAt); + return { + avg_resolution_time: msToMins(getAvgResolutionTime(closedTickets)), + avg_response_time: msToMins(getAvgResponseTime(closedTickets)), + categories: guild.categories.length, + features: { + auto_close: msToMins(guild.autoClose), + claiming: guild.categories.filter(c => c.claiming).length, + feedback: guild.categories.filter(c => c.enableFeedback).length, + logs: !!guild.logChannel, + questions: guild.categories.filter(c => c._count.questions).length, + tags: guild.tags.length, + tags_regex: guild.tags.filter(t => t.regex).length, + topic: guild.categories.filter(c => c.requireTopic).length, + }, + id: md5(guild.id), + locale: guild.locale, + members: guild.members, + messages, // * global not guild, don't count archivedMessage table rows, they can be deleted + tickets: guild.tickets.length, + }; + }, + getAvgResolutionTime, + getAvgResponseTime, +});