From 6dd05c10ca60b93f6dfb3fd412b02bec7c30ece7 Mon Sep 17 00:00:00 2001 From: Dominik K Date: Sun, 12 Nov 2023 19:38:08 +0100 Subject: [PATCH] Add Prometheus/Grafana metrics (#232) * add promhandler * oop * add: user/server metrics * fix: text fill translations --- package.json | 1 + pnpm-lock.yaml | 26 +++++++++++ src/commands/settings/custom.ts | 7 ++- src/languages/de_DE.json | 10 +++- src/languages/es_ES.json | 16 +++++-- src/languages/fr_FR.json | 16 +++++-- src/util/generateText.ts | 82 ++++++++++++++++++++++++++++----- src/util/promHandler.ts | 49 ++++++++++++++++++++ src/util/wouldYou.ts | 6 +++ 9 files changed, 185 insertions(+), 28 deletions(-) create mode 100644 src/util/promHandler.ts diff --git a/package.json b/package.json index 3bec184f..b771633e 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "moment-timezone": "^0.5.43", "mongoose": "^7.5.0", "profanity-check": "^0.0.3", + "prom-client": "^15.0.0", "quickchart-js": "^3.1.3", "topgg-autoposter": "^2.0.1", "uuid": "^9.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d82629f4..42112127 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ dependencies: profanity-check: specifier: ^0.0.3 version: 0.0.3 + prom-client: + specifier: ^15.0.0 + version: 15.0.0 quickchart-js: specifier: ^3.1.3 version: 3.1.3 @@ -259,6 +262,11 @@ packages: '@napi-rs/canvas-win32-x64-msvc': 0.1.44 dev: false + /@opentelemetry/api@1.7.0: + resolution: {integrity: sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==} + engines: {node: '>=8.0.0'} + dev: false + /@sapphire/async-queue@1.5.0: resolution: {integrity: sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} @@ -493,6 +501,10 @@ packages: engines: {node: '>=8'} dev: true + /bintrees@1.0.2: + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + dev: false + /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -1307,6 +1319,14 @@ packages: engines: {node: '>=10'} dev: false + /prom-client@15.0.0: + resolution: {integrity: sha512-UocpgIrKyA2TKLVZDSfm8rGkL13C19YrQBAiG3xo3aDFWcHedxRxI3z+cIcucoxpSO0h5lff5iv/SXoxyeopeA==} + engines: {node: ^16 || ^18 || >=20} + dependencies: + '@opentelemetry/api': 1.7.0 + tdigest: 0.1.2 + dev: false + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -1542,6 +1562,12 @@ packages: has-flag: 3.0.0 dev: true + /tdigest@0.1.2: + resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==} + dependencies: + bintrees: 1.0.2 + dev: false + /through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} dependencies: diff --git a/src/commands/settings/custom.ts b/src/commands/settings/custom.ts index 96de1c66..4bdf25f3 100644 --- a/src/commands/settings/custom.ts +++ b/src/commands/settings/custom.ts @@ -143,13 +143,12 @@ const command: ChatInputCommand = { message = interaction.options.getString("message"); let newID = makeID(6); - if (option === "wouldyourather") - generativeText = generateWYR(client, message || "", newID); + generativeText = generateWYR(client, message || "", newID, guildDb); else if (option === "neverhaveiever") - generativeText = generateNHIE(client, message || "", newID); + generativeText = generateNHIE(client, message || "", newID, guildDb); else if (option === "wwyd") - generativeText = generateWWYD(client, message || "", newID); + generativeText = generateWWYD(client, message || "", newID, guildDb); typeEmbed = new EmbedBuilder() .setTitle( diff --git a/src/languages/de_DE.json b/src/languages/de_DE.json index 6727074b..a6cf07b6 100644 --- a/src/languages/de_DE.json +++ b/src/languages/de_DE.json @@ -81,7 +81,13 @@ "descID": "ID", "descMsg": "Nachricht", "descCat": "Kategorie", - "descCont": "Inhalt" + "descCont": "Inhalt", + "descAccept": "Es scheint, als hättest du \"{type}\" nicht an den Anfang deines Textes gesetzt. Möchtest du, dass ich es automatisch hinzufüge?", + "descWYR": "Würdest du eher", + "descNHIE": "Ich habe noch nie", + "descWWYD": "Was würdest du tun", + "footerDisable": "Dieser Button wird in 30 Sekunden deaktiviert.", + "success": "Erfolgreich \"{type}\" zum Anfang des Textes hinzugefügt!" }, "embedRemove": { "title": "Diese Would You Nachricht wurde erfolgreich gelöscht!" @@ -157,7 +163,7 @@ }, "Vote": { "embed": { - "title": "Voten hilft **Would You** mehr Server zu erreichen. Also denk daran jeden Tag zu Voten!", + "title": "Abstimmen hilft **Würdest du** mehr Popularität unter den Nutzern auf Discord zu gewinnen! Du kannst alle 12 Stunden abstimmen und den Bot unterstützen!", "value": "Klicke um zu Voten", "footer": "Would You" } diff --git a/src/languages/es_ES.json b/src/languages/es_ES.json index ebac1bf8..29f90bb6 100644 --- a/src/languages/es_ES.json +++ b/src/languages/es_ES.json @@ -43,7 +43,7 @@ "turnOff": "Desactivar", "on": "Activado", "off": "Desactivado", - "desc": "En activant la confidentialité, ton nom d'utilisateur ne sera plus affiché sur les résultats des votes! Tes votes seront complètement anonymes si cette option est activée." + "desc": "¡Activar la privacidad removerá tu nombre de usuario de ser mostrado en los resultados! Tus votos serán completamente anónimos si esto está habilitado." }, "wyCustom": { "error": { @@ -81,7 +81,13 @@ "descID": "ID", "descMsg": "Mensaje", "descCat": "Categoría", - "descCont": "Contenido" + "descCont": "Contenido", + "descAccept": "Parece que no has añadido \"{type}\" al comienzo de tu texto. ¿Quieres que lo añada automáticamente?", + "descWYR": "Prefieres", + "descNHIE": "Nunca he", + "descWWYD": "Qué harías", + "footerDisable": "Este botón se desactivará en 30 segundos.", + "success": "¡Se ha añadido con éxito \"{type}\" al comienzo del texto!" }, "embedRemove": { "title": "¡Se eliminó con éxito ese mensaje Would You!" @@ -97,7 +103,7 @@ "descCatNHIE": "**Categoría**: Nunca lo he hecho nunca", "descCatWYR": "**Categoría**: ¿Podrías más bien", "descCatTRUTH": "**Categoría**: Verdad", - "descCatDARE": "**Categoría**: Atrévete", + "descCatDARE": "**Categoría**: Reto", "descCatWWYD": "**Categoría**: ¿Qué harías tú" } } @@ -142,7 +148,7 @@ "Help": { "embed": { "title": "Información", - "description": "**Would You** es un bot de discordia creado para aumentar la actividad del servidor de discordia. Te permite jugar a **Verdad o reto**, **Preferirías**, **Más alto o más bajo**, **Nunca lo he hecho** y **Qué harías**.", + "description": "**Would You** es un bot de discord construido para aumentar la actividad del servidor. Te permite jugar **Verdad o Reto**, **Preferías**, **Mayor o Menor**, **Nunca he** y **Qué Harías**.", "footer": "Would You", "Fields": { "name": "**Mis Comandos**", @@ -157,7 +163,7 @@ }, "Vote": { "embed": { - "title": "¡Votar ayuda **Would You** a ganar más usuarios, asegúrate de votar todos los días!", + "title": "¡Votar ayuda a **Would You** a ganar más popularidad entre los usuarios en discord! ¡Puedes votar cada 12 horas y apoyar al bot!", "value": "Clica para votar", "footer": "Would You" } diff --git a/src/languages/fr_FR.json b/src/languages/fr_FR.json index 4254fdd8..982d1e56 100644 --- a/src/languages/fr_FR.json +++ b/src/languages/fr_FR.json @@ -81,7 +81,13 @@ "descID": "ID", "descMsg": "Message", "descCat": "Catégorie", - "descCont": "Contenue" + "descCont": "Contenue", + "descAccept": "Il semble que vous n'ayez pas ajouté \"{type}\" au début de votre texte. Souhaitez-vous que je l'ajoute automatiquement ?", + "descWYR": "Préférez-vous", + "descNHIE": "Jamais je n'ai", + "descWWYD": "Que feriez-vous", + "footerDisable": "Ce bouton sera désactivé dans 30 secondes.", + "success": "L'ajout de \"{type}\" au début du texte a été effectué avec succès !" }, "embedRemove": { "title": "Vous avez supprimé ce message personnalisé avec succès !" @@ -96,8 +102,8 @@ "title": "Messages Personnalisés Préférez-vous", "descCatNHIE": "**Catégorie**: Je n'ai jamais", "descCatWYR": "**Catégorie**: Préférez-vous", - "descCatTRUTH": "**Catégorie**: Vérité", - "descCatDARE": "**Catégorie**: Ose", + "descCatTRUTH": "**Catégorie** : Vérité", + "descCatDARE": "**Catégorie** : Action", "descCatWWYD": "**Catégorie**: Que feriez-vous" } } @@ -142,7 +148,7 @@ "Help": { "embed": { "title": "Information", - "description": "**Would You** est un bot discord conçu pour augmenter l'activité du serveur discord. Il te permet de jouer à **Truth or Dare**, **Would You Rather**, **Higher or Lower**, **Never Have I Ever** et **What Would You Do**.", + "description": "**Would You** est un bot discord conçu pour augmenter l'activité du serveur discord. Il vous permet de jouer à **Action ou Vérité**, **Préférez-vous**, **Plus ou moins**, **Je n'ai jamais** et **Que feriez-vous**.", "footer": "Would You", "Fields": { "name": "**Mes commandes**", @@ -157,7 +163,7 @@ }, "Vote": { "embed": { - "title": "Voter aide **Would You** à gagner plus d'utilisateurs ! Assurez-vous de voter tous les jours !", + "title": "Le vote permet à **Would You** de gagner en popularité auprès des utilisateurs sur discord ! Vous pouvez voter toutes les 12 heures et soutenir le bot !", "value": "Clique pour voter", "footer": "Would You" } diff --git a/src/util/generateText.ts b/src/util/generateText.ts index 35a9156a..a115c10e 100644 --- a/src/util/generateText.ts +++ b/src/util/generateText.ts @@ -1,62 +1,120 @@ import WouldYou from "./wouldYou"; + +interface GuildDB { + language: string; +} + export function generateWYR( client: WouldYou, text: string, id: string, + guilddb: GuildDB, ): object { - const wyr = /^(?!.*(?:would you rather)).*$/i; + const wyrRegexes: Record = { + "en_EN": /^(?!.*(?:would you rather)).*$/i, + "de_DE": /^(?!.*(?:würdest du eher)).*$/i, + "es_ES": /^(?!.*(?:preferirías)).*$/i, + "fr_FR": /^(?!.*(?:préfères-tu)).*$/i, + }; - if (wyr.test(text)) { + const wyrMap: Record = { + "en_EN": "Would you rather", + "de_DE": "Würdest du eher", + "es_ES": "Preferirías", + "fr_FR": "Préfères-tu", + }; + + const guildLanguage = guilddb.language as string; + const wyrRegexPattern = wyrRegexes[guildLanguage]; + const responsePhrase = wyrMap[guildLanguage]; + + if (wyrRegexPattern.test(text.toLowerCase())) { client.customAdd.set(id, { type: "wouldyourather", - text: `Would you rather ${text}`, + text: `${responsePhrase} ${text}`, }); return { value: false, type: "wouldyourather", - text: `Would you rather ${text}`, + text: `${responsePhrase} ${text}`, }; } else { return { value: true, type: "wouldyourather", text: text }; } } + export function generateWWYD( client: WouldYou, text: string, id: string, + guilddb: GuildDB, ): object { - const wwyd = /^(?!.*(?:what would you do)).*$/i; + const languageRegex: Record = { + "en_EN": /^(?!.*(?:what would you do)).*$/i, + "de_DE": /^(?!.*(?:was würdest du tun)).*$/i, + "es_ES": /^(?!.*(?:qué harías)).*$/i, + "fr_FR": /^(?!.*(?:que ferais-tu)).*$/i, + }; - if (wwyd.test(text)) { + const languageMap: Record = { + "en_EN": "What would you do", + "de_DE": "Was würdest du tun", + "es_ES": "Qué harías", + "fr_FR": "Que ferais-tu", + }; + + const guildLanguage = guilddb.language as string; + const languageRegexPattern = languageRegex[guildLanguage]; + const responsePhrase = languageMap[guildLanguage]; + if (languageRegexPattern.test(text.toLowerCase())) { client.customAdd.set(id, { type: "wwyd", - text: `What would you do ${text}`, + text: `${responsePhrase} ${text}`, }); - return { value: false, type: "wwyd", text: `What would you do ${text}` }; + return { value: false, type: "wwyd", text: `${responsePhrase} ${text}` }; } else { return { value: true, type: "wwyd", text: text }; } } + export function generateNHIE( client: WouldYou, text: string, id: string, + guilddb: GuildDB, ): object { - const nhie = /^(?!.*(?:never have i ever)).*$/i; + const nhieRegexes: Record = { + "en_EN": /^(?!.*(?:never have i ever)).*$/i, + "de_DE": /^(?!.*(?:niemals habe ich)).*$/i, + "es_ES": /^(?!.*(?:nunca he)).*$/i, + "fr_FR": /^(?!.*(?:jamais je)).*$/i, + }; + + const nhieMap: Record = { + "en_EN": "Never have I ever", + "de_DE": "Niemals habe ich", + "es_ES": "Nunca he", + "fr_FR": "Jamais je", + }; - if (nhie.test(text)) { + const guildLanguage = guilddb.language as string; + const nhieRegexPattern = nhieRegexes[guildLanguage]; + const responsePhrase = nhieMap[guildLanguage]; + + if (nhieRegexPattern.test(text.toLowerCase())) { client.customAdd.set(id, { type: "neverhaveiever", - text: `Never have I ever ${text}`, + text: `${responsePhrase} ${text}`, }); return { value: false, type: "neverhaveiever", - text: `Never have I ever ${text}`, + text: `${responsePhrase} ${text}`, }; } else { return { value: true, type: "neverhaveiever", text: text }; } } + diff --git a/src/util/promHandler.ts b/src/util/promHandler.ts new file mode 100644 index 00000000..596dde10 --- /dev/null +++ b/src/util/promHandler.ts @@ -0,0 +1,49 @@ +import express from "express"; +import { Registry, Gauge } from "prom-client" +import WouldYou from "./wouldYou"; + +export default class prometheusClient { + private registry: Registry; + private serverGauge: Gauge; + private userGauge: Gauge; + private client: WouldYou; + + constructor(client: WouldYou) { + this.client = client; + } + + initialize() { + this.registry = new Registry(); + this.serverGauge = new Gauge({ + name: "server_count", + help: "The amount of servers the bot is in.", + registers: [this.registry], + }); + this.userGauge = new Gauge({ + name: "user_count", + help: "The amount of users the bot has.", + registers: [this.registry], + }); + + const app = express(); + + app.use(express.json()); + + // Expose Prometheus metrics endpoint + app.get("/metrics", async (req, res) => { + this.serverGauge.set(this.client.guilds.cache.size); + this.userGauge.set(this.client.guilds.cache + .reduce((a, b) => a + b.memberCount, 0)); + res.end(await this.registry.metrics()); + }); + + app.listen(3029, () => { + console.log("Prometheus metrics listening on port 3029"); + }); + } + + + getMetrics() { + return this.registry.metrics(); + } +} diff --git a/src/util/wouldYou.ts b/src/util/wouldYou.ts index 01dc4905..260fb0c0 100644 --- a/src/util/wouldYou.ts +++ b/src/util/wouldYou.ts @@ -19,6 +19,7 @@ import WebhookHandler from "./webhookHandler"; import CooldownHandler from "./cooldownHandler"; import DailyMessage from "./dailyMessage"; import VoteLogger from "./voteLogger"; +import PrometheusClient from "./promHandler"; import Voting from "./votingHandler"; import { Button, ChatInputCommand } from "../models"; import { fileToCollection } from "./Functions/fileToCollection"; @@ -41,6 +42,7 @@ export default class WouldYou extends Client { readonly webhookHandler: WebhookHandler; readonly keepAlive: KeepAlive; readonly voteLogger: VoteLogger; + readonly prometheusClient: PrometheusClient; readonly dailyMessage: DailyMessage; readonly voting: Voting; @@ -100,6 +102,10 @@ export default class WouldYou extends Client { // Init the cluster client this.cluster = new ClusterClient(this); + // Init the prometheus client + this.prometheusClient = new PrometheusClient(this); + this.prometheusClient.initialize() + // The database handler this.database = new DatabaseHandler(process.env.MONGO_URI as string); this.database.connectToDatabase().then(() => {