diff --git a/config.json.example b/config.json.example index 02dea3148..e5c2b73a9 100644 --- a/config.json.example +++ b/config.json.example @@ -1,9 +1,9 @@ { "TOKEN": "", "MAX_PLAYLIST_SIZE": 10, - "PREFIX": "/", "PRUNING": false, "LOCALE": "en", "STAY_TIME": 30, - "DEFAULT_VOLUME": 100 + "DEFAULT_VOLUME": 100, + "METRICS_PORT": 3000 } \ No newline at end of file diff --git a/index.ts b/index.ts index ebc4fca37..e7aeae553 100644 --- a/index.ts +++ b/index.ts @@ -1,6 +1,9 @@ import { Client, GatewayIntentBits } from "discord.js"; +import startMetricsServer from "./metrics"; import { Bot } from "./structs/Bot"; +startMetricsServer(); + export const bot = new Bot( new Client({ intents: [ diff --git a/interfaces/Config.ts b/interfaces/Config.ts index 3a91d22a8..9cc8961e0 100644 --- a/interfaces/Config.ts +++ b/interfaces/Config.ts @@ -1,9 +1,9 @@ export interface Config { TOKEN: string; - PREFIX: string; MAX_PLAYLIST_SIZE: number; PRUNING: boolean; STAY_TIME: number; DEFAULT_VOLUME: number; LOCALE: string; + METRICS_PORT: number; } diff --git a/metrics/index.ts b/metrics/index.ts new file mode 100644 index 000000000..2d78889dc --- /dev/null +++ b/metrics/index.ts @@ -0,0 +1,34 @@ +import http from 'http'; +import promClient from 'prom-client'; +import { config } from '../utils/config'; +import { Logger } from '../utils/logger'; + +const PORT = config.METRICS_PORT; + +export default function startMetricsServer() { + promClient.collectDefaultMetrics(); + + const server = http.createServer(async function (req, res) { + try { + if (req.url === '/metrics') { + res.setHeader('Content-Type', promClient.register.contentType); + res.write(await promClient.register.metrics()); + res.end(); + return; + } + res.statusCode = 404; + res.write('Not found'); + res.end(); + } catch (err) { + Logger.error(`Metrics server response error`); + Logger.error(err); + res.statusCode = 500; + res.write('Internal server error'); + res.end(); + } + }) + + server.listen(PORT, function () { + console.log(`Metrics server started at port ${PORT}`); + }); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 815eda9e2..616a9ba35 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "i18n": "^0.15.1", "lyrics-finder": "^21.0.5", "play-dl": "^1.9.7", + "prom-client": "^15.0.0", "soundcloud-downloader": "^0.2.3", "string-progressbar": "^1.0.4", "typescript": "^5.2.2", @@ -612,6 +613,14 @@ "make-plural": "^7.0.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@sapphire/async-queue": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", @@ -897,6 +906,11 @@ "node": ">=8" } }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2911,6 +2925,18 @@ "node": ">=0.4.0" } }, + "node_modules/prom-client": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.0.0.tgz", + "integrity": "sha512-UocpgIrKyA2TKLVZDSfm8rGkL13C19YrQBAiG3xo3aDFWcHedxRxI3z+cIcucoxpSO0h5lff5iv/SXoxyeopeA==", + "dependencies": { + "@opentelemetry/api": "^1.4.0", + "tdigest": "^0.1.1" + }, + "engines": { + "node": "^16 || ^18 || >=20" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -3246,6 +3272,14 @@ "node": ">=10" } }, + "node_modules/tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "dependencies": { + "bintrees": "1.0.2" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index 03d397c68..5ce94bb94 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "i18n": "^0.15.1", "lyrics-finder": "^21.0.5", "play-dl": "^1.9.7", + "prom-client": "^15.0.0", "soundcloud-downloader": "^0.2.3", "string-progressbar": "^1.0.4", "typescript": "^5.2.2", diff --git a/structs/Bot.ts b/structs/Bot.ts index 0443b84e9..0c01d5ad3 100644 --- a/structs/Bot.ts +++ b/structs/Bot.ts @@ -23,7 +23,7 @@ import { Song } from "./Song"; export class Bot { private readonly logger = new Logger(this.constructor.name); - public readonly prefix = config.PREFIX; + public readonly prefix = `/`; public commands = new Collection(); public slashCommands = new Array(); public slashCommandsMap = new Collection(); diff --git a/utils/config.ts b/utils/config.ts index 95de3be0d..dc263745f 100644 --- a/utils/config.ts +++ b/utils/config.ts @@ -8,12 +8,12 @@ try { } catch (error) { config = { TOKEN: process.env.TOKEN || "", - PREFIX: process.env.PREFIX || "!", MAX_PLAYLIST_SIZE: 500, // parseInt(process.env.MAX_PLAYLIST_SIZE!) || 10, PRUNING: process.env.PRUNING === "true" ? true : false, STAY_TIME: parseInt(process.env.STAY_TIME!) || 30, DEFAULT_VOLUME: parseInt(process.env.DEFAULT_VOLUME!) || 100, - LOCALE: process.env.LOCALE || "en" + LOCALE: process.env.LOCALE || "en", + METRICS_PORT: parseInt(process.env.METRICS_PORT!) || 3000, }; }