diff --git a/config.default.jsonc b/config.default.jsonc index 9ca3f0b..7dd64dd 100644 --- a/config.default.jsonc +++ b/config.default.jsonc @@ -149,6 +149,9 @@ "youtube": { "enabled": true }, + "bot": { + "enabled": true + }, "autopaste": { "enabled": true, // Make sure the ids are all strings, not numbers diff --git a/package-lock.json b/package-lock.json index 05babce..18c703d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "version": "0.1.0", "dependencies": { + "@types/node-os-utils": "^1.3.1", "bufferutil": "^4.0.7", "chalk": "^4.1.2", "discord.js": "^14.11.0", @@ -14,6 +15,7 @@ "jsonc-parser": "^3.2.0", "mongodb": "^5.6.0", "node": "^19.8.1", + "node-os-utils": "^1.3.7", "typescript": "^5.0.2", "undici": "^5.22.1", "utf-8-validate": "^6.0.3", @@ -899,6 +901,11 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" }, + "node_modules/@types/node-os-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/node-os-utils/-/node-os-utils-1.3.1.tgz", + "integrity": "sha512-gokG1AaQo78X3f1KXOPAfwbhERX95XL0nhosOhwFck0hZ3BG52Mfch3oj3gAhXuUsou3lwi+ewZWjDo0wshKwQ==" + }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", @@ -6053,6 +6060,11 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-os-utils": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/node-os-utils/-/node-os-utils-1.3.7.tgz", + "integrity": "sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==" + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", diff --git a/package.json b/package.json index 06f8cdf..49701da 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "fix": "gts fix" }, "dependencies": { + "@types/node-os-utils": "^1.3.1", "bufferutil": "^4.0.7", "chalk": "^4.1.2", "discord.js": "^14.11.0", @@ -21,6 +22,7 @@ "jsonc-parser": "^3.2.0", "mongodb": "^5.6.0", "node": "^19.8.1", + "node-os-utils": "^1.3.7", "typescript": "^5.0.2", "undici": "^5.22.1", "utf-8-validate": "^6.0.3", diff --git a/src/modules/meta/bot.ts b/src/modules/meta/bot.ts new file mode 100644 index 0000000..4b41350 --- /dev/null +++ b/src/modules/meta/bot.ts @@ -0,0 +1,136 @@ +/** + * @file + * Modules: + * - {@link bot} - Info + */ + +import {Colors, EmbedBuilder, EmbedField} from 'discord.js'; +import process from 'node:process'; +import osutils from 'node-os-utils'; +import * as util from '../../core/util.js'; + +/** Function that returns an array of the latest 5 ticks' latencies */ +async function getTickLatency(): Promise { + return new Promise(resolve => { + let iteration = 0; + const tickDelay: number[] = []; + + /** Function to push the tick latency to tickDelay, resolve on the fifth tick */ + function measureIteration() { + // Unix timestamp of start in ms + const startRaw = process.hrtime(); + const start = startRaw[0] * 1_000 + startRaw[1] / 1_000_000; + + process.nextTick(() => { + // Unix timestamp of iteration in ms + const lagTime = process.hrtime(); + const lag = lagTime[0] * 1_000 + lagTime[1] / 1_000_000; + + tickDelay.push(lag - start); + iteration++; + + if (iteration < 5) { + measureIteration(); + } else { + resolve(tickDelay); + } + }); + } + + measureIteration(); + }); +} + +/** The root bot command definition */ +const bot = new util.RootModule( + 'bot', + 'Bot info and management command group', + [], + [] +); + +bot.registerSubModule( + new util.SubModule( + 'info', + 'Prints information about the bot', + [], + async (_, interaction) => { + const embed: EmbedBuilder = new EmbedBuilder() + .setColor(Colors.Blurple) + .setTitle(util.client.user!.username) + .setThumbnail(util.client.user!.displayAvatarURL()); + + // Gets the Average tick delay + const tickDelay = await getTickLatency(); + + // Averages the delays out and rounds it to 4 decimal points + const sum: number = tickDelay.reduce((a, b) => a + b, 0); + const averageDelay: string = (sum / tickDelay.length).toFixed(4); + + // Gets the CPU usage + let cpuUsage = 'Unable to get the cpu usage'; + + await osutils.cpu.usage().then(cpuPercentage => { + cpuUsage = cpuPercentage.toString(); + }); + + const memoryUsage: number = process.memoryUsage.rss() / 1_000_000; // MB + + const fields: EmbedField[] = [ + { + name: 'Started', + value: util.client.readyAt!.toString(), + inline: true, + }, + { + name: 'IRC', + value: 'IRC is not implemented yet you dingus', + inline: true, + }, + // Line break + { + name: '\u200B', + value: '\u200B', + inline: false, + }, + { + name: 'Bot latency', + value: `${Date.now() - interaction.createdTimestamp} ms`, + inline: true, + }, + { + name: 'API latency', + value: `${util.client.ws.ping} ms`, + inline: true, + }, + // Line break + { + name: '\u200B', + value: '\u200B', + inline: false, + }, + { + name: 'Average tick delay', + value: `${averageDelay} ms`, + inline: true, + }, + { + name: 'CPU Usage', + value: `${cpuUsage}%`, + inline: true, + }, + { + name: 'Memory usage', + value: `${memoryUsage.toFixed(2)} MB`, + inline: true, + }, + ]; + + embed.setFields(fields); + + await util.replyToInteraction(interaction, {embeds: [embed]}); + } + ) +); + +export default bot; diff --git a/src/modules/meta/meta.ts b/src/modules/meta/meta.ts new file mode 100644 index 0000000..edef775 --- /dev/null +++ b/src/modules/meta/meta.ts @@ -0,0 +1,9 @@ +/** + * @file This file exports all meta modules + * (This has to be done because of how modules are resolved - only the file with the same name as + * its folder will get resolved) + */ + +import bot from './bot.js'; + +export default bot;