From 4b4dcd236eece95dd420700d3afcf917dbc68d49 Mon Sep 17 00:00:00 2001 From: Isaac Date: Thu, 13 Jul 2023 12:54:31 +0100 Subject: [PATCH] feat: stats houston v4 --- src/lib/misc.js | 4 +- src/lib/stats.js | 81 +++++++++++++++++++++++++++++++++++ src/listeners/client/ready.js | 49 ++------------------- 3 files changed, 87 insertions(+), 47 deletions(-) diff --git a/src/lib/misc.js b/src/lib/misc.js index 8b0d15a..35885e6 100644 --- a/src/lib/misc.js +++ b/src/lib/misc.js @@ -1 +1,3 @@ -module.exports.msToMins = ms => Number((ms / 1000 / 60).toFixed(1)); \ No newline at end of file +const { createHash } = require('crypto'); +module.exports.md5 = str => createHash('md5').update(str).digest('hex'); +module.exports.msToMins = ms => Number((ms / 1000 / 60).toFixed(2)); diff --git a/src/lib/stats.js b/src/lib/stats.js index 3ced74a..8416d0f 100644 --- a/src/lib/stats.js +++ b/src/lib/stats.js @@ -1,2 +1,83 @@ +const { version } = require('../../package.json'); +const { + md5, + msToMins, +} = require('./misc'); + module.exports.getAvgResolutionTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) || 1) / Math.max(tickets.length, 1); + module.exports.getAvgResponseTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) || 1) / Math.max(tickets.length, 1); + +/** + * + * @param {import("../client")} client + */ +module.exports.sendToHouston = async client => { + const guilds = await client.prisma.guild.findMany({ + include: { + categories: { include: { _count: { select: { questions: true } } } }, + tags: true, + tickets: { + select: { + closedAt: true, + createdAt: true, + firstResponseAt: true, + }, + }, + }, + }); + const users = await client.prisma.user.findMany({ select: { messageCount: true } }); + const stats = { + activated_users: users.length, + arch: process.arch, + database: process.env.DB_PROVIDER, + guilds: guilds.filter(guild => { + if (!client.guilds.cache.has(guild.id)) { + client.log.warn('Guild %s is in the database but is not cached and might not exist. It will be excluded from the stats report.', guild.id); + return false; + } + return true; + }).map(guild => { + const closedTickets = guild.tickets.filter(t => 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, + // eslint-disable-next-line no-underscore-dangle + 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: users.reduce((total, user) => total + user.messageCount, 0), // global not guild, don't count archivedMessage table rows, they can be deleted + tickets: guilds.reduce((total, guild) => total + guild.tickets.length, 0), + }; + }), + id: md5(client.user.id), + node: process.version, + os: process.platform, + version, + }; + try { + client.log.verbose('Reporting to Houston:', stats); + const res = await fetch('https://stats.discordtickets.app/api/v4/houston', { + body: JSON.stringify(stats), + headers: { 'content-type': 'application/json' }, + method: 'POST', + }); + if (!res.ok) throw res; + client.log.success('Posted client stats'); + client.log.debug(res); + } catch (res) { + client.log.error('An error occurred whilst posting stats:', (await res.json())?.error); + client.log.debug(res); + } +}; diff --git a/src/listeners/client/ready.js b/src/listeners/client/ready.js index 228a68c..4286564 100644 --- a/src/listeners/client/ready.js +++ b/src/listeners/client/ready.js @@ -1,12 +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'); const sync = require('../../lib/sync'); const checkForUpdates = require('../../lib/updates'); const { isStaff } = require('../../lib/users'); @@ -16,6 +13,7 @@ const { ButtonStyle, } = require('discord.js'); const ExtendedEmbedBuilder = require('../../lib/embed'); +const { sendToHouston } = require('../../lib/stats'); module.exports = class extends Listener { constructor(client, options) { @@ -94,49 +92,8 @@ module.exports = class extends Listener { // stats posting if (client.config.stats) { - const send = async () => { - const tickets = await client.prisma.ticket.findMany({ - select: { - createdAt: true, - firstResponseAt: true, - }, - }); - const closedTickets = tickets.filter(t => t.closedAt); - const users = await client.prisma.user.findMany({ select: { messageCount: true } }); - const stats = { - activated_users: users.length, - arch: process.arch, - 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: await client.prisma.category.count(), - database: process.env.DB_PROVIDER, - guilds: client.guilds.cache.size, - id: crypto.createHash('md5').update(client.user.id).digest('hex'), - members: client.guilds.cache.reduce((t, g) => t + g.memberCount, 0), - messages: users.reduce((total, user) => total + user.messageCount, 0), // don't count archivedMessage table rows, they can be deleted, - node: process.version, - os: process.platform, - tags: await client.prisma.tag.count(), - tickets: tickets.length, - version, - }; - try { - const res = await fetch('https://stats.discordtickets.app/api/v3/houston', { - body: JSON.stringify(stats), - headers: { 'content-type': 'application/json' }, - method: 'POST', - }); - if (!res.ok) throw res; - client.log.success('Posted client stats'); - client.log.verbose(stats); - client.log.debug(res); - } catch (res) { - client.log.error('An error occurred whilst posting stats', (await res.json())?.error); - client.log.debug(res); - } - }; - send(); - setInterval(() => send(), ms('12h')); + sendToHouston(client); + setInterval(() => sendToHouston(client), ms('12h')); } if (client.config.updates) {