const { Command } = require('../../../../interfaces/'); const os = require('os'); class StatsCommand extends Command { constructor(client) { super(client, { name: 'stats', module: 'developer', aliases: [ ], usage: '', restricted: true, arguments: [ { name: 'log', type: 'BOOLEAN', types: ['FLAG'], description: 'Logs the output in the console.' } ], showUsage: false, archivable: false }); } async execute(message) { // TODO: // Add some stuff that only shows when a dev runs the command, for instance amount of cached messages etc const { guild, author } = message; const { shard } = this.client; //Shards eval const evalFunc = (thisArg) => { return { users: thisArg.users.cache.size, guilds: thisArg.guilds.cache.size, channels: thisArg.channels.cache.size, memory: Math.floor(process.memoryUsage().heapUsed / 1024 / 1024), uptime: thisArg.uptime }; }; //Manager eval const mEvalFunc = (thisArg) => { return { memory: Math.floor(process.memoryUsage().heapUsed / 1024 / 1024), uptime: Date.now() - thisArg.readyAt }; }; const shardResults = await shard.broadcastEval(evalFunc).catch((error) => this.client.logger.error(error)); const managerResult = await this.client.managerEval(`(${mEvalFunc})(this)`).catch((error) => this.client.logger.error(error)); //Current shard const mainValues = { uptime: this.client.resolver.timeAgo(Math.floor(managerResult.uptime / 1000), true, true, true), memory: managerResult.memory, shards: shard.count, library: require('discord.js').version }; //All shards combined const shardValues = { cachedUsers: this.client.users.cache.size, guilds: this.client.guilds.cache.size, channels: this.client.channels.cache.size, uptime: this.client.resolver.timeAgo(Math.floor(this.client.uptime / 1000), true, true, true), memory: Math.floor(process.memoryUsage().heapUsed / 1024 / 1024) }; //Compile shard data const totalValues = shardResults.reduce((acc, curr) => { Object.entries(curr).forEach(([key, val]) => { if (!acc[key]) acc[key] = 0; acc[key] += val; }); return acc; }, {}); totalValues.uptime = this.client.resolver.timeAgo(Math.floor(totalValues.uptime / 1000), true, true, true); //System information const CPU = os.cpus(); const memoryFree = (os.freemem() / 1024 / 1024 / 1024).toFixed(1); const memoryTotal = (os.totalmem() / 1024 / 1024 / 1024).toFixed(1); const sysInfo = { devs: await this.client.resolveUsers(this.client._options.bot.owners).then((owners) => { return owners.map((o) => o.tag).join(', '); }), cpu: CPU[0].model, cores: CPU.length, memoryUsed: (memoryTotal - memoryFree).toFixed(1), memoryTotal, osType: os.type(), uptime: this.client.resolver.timeAgo(os.uptime(), true, true, true), hostname: os.hostname() }; //Command statistics - probably expand this further const commands = this.client.registry.components.filter((comp) => comp.type === 'command'); const commandValues = { invokes: commands.reduce((acc, cmd) => acc + cmd._invokes.success + cmd._invokes.fail, 0), success: commands.reduce((acc, cmd) => acc + cmd._invokes.success, 0), fail: commands.reduce((acc, cmd) => acc + cmd._invokes.fail, 0) }; const embed = { title: message.format('C_STATS_TITLE', { client: this.client.user.tag, version: require('../../../../../package.json').version }), description: message.format('C_STATS_SYSTEM_VALUE', sysInfo), fields: [ { name: message.format('C_STATS_MAIN'), value: message.format('C_STATS_MAIN_VALUE', mainValues), inline: true }, { name: message.format('C_STATS_CURRENT_SHARD', { shard: guild.shardID }), value: message.format('C_STATS_CURRENT_SHARDS_VALUE', shardValues), inline: true }, { name: message.format('C_STATS_TOTAL'), value: message.format('C_STATS_TOTAL_VALUE', totalValues), inline: true }, { name: message.format('C_STATS_COMMANDS'), value: message.format('C_STATS_COMMANDS_VALUE', commandValues), inline: true } ] }; if (author.developer) { //Mongo DB const stats = await this.client.storage.mongodb.stats(); const dbStats = { db: stats.db, collections: stats.collections, objects: stats.objects, avgObjSize: (stats.avgObjSize / 1024 / 1024).toFixed(1), totalSize: (stats.dataSize / 1024 / 1024 / 1024).toFixed(2) }; embed.fields.push({ name: message.format('C_STATS_MONGODB'), value: message.format('C_STATS_MONGODB_VALUE', dbStats), inline: true }); //Other const evalFunc2 = () => { let msg = 0, mem = 0; // eslint-disable-next-line no-return-assign this.channels.cache.forEach((c) => { msg += c.messages?.cache.size || 0; }); this.guilds.cache.forEach((g) => { mem += g.members.cache.size; }); return { cachedMessages: msg, cachedMembers: mem }; }; const result = await shard.broadcastEval(evalFunc2).catch((error) => this.client.logger.error(error.stack)); const other = result.reduce((acc, curr) => { Object.entries(curr).forEach(([key, val]) => { if (!acc[key]) acc[key] = 0; acc[key] += val; }); return acc; }, {}); other.memberAvg = Math.floor(other.cachedMembers / shard.count); other.messageAvg = Math.floor(other.cachedMessages / shard.count); embed.fields.push({ name: message.format('C_STATS_CACHE'), value: message.format('C_STATS_CACHE_VALUE', other), inline: true }); } message.embed(embed); } } module.exports = StatsCommand;