From 9c50e3963fe0a7c6fd95818e3207cf035f4ac641 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:08:25 +0530 Subject: [PATCH 01/15] feat: add locales support and move API inside `src/` --- README.md | 4 +- api/.gitignore | 3 - api/index.ts | 42 - api/package-lock.json | 2638 ----------------------- api/package.json | 27 - api/tsconfig.json | 109 - locales/en.yml | 51 + locales/ja.yml | 20 + package-lock.json | 1543 ++++++++++++- package.json | 7 +- prisma/schema.prisma | 1 + src/api/index.ts | 59 + src/commands/context-menu/blacklist.ts | 13 +- src/commands/slash/Information/rules.ts | 18 +- src/commands/slash/Information/vote.ts | 26 +- src/index.ts | 6 +- src/managers/CommandManager.ts | 11 +- src/scripts/network/onboarding.ts | 57 +- src/typings/index.d.ts | 4 + src/utils/Constants.ts | 19 +- src/utils/locales.ts | 18 + 21 files changed, 1736 insertions(+), 2940 deletions(-) delete mode 100644 api/.gitignore delete mode 100644 api/index.ts delete mode 100644 api/package-lock.json delete mode 100644 api/package.json delete mode 100644 api/tsconfig.json create mode 100644 locales/en.yml create mode 100644 locales/ja.yml create mode 100644 src/api/index.ts create mode 100644 src/utils/locales.ts diff --git a/README.md b/README.md index 3a2f4653f..9746edcdb 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ This repo contains the source code for the InterChat Discord bot. InterChat is a ### Prerequisites -1. [Node.js v18.0.0](https://nodejs.org/en/download/current/) +1. [Node.js v18.0.0+](https://nodejs.org/en/download/current/) () 2. [Git](https://git-scm.com/downloads) 3. [MongoDB](https://www.mongodb.com/try/download/community) 4. [NPM](https://www.npmjs.com/get-npm) or [Yarn](https://yarnpkg.com/getting-started/install) (we are using npm in this guide) 5. [An Imgur API Key](https://api.imgur.com/oauth2/addclient) (optional, for setting hub icon and banner) -6. [Python 3.8+](https://www.python.org/downloads/) & [Visual Studio Build Tools (Windows Only)](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) (optional, for the API) +6. [Python 2.7](https://www.python.org/downloads/release/python-2718/) & [Visual Studio Build Tools (Windows Only)](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) (optional, for the API) > [!NOTE] > If you are on Windows, you will need to delete the `api/package-lock.json` before installing dependencies. And if that doesn't work, run `npm i` in a terminal with admin privileges. And if you're getting errors related to node-gyp, install `node-gyp` globally using `npm i -g node-gyp`. Or follow the [Installation Instructions](https://github.com/nodejs/node-gyp?tab=readme-ov-file#on-windows). Honestly, why are you on Windows? You can also just re-install Node.js and tick the "Automatically install the necessary tools" option, which should do all the stuff mentioned above for you. diff --git a/api/.gitignore b/api/.gitignore deleted file mode 100644 index c7df64ac6..000000000 --- a/api/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules - -dist/ \ No newline at end of file diff --git a/api/index.ts b/api/index.ts deleted file mode 100644 index b84d8daec..000000000 --- a/api/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import express from 'express'; -import { node } from '@tensorflow/tfjs-node'; -import { load, predictionType } from 'nsfwjs'; - -const model = await load(); -const app = express(); -const port = 3000; - -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -const analyzeImage = async (url: string): Promise => { - const imageBuffer = await (await fetch(url)).arrayBuffer(); - - const imageTensor = await node.decodeImage(Buffer.from(imageBuffer), 3) as any; - const predictions = await model.classify(imageTensor); - imageTensor.dispose(); - - return predictions; -}; - -app.get('/nsfw', async (req, res) => { - const url = req.query.url; - - if (!url || typeof url !== 'string') return res.status(400).json({ error: 'Missing url query parameter.' }); - - const regex = - /(?:(?:(?:[A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)(?:(?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)(?:\.jpg|\.jpeg|\.png)/; - if (!regex.test(url)) { - return res - .status(400) - .send({ error: 'Invalid url parameter. Must be a valid PNG, JPG or JPEG image URL.' }); - } - - const predictions = await analyzeImage(url); - if (!predictions) return res.status(500).json({ error: 'Something went wrong while analyzing the image.' }); - return res.status(200).json(predictions); -}); - -app.listen(port, () => { - console.log(`API listening on port ${port}`); -}); diff --git a/api/package-lock.json b/api/package-lock.json deleted file mode 100644 index 4ad9c66c1..000000000 --- a/api/package-lock.json +++ /dev/null @@ -1,2638 +0,0 @@ -{ - "name": "api", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "api", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@tensorflow/tfjs-node": "^4.11.0", - "express": "^4.18.2", - "nsfwjs": "^2.4.2", - "tsc-watch": "^6.0.4" - }, - "devDependencies": { - "@types/express": "^4.17.19", - "@types/node": "^20.8.4", - "typescript": "^5.2.2" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz", - "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nsfw-filter/gif-frames": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@nsfw-filter/gif-frames/-/gif-frames-1.0.2.tgz", - "integrity": "sha512-XZrbJWEN8YfVla5i+PD4Wj51rRlJ8OgnXiPjjOt/OsrbsCR9GZRD4jr953oNWcwiRaoIcOCFWQNMQukO7Yb1dA==", - "dependencies": { - "@nsfw-filter/save-pixels": "^2.3.4", - "get-pixels-frame-info-update": "3.3.2", - "multi-integer-range": "3.0.0" - } - }, - "node_modules/@nsfw-filter/save-pixels": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@nsfw-filter/save-pixels/-/save-pixels-2.3.4.tgz", - "integrity": "sha512-dRZXwrXadMvxwJYKChrDBqC6GNvxVqlmdkyvZJO5DV65qyBsHZw8bPg9CnX7EgpxGl6+4ba/MAdHDLxs2XoD0Q==", - "dependencies": { - "gif-encoder": "0.4.1", - "ndarray": "1.0.18", - "ndarray-ops": "1.2.2", - "pngjs-nozlib": "1.0.0", - "through": "2.3.4" - } - }, - "node_modules/@tensorflow/tfjs": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-3.21.0.tgz", - "integrity": "sha512-khcARd3/872llL/oF4ouR40qlT71mylU66PGT8kHP/GJ5YKj44sv8lDRjU7lOVlJK7jsJFWEsNVHI3eMc/GWNQ==", - "peer": true, - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "3.21.0", - "@tensorflow/tfjs-backend-webgl": "3.21.0", - "@tensorflow/tfjs-converter": "3.21.0", - "@tensorflow/tfjs-core": "3.21.0", - "@tensorflow/tfjs-data": "3.21.0", - "@tensorflow/tfjs-layers": "3.21.0", - "argparse": "^1.0.10", - "chalk": "^4.1.0", - "core-js": "3", - "regenerator-runtime": "^0.13.5", - "yargs": "^16.0.3" - }, - "bin": { - "tfjs-custom-module": "dist/tools/custom_module/cli.js" - } - }, - "node_modules/@tensorflow/tfjs-backend-cpu": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-3.21.0.tgz", - "integrity": "sha512-88S21UAdzyK0CsLUrH17GPTD+26E85OP9CqmLZslaWjWUmBkeTQ5Zqyp6iK+gELnLxPx6q7JsNEeFuPv4254lQ==", - "peer": true, - "dependencies": { - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "3.21.0" - } - }, - "node_modules/@tensorflow/tfjs-backend-webgl": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-3.21.0.tgz", - "integrity": "sha512-N4zitIAT9IX8B8oe489qM3f3VcESxGZIZvHmVP8varOQakTvTX859aaPo1s8hK1qCy4BjSGbweooZe4U8D4kTQ==", - "peer": true, - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "3.21.0", - "@types/offscreencanvas": "~2019.3.0", - "@types/seedrandom": "^2.4.28", - "@types/webgl-ext": "0.0.30", - "@types/webgl2": "0.0.6", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "3.21.0" - } - }, - "node_modules/@tensorflow/tfjs-converter": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-3.21.0.tgz", - "integrity": "sha512-12Y4zVDq3yW+wSjSDpSv4HnpL2sDZrNiGSg8XNiDE4HQBdjdA+a+Q3sZF/8NV9y2yoBhL5L7V4mMLDdbZBd9/Q==", - "peer": true, - "peerDependencies": { - "@tensorflow/tfjs-core": "3.21.0" - } - }, - "node_modules/@tensorflow/tfjs-core": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-3.21.0.tgz", - "integrity": "sha512-YSfsswOqWfd+M4bXIhT3hwtAb+IV8+ODwIxwdFR/7jTAPZP1wMVnSlpKnXHAN64HFOiP+Tm3HmKusEZ0+09A0w==", - "peer": true, - "dependencies": { - "@types/long": "^4.0.1", - "@types/offscreencanvas": "~2019.3.0", - "@types/seedrandom": "^2.4.28", - "@types/webgl-ext": "0.0.30", - "@webgpu/types": "0.1.16", - "long": "4.0.0", - "node-fetch": "~2.6.1", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - } - }, - "node_modules/@tensorflow/tfjs-data": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-3.21.0.tgz", - "integrity": "sha512-eFLfw2wIcFNxnP2Iv/SnVlihehzKMumk1b5Prcx1ixk/SbkCo5u0Lt7OVOWaEOKVqvB2sT+dJcTjAh6lrCC/QA==", - "peer": true, - "dependencies": { - "@types/node-fetch": "^2.1.2", - "node-fetch": "~2.6.1", - "string_decoder": "^1.3.0" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "3.21.0", - "seedrandom": "^3.0.5" - } - }, - "node_modules/@tensorflow/tfjs-layers": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-3.21.0.tgz", - "integrity": "sha512-CMVXsraakXgnXEnqD9QbtResA7nvV7Jz20pGmjFIodcQkClgmFFhdCG5N+zlVRHEz7VKG2OyfhltZ0dBq/OAhA==", - "peer": true, - "peerDependencies": { - "@tensorflow/tfjs-core": "3.21.0" - } - }, - "node_modules/@tensorflow/tfjs-node": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-4.11.0.tgz", - "integrity": "sha512-dKMabHsyXEjVM9hSPITa9s7+SA7mqIRCN3ITbOoiVQ4JVlpSg2sffORWOQaRbYISP7F+l6RFiw0EB7t5vCXPzg==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "1.0.9", - "@tensorflow/tfjs": "4.11.0", - "adm-zip": "^0.5.2", - "google-protobuf": "^3.9.2", - "https-proxy-agent": "^2.2.1", - "progress": "^2.0.0", - "rimraf": "^2.6.2", - "tar": "^4.4.6" - }, - "engines": { - "node": ">=8.11.0" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-4.11.0.tgz", - "integrity": "sha512-s6Vbz3IvMz2zNbH8/VptpRXzkwVjmuzT48esYLXJxMKtTcob4m5Srdxo7B+eJSDrWYkutXruiivaWmihFmu5rA==", - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "4.11.0", - "@tensorflow/tfjs-backend-webgl": "4.11.0", - "@tensorflow/tfjs-converter": "4.11.0", - "@tensorflow/tfjs-core": "4.11.0", - "@tensorflow/tfjs-data": "4.11.0", - "@tensorflow/tfjs-layers": "4.11.0", - "argparse": "^1.0.10", - "chalk": "^4.1.0", - "core-js": "3.29.1", - "regenerator-runtime": "^0.13.5", - "yargs": "^16.0.3" - }, - "bin": { - "tfjs-custom-module": "dist/tools/custom_module/cli.js" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-backend-cpu": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-4.11.0.tgz", - "integrity": "sha512-2zmGX9MuR8AwscSGOybz4fBOFgQDnj+ZCWGkLxDzbKecy9GxuilukT46xB2zU0kSq7Mf3ncfE/9eUEy6a7ZDqQ==", - "dependencies": { - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.11.0" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-backend-webgl": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-4.11.0.tgz", - "integrity": "sha512-sM/B65u+1T3U+Ctiq1fn5j6VmiLEZW7BpuSa3ZXDXtIS07MoZ2FTuO8BMudxEY4xGpTyoOzqTOGT9BaGO3qrWg==", - "dependencies": { - "@tensorflow/tfjs-backend-cpu": "4.11.0", - "@types/offscreencanvas": "~2019.3.0", - "@types/seedrandom": "^2.4.28", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.11.0" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-converter": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-4.11.0.tgz", - "integrity": "sha512-j2JEVwkqh+pyin+sxUiNUG7QOIU2S0+5SzN8LFXHlR90/bPvC2qiaaSPYdGG/BYidFc27QCHD3obBXrb1EE/ow==", - "peerDependencies": { - "@tensorflow/tfjs-core": "4.11.0" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-core": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-4.11.0.tgz", - "integrity": "sha512-t0mCNIco8wg6aZdHWT1d6ZuKtbbdY5y871ELWLSUA1+grXDvvaroHYh5eeJexJYXeg+EQ0/hzB0G8nLsLjlyVQ==", - "dependencies": { - "@types/long": "^4.0.1", - "@types/offscreencanvas": "~2019.7.0", - "@types/seedrandom": "^2.4.28", - "@webgpu/types": "0.1.30", - "long": "4.0.0", - "node-fetch": "~2.6.1", - "seedrandom": "^3.0.5" - }, - "engines": { - "yarn": ">= 1.3.2" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-core/node_modules/@types/offscreencanvas": { - "version": "2019.7.1", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.1.tgz", - "integrity": "sha512-+HSrJgjBW77ALieQdMJvXhRZUIRN1597L+BKvsyeiIlHHERnqjcuOLyodK3auJ3Y3zRezNKtKAhuQWYJfEgFHQ==" - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-data": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-4.11.0.tgz", - "integrity": "sha512-8E6CVpd7kxRFtVL7kvz6WF5jH18pNN2wEcm2yA87xq37JwcRsIPTkrmfyqCHlJZmiWn3RQbP59Sl05gbBnFo5w==", - "dependencies": { - "@types/node-fetch": "^2.1.2", - "node-fetch": "~2.6.1", - "string_decoder": "^1.3.0" - }, - "peerDependencies": { - "@tensorflow/tfjs-core": "4.11.0", - "seedrandom": "^3.0.5" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@tensorflow/tfjs-layers": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-4.11.0.tgz", - "integrity": "sha512-ErVqwxjpu2YM3uJRj2o5GbBTYViUwnqOb0wKWuCVukVmGeWrUzf1X00Ky3dP4xfilfAvq+B26dg7QN4YNHeaKg==", - "peerDependencies": { - "@tensorflow/tfjs-core": "4.11.0" - } - }, - "node_modules/@tensorflow/tfjs-node/node_modules/@webgpu/types": { - "version": "0.1.30", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.30.tgz", - "integrity": "sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==" - }, - "node_modules/@tensorflow/tfjs-node/node_modules/core-js": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.1.tgz", - "integrity": "sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", - "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.36", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", - "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.19.tgz", - "integrity": "sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.37", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", - "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", - "dev": true - }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" - }, - "node_modules/@types/mime": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.8.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz", - "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==", - "dependencies": { - "undici-types": "~5.25.1" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/offscreencanvas": { - "version": "2019.3.0", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", - "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" - }, - "node_modules/@types/qs": { - "version": "6.9.8", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", - "dev": true - }, - "node_modules/@types/seedrandom": { - "version": "2.4.31", - "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.31.tgz", - "integrity": "sha512-O8t5IyMqJ5qSxOR/UJ4hWL64ix05ofO7FV9IgMwVtUvHu7EsI8YyMJOg7SAWrWhDqizj1oxNZAGgfkCrhk7GTQ==" - }, - "node_modules/@types/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", - "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", - "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/webgl-ext": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz", - "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==", - "peer": true - }, - "node_modules/@types/webgl2": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.6.tgz", - "integrity": "sha512-50GQhDVTq/herLMiqSQkdtRu+d5q/cWHn4VvKJtrj4DJAjo1MNkWYa2MA41BaBO1q1HgsUjuQvEOk0QHvlnAaQ==", - "peer": true - }, - "node_modules/@webgpu/types": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.16.tgz", - "integrity": "sha512-9E61voMP4+Rze02jlTXud++Htpjyyk8vw5Hyw9FGRrmhHQg2GqbuOfwf5Klrb8vTxc2XWI3EfO7RUHMpxTj26A==", - "peer": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/core-js": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.0.tgz", - "integrity": "sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw==", - "hasInstallScript": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cwise-compiler": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", - "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", - "dependencies": { - "uniq": "^1.0.0" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz", - "integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", - "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" - }, - "node_modules/fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dependencies": { - "minipass": "^2.6.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-pixels-frame-info-update": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/get-pixels-frame-info-update/-/get-pixels-frame-info-update-3.3.2.tgz", - "integrity": "sha512-LzVij57X/gK4Y6LpcDdqj+R9WCpD6Sv3ZH85GMA+S3xgPGCz81mHql4GiSnF4GijRjk7TE0ja2sDr8FFYKLe2g==", - "dependencies": { - "data-uri-to-buffer": "0.0.3", - "jpeg-js": "^0.3.2", - "mime-types": "^2.0.1", - "ndarray": "^1.0.13", - "ndarray-pack": "^1.1.1", - "node-bitmap": "0.0.1", - "omggif": "^1.0.5", - "parse-data-uri": "^0.2.0", - "pngjs": "^3.3.3", - "request": "^2.44.0", - "through": "^2.3.4" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/gif-encoder": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/gif-encoder/-/gif-encoder-0.4.1.tgz", - "integrity": "sha512-++rNGpDBgWQ9eXj9JfTBLHMUEd7lDOdzIvFyHQM9yL8ffxkcg4G6jWmsgu/r59Uq6nHc3wcVwtgy3geLnIWunQ==", - "dependencies": { - "readable-stream": "~1.1.9" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/gif-encoder/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/gif-encoder/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/google-protobuf": { - "version": "3.21.2", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", - "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/iota-array": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", - "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "node_modules/jpeg-js": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", - "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==" - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dependencies": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "node_modules/minipass/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dependencies": { - "minipass": "^2.9.0" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/multi-integer-range": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/multi-integer-range/-/multi-integer-range-3.0.0.tgz", - "integrity": "sha512-uQzynjVJ8F7x5wjaK0g4Ybhy2TvO/pk96+YHyS5g1W4GuUEV6HMebZ8HcRwWgKIRCUT2MLbM5uCKwYcAqkS+8Q==" - }, - "node_modules/ndarray": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz", - "integrity": "sha512-jUz6G+CIsEsqs2VlB1EvaQSAA0Jkf8YKm7eFBleKyhiQjYWzTxXqHzWEOm3jFoGCpxGh4DnPUYHB4ECWE+n9SQ==", - "dependencies": { - "iota-array": "^1.0.0", - "is-buffer": "^1.0.2" - } - }, - "node_modules/ndarray-ops": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", - "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", - "dependencies": { - "cwise-compiler": "^1.0.0" - } - }, - "node_modules/ndarray-pack": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", - "integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==", - "dependencies": { - "cwise-compiler": "^1.1.2", - "ndarray": "^1.0.13" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-bitmap": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz", - "integrity": "sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==", - "engines": { - "node": ">=v0.6.5" - } - }, - "node_modules/node-cleanup": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", - "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==" - }, - "node_modules/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/nsfwjs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/nsfwjs/-/nsfwjs-2.4.2.tgz", - "integrity": "sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==", - "dependencies": { - "@nsfw-filter/gif-frames": "1.0.2" - }, - "peerDependencies": { - "@tensorflow/tfjs": "^3.18.0" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/omggif": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", - "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parse-data-uri": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz", - "integrity": "sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==", - "dependencies": { - "data-uri-to-buffer": "0.0.3" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "dependencies": { - "through": "~2.3" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pngjs-nozlib": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz", - "integrity": "sha512-N1PggqLp9xDqwAoKvGohmZ3m4/N9xpY0nDZivFqQLcpLHmliHnCp9BuNCsOeqHWMuEEgFjpEaq9dZq6RZyy0fA==", - "engines": { - "iojs": ">= 1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dependencies": { - "event-stream": "=3.3.4" - }, - "bin": { - "ps-tree": "bin/ps-tree.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", - "dependencies": { - "duplexer": "~0.1.1" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "dependencies": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "engines": { - "node": ">=4.5" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/through": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.4.tgz", - "integrity": "sha512-DwbmSAcABsMazNkLOJJSLRC3gfh4cPxUxJCn9npmvbcI6undhgoJ2ShvEOgZrW8BH62Gyr9jKboGbfFcmY5VsQ==" - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/tsc-watch": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.0.4.tgz", - "integrity": "sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "node-cleanup": "^2.1.2", - "ps-tree": "^1.2.0", - "string-argv": "^0.3.1" - }, - "bin": { - "tsc-watch": "dist/lib/tsc-watch.js" - }, - "engines": { - "node": ">=12.12.0" - }, - "peerDependencies": { - "typescript": "*" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.25.3", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" - }, - "node_modules/uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - } - } -} diff --git a/api/package.json b/api/package.json deleted file mode 100644 index b653d7403..000000000 --- a/api/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "api", - "version": "1.0.0", - "description": "", - "main": "dist/index.js", - "scripts": { - "build": "npx tsc", - "start": "node dist/index.js", - "serve": "tsc-watch --outDir ./dist --onSuccess \"node .\"", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "type": "module", - "devDependencies": { - "@types/express": "^4.17.19", - "@types/node": "^20.8.4", - "typescript": "^5.2.2" - }, - "dependencies": { - "@tensorflow/tfjs-node": "^4.11.0", - "express": "^4.18.2", - "nsfwjs": "^2.4.2", - "tsc-watch": "^6.0.4" - } -} diff --git a/api/tsconfig.json b/api/tsconfig.json deleted file mode 100644 index fb43ed72a..000000000 --- a/api/tsconfig.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "NodeNext", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 000000000..2bbf07174 --- /dev/null +++ b/locales/en.yml @@ -0,0 +1,51 @@ +commands: + rules: + embed: + description: | + ### 📜 InterChat Network Rules + + 1. **Use Common Sense:** Be considerate of others and their views. No slurs, extreme derogatory language or any actions that can disrupt the chat's comfort. + 2. **No Spamming or Flooding:** Avoid repeated, nonsensical, or overly lengthy messages. + 3. **Keep Private Matters Private:** Avoid sharing personal information across the network. + 4. **No Harassment:** Trolling, insults, or harassment of any kind are not tolerated. + 5. **No NSFW/NSFL Content:** Posting explicit NSFW/NSFL content will result in immediate blacklist. + 6. **Respect Sensitive Topics:** Do not trivialize self-harm, suicide, violence, or other offensive topics. + 7. **Report Concerns:** If you observe a violation of these rules, report it to the appropriate hub moderator or InterChat staff for further action. + + Any questions? Join our [support server]({{{support_invite}}}). + vote: + embed: + description: | + ## 🗳️ Vote for InterChat and Enjoy Exclusive Perks + Your contribution is invaluable in elevating InterChat's position on Top.gg. Each and every vote makes a significant difference! + + As our way of expressing gratitude for your support, we are thrilled to offer you exclusive advantages. By casting your vote for InterChat, you'll unlock: + + - Edit messages within hubs + - Translating messages (and much more on the way!) + + We deeply appreciate your unwavering support. Thank you! 🙏 + +network: + onboarding: + embed: + title: 👋 Hey there, welcome to {{hubName}}! + description: | + To keep things organized, it's recommended to use a separate channel for just for this hub. But don't worry, you can always change this later. + + **How it works:** The InterChat Network is like a magic bridge that links channels on different servers that are with us in this hub. Learn more at our [guide](https://discord-interchat.github.io/docs). + + footer: "InterChat Network | Version {{version}}" + +blacklist: + user: + success: + embed: + description: "{{emoji}} **{{username}}** has been successfully blacklisted!" + +errors: + messageNotSentOrExpired: "{{emoji}} This message was not sent in a hub, has expired, or you lack permissions to perform this action." + cannotPerformAction: "{{emoji}} Sorry, you can't perform this action. Please run the command yourself." + +other: + interchatVersion: "InterChat {{version}}" diff --git a/locales/ja.yml b/locales/ja.yml new file mode 100644 index 000000000..3113ae62e --- /dev/null +++ b/locales/ja.yml @@ -0,0 +1,20 @@ +commands: + rules: + embed: + description: | + ### 📜 InterChat Networkのルール + + 1. 常識を使う: 他人や彼らの意見を考慮してください。差別用語、極端な軽蔑的な言葉、またはチャットの快適さを妨げる可能性のある行動は禁止されています。 + 2. スパムやフラッディングは禁止: 繰り返された、意味不明な、または非常に長いメッセージは避けてください。 + 3. プライベートな事柄はプライベートに: ネットワーク全体での個人情報の共有は避けてください。 + 4. 嫌がらせは禁止: トローリング、侮辱、またはあらゆる形の嫌がらせは許容されません。 + 5. NSFW/NSFLコンテンツは禁止: 露骨なNSFW/NSFLコンテンツの投稿は即時にブラックリスト入りします。 + 6. 感 sensitiveなトピックに敬意を払う: 自傷行為、自殺、暴力、またはその他の攻撃的なトピックを軽視しないでください。 + 7. 問題を報告する: これらのルールの違反を目撃した場合は、適切なハブモデレーターかInterChatスタッフに報告してください。 + + 質問はありますか?[サポートサーバーに参加する]({{{support_invite}}}). +blacklist: + user: + success: + embed: + description: blacklist.user.success.embed.description diff --git a/package-lock.json b/package-lock.json index 44ec9e96a..ea36bf543 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,19 +11,24 @@ "dependencies": { "@prisma/client": "^5.6.0", "@sentry/node": "^7.80.1", + "@tensorflow/tfjs-node": "3.18.0", "@translate-tools/core": "^1.0.0", "common-tags": "^1.8.2", "discord-arts": "^0.5.8", "discord-hybrid-sharding": "^2.1.4", "discord.js": "^14.14.1", "dotenv": "^16.3.1", + "i18n": "^0.15.1", "lodash": "^4.17.21", + "nsfwjs": "^2.4.2", "parse-duration": "^1.1.0", - "winston": "^3.11.0" + "winston": "^3.11.0", + "yaml": "^2.3.4" }, "devDependencies": { "@sentry/cli": "^2.21.5", "@types/common-tags": "^1.8.4", + "@types/i18n": "^0.13.10", "@types/jest": "^29.5.8", "@types/lodash": "^4.14.201", "@types/node": "^20.9.2", @@ -1901,6 +1906,174 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.4.tgz", + "integrity": "sha512-M669Qo4nRT7iDmQEjQYC7RU8Z6dpz9UmSbkJ1OFEja3uevCdLKh7IZZki7L1TZj02kRyl82snXFY8QqkyfowrQ==", + "dependencies": { + "detect-libc": "^1.0.3", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.1", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "rimraf": "^3.0.2", + "semver": "^7.3.4", + "tar": "^6.1.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@messageformat/core": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.3.0.tgz", + "integrity": "sha512-YcXd3remTDdeMxAlbvW6oV9d/01/DZ8DHUFwSttO3LMzIZj3iO0NRw+u1xlsNNORFI+u0EQzD52ZX3+Udi0T3g==", + "dependencies": { + "@messageformat/date-skeleton": "^1.0.0", + "@messageformat/number-skeleton": "^1.0.0", + "@messageformat/parser": "^5.1.0", + "@messageformat/runtime": "^3.0.1", + "make-plural": "^7.0.0", + "safe-identifier": "^0.4.1" + } + }, + "node_modules/@messageformat/date-skeleton": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.0.1.tgz", + "integrity": "sha512-jPXy8fg+WMPIgmGjxSlnGJn68h/2InfT0TNSkVx0IGXgp4ynnvYkbZ51dGWmGySEK+pBiYUttbQdu5XEqX5CRg==" + }, + "node_modules/@messageformat/number-skeleton": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz", + "integrity": "sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==" + }, + "node_modules/@messageformat/parser": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.0.tgz", + "integrity": "sha512-jKlkls3Gewgw6qMjKZ9SFfHUpdzEVdovKFtW1qRhJ3WI4FW5R/NnGDqr8SDGz+krWDO3ki94boMmQvGke1HwUQ==", + "dependencies": { + "moo": "^0.5.1" + } + }, + "node_modules/@messageformat/runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz", + "integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==", + "dependencies": { + "make-plural": "^7.0.0" + } + }, "node_modules/@napi-rs/canvas": { "version": "0.1.44", "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.44.tgz", @@ -2094,6 +2267,33 @@ "node": ">= 8" } }, + "node_modules/@nsfw-filter/gif-frames": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@nsfw-filter/gif-frames/-/gif-frames-1.0.2.tgz", + "integrity": "sha512-XZrbJWEN8YfVla5i+PD4Wj51rRlJ8OgnXiPjjOt/OsrbsCR9GZRD4jr953oNWcwiRaoIcOCFWQNMQukO7Yb1dA==", + "dependencies": { + "@nsfw-filter/save-pixels": "^2.3.4", + "get-pixels-frame-info-update": "3.3.2", + "multi-integer-range": "3.0.0" + } + }, + "node_modules/@nsfw-filter/save-pixels": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@nsfw-filter/save-pixels/-/save-pixels-2.3.4.tgz", + "integrity": "sha512-dRZXwrXadMvxwJYKChrDBqC6GNvxVqlmdkyvZJO5DV65qyBsHZw8bPg9CnX7EgpxGl6+4ba/MAdHDLxs2XoD0Q==", + "dependencies": { + "gif-encoder": "0.4.1", + "ndarray": "1.0.18", + "ndarray-ops": "1.2.2", + "pngjs-nozlib": "1.0.0", + "through": "2.3.4" + } + }, + "node_modules/@nsfw-filter/save-pixels/node_modules/through": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.4.tgz", + "integrity": "sha512-DwbmSAcABsMazNkLOJJSLRC3gfh4cPxUxJCn9npmvbcI6undhgoJ2ShvEOgZrW8BH62Gyr9jKboGbfFcmY5VsQ==" + }, "node_modules/@prisma/client": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.6.0.tgz", @@ -2260,6 +2460,294 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tensorflow/tfjs": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-3.18.0.tgz", + "integrity": "sha512-mOzz4jJdgIpqFS7EHndVuxrQnLUDVIKGyTqOPTYps89fZwcOFfTVxi4BHemDNQpqlVE8IaGh9UUxVXpjgPY5+Q==", + "dependencies": { + "@tensorflow/tfjs-backend-cpu": "3.18.0", + "@tensorflow/tfjs-backend-webgl": "3.18.0", + "@tensorflow/tfjs-converter": "3.18.0", + "@tensorflow/tfjs-core": "3.18.0", + "@tensorflow/tfjs-data": "3.18.0", + "@tensorflow/tfjs-layers": "3.18.0", + "argparse": "^1.0.10", + "chalk": "^4.1.0", + "core-js": "3", + "regenerator-runtime": "^0.13.5", + "yargs": "^16.0.3" + }, + "bin": { + "tfjs-custom-module": "dist/tools/custom_module/cli.js" + } + }, + "node_modules/@tensorflow/tfjs-backend-cpu": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-cpu/-/tfjs-backend-cpu-3.18.0.tgz", + "integrity": "sha512-LcSqlylzGtpgngcMFIL3q9Q3eVaPRJ7ITZt7ivhzkCj4R5ZsnPa9qM3DCVihkQ77heAwSw4hPTo2jp5C4mJ4Cg==", + "dependencies": { + "@types/seedrandom": "2.4.27", + "seedrandom": "2.4.3" + }, + "engines": { + "yarn": ">= 1.3.2" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "3.18.0" + } + }, + "node_modules/@tensorflow/tfjs-backend-cpu/node_modules/seedrandom": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", + "integrity": "sha512-2CkZ9Wn2dS4mMUWQaXLsOAfGD+irMlLEeSP3cMxpGbgyOOzJGFa+MWCOMTOCMyZinHRPxyOj/S/C57li/1to6Q==" + }, + "node_modules/@tensorflow/tfjs-backend-webgl": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-backend-webgl/-/tfjs-backend-webgl-3.18.0.tgz", + "integrity": "sha512-3NknSzS1oX2BEBOrpjPMZl823S12RgshQthmIbG6QADHb4bCJA8aM4UjWpw+3bNQnRKbRDQdFbuvj10Un79s2A==", + "dependencies": { + "@tensorflow/tfjs-backend-cpu": "3.18.0", + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "2.4.27", + "@types/webgl-ext": "0.0.30", + "@types/webgl2": "0.0.6", + "seedrandom": "2.4.3" + }, + "engines": { + "yarn": ">= 1.3.2" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "3.18.0" + } + }, + "node_modules/@tensorflow/tfjs-backend-webgl/node_modules/seedrandom": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", + "integrity": "sha512-2CkZ9Wn2dS4mMUWQaXLsOAfGD+irMlLEeSP3cMxpGbgyOOzJGFa+MWCOMTOCMyZinHRPxyOj/S/C57li/1to6Q==" + }, + "node_modules/@tensorflow/tfjs-converter": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-3.18.0.tgz", + "integrity": "sha512-hpChA+zVNQOVwRnCfqDb1WI9jbEAKA6DuEm4m75Zb3dIlE6VVooDmAaHBhlc++z2q2G1sBzF9A4Bv48SUpN6vA==", + "peerDependencies": { + "@tensorflow/tfjs-core": "3.18.0" + } + }, + "node_modules/@tensorflow/tfjs-core": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-3.18.0.tgz", + "integrity": "sha512-gMxisZozqsr5sCKlphF/eVBLg91MjlBiN60tjX8hJAu0WlSn6Gi5k65GNIL+Pq6hrxpvImcfdCmTH/2XJVZ0Mg==", + "dependencies": { + "@types/long": "^4.0.1", + "@types/offscreencanvas": "~2019.3.0", + "@types/seedrandom": "2.4.27", + "@types/webgl-ext": "0.0.30", + "@webgpu/types": "^0.1.16", + "long": "4.0.0", + "node-fetch": "~2.6.1", + "seedrandom": "2.4.3" + }, + "engines": { + "yarn": ">= 1.3.2" + } + }, + "node_modules/@tensorflow/tfjs-core/node_modules/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@tensorflow/tfjs-core/node_modules/seedrandom": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", + "integrity": "sha512-2CkZ9Wn2dS4mMUWQaXLsOAfGD+irMlLEeSP3cMxpGbgyOOzJGFa+MWCOMTOCMyZinHRPxyOj/S/C57li/1to6Q==" + }, + "node_modules/@tensorflow/tfjs-data": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-3.18.0.tgz", + "integrity": "sha512-s43vISJh8K/UN2E2zGRhtj/Kyn8dr4ll8EQkapwzm7fGO9afXCnMsTp6rkZq3fFXouCYA2k1B/j7JssIDr50+w==", + "dependencies": { + "@types/node-fetch": "^2.1.2", + "node-fetch": "~2.6.1" + }, + "peerDependencies": { + "@tensorflow/tfjs-core": "3.18.0", + "seedrandom": "~2.4.3" + } + }, + "node_modules/@tensorflow/tfjs-data/node_modules/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@tensorflow/tfjs-layers": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-3.18.0.tgz", + "integrity": "sha512-AV7yDnPlH+RCcq8VPqkX1iyEchObE+e66m0XmJvLj+ncfKHYLa+39ZNroUA+OgB2/cMG6jgq77R4EhZbT6hwJA==", + "peerDependencies": { + "@tensorflow/tfjs-core": "3.18.0" + } + }, + "node_modules/@tensorflow/tfjs-node": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-node/-/tfjs-node-3.18.0.tgz", + "integrity": "sha512-jW6bFKO6hH4tGvlijvpcIbCRRa6vDZ2xIIbEO7qZ17s0QOMPBGjDLyQGUaSQ00uog0Rid/sdZNpiueDqB0ZGbA==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "1.0.4", + "@tensorflow/tfjs": "3.18.0", + "adm-zip": "^0.5.2", + "google-protobuf": "^3.9.2", + "https-proxy-agent": "^2.2.1", + "progress": "^2.0.0", + "rimraf": "^2.6.2", + "tar": "^4.4.6" + }, + "engines": { + "node": ">=8.11.0" + } + }, + "node_modules/@tensorflow/tfjs-node/node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@tensorflow/tfjs-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@tensorflow/tfjs-node/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/@tensorflow/tfjs-node/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@tensorflow/tfjs/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@tensorflow/tfjs/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@translate-tools/core": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@translate-tools/core/-/core-1.0.0.tgz", @@ -2364,6 +2852,12 @@ "@types/node": "*" } }, + "node_modules/@types/i18n": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/@types/i18n/-/i18n-0.13.10.tgz", + "integrity": "sha512-V5uwFXEzC1BRvDSQkd5zWB7ktZa1yfLKFimlluTFrgBGeFJQNNaLk3J67Sje+c0+m2C7r1BU47cItOk/WRfJcQ==", + "dev": true + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", @@ -2410,6 +2904,11 @@ "integrity": "sha512-y9euML0cim1JrykNxADLfaG0FgD1g/yTHwUs/Jg9ZIU7WKj2/4IW9Lbb1WZbvck78W/lfGXFfe+u2EGfIJXdLQ==", "dev": true }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/minimist": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.3.tgz", @@ -2424,12 +2923,31 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz", "integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==", "dev": true }, + "node_modules/@types/offscreencanvas": { + "version": "2019.3.0", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz", + "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==" + }, + "node_modules/@types/seedrandom": { + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.27.tgz", + "integrity": "sha512-YvMLqFak/7rt//lPBtEHv3M4sRNA+HGxrhFZ+DQs9K2IkYJbNwVIb8avtJfhDiuaUBX/AW0jnjv48FV8h3u9bQ==" + }, "node_modules/@types/semver": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", @@ -2447,6 +2965,16 @@ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz", "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g==" }, + "node_modules/@types/webgl-ext": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz", + "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg==" + }, + "node_modules/@types/webgl2": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.6.tgz", + "integrity": "sha512-50GQhDVTq/herLMiqSQkdtRu+d5q/cWHn4VvKJtrj4DJAjo1MNkWYa2MA41BaBO1q1HgsUjuQvEOk0QHvlnAaQ==" + }, "node_modules/@types/ws": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", @@ -2674,6 +3202,11 @@ "npm": ">=7.0.0" } }, + "node_modules/@webgpu/types": { + "version": "0.1.40", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.40.tgz", + "integrity": "sha512-/BBkHLS6/eQjyWhY2H7Dx5DHcVrS2ICj9owvSRdgtQT6KcafLZA86tPze0xAOsd4FbsYKCUBUQyNi87q7gV7kw==" + }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -2682,6 +3215,11 @@ "node": ">=10.0.0" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -2719,6 +3257,14 @@ "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", "dev": true }, + "node_modules/adm-zip": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -2778,7 +3324,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2808,6 +3353,47 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2845,11 +3431,32 @@ "node": ">=0.10.0" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -2859,6 +3466,19 @@ "node": ">= 4.0.0" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3048,8 +3668,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -3071,6 +3690,14 @@ } ] }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -3082,11 +3709,15 @@ "readable-stream": "^3.4.0" } }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3251,6 +3882,11 @@ } ] }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3280,6 +3916,11 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -3404,7 +4045,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3430,6 +4070,14 @@ "node": ">= 0.12.0" } }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -3482,6 +4130,17 @@ "text-hex": "1.0.x" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -3542,8 +4201,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "2.0.0", @@ -3560,6 +4218,11 @@ "typedarray": "^0.0.6" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/conventional-changelog": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", @@ -3833,11 +4496,20 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-js": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz", + "integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/cosmiconfig": { "version": "8.3.6", @@ -3994,6 +4666,14 @@ "node": ">= 8" } }, + "node_modules/cwise-compiler": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz", + "integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==", + "dependencies": { + "uniq": "^1.0.0" + } + }, "node_modules/cz-conventional-changelog": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", @@ -4023,6 +4703,22 @@ "node": ">=8" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz", + "integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==" + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -4123,6 +4819,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -4141,6 +4850,17 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4348,6 +5068,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.559", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.559.tgz", @@ -4369,8 +5098,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/enabled": { "version": "2.0.0", @@ -4386,11 +5114,23 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -4768,6 +5508,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -4782,6 +5527,14 @@ "node": ">=4" } }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4818,8 +5571,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -4827,6 +5579,17 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-printf": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/fast-printf/-/fast-printf-1.6.9.tgz", + "integrity": "sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg==", + "dependencies": { + "boolean": "^3.1.4" + }, + "engines": { + "node": ">=10.0" + } + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -4988,6 +5751,27 @@ } } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -5009,11 +5793,18 @@ "node": ">=10" } }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dependencies": { + "minipass": "^2.6.0" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -5029,6 +5820,64 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -5042,7 +5891,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -5056,6 +5904,24 @@ "node": ">=8.0.0" } }, + "node_modules/get-pixels-frame-info-update": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/get-pixels-frame-info-update/-/get-pixels-frame-info-update-3.3.2.tgz", + "integrity": "sha512-LzVij57X/gK4Y6LpcDdqj+R9WCpD6Sv3ZH85GMA+S3xgPGCz81mHql4GiSnF4GijRjk7TE0ja2sDr8FFYKLe2g==", + "dependencies": { + "data-uri-to-buffer": "0.0.3", + "jpeg-js": "^0.3.2", + "mime-types": "^2.0.1", + "ndarray": "^1.0.13", + "ndarray-pack": "^1.1.1", + "node-bitmap": "0.0.1", + "omggif": "^1.0.5", + "parse-data-uri": "^0.2.0", + "pngjs": "^3.3.3", + "request": "^2.44.0", + "through": "^2.3.4" + } + }, "node_modules/get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", @@ -5126,6 +5992,46 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/gif-encoder": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/gif-encoder/-/gif-encoder-0.4.1.tgz", + "integrity": "sha512-++rNGpDBgWQ9eXj9JfTBLHMUEd7lDOdzIvFyHQM9yL8ffxkcg4G6jWmsgu/r59Uq6nHc3wcVwtgy3geLnIWunQ==", + "dependencies": { + "readable-stream": "~1.1.9" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/gif-encoder/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/gif-encoder/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/gif-encoder/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, "node_modules/git-raw-commits": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", @@ -5196,7 +6102,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5314,6 +6219,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -5341,12 +6251,53 @@ "handlebars": "bin/handlebars" }, "engines": { - "node": ">=0.4.7" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -5374,6 +6325,11 @@ "node": ">=4" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -5404,6 +6360,20 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -5440,6 +6410,25 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/i18n": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/i18n/-/i18n-0.15.1.tgz", + "integrity": "sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA==", + "dependencies": { + "@messageformat/core": "^3.0.0", + "debug": "^4.3.3", + "fast-printf": "^1.6.9", + "make-plural": "^7.0.0", + "math-interval-parser": "^2.0.1", + "mustache": "^4.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/mashpie" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5547,7 +6536,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5660,12 +6648,22 @@ "node": ">=8" } }, + "node_modules/iota-array": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz", + "integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==" + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-core-module": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", @@ -5789,6 +6787,11 @@ "node": ">=0.10.0" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -5819,8 +6822,7 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isexe": { "version": "2.0.0", @@ -5828,6 +6830,11 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -7615,6 +8622,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jpeg-js": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", + "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7633,6 +8645,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -7663,6 +8680,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -7679,8 +8701,7 @@ "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, "node_modules/json5": { "version": "2.2.3", @@ -7731,6 +8752,20 @@ "node": "*" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -8330,6 +9365,11 @@ "triple-beam": "^1.3.0" } }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, "node_modules/longest": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", @@ -8343,7 +9383,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -8377,6 +9416,11 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/make-plural": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.3.0.tgz", + "integrity": "sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw==" + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -8404,6 +9448,14 @@ "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", "dev": true }, + "node_modules/math-interval-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-2.0.1.tgz", + "integrity": "sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/meow": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", @@ -8604,6 +9656,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -8629,7 +9700,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8641,7 +9711,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8660,6 +9729,39 @@ "node": ">= 6" } }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -8669,11 +9771,29 @@ "node": ">=0.10.0" } }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/multi-integer-range": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multi-integer-range/-/multi-integer-range-3.0.0.tgz", + "integrity": "sha512-uQzynjVJ8F7x5wjaK0g4Ybhy2TvO/pk96+YHyS5g1W4GuUEV6HMebZ8HcRwWgKIRCUT2MLbM5uCKwYcAqkS+8Q==" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -8686,12 +9806,46 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/ndarray": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.18.tgz", + "integrity": "sha512-jUz6G+CIsEsqs2VlB1EvaQSAA0Jkf8YKm7eFBleKyhiQjYWzTxXqHzWEOm3jFoGCpxGh4DnPUYHB4ECWE+n9SQ==", + "dependencies": { + "iota-array": "^1.0.0", + "is-buffer": "^1.0.2" + } + }, + "node_modules/ndarray-ops": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz", + "integrity": "sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==", + "dependencies": { + "cwise-compiler": "^1.0.0" + } + }, + "node_modules/ndarray-pack": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz", + "integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==", + "dependencies": { + "cwise-compiler": "^1.1.2", + "ndarray": "^1.0.13" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/node-bitmap": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz", + "integrity": "sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==", + "engines": { + "node": ">=v0.6.5" + } + }, "node_modules/node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -8729,6 +9883,20 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -8780,11 +9948,61 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nsfwjs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/nsfwjs/-/nsfwjs-2.4.2.tgz", + "integrity": "sha512-i4Pp2yt59qPQgeZFyg3wXFBX52uSeu/hkDoqdZfe+sILRxNBUu0VDogj7Lmqak0GlrXviS/wLiVeIx40IDUu7A==", + "dependencies": { + "@nsfw-filter/gif-frames": "1.0.2" + }, + "peerDependencies": { + "@tensorflow/tfjs": "^3.18.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -8982,6 +10200,14 @@ "node": ">=6" } }, + "node_modules/parse-data-uri": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz", + "integrity": "sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==", + "dependencies": { + "data-uri-to-buffer": "0.0.3" + } + }, "node_modules/parse-duration": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-1.1.0.tgz", @@ -9027,7 +10253,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9065,6 +10290,11 @@ "through": "~2.3" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -9177,6 +10407,23 @@ "node": ">=8" } }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pngjs-nozlib": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pngjs-nozlib/-/pngjs-nozlib-1.0.0.tgz", + "integrity": "sha512-N1PggqLp9xDqwAoKvGohmZ3m4/N9xpY0nDZivFqQLcpLHmliHnCp9BuNCsOeqHWMuEEgFjpEaq9dZq6RZyy0fA==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -9246,14 +10493,12 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -9292,11 +10537,15 @@ "node": ">= 0.10" } }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, "engines": { "node": ">=6" } @@ -9327,6 +10576,14 @@ "teleport": ">=0.2.0" } }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/query-string": { "version": "6.14.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", @@ -9547,11 +10804,59 @@ "node": ">=8" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -9696,7 +11001,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -9767,6 +11071,11 @@ } ] }, + "node_modules/safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==" + }, "node_modules/safe-stable-stringify": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", @@ -9778,14 +11087,18 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/seedrandom": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.4.tgz", + "integrity": "sha512-9A+PDmgm+2du77B5i0Ip2cxOqqHjgNxnBgglxLcX78A2D6c2rTo61z4jnVABpF4cKeDMDG+cmXXvdnqse2VqMA==", + "peer": true }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9796,6 +11109,11 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -9820,8 +11138,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -9962,8 +11279,31 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/stack-trace": { "version": "0.0.10", @@ -10073,7 +11413,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10087,7 +11426,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -10103,7 +11441,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10180,6 +11517,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -10217,8 +11576,7 @@ "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, "node_modules/through2": { "version": "4.0.2", @@ -10268,6 +11626,18 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -10429,6 +11799,22 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10510,6 +11896,11 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==" + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -10553,7 +11944,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -10563,6 +11953,15 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -10604,6 +12003,24 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -10651,6 +12068,14 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/winston": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", @@ -10723,7 +12148,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10740,7 +12164,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -10755,7 +12178,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -10766,14 +12188,12 @@ "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -10829,7 +12249,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -10837,14 +12256,12 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "dev": true, "engines": { "node": ">= 14" } @@ -10853,7 +12270,6 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -10871,7 +12287,6 @@ "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, "engines": { "node": ">=10" } diff --git a/package.json b/package.json index fbf2e0bf1..f3fcda768 100644 --- a/package.json +++ b/package.json @@ -23,19 +23,24 @@ "dependencies": { "@prisma/client": "^5.6.0", "@sentry/node": "^7.80.1", + "@tensorflow/tfjs-node": "3.18.0", "@translate-tools/core": "^1.0.0", "common-tags": "^1.8.2", "discord-arts": "^0.5.8", "discord-hybrid-sharding": "^2.1.4", "discord.js": "^14.14.1", "dotenv": "^16.3.1", + "i18n": "^0.15.1", "lodash": "^4.17.21", + "nsfwjs": "^2.4.2", "parse-duration": "^1.1.0", - "winston": "^3.11.0" + "winston": "^3.11.0", + "yaml": "^2.3.4" }, "devDependencies": { "@sentry/cli": "^2.21.5", "@types/common-tags": "^1.8.4", + "@types/i18n": "^0.13.10", "@types/jest": "^29.5.8", "@types/lodash": "^4.14.201", "@types/node": "^20.9.2", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 142a3b524..65592cba1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -143,6 +143,7 @@ model userData { id String @id @default(auto()) @map("_id") @db.ObjectId userId String @unique username String + locale String? blacklistedFrom hubBlacklist[] // if user has seen the welcome message when they first use the network viewedNetworkWelcome Boolean @default(false) diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 000000000..72cb126e7 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,59 @@ +import http from 'http'; +import { node } from '@tensorflow/tfjs-node'; +import { load } from 'nsfwjs'; +import Logger from '../utils/Logger.js'; +import { captureException } from '@sentry/node'; + +const model = await load(); +const port = 3000; + +export default function start() { + const server = http.createServer(async (req, res) => { + if (req.method === 'GET' && req.url?.startsWith('/nsfw')) { + const url = new URL(req.url, `http://${req.headers.host}`); + const imageUrl = url.searchParams.get('url'); + + if (!imageUrl || typeof imageUrl !== 'string') { + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: 'Missing url query parameter.' })); + return; + } + + const regex = /\bhttps?:\/\/\S+?\.(?:png|jpe?g)(?:\?\S+)?\b/; + if (!regex.test(imageUrl)) { + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ + error: 'Invalid url parameter. Must be a valid PNG, JPG, or JPEG image URL.', + }), + ); + return; + } + + try { + const imageBuffer = await (await fetch(imageUrl)).arrayBuffer(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const imageTensor = (await node.decodeImage(Buffer.from(imageBuffer), 3)) as any; + const predictions = await model.classify(imageTensor); + imageTensor.dispose(); + + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(predictions)); + } + catch (error) { + Logger.error(error); + captureException(error); + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end('500 Internal Server Error'); + } + } + else { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('404 Not Found'); + } + }); + + server.listen(port, () => { + Logger.info(`Server listening on port ${port}`); + }); +} diff --git a/src/commands/context-menu/blacklist.ts b/src/commands/context-menu/blacklist.ts index 4edff8b4f..724dedd5d 100644 --- a/src/commands/context-menu/blacklist.ts +++ b/src/commands/context-menu/blacklist.ts @@ -20,6 +20,7 @@ import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { errorEmbed } from '../../utils/Utils.js'; import parse from 'parse-duration'; import NetworkLogger from '../../utils/NetworkLogger.js'; +import locales from '../../utils/locales.js'; export default class Blacklist extends BaseCommand { data: RESTPostAPIApplicationCommandsJSONBody = { @@ -45,7 +46,10 @@ export default class Blacklist extends BaseCommand { interaction.reply({ embeds: [ errorEmbed( - `${emojis.info} This message was not sent in a hub, has expired, or you lack permissions to perform this action.`, + locales( + { phrase: 'errors.messageNotSentOrExpired', locale: interaction.user.locale }, + { emoji: emojis.info }, + ), ), ], ephemeral: true, @@ -98,7 +102,7 @@ export default class Blacklist extends BaseCommand { await interaction.reply({ embeds: [ errorEmbed( - `${emojis.no} Sorry, you can't perform this action. Please run the command yourself.`, + locales({ phrase: 'errors.cannotPerformAction', locale: interaction.user.locale }), ), ], ephemeral: true, @@ -184,7 +188,10 @@ export default class Blacklist extends BaseCommand { if (blacklistType.startsWith('u=')) { const user = await interaction.client.users.fetch(messageInDb.authorId).catch(() => null); successEmbed.setDescription( - `${emojis.tick} **${user?.username}** has been successfully blacklisted!`, + locales( + { phrase: 'blacklist.user.success.embed.description', locale: interaction.user.locale }, + { username: user?.username ?? 'Unknown User' }, + ), ); await blacklistManager.addUserBlacklist( messageInDb.hubId, diff --git a/src/commands/slash/Information/rules.ts b/src/commands/slash/Information/rules.ts index 6b19654cc..7b38b975a 100644 --- a/src/commands/slash/Information/rules.ts +++ b/src/commands/slash/Information/rules.ts @@ -1,13 +1,23 @@ -import { ChatInputCommandInteraction } from 'discord.js'; -import { rulesEmbed } from '../../../utils/Constants.js'; +import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; - +import { LINKS, colors } from '../../../utils/Constants.js'; +import locales from '../../../utils/locales.js'; export default class Rules extends BaseCommand { readonly data = { name: 'rules', description: 'Sends the network rules for InterChat.', }; async execute(interaction: ChatInputCommandInteraction) { + const rulesEmbed = new EmbedBuilder() + .setDescription( + locales( + { phrase: 'commands.rules.embed.description', locale: interaction.user.locale }, + { support_invite: LINKS.SUPPORT_INVITE }, + ), + ) + .setImage(LINKS.RULES_BANNER) + .setColor(colors.interchatBlue); + await interaction.reply({ embeds: [rulesEmbed] }); } -} \ No newline at end of file +} diff --git a/src/commands/slash/Information/vote.ts b/src/commands/slash/Information/vote.ts index 6b1b0b921..a65bcfa70 100644 --- a/src/commands/slash/Information/vote.ts +++ b/src/commands/slash/Information/vote.ts @@ -1,7 +1,13 @@ -import { stripIndents } from 'common-tags'; -import { ChatInputCommandInteraction, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { + ChatInputCommandInteraction, + EmbedBuilder, + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, +} from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { colors } from '../../../utils/Constants.js'; +import locales from '../../../utils/locales.js'; export default class Vote extends BaseCommand { readonly data = { @@ -10,17 +16,9 @@ export default class Vote extends BaseCommand { }; async execute(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() - .setDescription(stripIndents` - ## 🗳️ Vote for InterChat and Enjoy Exclusive Perks - Your contribution is invaluable in elevating InterChat's position on Top.gg. Each and every vote makes a significant difference! - - As our way of expressing gratitude for your support, we are thrilled to offer you exclusive advantages. By casting your vote for InterChat, you'll unlock: - - - Edit messages within hubs - - Translating messages (and much more on the way!) - - We deeply appreciate your unwavering support. Thank you! 🙏 - `) + .setDescription( + locales({ phrase: 'commands.vote.embed.description', locale: interaction.user.locale }), + ) .setColor(colors.interchatBlue); const button = new ActionRowBuilder().addComponents( @@ -33,4 +31,4 @@ export default class Vote extends BaseCommand { await interaction.reply({ embeds: [embed], components: [button] }); } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 263cc605a..cc19b203f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import db from './utils/Db.js'; import Logger from './utils/Logger.js'; +import startApi from './api/index.js'; import Scheduler from './services/SchedulerService.js'; import BlacklistManager from './managers/BlacklistManager.js'; import { ClusterManager } from 'discord-hybrid-sharding'; @@ -27,7 +28,7 @@ const syncBotlistStats = async () => { } shards`, ); // update stats - updateTopGGStats( + await updateTopGGStats( count.reduce((p, n) => p + n, 0), manager.totalShards, ); @@ -106,3 +107,6 @@ manager.on('clusterCreate', async (cluster) => { scheduler.addRecurringTask('deleteOldMessages', 60 * 60 * 12_000, deleteOldMessages); } }); + +// start the api that handles nsfw filter +startApi(); diff --git a/src/managers/CommandManager.ts b/src/managers/CommandManager.ts index fecb0454b..48bdf3bfd 100644 --- a/src/managers/CommandManager.ts +++ b/src/managers/CommandManager.ts @@ -7,6 +7,7 @@ import { CustomID } from '../utils/CustomID.js'; import { Interaction } from 'discord.js'; import { captureException } from '@sentry/node'; import { errorEmbed } from '../utils/Utils.js'; +import db from '../utils/Db.js'; const __filename = new URL(import.meta.url).pathname; const __dirname = dirname(__filename); @@ -19,6 +20,9 @@ export default class CommandManager extends Factory { /** Handle interactions from the `InteractionCreate` event */ async handleInteraction(interaction: Interaction): Promise { try { + const userData = await db.userData.findFirst({ where: { userId: interaction.user.id } }); + interaction.user.locale = userData?.locale ?? 'en'; + if (interaction.isAutocomplete()) { const command = this.client.commands.get(interaction.commandName); if (command?.autocomplete) command.autocomplete(interaction); @@ -71,8 +75,11 @@ export default class CommandManager extends Factory { // check if command is in cooldown for the user if (remainingCooldown) { await interaction.reply({ - content: `${emojis.timeout} This command is on a cooldown! You can use it again: .`, + content: `${ + emojis.timeout + } This command is on a cooldown! You can use it again: .`, ephemeral: true, }); return; diff --git a/src/scripts/network/onboarding.ts b/src/scripts/network/onboarding.ts index a1a3c6af2..eac32b376 100644 --- a/src/scripts/network/onboarding.ts +++ b/src/scripts/network/onboarding.ts @@ -1,4 +1,3 @@ -import { stripIndents } from 'common-tags'; import { ActionRowBuilder, ButtonStyle, @@ -10,7 +9,8 @@ import { AnySelectMenuInteraction, Collection, } from 'discord.js'; -import { colors, rulesEmbed } from '../../utils/Constants.js'; +import { LINKS, colors } from '../../utils/Constants.js'; +import locales from '../../utils/locales.js'; const onboardingInProgress = new Collection(); @@ -35,20 +35,35 @@ export async function showOnboarding( onboardingInProgress.set(channelId, channelId); const embed = new EmbedBuilder() - .setTitle(`👋 Hey there, welcome to ${hubName}!`) + .setTitle( + locales( + { phrase: 'network.onboarding.embed.title', locale: interaction.user.locale }, + { hubName }, + ), + ) .setDescription( - stripIndents` - To keep things organized, it's recommended to use a separate channel for just for this hub. But don't worry, you can always change this later. - - **How it works:** The InterChat Network is like a magic bridge that links channels on different servers that are with us in this hub. Learn more at our [guide](https://discord-interchat.github.io/docs). - `, + locales( + { phrase: 'network.onboarding.embed.description', locale: interaction.user.locale }, + { hubName }, + ), ) .setColor(colors.interchatBlue) - .setFooter({ text: `InterChat Network | Version ${interaction.client.version}` }); + .setFooter({ + text: locales( + { phrase: 'network.onboarding.embed.footer', locale: interaction.user.locale }, + { version: interaction.client.version }, + ), + }); const nextButton = new ActionRowBuilder().addComponents( - new ButtonBuilder().setCustomId('onboarding_:cancel').setLabel('Cancel').setStyle(ButtonStyle.Danger), - new ButtonBuilder().setCustomId('onboarding_:next').setLabel('Next').setStyle(ButtonStyle.Success), + new ButtonBuilder() + .setCustomId('onboarding_:cancel') + .setLabel('Cancel') + .setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('onboarding_:next') + .setLabel('Next') + .setStyle(ButtonStyle.Success), ); const replyMsg = { @@ -74,10 +89,26 @@ export async function showOnboarding( if (response?.customId === 'onboarding_:next') { const acceptButton = new ActionRowBuilder().addComponents( - new ButtonBuilder().setCustomId('onboarding_:cancel').setLabel('Cancel').setStyle(ButtonStyle.Danger), - new ButtonBuilder().setCustomId('onboarding_:accept').setLabel('Accept').setStyle(ButtonStyle.Success), + new ButtonBuilder() + .setCustomId('onboarding_:cancel') + .setLabel('Cancel') + .setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('onboarding_:accept') + .setLabel('Accept') + .setStyle(ButtonStyle.Success), ); + const rulesEmbed = new EmbedBuilder() + .setDescription( + locales( + { phrase: 'commands.rules.embed.description', locale: interaction.user.locale }, + { support_invite: LINKS.SUPPORT_INVITE }, + ), + ) + .setImage(LINKS.RULES_BANNER) + .setColor(colors.interchatBlue); + const acceptOnboarding = await response.update({ embeds: [rulesEmbed], components: [acceptButton], diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index c8054b7b2..cbfd328a6 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -31,4 +31,8 @@ declare module 'discord.js' { getBlacklistManager(): BlacklistManager; getNSFWDetector(): NSFWClient; } + + export interface User { + locale?: string; + } } diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index c25badb77..6e50ac049 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -1,5 +1,4 @@ -import { stripIndents } from 'common-tags'; -import { Colors, EmbedBuilder, HexColorString } from 'discord.js'; +import { Colors, HexColorString } from 'discord.js'; import { normal, badge, mascot } from './JSON/emojis.json'; import { createRequire } from 'module'; import 'dotenv/config'; @@ -47,6 +46,7 @@ export const LINKS = { DOCS: 'https://discord-interchat.github.io/docs', SUPPORT_INVITE: 'https://discord.gg/6bhXQynAPs', APP_DIRECTORY: 'https://discord.com/application-directory/769921109209907241', + RULES_BANNER: 'https://i.imgur.com/MBG0Rks.png', } as const; export const channels = { @@ -97,18 +97,3 @@ export const colors = { christmas: ['#00B32C', '#D6001C', '#FFFFFF'] as HexColorString[], } as const; -export const rulesEmbed = new EmbedBuilder() - .setColor(colors.interchatBlue) - .setImage('https://i.imgur.com/MBG0Rks.png').setDescription(stripIndents` - ### 📜 InterChat Network Rules - - 1. **Use Common Sense:** Be considerate of others and their views. No slurs, extreme derogatory language or any actions that can disrupt the chat's comfort. - 2. **No Spamming or Flooding:** Avoid repeated, nonsensical, or overly lengthy messages. - 3. **Keep Private Matters Private:** Avoid sharing personal information across the network. - 4. **No Harassment:** Trolling, insults, or harassment of any kind are not tolerated. - 5. **No NSFW/NSFL Content:** Posting explicit NSFW/NSFL content will result in immediate blacklist. - 6. **Respect Sensitive Topics:** Do not trivialize self-harm, suicide, violence, or other offensive topics. - 7. **Report Concerns:** If you observe a violation of these rules, report it to the appropriate hub moderator or InterChat staff for further action. - - Any questions? Join our [support server](${LINKS.SUPPORT_INVITE}). -`); diff --git a/src/utils/locales.ts b/src/utils/locales.ts new file mode 100644 index 000000000..9c2e47a92 --- /dev/null +++ b/src/utils/locales.ts @@ -0,0 +1,18 @@ +import { I18n } from 'i18n'; +import Logger from './Logger.js'; +import YAML from 'yaml'; + +const i18n = new I18n(); + +i18n.configure({ + directory: './locales', + fallbacks: { '*': 'en' }, + objectNotation: true, + parser: YAML, + extension: '.yml', + logDebugFn: Logger.info, + logWarnFn: Logger.warn, + logErrorFn: Logger.error, +}); + +export default i18n.__; From a1041f1b63cf09d421ad7b4ceceecc87ee4100c2 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:06:43 +0000 Subject: [PATCH 02/15] chore: update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9746edcdb..a350239e0 100644 --- a/README.md +++ b/README.md @@ -150,4 +150,4 @@ Run `git cz` or `cz commit` to commit using commitizen. * [x] 1000 servers using InterChat 🎉 -* [ ] 101 votes on topgg in a month +* [x] 101 votes on topgg in a month From 8ef69c8766eef584f104600de4308fad35a58d43 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Tue, 28 Nov 2023 19:59:46 +0530 Subject: [PATCH 03/15] chore(git): add locales submodule --- .gitmodules | 3 +++ locales | 1 + locales/en.yml | 51 -------------------------------------------------- locales/ja.yml | 20 -------------------- 4 files changed, 4 insertions(+), 71 deletions(-) create mode 100644 .gitmodules create mode 160000 locales delete mode 100644 locales/en.yml delete mode 100644 locales/ja.yml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..ecb1bcd86 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "locales"] + path = locales + url = https://github.com/Discord-InterChat/locales.git diff --git a/locales b/locales new file mode 160000 index 000000000..a77122071 --- /dev/null +++ b/locales @@ -0,0 +1 @@ +Subproject commit a77122071531ad218ccbe42d0855741f65ef5d3b diff --git a/locales/en.yml b/locales/en.yml deleted file mode 100644 index 2bbf07174..000000000 --- a/locales/en.yml +++ /dev/null @@ -1,51 +0,0 @@ -commands: - rules: - embed: - description: | - ### 📜 InterChat Network Rules - - 1. **Use Common Sense:** Be considerate of others and their views. No slurs, extreme derogatory language or any actions that can disrupt the chat's comfort. - 2. **No Spamming or Flooding:** Avoid repeated, nonsensical, or overly lengthy messages. - 3. **Keep Private Matters Private:** Avoid sharing personal information across the network. - 4. **No Harassment:** Trolling, insults, or harassment of any kind are not tolerated. - 5. **No NSFW/NSFL Content:** Posting explicit NSFW/NSFL content will result in immediate blacklist. - 6. **Respect Sensitive Topics:** Do not trivialize self-harm, suicide, violence, or other offensive topics. - 7. **Report Concerns:** If you observe a violation of these rules, report it to the appropriate hub moderator or InterChat staff for further action. - - Any questions? Join our [support server]({{{support_invite}}}). - vote: - embed: - description: | - ## 🗳️ Vote for InterChat and Enjoy Exclusive Perks - Your contribution is invaluable in elevating InterChat's position on Top.gg. Each and every vote makes a significant difference! - - As our way of expressing gratitude for your support, we are thrilled to offer you exclusive advantages. By casting your vote for InterChat, you'll unlock: - - - Edit messages within hubs - - Translating messages (and much more on the way!) - - We deeply appreciate your unwavering support. Thank you! 🙏 - -network: - onboarding: - embed: - title: 👋 Hey there, welcome to {{hubName}}! - description: | - To keep things organized, it's recommended to use a separate channel for just for this hub. But don't worry, you can always change this later. - - **How it works:** The InterChat Network is like a magic bridge that links channels on different servers that are with us in this hub. Learn more at our [guide](https://discord-interchat.github.io/docs). - - footer: "InterChat Network | Version {{version}}" - -blacklist: - user: - success: - embed: - description: "{{emoji}} **{{username}}** has been successfully blacklisted!" - -errors: - messageNotSentOrExpired: "{{emoji}} This message was not sent in a hub, has expired, or you lack permissions to perform this action." - cannotPerformAction: "{{emoji}} Sorry, you can't perform this action. Please run the command yourself." - -other: - interchatVersion: "InterChat {{version}}" diff --git a/locales/ja.yml b/locales/ja.yml deleted file mode 100644 index 3113ae62e..000000000 --- a/locales/ja.yml +++ /dev/null @@ -1,20 +0,0 @@ -commands: - rules: - embed: - description: | - ### 📜 InterChat Networkのルール - - 1. 常識を使う: 他人や彼らの意見を考慮してください。差別用語、極端な軽蔑的な言葉、またはチャットの快適さを妨げる可能性のある行動は禁止されています。 - 2. スパムやフラッディングは禁止: 繰り返された、意味不明な、または非常に長いメッセージは避けてください。 - 3. プライベートな事柄はプライベートに: ネットワーク全体での個人情報の共有は避けてください。 - 4. 嫌がらせは禁止: トローリング、侮辱、またはあらゆる形の嫌がらせは許容されません。 - 5. NSFW/NSFLコンテンツは禁止: 露骨なNSFW/NSFLコンテンツの投稿は即時にブラックリスト入りします。 - 6. 感 sensitiveなトピックに敬意を払う: 自傷行為、自殺、暴力、またはその他の攻撃的なトピックを軽視しないでください。 - 7. 問題を報告する: これらのルールの違反を目撃した場合は、適切なハブモデレーターかInterChatスタッフに報告してください。 - - 質問はありますか?[サポートサーバーに参加する]({{{support_invite}}}). -blacklist: - user: - success: - embed: - description: blacklist.user.success.embed.description From faff7ff2fde5c64f2c098fe39f3c1dcb3b090c16 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:12:29 +0530 Subject: [PATCH 04/15] chore: update prerequisites in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a350239e0..74c11adb6 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ This repo contains the source code for the InterChat Discord bot. InterChat is a ### Prerequisites -1. [Node.js v18.0.0+](https://nodejs.org/en/download/current/) () +1. [Node.js v18.0.0](https://nodejs.org/en/download/current/), or higher for linux users 2. [Git](https://git-scm.com/downloads) 3. [MongoDB](https://www.mongodb.com/try/download/community) 4. [NPM](https://www.npmjs.com/get-npm) or [Yarn](https://yarnpkg.com/getting-started/install) (we are using npm in this guide) 5. [An Imgur API Key](https://api.imgur.com/oauth2/addclient) (optional, for setting hub icon and banner) -6. [Python 2.7](https://www.python.org/downloads/release/python-2718/) & [Visual Studio Build Tools (Windows Only)](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) (optional, for the API) +6. [Python 3.9.3+](https://www.python.org/downloads/release/python-2718/) & [Visual Studio Build Tools (Windows Only)](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) (for the API) > [!NOTE] -> If you are on Windows, you will need to delete the `api/package-lock.json` before installing dependencies. And if that doesn't work, run `npm i` in a terminal with admin privileges. And if you're getting errors related to node-gyp, install `node-gyp` globally using `npm i -g node-gyp`. Or follow the [Installation Instructions](https://github.com/nodejs/node-gyp?tab=readme-ov-file#on-windows). Honestly, why are you on Windows? You can also just re-install Node.js and tick the "Automatically install the necessary tools" option, which should do all the stuff mentioned above for you. +> If you are using Windows, make sure to install the build tools from the link above and install the exact Python version v3.9.3 or you will get errors during package installation. Ignore this step if you're a linux user (I use arch btw. - dev-737) ### Setting up the bot From 111415b4a0088aca091e73ea97d6bede1dabafde Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:57:18 +0530 Subject: [PATCH 05/15] fix: JSON type in messageData & add more tl and remove logger method from SuperClient --- prisma/schema.prisma | 2 +- src/InterChat.ts | 9 +++--- src/SuperClient.ts | 5 +--- src/commands/context-menu/blacklist.ts | 21 ++++++++------ src/commands/context-menu/deleteMsg.ts | 32 +++++++++++++++++++-- src/commands/context-menu/editMsg.ts | 16 +++++++++-- src/commands/slash/Main/blacklist/server.ts | 7 +++-- src/commands/slash/Main/hub/index.ts | 19 +++++++++++- src/commands/slash/Main/hub/invite.ts | 3 +- src/commands/slash/Staff/purge.ts | 3 +- src/decorators/Interaction.ts | 2 +- src/managers/CommandManager.ts | 16 +++++++++-- src/typings/index.d.ts | 2 -- src/utils/Utils.ts | 23 +++++++++++---- 14 files changed, 121 insertions(+), 39 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 65592cba1..f87793307 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -122,7 +122,7 @@ model messageData { referenceDocId String? @db.ObjectId serverId String timestamp DateTime - reactions Json // eg. {"👎": ["9893820930928", "39283902803982"]} "emoji": userId[] basically + reactions Json? // eg. {"👎": ["9893820930928", "39283902803982"]} "emoji": userId[] basically hub hubs? @relation(fields: [hubId], references: [id]) hubId String? @db.ObjectId } diff --git a/src/InterChat.ts b/src/InterChat.ts index 67c7ed21c..8ca0b3397 100644 --- a/src/InterChat.ts +++ b/src/InterChat.ts @@ -16,6 +16,7 @@ import { } from 'discord.js'; import { stripIndents } from 'common-tags'; import { LINKS, channels, colors, emojis, mascotEmojis } from './utils/Constants.js'; +import Logger from './utils/Logger.js'; class InterChat extends SuperClient { public constructor() { @@ -28,13 +29,13 @@ class InterChat extends SuperClient { // load commands CommandManager.loadCommandFiles(); - this.logger.info( + Logger.info( `Logged in as ${this.user?.tag}! Cached ${this.guilds.cache.size} guilds on Cluster ${this.cluster?.id}.`, ); }); - this.on('shardReady', (shard) => { - this.logger.info(`Shard ${shard} is ready!`); + this.on('shardReady', (shard, uGuilds) => { + Logger.info(`Shard ${shard} is ready! Unable to cache ${uGuilds?.size ?? 0} guilds.`); }); this.on('guildCreate', async (guild) => { @@ -163,7 +164,7 @@ class InterChat extends SuperClient { this.on('guildDelete', async (guild) => { if (!guild.available) return; - this.logger.info(`Left ${guild.name} (${guild.id})`); + Logger.info(`Left ${guild.name} (${guild.id})`); await db.connectedList.deleteMany({ where: { serverId: guild.id } }); const count = (await this.cluster.fetchClientValues('guilds.cache.size')) as number[]; diff --git a/src/SuperClient.ts b/src/SuperClient.ts index 47418bf4e..7709b79c8 100644 --- a/src/SuperClient.ts +++ b/src/SuperClient.ts @@ -9,7 +9,6 @@ import { } from 'discord.js'; import { ClusterClient, getInfo } from 'discord-hybrid-sharding'; import { commandsMap, interactionsMap } from './commands/BaseCommand.js'; -import Logger from './utils/Logger.js'; import Sentry from '@sentry/node'; import Scheduler from './services/SchedulerService.js'; import NSFWClient from './utils/NSFWDetection.js'; @@ -24,8 +23,6 @@ import { ActivityType } from 'discord.js'; import 'dotenv/config'; export default abstract class SuperClient extends Client { - readonly logger = Logger; - readonly description = 'The only cross-server chatting bot you\'ll ever need.'; readonly version = process.env.npm_package_version ?? 'Unknown'; readonly commands = commandsMap; @@ -76,7 +73,7 @@ export default abstract class SuperClient extends Client { status: 'idle', activities: [ { - state: 'Watching over 400+ networks | /hub browse', + state: 'Watching over 500+ networks | /hub browse', name: 'custom', type: ActivityType.Custom, }, diff --git a/src/commands/context-menu/blacklist.ts b/src/commands/context-menu/blacklist.ts index 724dedd5d..1778ef361 100644 --- a/src/commands/context-menu/blacklist.ts +++ b/src/commands/context-menu/blacklist.ts @@ -59,9 +59,7 @@ export default class Blacklist extends BaseCommand { const embed = new EmbedBuilder() .setTitle('Blacklist') - .setDescription( - 'Blacklist the server or user of this message from this hub. This will prevent messages by them from being sent.', - ) + .setDescription('Blacklist the server or user of this message from this hub. This will prevent messages by them from being sent.') .setColor('Blurple'); const buttons = new ActionRowBuilder().addComponents( @@ -102,7 +100,10 @@ export default class Blacklist extends BaseCommand { await interaction.reply({ embeds: [ errorEmbed( - locales({ phrase: 'errors.cannotPerformAction', locale: interaction.user.locale }), + locales( + { phrase: 'errors.cannotPerformAction', locale: interaction.user.locale }, + { emoji: emojis.no }, + ), ), ], ephemeral: true, @@ -159,7 +160,7 @@ export default class Blacklist extends BaseCommand { if (!messageInDb?.hubId) { await interaction.reply({ - content: 'This message has expired.', + content: locales({ phrase: 'errors.networkMessageExpired', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -189,8 +190,8 @@ export default class Blacklist extends BaseCommand { const user = await interaction.client.users.fetch(messageInDb.authorId).catch(() => null); successEmbed.setDescription( locales( - { phrase: 'blacklist.user.success.embed.description', locale: interaction.user.locale }, - { username: user?.username ?? 'Unknown User' }, + { phrase: 'blacklist.user.success', locale: interaction.user.locale }, + { username: user?.username ?? 'Unknown User', emoji: emojis.tick }, ), ); await blacklistManager.addUserBlacklist( @@ -221,8 +222,12 @@ export default class Blacklist extends BaseCommand { const server = interaction.client.guilds.cache.get(messageInDb.serverId); successEmbed.setDescription( - `${emojis.tick} **${server?.name}** has been successfully blacklisted!`, + locales( + { phrase: 'blacklist.server.success', locale: interaction.user.locale }, + { username: server?.name ?? 'Unknown Server', emoji: emojis.tick }, + ), ); + await blacklistManager.addServerBlacklist( messageInDb.serverId, messageInDb.hubId, diff --git a/src/commands/context-menu/deleteMsg.ts b/src/commands/context-menu/deleteMsg.ts index 0c99070e9..b63871555 100644 --- a/src/commands/context-menu/deleteMsg.ts +++ b/src/commands/context-menu/deleteMsg.ts @@ -8,6 +8,7 @@ import BaseCommand from '../BaseCommand.js'; import { checkIfStaff } from '../../utils/Utils.js'; import { emojis } from '../../utils/Constants.js'; import db from '../../utils/Db.js'; +import locales from '../../utils/locales.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -26,7 +27,13 @@ export default class DeleteMessage extends BaseCommand { if (!messageInDb) { return await interaction.editReply( - 'Unknown Message. If it has been sent in the past minute, please wait few more seconds and try again.', + locales( + { + phrase: 'errors.unknownNetworkMessage', + locale: interaction.user.locale, + }, + { emoji: emojis.no }, + ), ); } @@ -37,7 +44,15 @@ export default class DeleteMessage extends BaseCommand { messageInDb.hub?.ownerId !== interaction.user.id && interaction.user.id !== messageInDb.authorId ) { - return await interaction.editReply(`${emojis.no} You are not the author of this message.`); + return await interaction.editReply( + locales( + { + phrase: 'errors.notMessageAuthor', + locale: interaction.user.locale, + }, + { emoji: emojis.no }, + ), + ); } // find all the messages through the network @@ -66,7 +81,18 @@ export default class DeleteMessage extends BaseCommand { const deleted = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); await interaction .editReply( - `${emojis.yes} Message by <@${messageInDb.authorId}> has been deleted from __**${deleted}/${resultsArray.length}**__ servers.`, + locales( + { + phrase: 'network.messageDeleted', + locale: interaction.user.locale, + }, + { + emoji: emojis.yes, + user: `<@${messageInDb.authorId}>`, + deleted: deleted.toString(), + total: resultsArray.length.toString(), + }, + ), ) .catch(() => null); diff --git a/src/commands/context-menu/editMsg.ts b/src/commands/context-menu/editMsg.ts index ccd224b0f..27c343829 100644 --- a/src/commands/context-menu/editMsg.ts +++ b/src/commands/context-menu/editMsg.ts @@ -18,6 +18,7 @@ import { checkIfStaff, hasVoted, replaceLinks } from '../../utils/Utils.js'; import { censor } from '../../utils/Profanity.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; +import locales from '../../utils/locales.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -44,14 +45,25 @@ export default class DeleteMessage extends BaseCommand { if (!messageInDb) { await interaction.reply({ - content: 'This message has expired. If not, please wait a few seconds and try again.', + content: locales( + locales( + { + phrase: 'errors.unknownNetworkMessage', + locale: interaction.user.locale, + }, + { emoji: emojis.no }, + ), + ), ephemeral: true, }); return; } else if (interaction.user.id != messageInDb?.authorId) { await interaction.reply({ - content: 'You are not the author of this message.', + content: locales( + { phrase: 'errors.notMessageAuthor', locale: interaction.user.locale }, + { emoji: emojis.no }, + ), ephemeral: true, }); return; diff --git a/src/commands/slash/Main/blacklist/server.ts b/src/commands/slash/Main/blacklist/server.ts index e759c10b2..e01b4c898 100644 --- a/src/commands/slash/Main/blacklist/server.ts +++ b/src/commands/slash/Main/blacklist/server.ts @@ -1,12 +1,13 @@ import { captureException } from '@sentry/node'; import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; +import { errorEmbed } from '../../../../utils/Utils.js'; import { emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; import BlacklistCommand from './index.js'; import BlacklistManager from '../../../../managers/BlacklistManager.js'; -import parse from 'parse-duration'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; +import parse from 'parse-duration'; +import Logger from '../../../../utils/Logger.js'; export default class UserBlacklist extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -64,7 +65,7 @@ export default class UserBlacklist extends BlacklistCommand { ); } catch (err) { - interaction.client.logger.error(err); + Logger.error(err); captureException(err); interaction.followUp( `Failed to blacklist **${server.name}**. Enquire with the bot developer for more information.`, diff --git a/src/commands/slash/Main/hub/index.ts b/src/commands/slash/Main/hub/index.ts index aab095d57..a7f218466 100644 --- a/src/commands/slash/Main/hub/index.ts +++ b/src/commands/slash/Main/hub/index.ts @@ -9,6 +9,9 @@ import { } from 'discord.js'; import BaseCommand from '../../../BaseCommand.js'; import db from '../../../../utils/Db.js'; +import Logger from '../../../../utils/Logger.js'; +import { captureException } from '@sentry/node'; +import { errorEmbed, genCommandErrMsg } from '../../../../utils/Utils.js'; const hubOption: APIApplicationCommandBasicOption = { type: ApplicationCommandOptionType.String, @@ -317,7 +320,21 @@ export default class Hub extends BaseCommand { const apiSubcommandName = interaction.options.getSubcommandGroup() || interaction.options.getSubcommand(); const subcommand = Hub.subcommands?.get(apiSubcommandName); - if (subcommand) return await subcommand.execute(interaction); + + if (!subcommand) return; + await subcommand.execute(interaction).catch((e) => { + Logger.error(e); + captureException(e); + + if ('reply' in interaction) { + const method = interaction.replied ? 'editReply' : 'reply'; + + interaction[method]({ + embeds: [errorEmbed(genCommandErrMsg(interaction, e))], + ephemeral: true, + }).catch(() => null); + } + }); } async autocomplete(interaction: AutocompleteInteraction): Promise { diff --git a/src/commands/slash/Main/hub/invite.ts b/src/commands/slash/Main/hub/invite.ts index b8c6e2348..1e1a9b8bc 100644 --- a/src/commands/slash/Main/hub/invite.ts +++ b/src/commands/slash/Main/hub/invite.ts @@ -4,6 +4,7 @@ import { captureException } from '@sentry/node'; import { stripIndents } from 'common-tags'; import { emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; +import Logger from '../../../../utils/Logger.js'; export default class Invite extends Hub { readonly cooldown = 3000; // 3 seconds @@ -97,7 +98,7 @@ export default class Invite extends Hub { }); } catch (e) { - interaction.client.logger.error(e); + Logger.error(e); captureException(e); await interaction .reply({ diff --git a/src/commands/slash/Staff/purge.ts b/src/commands/slash/Staff/purge.ts index 1b6858269..f65c8fb51 100644 --- a/src/commands/slash/Staff/purge.ts +++ b/src/commands/slash/Staff/purge.ts @@ -13,6 +13,7 @@ import { stripIndents } from 'common-tags'; import { emojis } from '../../../utils/Constants.js'; import { messageData as messageDataCol } from '@prisma/client'; import { errorEmbed, msToReadable } from '../../../utils/Utils.js'; +import Logger from '../../../utils/Logger.js'; const limitOpt: APIApplicationCommandBasicOption = { type: ApplicationCommandOptionType.Integer, @@ -239,7 +240,7 @@ export default class Purge extends BaseCommand { return interaction.client.resolveEval(evalRes) || []; } catch (e) { - interaction.client.logger.error(e); + Logger.error(e); captureException(e); } diff --git a/src/decorators/Interaction.ts b/src/decorators/Interaction.ts index 866705358..f0f46c4e0 100644 --- a/src/decorators/Interaction.ts +++ b/src/decorators/Interaction.ts @@ -3,7 +3,7 @@ import { interactionsMap } from '../commands/BaseCommand.js'; export type InteractionFunction = ( interaction: MessageComponentInteraction | ModalSubmitInteraction, -) => Promise | void; +) => Promise; /** Decorator function to call a specified method when an interaction is created (ie. interactionCreate event) */ export function RegisterInteractionHandler(customId: string): MethodDecorator { diff --git a/src/managers/CommandManager.ts b/src/managers/CommandManager.ts index 48bdf3bfd..b7853e2b9 100644 --- a/src/managers/CommandManager.ts +++ b/src/managers/CommandManager.ts @@ -6,8 +6,9 @@ import { emojis } from '../utils/Constants.js'; import { CustomID } from '../utils/CustomID.js'; import { Interaction } from 'discord.js'; import { captureException } from '@sentry/node'; -import { errorEmbed } from '../utils/Utils.js'; +import { errorEmbed, genCommandErrMsg } from '../utils/Utils.js'; import db from '../utils/Db.js'; +import Logger from '../utils/Logger.js'; const __filename = new URL(import.meta.url).pathname; const __dirname = dirname(__filename); @@ -113,8 +114,19 @@ export default class CommandManager extends Factory { } } catch (e) { - interaction.client.logger.error(e); + Logger.error(e); captureException(e); + + if ('reply' in interaction) { + const errFormat = { + embeds: [errorEmbed(genCommandErrMsg(interaction, e))], + ephemeral: true, + }; + + interaction.replied || interaction.deferred + ? await interaction.editReply(errFormat) + : await interaction.reply(errFormat); + } } } diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index cbfd328a6..240ea9b51 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -1,6 +1,5 @@ import { ClusterClient } from 'discord-hybrid-sharding'; import { Collection, Snowflake } from 'discord.js'; -import { Logger } from 'winston'; import { Scheduler } from '../services/SchedulerService.ts'; import NSFWClient from '../utils/NSFWDetection.ts'; import NetworkManager from '../managers/NetworkManager.ts'; @@ -14,7 +13,6 @@ type RemoveMethods = { declare module 'discord.js' { export interface Client { - readonly logger: Logger; readonly version: string; readonly development: boolean; readonly description: string; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 9828a1d3c..cf3f39e31 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -5,6 +5,7 @@ import { ColorResolvable, ComponentType, EmbedBuilder, + Interaction, Message, MessageActionRowComponent, NewsChannel, @@ -12,13 +13,14 @@ import { TextChannel, ThreadChannel, } from 'discord.js'; -import { DeveloperIds, REGEX, StaffIds, SupporterIds, LINKS, colors } from './Constants.js'; +import { DeveloperIds, REGEX, StaffIds, SupporterIds, LINKS, colors, emojis } from './Constants.js'; import { randomBytes } from 'crypto'; import Scheduler from '../services/SchedulerService.js'; import db from './Db.js'; import startCase from 'lodash/startCase.js'; import toLower from 'lodash/toLower.js'; import 'dotenv/config'; +import locales from './locales.js'; /** Convert milliseconds to a human readable time (eg: 1d 2h 3m 4s) */ export function msToReadable(milliseconds: number): string { @@ -92,10 +94,12 @@ export async function getOrCreateWebhook( return existingWebhook; } - return await channelOrParent?.createWebhook({ - name: 'InterChat Network', - avatar, - }).catch(() => undefined); + return await channelOrParent + ?.createWebhook({ + name: 'InterChat Network', + avatar, + }) + .catch(() => undefined); } export function getCredits() { @@ -201,4 +205,11 @@ export async function checkAndFetchImgurUrl(url: string): Promise Date: Sun, 3 Dec 2023 21:31:27 +0530 Subject: [PATCH 06/15] chore: add blacklist translations --- locales | 2 +- src/InterChat.ts | 14 +- src/commands/context-menu/blacklist.ts | 26 ++-- src/commands/context-menu/deleteMsg.ts | 28 ++-- src/commands/context-menu/editMsg.ts | 42 +++--- src/commands/context-menu/messageInfo.ts | 137 ++++++++------------ src/commands/context-menu/translate.ts | 15 +-- src/commands/slash/Information/invite.ts | 10 +- src/commands/slash/Information/rules.ts | 4 +- src/commands/slash/Information/vote.ts | 4 +- src/commands/slash/Main/blacklist/list.ts | 50 +++---- src/commands/slash/Main/blacklist/server.ts | 53 ++++++-- src/commands/slash/Main/blacklist/user.ts | 46 +++++-- src/scripts/network/onboarding.ts | 10 +- src/utils/Utils.ts | 7 +- src/utils/locales.ts | 18 --- 16 files changed, 237 insertions(+), 229 deletions(-) delete mode 100644 src/utils/locales.ts diff --git a/locales b/locales index a77122071..d3e45a0c3 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit a77122071531ad218ccbe42d0855741f65ef5d3b +Subproject commit d3e45a0c384aee4156b92ac0864d16d18b8dce4e diff --git a/src/InterChat.ts b/src/InterChat.ts index 8ca0b3397..8465730a1 100644 --- a/src/InterChat.ts +++ b/src/InterChat.ts @@ -17,7 +17,8 @@ import { import { stripIndents } from 'common-tags'; import { LINKS, channels, colors, emojis, mascotEmojis } from './utils/Constants.js'; import Logger from './utils/Logger.js'; - +import { I18n } from 'i18n'; +import YAML from 'yaml'; class InterChat extends SuperClient { public constructor() { super(); @@ -26,6 +27,17 @@ class InterChat extends SuperClient { // initialize the client this.init(); + new I18n().configure({ + directory: './locales', + fallbacks: { '*': 'en' }, + objectNotation: true, + parser: YAML, + extension: '.yml', + logDebugFn: Logger.info, + logWarnFn: Logger.warn, + logErrorFn: Logger.error, + }); + // load commands CommandManager.loadCommandFiles(); diff --git a/src/commands/context-menu/blacklist.ts b/src/commands/context-menu/blacklist.ts index 1778ef361..7bc131a91 100644 --- a/src/commands/context-menu/blacklist.ts +++ b/src/commands/context-menu/blacklist.ts @@ -14,13 +14,13 @@ import { } from 'discord.js'; import BaseCommand from '../BaseCommand.js'; import db from '../../utils/Db.js'; -import { emojis } from '../../utils/Constants.js'; +import { colors, emojis } from '../../utils/Constants.js'; import { CustomID } from '../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { errorEmbed } from '../../utils/Utils.js'; import parse from 'parse-duration'; import NetworkLogger from '../../utils/NetworkLogger.js'; -import locales from '../../utils/locales.js'; +import { __ } from '../../utils/Utils.js'; export default class Blacklist extends BaseCommand { data: RESTPostAPIApplicationCommandsJSONBody = { @@ -46,7 +46,7 @@ export default class Blacklist extends BaseCommand { interaction.reply({ embeds: [ errorEmbed( - locales( + __( { phrase: 'errors.messageNotSentOrExpired', locale: interaction.user.locale }, { emoji: emojis.info }, ), @@ -59,8 +59,11 @@ export default class Blacklist extends BaseCommand { const embed = new EmbedBuilder() .setTitle('Blacklist') - .setDescription('Blacklist the server or user of this message from this hub. This will prevent messages by them from being sent.') - .setColor('Blurple'); + .setDescription( + // FIXME: either remove or improve this + 'Blacklist the server or user of this message from this hub. This will prevent messages by them from being sent.', + ) + .setColor(colors.interchatBlue); const buttons = new ActionRowBuilder().addComponents( new ButtonBuilder() @@ -99,12 +102,7 @@ export default class Blacklist extends BaseCommand { if (interaction.user.id !== customId.args[0]) { await interaction.reply({ embeds: [ - errorEmbed( - locales( - { phrase: 'errors.cannotPerformAction', locale: interaction.user.locale }, - { emoji: emojis.no }, - ), - ), + errorEmbed(__({ phrase: 'errors.cannotPerformAction', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -160,7 +158,7 @@ export default class Blacklist extends BaseCommand { if (!messageInDb?.hubId) { await interaction.reply({ - content: locales({ phrase: 'errors.networkMessageExpired', locale: interaction.user.locale }), + content: __({ phrase: 'errors.networkMessageExpired', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -189,7 +187,7 @@ export default class Blacklist extends BaseCommand { if (blacklistType.startsWith('u=')) { const user = await interaction.client.users.fetch(messageInDb.authorId).catch(() => null); successEmbed.setDescription( - locales( + __( { phrase: 'blacklist.user.success', locale: interaction.user.locale }, { username: user?.username ?? 'Unknown User', emoji: emojis.tick }, ), @@ -222,7 +220,7 @@ export default class Blacklist extends BaseCommand { const server = interaction.client.guilds.cache.get(messageInDb.serverId); successEmbed.setDescription( - locales( + __( { phrase: 'blacklist.server.success', locale: interaction.user.locale }, { username: server?.name ?? 'Unknown Server', emoji: emojis.tick }, ), diff --git a/src/commands/context-menu/deleteMsg.ts b/src/commands/context-menu/deleteMsg.ts index b63871555..569b14c40 100644 --- a/src/commands/context-menu/deleteMsg.ts +++ b/src/commands/context-menu/deleteMsg.ts @@ -8,7 +8,7 @@ import BaseCommand from '../BaseCommand.js'; import { checkIfStaff } from '../../utils/Utils.js'; import { emojis } from '../../utils/Constants.js'; import db from '../../utils/Db.js'; -import locales from '../../utils/locales.js'; +import { __ } from '../../utils/Utils.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -27,13 +27,10 @@ export default class DeleteMessage extends BaseCommand { if (!messageInDb) { return await interaction.editReply( - locales( - { - phrase: 'errors.unknownNetworkMessage', - locale: interaction.user.locale, - }, - { emoji: emojis.no }, - ), + __({ + phrase: 'errors.unknownNetworkMessage', + locale: interaction.user.locale, + }), ); } @@ -45,13 +42,10 @@ export default class DeleteMessage extends BaseCommand { interaction.user.id !== messageInDb.authorId ) { return await interaction.editReply( - locales( - { - phrase: 'errors.notMessageAuthor', - locale: interaction.user.locale, - }, - { emoji: emojis.no }, - ), + __({ + phrase: 'errors.notMessageAuthor', + locale: interaction.user.locale, + }), ); } @@ -81,9 +75,9 @@ export default class DeleteMessage extends BaseCommand { const deleted = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); await interaction .editReply( - locales( + __( { - phrase: 'network.messageDeleted', + phrase: 'network.deleteSuccess', locale: interaction.user.locale, }, { diff --git a/src/commands/context-menu/editMsg.ts b/src/commands/context-menu/editMsg.ts index 27c343829..e3ac310d8 100644 --- a/src/commands/context-menu/editMsg.ts +++ b/src/commands/context-menu/editMsg.ts @@ -13,12 +13,11 @@ import { import db from '../../utils/Db.js'; import BaseCommand from '../BaseCommand.js'; import { HubSettingsBitField } from '../../utils/BitFields.js'; -import { emojis } from '../../utils/Constants.js'; import { checkIfStaff, hasVoted, replaceLinks } from '../../utils/Utils.js'; import { censor } from '../../utils/Profanity.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; -import locales from '../../utils/locales.js'; +import { __ } from '../../utils/Utils.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -32,7 +31,7 @@ export default class DeleteMessage extends BaseCommand { if (!checkIfStaff(interaction.user.id) && !(await hasVoted(interaction.user.id))) { await interaction.reply({ - content: `${emojis.no} You must [vote]() to use this command.`, + content: __({ phrase: 'errors.mustVote', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -45,14 +44,11 @@ export default class DeleteMessage extends BaseCommand { if (!messageInDb) { await interaction.reply({ - content: locales( - locales( - { - phrase: 'errors.unknownNetworkMessage', - locale: interaction.user.locale, - }, - { emoji: emojis.no }, - ), + content: __( + __({ + phrase: 'errors.unknownNetworkMessage', + locale: interaction.user.locale, + }), ), ephemeral: true, }); @@ -60,10 +56,7 @@ export default class DeleteMessage extends BaseCommand { } else if (interaction.user.id != messageInDb?.authorId) { await interaction.reply({ - content: locales( - { phrase: 'errors.notMessageAuthor', locale: interaction.user.locale }, - { emoji: emojis.no }, - ), + content: __({ phrase: 'errors.notMessageAuthor', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -99,13 +92,19 @@ export default class DeleteMessage extends BaseCommand { const messageId = customId.args[0]; const target = await interaction.channel?.messages.fetch(messageId).catch(() => null); - if (!target) return await interaction.reply('Unknown Message.'); + if (!target) { + return await interaction.reply(__({ phrase: 'errors.unknownNetworkMessage' })); + } const messageInDb = await db.messageData.findFirst({ where: { channelAndMessageIds: { some: { messageId: { equals: target.id } } } }, include: { hub: true }, }); - if (!messageInDb?.hub) return await interaction.reply('Unknown Message.'); + if (!messageInDb?.hub) { + return await interaction.reply( + __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), + ); + } // defer it because it takes a while to edit the message await interaction.deferReply({ ephemeral: true }); @@ -123,7 +122,7 @@ export default class DeleteMessage extends BaseCommand { newMessage.includes('dsc.gg')) ) { await interaction.editReply( - `${emojis.no} Do not advertise or promote servers in the network. Set an invite in \`/connection\` instead!`, + __({ phrase: 'errors.inviteLinks', locale: interaction.user.locale }), ); return; } @@ -206,9 +205,12 @@ export default class DeleteMessage extends BaseCommand { }); const resultsArray = await Promise.all(results); - const deleted = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); + const edited = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); await interaction.editReply( - `${emojis.yes} Your message has been edited in __**${deleted}/${resultsArray.length}**__ servers.`, + __( + { phrase: 'editMsg.editSuccess', locale: interaction.user.locale }, + { edited: `${edited}`, total: `${resultsArray.length}` }, + ), ); } } diff --git a/src/commands/context-menu/messageInfo.ts b/src/commands/context-menu/messageInfo.ts index 687672706..3d10928db 100644 --- a/src/commands/context-menu/messageInfo.ts +++ b/src/commands/context-menu/messageInfo.ts @@ -12,12 +12,12 @@ import { RESTPostAPIApplicationCommandsJSONBody, } from 'discord.js'; import db from '../../utils/Db.js'; -import { stripIndents } from 'common-tags'; import { profileImage } from 'discord-arts'; import { colors, emojis } from '../../utils/Constants.js'; import BaseCommand from '../BaseCommand.js'; import { CustomID } from '../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; +import { __ } from '../../utils/Utils.js'; export default class MessageInfo extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -35,35 +35,30 @@ export default class MessageInfo extends BaseCommand { if (!networkMessage) { await interaction.reply({ - content: 'Information about this message is no longer available.', + content: __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), ephemeral: true, }); return; } - const guildConnected = await db.connectedList.findFirst({ where: { serverId: networkMessage.serverId } }); + const guildConnected = await db.connectedList.findFirst({ + where: { serverId: networkMessage.serverId }, + }); const author = await interaction.client.users.fetch(networkMessage.authorId); const server = await interaction.client.fetchGuild(networkMessage.serverId); const embed = new EmbedBuilder() .setDescription( - stripIndents` - ## ${emojis.clipart} Message Info - - **Sent By:** - __${author.discriminator !== '0' ? author.tag : author.username}__ - - **Sent From:** - __${server?.name}__ - - **Message ID:** - __${target.id}__ - - **Sent In (Hub):** - __${networkMessage.hub?.name}__ - - **Message Created:** - - `, + __( + { phrase: 'msgInfo.message.description', locale: interaction.user.locale }, + { + emoji: emojis.clipart, + author: author.discriminator !== '0' ? author.tag : author.username, + server: `${server?.name}`, + messageId: target.id, + hub: `${networkMessage.hub?.name}`, + created: `${Math.floor(target.createdTimestamp / 1000)}`, + }, + ), ) .setThumbnail(`https://cdn.discordapp.com/icons/${server?.id}/${server?.icon}.png`) .setColor('Random'); @@ -101,7 +96,7 @@ export default class MessageInfo extends BaseCommand { }); if (!networkMessage) { return await interaction.update({ - content: 'Information about this message is no longer available.', + content: __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), embeds: [], components: [], }); @@ -133,19 +128,13 @@ export default class MessageInfo extends BaseCommand { case 'serverInfo': { if (!server) { return await interaction.update({ - content: 'Unable to find server!', + content: __({ phrase: 'errors.unknownServer', locale: interaction.user.locale }), embeds: [], components: [], }); } const owner = await interaction.client.users.fetch(server.ownerId); - - if (!server) { - await interaction.update({ content: 'Unable to find server!', embeds: [] }); - return; - } - const createdAt = Math.round(server.createdTimestamp / 1000); const iconUrl = server.icon @@ -154,31 +143,26 @@ export default class MessageInfo extends BaseCommand { const bannerUrL = server.icon ? `https://cdn.discordapp.com/icons/${server.id}/${server.banner}.png` : null; - const inviteString = guildConnected?.invite - ? `${guildConnected.invite}` - : 'Not Set.'; + const inviteString = guildConnected?.invite ? `${guildConnected.invite}` : 'Not Set.'; const serverEmbed = new EmbedBuilder() .setColor(colors.invisible) .setThumbnail(iconUrl) .setImage(bannerUrL) .setDescription( - stripIndents` - ## ${server?.name} - ${server.description || 'No Description.'} - - **Owner:** - __${owner.username}__${owner.discriminator !== '0' ? `#${owner.discriminator}` : ''} - - **Created:** - () - - **Member Count:** - __${server.memberCount}__ - - **Invite:** - __${inviteString}__, - `, + __( + { phrase: 'msgInfo.server.description', locale: interaction.user.locale }, + { + server: server.name, + description: server.description || 'No Description.', + owner: `${owner.username}#${ + owner.discriminator !== '0' ? `#${owner.discriminator}` : '' + }`, + createdAt: `${createdAt}`, + memberCount: `${server.memberCount}`, + invite: `${inviteString}`, + }, + ), ) .setFooter({ text: `ID: ${server.id}` }); @@ -199,22 +183,16 @@ export default class MessageInfo extends BaseCommand { .setColor('Random') .setImage(author.bannerURL() ?? null) .setDescription( - stripIndents` - ## ${author.username} - __${author.discriminator !== '0' ? author.tag : author.username}__ - - **ID:** - __${author.id}__ - - **Created:** - () - - **Display Name:** - __${author.globalName || 'Not Set.'}__ - - **Hubs Owned:** - __${await db.hubs.count({ where: { ownerId: author.id } })}__ - `, + __( + { phrase: 'msgInfo.user.description', locale: interaction.user.locale }, + { + user: author.discriminator !== '0' ? author.tag : author.username, + id: author.id, + createdAt: `${createdAt}`, + globalName: author.globalName || 'Not Set.', + hubsOwned: `${await db.hubs.count({ where: { ownerId: author.id } })}`, + }, + ), ) .setImage('attachment://customCard.png') // link to image that will be generated afterwards .setTimestamp(); @@ -242,7 +220,7 @@ export default class MessageInfo extends BaseCommand { if (!message) { await interaction.update({ - content: 'Unable to find message!', + content: __({ phrase: 'errors.unknownMessage', locale: interaction.user.locale }), embeds: [], components: [], }); @@ -251,24 +229,17 @@ export default class MessageInfo extends BaseCommand { const embed = new EmbedBuilder() .setDescription( - stripIndents` - ## ${emojis.clipart} Message Info - - **Sent By:** - __${author.discriminator !== '0' ? author.tag : author.username}__ - - **Sent From:** - __${server?.name}__ - - **Message ID:** - __${message.id}__ - - **Sent In (Hub):** - __${networkMessage.hub?.name}__ - - **Message Created:** - - `, + __( + { phrase: 'msgInfo.message.description', locale: interaction.user.locale }, + { + emoji: emojis.clipart, + author: author.discriminator !== '0' ? author.tag : author.username, + server: `${server?.name}`, + messageId: message.id, + hub: `${networkMessage.hub?.name}`, + createdAt: `${Math.floor(message.createdTimestamp / 1000)}`, + }, + ), ) .setThumbnail( server?.icon diff --git a/src/commands/context-menu/translate.ts b/src/commands/context-menu/translate.ts index 96551dd23..585492413 100644 --- a/src/commands/context-menu/translate.ts +++ b/src/commands/context-menu/translate.ts @@ -15,12 +15,12 @@ import { } from 'discord.js'; import db from '../../utils/Db.js'; import BaseCommand from '../BaseCommand.js'; -import { emojis } from '../../utils/Constants.js'; import { hasVoted } from '../../utils/Utils.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; import { supportedLanguages } from '@translate-tools/core/translators/GoogleTranslator/index.js'; import translator from '../../utils/Translator.cjs'; +import { __ } from '../../utils/Utils.js'; export default class Translate extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -34,7 +34,7 @@ export default class Translate extends BaseCommand { if (!(await hasVoted(interaction.user.id))) { return await interaction.editReply( - 'Please [vote](https://top.gg/bot/769921109209907241/vote) for Interchat to use this command, your support is very much appreciated!', + __({ phrase: 'errors.mustVote', locale: interaction.user.locale }), ); } @@ -46,7 +46,7 @@ export default class Translate extends BaseCommand { if (!messageInDb) { return interaction.editReply( - 'This message has expired. If not, please wait a few seconds and try again.', + __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), ); } @@ -75,11 +75,7 @@ export default class Translate extends BaseCommand { components: [ new ActionRowBuilder().addComponents( new ButtonBuilder() - .setCustomId( - new CustomID() - .setIdentifier('translate', 'lang') - .toString(), - ) + .setCustomId(new CustomID().setIdentifier('translate', 'lang').toString()) .setLabel('Specify Language') .setStyle(ButtonStyle.Secondary) .setEmoji('🌐'), @@ -125,12 +121,11 @@ export default class Translate extends BaseCommand { const messageContent = originalMessage.embeds[0]?.fields[0].value; if (!messageContent) return await interaction.reply('This message is not translatable.'); - const to = interaction.fields.getTextInputValue('to'); const from = interaction.fields.getTextInputValue('from'); if (!supportedLanguages.includes(from) || !supportedLanguages.includes(to)) { await interaction.reply({ - content: `${emojis.no} Invalid language code. Please use one from the [here](https://cloud.google.com/translate/docs/languages).`, + content: __({ phrase: 'errors.invalidLangCode', locale: interaction.user.locale }), ephemeral: true, }); return; diff --git a/src/commands/slash/Information/invite.ts b/src/commands/slash/Information/invite.ts index c9b63d3db..835588653 100644 --- a/src/commands/slash/Information/invite.ts +++ b/src/commands/slash/Information/invite.ts @@ -7,7 +7,7 @@ import { } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { LINKS, emojis } from '../../../utils/Constants.js'; -import { stripIndents } from 'common-tags'; +import { __ } from '../../../utils/Utils.js'; export default class Invite extends BaseCommand { readonly data = { @@ -30,10 +30,10 @@ export default class Invite extends BaseCommand { .setDisabled(false), ]); await interaction.reply({ - content: stripIndents` - Thank you for choosing to invite InterChat. Simply click the button below to invite me! - - - **__Support Server__:** ${LINKS.SUPPORT_INVITE}`, + content: __( + { phrase: 'invite', locale: interaction.user.locale }, + { support: LINKS.SUPPORT_INVITE }, + ), components: [InviteButton], }); } diff --git a/src/commands/slash/Information/rules.ts b/src/commands/slash/Information/rules.ts index 7b38b975a..7186fa90a 100644 --- a/src/commands/slash/Information/rules.ts +++ b/src/commands/slash/Information/rules.ts @@ -1,7 +1,7 @@ import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { LINKS, colors } from '../../../utils/Constants.js'; -import locales from '../../../utils/locales.js'; +import { __ } from '../../../utils/Utils.js'; export default class Rules extends BaseCommand { readonly data = { name: 'rules', @@ -10,7 +10,7 @@ export default class Rules extends BaseCommand { async execute(interaction: ChatInputCommandInteraction) { const rulesEmbed = new EmbedBuilder() .setDescription( - locales( + __( { phrase: 'commands.rules.embed.description', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), diff --git a/src/commands/slash/Information/vote.ts b/src/commands/slash/Information/vote.ts index a65bcfa70..a50ee5ffb 100644 --- a/src/commands/slash/Information/vote.ts +++ b/src/commands/slash/Information/vote.ts @@ -7,7 +7,7 @@ import { } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { colors } from '../../../utils/Constants.js'; -import locales from '../../../utils/locales.js'; +import { __ } from '../../../utils/Utils.js'; export default class Vote extends BaseCommand { readonly data = { @@ -17,7 +17,7 @@ export default class Vote extends BaseCommand { async execute(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setDescription( - locales({ phrase: 'commands.vote.embed.description', locale: interaction.user.locale }), + __({ phrase: 'commands.vote.embed.description', locale: interaction.user.locale }), ) .setColor(colors.interchatBlue); diff --git a/src/commands/slash/Main/blacklist/list.ts b/src/commands/slash/Main/blacklist/list.ts index 3175bf97c..6d58a64aa 100644 --- a/src/commands/slash/Main/blacklist/list.ts +++ b/src/commands/slash/Main/blacklist/list.ts @@ -1,10 +1,10 @@ import { APIEmbedField, ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import db from '../../../../utils/Db.js'; import BlacklistCommand from './index.js'; -import { stripIndents } from 'common-tags'; import { paginate } from '../../../../utils/Pagination.js'; -import { colors, emojis } from '../../../../utils/Constants.js'; +import { colors } from '../../../../utils/Constants.js'; import { errorEmbed } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Utils.js'; export default class ListBlacklists extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -25,9 +25,7 @@ export default class ListBlacklists extends BlacklistCommand { if (!hubInDb) { await interaction.editReply({ embeds: [ - errorEmbed( - `${emojis.no} Unknown hub. Make sure you are the owner or a moderator of the hub.`, - ), + errorEmbed(__({ phrase: 'errors.modUnknownHub', locale: interaction.user.locale })), ], }); return; @@ -60,16 +58,17 @@ export default class ListBlacklists extends BlacklistCommand { fields.push({ name: data.serverName, - value: stripIndents` - **ServerId:** ${data.serverId} - **Moderator:** ${ - moderator ? `@${moderator.username} (${hubData?.moderatorId})` : 'Unknown' -} - **Reason:** ${hubData?.reason} - **Expires:** ${ - !hubData?.expires ? 'Never.' : `` -} - `, + value: __( + { phrase: 'blacklist.list.server', locale: interaction.user.locale }, + { + serverId: data.serverId, + moderator: moderator ? `@${moderator.username} (${hubData?.moderatorId})` : 'Unknown', + reason: `${hubData?.reason}`, + expires: !hubData?.expires + ? 'Never.' + : ``, + }, + ), }); counter++; @@ -103,16 +102,17 @@ export default class ListBlacklists extends BlacklistCommand { fields.push({ name: data.username, - value: stripIndents` - **UserID:** ${data.userId} - **Moderator:** ${ - moderator ? `@${moderator.username} (${hubData?.moderatorId})` : 'Unknown' -} - **Reason:** ${hubData?.reason} - **Expires:** ${ - !hubData?.expires ? 'Never.' : `` -} - `, + value: __( + { phrase: 'blacklist.list.user', locale: interaction.user.locale }, + { + userId: data.userId, + moderator: moderator ? `@${moderator.username} (${hubData?.moderatorId})` : 'Unknown', + reason: `${hubData?.reason}`, + expires: !hubData?.expires + ? 'Never.' + : ``, + }, + ), }); counter++; diff --git a/src/commands/slash/Main/blacklist/server.ts b/src/commands/slash/Main/blacklist/server.ts index e01b4c898..64296893d 100644 --- a/src/commands/slash/Main/blacklist/server.ts +++ b/src/commands/slash/Main/blacklist/server.ts @@ -8,6 +8,7 @@ import BlacklistManager from '../../../../managers/BlacklistManager.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; import parse from 'parse-duration'; import Logger from '../../../../utils/Logger.js'; +import { __ } from '../../../../utils/Utils.js'; export default class UserBlacklist extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -29,9 +30,7 @@ export default class UserBlacklist extends BlacklistCommand { if (!hubInDb) { return await interaction.editReply({ embeds: [ - errorEmbed( - `${emojis.no} Unknown hub. Make sure you are the owner or a moderator of the hub.`, - ), + errorEmbed(__({ phrase: 'errors.modUnknownHub', locale: interaction.user.locale })), ], }); } @@ -49,11 +48,24 @@ export default class UserBlacklist extends BlacklistCommand { const serverInBlacklist = await BlacklistManager.fetchServerBlacklist(hubInDb.id, serverOpt); if (serverInBlacklist) { - return await interaction.followUp('The server is already blacklisted.'); + return await interaction.followUp({ + embeds: [ + errorEmbed( + __({ + phrase: 'blacklist.server.alreadyBlacklisted', + locale: interaction.user.locale, + }), + ), + ], + }); } const server = await interaction.client.guilds.fetch(serverOpt).catch(() => null); - if (!server) return await interaction.followUp('You have inputted an invalid server ID.'); + if (!server) { + return await interaction.followUp( + __({ phrase: 'errors.unknownServer', locale: interaction.user.locale }), + ); + } try { await blacklistManager.addServerBlacklist( @@ -67,9 +79,16 @@ export default class UserBlacklist extends BlacklistCommand { catch (err) { Logger.error(err); captureException(err); - interaction.followUp( - `Failed to blacklist **${server.name}**. Enquire with the bot developer for more information.`, - ); + interaction.followUp({ + embeds: [ + errorEmbed( + __({ + phrase: 'blacklist.server.unknownError', + locale: interaction.user.locale, + }), + ), + ], + }); return; } @@ -78,7 +97,12 @@ export default class UserBlacklist extends BlacklistCommand { } const successEmbed = new EmbedBuilder() - .setDescription(`${emojis.tick} **${server.name}** has been successfully blacklisted!`) + .setDescription( + __( + { phrase: 'blacklist.server.success', locale: interaction.user.locale }, + { emoji: emojis.tick, server: server.name }, + ), + ) .setColor('Green') .addFields( { @@ -106,11 +130,18 @@ export default class UserBlacklist extends BlacklistCommand { } else if (subCommandGroup == 'remove') { const result = await blacklistManager.removeBlacklist('server', hubInDb.id, serverOpt); - if (!result) return await interaction.followUp('The server is not blacklisted.'); + if (!result) { + return await interaction.followUp( + __({ phrase: 'errors.serverNotBlacklisted', locale: interaction.user.locale }), + ); + } // Using name from DB since the bot can't access server through API. await interaction.followUp( - `The server **${result.serverName}** has been removed from the blacklist.`, + __( + { phrase: 'blacklist.server.removed', locale: interaction.user.locale }, + { emoji: emojis.delete, server: result.serverName }, + ), ); // send log to hub's log channel diff --git a/src/commands/slash/Main/blacklist/user.ts b/src/commands/slash/Main/blacklist/user.ts index df2f270a4..f42f0f80a 100644 --- a/src/commands/slash/Main/blacklist/user.ts +++ b/src/commands/slash/Main/blacklist/user.ts @@ -6,6 +6,7 @@ import parse from 'parse-duration'; import { emojis } from '../../../../utils/Constants.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; import { errorEmbed } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Utils.js'; export default class Server extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -26,9 +27,7 @@ export default class Server extends BlacklistCommand { if (!hubInDb) { return await interaction.editReply({ embeds: [ - errorEmbed( - `${emojis.no} Unknown hub. Make sure you are the owner or a moderator of the hub.`, - ), + errorEmbed(__({ phrase: 'errors.modUnknownHub', locale: interaction.user.locale })), ], }); } @@ -50,15 +49,29 @@ export default class Server extends BlacklistCommand { interaction.client.users.cache.find((u) => u.username === userOpt) ?? (await interaction.client.users.fetch(userOpt).catch(() => null)); - if (!user) return interaction.followUp('Could not find user. Use an ID instead.'); - // if (user.id === interaction.user.id) return interaction.followUp('You cannot blacklist yourself.'); - if (user.id === interaction.client.user?.id) { - return interaction.followUp('You cannot blacklist the bot wtf.'); + if (!user) { + return interaction.followUp( + __({ phrase: 'errors.userNotFound', locale: interaction.user.locale }), + ); + } + + // if (user.id === interaction.user.id) { + // return await interaction.followUp('You cannot blacklist yourself.'); + // } + else if (user.id === interaction.client.user?.id) { + return interaction.followUp( + __({ + phrase: 'blacklist.easterEggs.blacklistBot', + locale: interaction.user.locale, + }), + ); } const userInBlacklist = await BlacklistManager.fetchUserBlacklist(hubInDb.id, userOpt); if (userInBlacklist) { - interaction.followUp(`**${user.username}** is already blacklisted.`); + await interaction.followUp( + __({ phrase: 'blacklist.user.alreadyBlacklisted', locale: interaction.user.locale }), + ); return; } @@ -74,7 +87,7 @@ export default class Server extends BlacklistCommand { blacklistManager.notifyBlacklist('user', user.id, hubInDb.id, expires, reason); const successEmbed = new EmbedBuilder() - .setDescription(`${emojis.tick} **${user.username}** has been successfully blacklisted!`) + .setDescription(__({ phrase: 'blacklist.user.success', locale: interaction.user.locale })) .setColor('Green') .addFields( { @@ -97,11 +110,18 @@ export default class Server extends BlacklistCommand { else if (subcommandGroup == 'remove') { // remove the blacklist const result = await blacklistManager.removeBlacklist('user', hubInDb.id, userId); - if (!result) return interaction.followUp('The inputted user is not blacklisted.'); - + if (!result) { + return await interaction.followUp( + __({ phrase: 'errors.userNotBlacklisted', locale: interaction.user.locale }), + ); + } const user = await interaction.client.users.fetch(userId).catch(() => null); - await interaction.followUp(`**${user?.username}** has been removed from the blacklist.`); - + await interaction.followUp( + __( + { phrase: 'blacklist.user.removed', locale: interaction.user.locale }, + { emoji: emojis.delete, server: result.username }, + ), + ); if (user) { // send log to hub's log channel await networkLogger.logUnblacklist('user', user.id, interaction.user, { reason }); diff --git a/src/scripts/network/onboarding.ts b/src/scripts/network/onboarding.ts index eac32b376..eceff64f6 100644 --- a/src/scripts/network/onboarding.ts +++ b/src/scripts/network/onboarding.ts @@ -10,7 +10,7 @@ import { Collection, } from 'discord.js'; import { LINKS, colors } from '../../utils/Constants.js'; -import locales from '../../utils/locales.js'; +import { __ } from '../../utils/Utils.js'; const onboardingInProgress = new Collection(); @@ -36,20 +36,20 @@ export async function showOnboarding( const embed = new EmbedBuilder() .setTitle( - locales( + __( { phrase: 'network.onboarding.embed.title', locale: interaction.user.locale }, { hubName }, ), ) .setDescription( - locales( + __( { phrase: 'network.onboarding.embed.description', locale: interaction.user.locale }, { hubName }, ), ) .setColor(colors.interchatBlue) .setFooter({ - text: locales( + text: __( { phrase: 'network.onboarding.embed.footer', locale: interaction.user.locale }, { version: interaction.client.version }, ), @@ -101,7 +101,7 @@ export async function showOnboarding( const rulesEmbed = new EmbedBuilder() .setDescription( - locales( + __( { phrase: 'commands.rules.embed.description', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index cf3f39e31..657b1fe71 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -20,7 +20,10 @@ import db from './Db.js'; import startCase from 'lodash/startCase.js'; import toLower from 'lodash/toLower.js'; import 'dotenv/config'; -import locales from './locales.js'; +import i18n from 'i18n'; + +// i18n is cjs :( +export const __ = i18n.__; /** Convert milliseconds to a human readable time (eg: 1d 2h 3m 4s) */ export function msToReadable(milliseconds: number): string { @@ -208,7 +211,7 @@ export function toTitleCase(str: string) { } export function genCommandErrMsg(interaction: Interaction, error: string) { - return locales( + return __( { phrase: 'errors.commandError', locale: interaction.user.locale }, { error, emoji: emojis.no, support_invite: LINKS.SUPPORT_INVITE }, ); diff --git a/src/utils/locales.ts b/src/utils/locales.ts deleted file mode 100644 index 9c2e47a92..000000000 --- a/src/utils/locales.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { I18n } from 'i18n'; -import Logger from './Logger.js'; -import YAML from 'yaml'; - -const i18n = new I18n(); - -i18n.configure({ - directory: './locales', - fallbacks: { '*': 'en' }, - objectNotation: true, - parser: YAML, - extension: '.yml', - logDebugFn: Logger.info, - logWarnFn: Logger.warn, - logErrorFn: Logger.error, -}); - -export default i18n.__; From 3bc07ef7d16126309eb2c0fbd1906bbd12fd1355 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:08:54 +0530 Subject: [PATCH 07/15] chore: update locales submodule --- locales | 2 +- src/InterChat.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/locales b/locales index d3e45a0c3..201fceb1a 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit d3e45a0c384aee4156b92ac0864d16d18b8dce4e +Subproject commit 201fceb1a1a1be8b32b6f311f8581788396a47f1 diff --git a/src/InterChat.ts b/src/InterChat.ts index 65950dfe6..b85211bd3 100644 --- a/src/InterChat.ts +++ b/src/InterChat.ts @@ -1,4 +1,6 @@ import db from './utils/Db.js'; +import YAML from 'yaml'; +import Logger from './utils/Logger.js'; import SuperClient from './SuperClient.js'; import CommandManager from './managers/CommandManager.js'; import { NetworkMessage } from './managers/NetworkManager.js'; @@ -16,9 +18,7 @@ import { } from 'discord.js'; import { stripIndents } from 'common-tags'; import { LINKS, channels, colors, emojis, mascotEmojis } from './utils/Constants.js'; -import Logger from './utils/Logger.js'; import { I18n } from 'i18n'; -import YAML from 'yaml'; class InterChat extends SuperClient { public constructor() { From d641254737c4aa9e312e032c84ba0e7367107e8e Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Tue, 5 Dec 2023 08:50:33 +0530 Subject: [PATCH 08/15] chore: add localization for `/connection` --- locales | 2 +- src/commands/slash/Information/rules.ts | 2 +- src/commands/slash/Information/vote.ts | 2 +- src/commands/slash/Main/connection.ts | 60 +++++++++++++++++-------- src/commands/slash/Main/hub/browse.ts | 3 +- src/scripts/network/onboarding.ts | 2 +- 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/locales b/locales index 201fceb1a..3bb92c116 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 201fceb1a1a1be8b32b6f311f8581788396a47f1 +Subproject commit 3bb92c1164664908c7e3a6fc1e5f84e3dc60963b diff --git a/src/commands/slash/Information/rules.ts b/src/commands/slash/Information/rules.ts index 7186fa90a..3503335b7 100644 --- a/src/commands/slash/Information/rules.ts +++ b/src/commands/slash/Information/rules.ts @@ -11,7 +11,7 @@ export default class Rules extends BaseCommand { const rulesEmbed = new EmbedBuilder() .setDescription( __( - { phrase: 'commands.rules.embed.description', locale: interaction.user.locale }, + { phrase: 'rules', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), ) diff --git a/src/commands/slash/Information/vote.ts b/src/commands/slash/Information/vote.ts index a50ee5ffb..f0243d072 100644 --- a/src/commands/slash/Information/vote.ts +++ b/src/commands/slash/Information/vote.ts @@ -17,7 +17,7 @@ export default class Vote extends BaseCommand { async execute(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setDescription( - __({ phrase: 'commands.vote.embed.description', locale: interaction.user.locale }), + __({ phrase: 'vote', locale: interaction.user.locale }), ) .setColor(colors.interchatBlue); diff --git a/src/commands/slash/Main/connection.ts b/src/commands/slash/Main/connection.ts index db901eace..fc317d9c7 100644 --- a/src/commands/slash/Main/connection.ts +++ b/src/commands/slash/Main/connection.ts @@ -30,6 +30,7 @@ import { getOrCreateWebhook, setComponentExpiry, } from '../../../utils/Utils.js'; +import { __ } from 'i18n'; export default class Connection extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -55,9 +56,7 @@ export default class Connection extends BaseCommand { if (!isInDb) { await interaction.reply({ embeds: [ - errorEmbed( - `${emojis.no} Invalid connection. Verify the channel ID or select from displayed options.`, - ), + errorEmbed(__({ phrase: 'connection.notFound', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -116,7 +115,7 @@ export default class Connection extends BaseCommand { { connected: !isInDb.connected }, ); await interaction.followUp({ - content: `${emojis.no} Automatically disconnected from network due to errors. Change the channel to use the network.`, + content: __({ phrase: 'connection.channelNotFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -165,7 +164,7 @@ export default class Connection extends BaseCommand { if (customId.args.at(1) && customId.args[1] !== interaction.user.id) { await interaction.reply({ embeds: [ - errorEmbed(`${emojis.no} Sorry, you can't perform this action. Please run the command yourself.`), + errorEmbed(__({ phrase: 'errors.cannotPerformAction', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -176,7 +175,7 @@ export default class Connection extends BaseCommand { const isInDb = await networkManager.fetchConnection({ channelId }); if (!isInDb || !channelId) { await interaction.reply({ - content: `${emojis.no} This connection no longer exists.`, + content: __({ phrase: 'connection.channelNotFound', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -251,14 +250,20 @@ export default class Connection extends BaseCommand { ); await interaction.reply({ - content: `${emojis.info} Select a channel to switch to using the select menu below:`, + content: __( + { phrase: 'connection.switchChannel', locale: interaction.user.locale }, + { emoji: emojis.info }, + ), components: [channelSelect], ephemeral: true, }); // current interaction will become outdated due to new channelId await interaction.message.edit({ - content: `${emojis.info} Channel switch called, use the command again to view new connection.`, + content: __( + { phrase: 'connection.switchCalled', locale: interaction.user.locale }, + { emoji: emojis.info }, + ), components: disableComponents(interaction.message), }); break; @@ -307,7 +312,10 @@ export default class Connection extends BaseCommand { const channelInHub = await networkManager.fetchConnection({ channelId: newChannel?.id }); if (channelInHub) { await interaction.reply({ - content: `${emojis.no} Channel ${newChannel} is already connected to a hub.`, + content: __( + { phrase: 'connection.alreadyConnected', locale: interaction.user.locale }, + { channel: `${newChannel}` }, + ), ephemeral: true, }); return; @@ -320,7 +328,10 @@ export default class Connection extends BaseCommand { ); await interaction.update({ - content: `${emojis.yes} Switched network channel to <#${newChannel?.id}>.`, + content: __( + { phrase: 'connection.switchSuccess', locale: interaction.user.locale }, + { channel: `${newChannel}`, emoji: emojis.yes }, + ), components: [], }); } @@ -336,21 +347,33 @@ export default class Connection extends BaseCommand { if (!invite) { await networkManager.updateConnection({ channelId }, { invite: { unset: true } }); - await interaction.reply({ content: `${emojis.yes} Invite Removed.`, ephemeral: true }); + await interaction.reply({ + content: __( + { phrase: 'connection.inviteRemoved', locale: interaction.user.locale }, + { emoji: emojis.yes }, + ), + ephemeral: true, + }); return; } const isValid = await interaction.client?.fetchInvite(invite).catch(() => null); if (isValid?.guild?.id !== interaction.guildId) { - await interaction.reply({ content: `${emojis.no} Invalid Invite.`, ephemeral: true }); + await interaction.reply({ + content: __({ phrase: 'connection.inviteInvalid', locale: interaction.user.locale }), + ephemeral: true, + }); return; } await networkManager.updateConnection({ channelId }, { invite }); await interaction.reply({ - content: `${emojis.yes} Invite Added. Others can now join the server by using \`Message Info\` Apps command in the network.`, + content: __( + { phrase: 'connection.inviteAdded', locale: interaction.user.locale }, + { emoji: emojis.yes }, + ), ephemeral: true, }); } @@ -360,7 +383,7 @@ export default class Connection extends BaseCommand { const hex_regex = /^#[0-9A-F]{6}$/i; if (embedColor && !hex_regex.test(embedColor)) { interaction.reply({ - content: `${emojis.no} Invalid hex color code. Please try again.`, + content: __({ phrase: 'connection.emColorInvalid', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -372,14 +395,15 @@ export default class Connection extends BaseCommand { }); await interaction.reply({ - content: `${emojis.yes} Embed color successfully ${ - embedColor ? `set to \`${embedColor}\`!` : 'unset' - }`, + content: __( + { phrase: 'connection.emColorChange', locale: interaction.user.locale }, + { action: embedColor ? `set to \`${embedColor}\`!` : 'unset', emoji: emojis.yes }, + ), ephemeral: true, }); } - interaction.message + await interaction.message ?.edit({ embeds: [await buildEmbed(interaction, customId.args[0])] }) .catch(() => null); } diff --git a/src/commands/slash/Main/hub/browse.ts b/src/commands/slash/Main/hub/browse.ts index 945259e7e..e768e1f25 100644 --- a/src/commands/slash/Main/hub/browse.ts +++ b/src/commands/slash/Main/hub/browse.ts @@ -25,6 +25,7 @@ import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; import { stripIndents } from 'common-tags'; import BlacklistManager from '../../../../managers/BlacklistManager.js'; +import { __ } from 'i18n'; export default class Browse extends Hub { async execute(interaction: ChatInputCommandInteraction): Promise { @@ -90,7 +91,7 @@ export default class Browse extends Hub { if (!hubList || hubList.length === 0) { interaction.reply({ - content: 'There are no hubs listed here at the moment. Please try again later!', + content: __({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), ephemeral: true, }); return; diff --git a/src/scripts/network/onboarding.ts b/src/scripts/network/onboarding.ts index eceff64f6..7541c71d2 100644 --- a/src/scripts/network/onboarding.ts +++ b/src/scripts/network/onboarding.ts @@ -102,7 +102,7 @@ export async function showOnboarding( const rulesEmbed = new EmbedBuilder() .setDescription( __( - { phrase: 'commands.rules.embed.description', locale: interaction.user.locale }, + { phrase: 'rules', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), ) From ccf43cfeebaa7247d98257d12411104977e6e4a9 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:38:59 +0530 Subject: [PATCH 09/15] refactor(localization): fix i18n imports --- src/InterChat.ts | 15 ++-------- src/api/index.ts | 4 +-- src/commands/context-menu/blacklist.ts | 2 +- src/commands/context-menu/deleteMsg.ts | 2 +- src/commands/context-menu/editMsg.ts | 2 +- src/commands/context-menu/messageInfo.ts | 2 +- src/commands/context-menu/translate.ts | 2 +- src/commands/slash/Information/invite.ts | 2 +- src/commands/slash/Information/rules.ts | 2 +- src/commands/slash/Information/vote.ts | 2 +- src/commands/slash/Main/blacklist/list.ts | 2 +- src/commands/slash/Main/blacklist/server.ts | 2 +- src/commands/slash/Main/blacklist/user.ts | 2 +- src/commands/slash/Main/connection.ts | 2 +- src/commands/slash/Main/hub/browse.ts | 32 +++++++++++++-------- src/scripts/network/onboarding.ts | 2 +- src/utils/Locale.ts | 21 ++++++++++++++ src/utils/Utils.ts | 5 +--- 18 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 src/utils/Locale.ts diff --git a/src/InterChat.ts b/src/InterChat.ts index b85211bd3..0a1f04468 100644 --- a/src/InterChat.ts +++ b/src/InterChat.ts @@ -1,5 +1,4 @@ import db from './utils/Db.js'; -import YAML from 'yaml'; import Logger from './utils/Logger.js'; import SuperClient from './SuperClient.js'; import CommandManager from './managers/CommandManager.js'; @@ -18,7 +17,7 @@ import { } from 'discord.js'; import { stripIndents } from 'common-tags'; import { LINKS, channels, colors, emojis, mascotEmojis } from './utils/Constants.js'; -import { I18n } from 'i18n'; +import { initI18n } from './utils/Locale.js'; class InterChat extends SuperClient { public constructor() { @@ -28,16 +27,8 @@ class InterChat extends SuperClient { // initialize the client this.init(); - new I18n().configure({ - directory: './locales', - fallbacks: { '*': 'en' }, - objectNotation: true, - parser: YAML, - extension: '.yml', - logDebugFn: Logger.info, - logWarnFn: Logger.warn, - logErrorFn: Logger.error, - }); + // initialize i18n for localization + initI18n(); // load commands CommandManager.loadCommandFiles(); diff --git a/src/api/index.ts b/src/api/index.ts index 72cb126e7..3e4602023 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -49,11 +49,11 @@ export default function start() { } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('404 Not Found'); + res.end('Why the hell are we even here? This is a 404.'); } }); server.listen(port, () => { - Logger.info(`Server listening on port ${port}`); + Logger.info(`API listening on port http://localhost:${port}.`); }); } diff --git a/src/commands/context-menu/blacklist.ts b/src/commands/context-menu/blacklist.ts index 7bc131a91..bab93cea1 100644 --- a/src/commands/context-menu/blacklist.ts +++ b/src/commands/context-menu/blacklist.ts @@ -20,7 +20,7 @@ import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { errorEmbed } from '../../utils/Utils.js'; import parse from 'parse-duration'; import NetworkLogger from '../../utils/NetworkLogger.js'; -import { __ } from '../../utils/Utils.js'; +import { __ } from '../../utils/Locale.js'; export default class Blacklist extends BaseCommand { data: RESTPostAPIApplicationCommandsJSONBody = { diff --git a/src/commands/context-menu/deleteMsg.ts b/src/commands/context-menu/deleteMsg.ts index 569b14c40..9965719a4 100644 --- a/src/commands/context-menu/deleteMsg.ts +++ b/src/commands/context-menu/deleteMsg.ts @@ -8,7 +8,7 @@ import BaseCommand from '../BaseCommand.js'; import { checkIfStaff } from '../../utils/Utils.js'; import { emojis } from '../../utils/Constants.js'; import db from '../../utils/Db.js'; -import { __ } from '../../utils/Utils.js'; +import { __ } from '../../utils/Locale.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { diff --git a/src/commands/context-menu/editMsg.ts b/src/commands/context-menu/editMsg.ts index e3ac310d8..f285592ca 100644 --- a/src/commands/context-menu/editMsg.ts +++ b/src/commands/context-menu/editMsg.ts @@ -17,7 +17,7 @@ import { checkIfStaff, hasVoted, replaceLinks } from '../../utils/Utils.js'; import { censor } from '../../utils/Profanity.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; -import { __ } from '../../utils/Utils.js'; +import { __ } from '../../utils/Locale.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { diff --git a/src/commands/context-menu/messageInfo.ts b/src/commands/context-menu/messageInfo.ts index 3d10928db..8685ce3b2 100644 --- a/src/commands/context-menu/messageInfo.ts +++ b/src/commands/context-menu/messageInfo.ts @@ -17,7 +17,7 @@ import { colors, emojis } from '../../utils/Constants.js'; import BaseCommand from '../BaseCommand.js'; import { CustomID } from '../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; -import { __ } from '../../utils/Utils.js'; +import { __ } from '../../utils/Locale.js'; export default class MessageInfo extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { diff --git a/src/commands/context-menu/translate.ts b/src/commands/context-menu/translate.ts index 585492413..8bec00bab 100644 --- a/src/commands/context-menu/translate.ts +++ b/src/commands/context-menu/translate.ts @@ -20,7 +20,7 @@ import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; import { supportedLanguages } from '@translate-tools/core/translators/GoogleTranslator/index.js'; import translator from '../../utils/Translator.cjs'; -import { __ } from '../../utils/Utils.js'; +import { __ } from '../../utils/Locale.js'; export default class Translate extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { diff --git a/src/commands/slash/Information/invite.ts b/src/commands/slash/Information/invite.ts index 835588653..d6c4a30b8 100644 --- a/src/commands/slash/Information/invite.ts +++ b/src/commands/slash/Information/invite.ts @@ -7,7 +7,7 @@ import { } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { LINKS, emojis } from '../../../utils/Constants.js'; -import { __ } from '../../../utils/Utils.js'; +import { __ } from '../../../utils/Locale.js'; export default class Invite extends BaseCommand { readonly data = { diff --git a/src/commands/slash/Information/rules.ts b/src/commands/slash/Information/rules.ts index 3503335b7..fd860f2d1 100644 --- a/src/commands/slash/Information/rules.ts +++ b/src/commands/slash/Information/rules.ts @@ -1,7 +1,7 @@ import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { LINKS, colors } from '../../../utils/Constants.js'; -import { __ } from '../../../utils/Utils.js'; +import { __ } from '../../../utils/Locale.js'; export default class Rules extends BaseCommand { readonly data = { name: 'rules', diff --git a/src/commands/slash/Information/vote.ts b/src/commands/slash/Information/vote.ts index f0243d072..08ae1a9c5 100644 --- a/src/commands/slash/Information/vote.ts +++ b/src/commands/slash/Information/vote.ts @@ -7,7 +7,7 @@ import { } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { colors } from '../../../utils/Constants.js'; -import { __ } from '../../../utils/Utils.js'; +import { __ } from '../../../utils/Locale.js'; export default class Vote extends BaseCommand { readonly data = { diff --git a/src/commands/slash/Main/blacklist/list.ts b/src/commands/slash/Main/blacklist/list.ts index 6d58a64aa..75689b961 100644 --- a/src/commands/slash/Main/blacklist/list.ts +++ b/src/commands/slash/Main/blacklist/list.ts @@ -4,7 +4,7 @@ import BlacklistCommand from './index.js'; import { paginate } from '../../../../utils/Pagination.js'; import { colors } from '../../../../utils/Constants.js'; import { errorEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; export default class ListBlacklists extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { diff --git a/src/commands/slash/Main/blacklist/server.ts b/src/commands/slash/Main/blacklist/server.ts index 64296893d..2c66420d6 100644 --- a/src/commands/slash/Main/blacklist/server.ts +++ b/src/commands/slash/Main/blacklist/server.ts @@ -8,7 +8,7 @@ import BlacklistManager from '../../../../managers/BlacklistManager.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; import parse from 'parse-duration'; import Logger from '../../../../utils/Logger.js'; -import { __ } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; export default class UserBlacklist extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { diff --git a/src/commands/slash/Main/blacklist/user.ts b/src/commands/slash/Main/blacklist/user.ts index f42f0f80a..be13386f5 100644 --- a/src/commands/slash/Main/blacklist/user.ts +++ b/src/commands/slash/Main/blacklist/user.ts @@ -6,7 +6,7 @@ import parse from 'parse-duration'; import { emojis } from '../../../../utils/Constants.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; import { errorEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; export default class Server extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { diff --git a/src/commands/slash/Main/connection.ts b/src/commands/slash/Main/connection.ts index fc317d9c7..a82469bb8 100644 --- a/src/commands/slash/Main/connection.ts +++ b/src/commands/slash/Main/connection.ts @@ -30,7 +30,7 @@ import { getOrCreateWebhook, setComponentExpiry, } from '../../../utils/Utils.js'; -import { __ } from 'i18n'; +import { __ } from '../../../utils/Locale.js'; export default class Connection extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { diff --git a/src/commands/slash/Main/hub/browse.ts b/src/commands/slash/Main/hub/browse.ts index e768e1f25..10ccbcdf6 100644 --- a/src/commands/slash/Main/hub/browse.ts +++ b/src/commands/slash/Main/hub/browse.ts @@ -25,7 +25,7 @@ import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; import { stripIndents } from 'common-tags'; import BlacklistManager from '../../../../managers/BlacklistManager.js'; -import { __ } from 'i18n'; +import i18n from 'i18n'; export default class Browse extends Hub { async execute(interaction: ChatInputCommandInteraction): Promise { @@ -91,7 +91,7 @@ export default class Browse extends Hub { if (!hubList || hubList.length === 0) { interaction.reply({ - content: __({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), + content: i18n.__({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -145,7 +145,7 @@ export default class Browse extends Hub { }); if (!hubDetails) { return await interaction.reply({ - content: 'Hub not found.', + content: i18n.__({ phrase: 'hub.notFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -174,7 +174,10 @@ export default class Browse extends Hub { const alreadyJoined = hubDetails.connections.find((c) => c.serverId === interaction.guildId); if (alreadyJoined) { interaction.reply({ - content: `You have already joined **${hubDetails.name}** from <#${alreadyJoined.channelId}>!`, + content: i18n.__( + { phrase: 'hub.alreadyJoined', locale: interaction.user.locale }, + { hub: hubDetails.name, channel: `<#${alreadyJoined.channelId}>` }, + ), ephemeral: true, }); return; @@ -212,11 +215,13 @@ export default class Browse extends Hub { // use current channel embed const embed = new EmbedBuilder() .setDescription( - stripIndents` - Are you sure you wish to join **${hubDetails.name}** from ${interaction.channel}? - - **Note:** You can always change this later using \`/connection\`. - `, + i18n.__( + { + phrase: 'hub.browse.joinConfirm', + locale: interaction.user.locale, + }, + { hub: hubDetails.name, channel: `${interaction.channel}` }, + ), ) .setColor('Aqua') .setFooter({ text: 'Want to use a different channel? Use the dropdown below.' }); @@ -234,7 +239,7 @@ export default class Browse extends Hub { else if (customId.postfix === 'channel_select' || customId.postfix === 'confirm') { if (!hubDetails) { return await interaction.reply({ - content: 'Hub not found.', + content: i18n.__({ phrase: 'hub.notFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -247,7 +252,10 @@ export default class Browse extends Hub { ); if (userBlacklisted) { return await interaction.reply({ - content: `You have been blacklisted from joining **${hubDetails.name}**.`, + content: i18n.__( + { phrase: 'hub.userBlacklisted', locale: interaction.user.locale }, + { hub: hubDetails.name }, + ), ephemeral: true, }); } @@ -258,7 +266,7 @@ export default class Browse extends Hub { ); if (serverBlacklisted) { return await interaction.reply({ - content: `This server has been blacklisted from joining **${hubDetails.name}**.`, + content: `This server is blacklisted from joining **${hubDetails.name}**.`, ephemeral: true, }); } diff --git a/src/scripts/network/onboarding.ts b/src/scripts/network/onboarding.ts index 7541c71d2..a478c5094 100644 --- a/src/scripts/network/onboarding.ts +++ b/src/scripts/network/onboarding.ts @@ -10,7 +10,7 @@ import { Collection, } from 'discord.js'; import { LINKS, colors } from '../../utils/Constants.js'; -import { __ } from '../../utils/Utils.js'; +import { __ } from '../../utils/Locale.js'; const onboardingInProgress = new Collection(); diff --git a/src/utils/Locale.ts b/src/utils/Locale.ts new file mode 100644 index 000000000..acba7ff0d --- /dev/null +++ b/src/utils/Locale.ts @@ -0,0 +1,21 @@ +import { I18n } from 'i18n'; +import YAML from 'yaml'; +import Logger from './Logger.js'; + +const { configure, __ } = new I18n(); + +export function initI18n(locale = 'en') { + configure({ + directory: './locales', + fallbacks: { '*': locale }, + objectNotation: true, + parser: YAML, + extension: '.yml', + logDebugFn: Logger.debug, + logWarnFn: Logger.warn, + logErrorFn: Logger.error, + }); +} + +// i18n is cjs :( +export { __ }; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 657b1fe71..306081643 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -20,10 +20,7 @@ import db from './Db.js'; import startCase from 'lodash/startCase.js'; import toLower from 'lodash/toLower.js'; import 'dotenv/config'; -import i18n from 'i18n'; - -// i18n is cjs :( -export const __ = i18n.__; +import { __ } from './Locale.js'; /** Convert milliseconds to a human readable time (eg: 1d 2h 3m 4s) */ export function msToReadable(milliseconds: number): string { From 668444df4d81d251b093f4f74433a7e48cebce9d Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Fri, 15 Dec 2023 22:16:09 +0530 Subject: [PATCH 10/15] chore: add more translations only /hub manage, messageCreate etc. errors and some components left... --- README.md | 90 +++---- locales | 2 +- src/commands/BaseCommand.ts | 19 +- src/commands/context-menu/blacklist.ts | 43 +-- src/commands/context-menu/editMsg.ts | 4 +- src/commands/context-menu/messageInfo.ts | 16 +- src/commands/context-menu/translate.ts | 2 +- src/commands/slash/Main/blacklist/index.ts | 17 +- src/commands/slash/Main/blacklist/list.ts | 4 +- src/commands/slash/Main/blacklist/server.ts | 13 +- src/commands/slash/Main/blacklist/user.ts | 4 +- src/commands/slash/Main/connection.ts | 6 +- src/commands/slash/Main/hub/browse.ts | 60 +++-- src/commands/slash/Main/hub/connections.ts | 114 -------- src/commands/slash/Main/hub/create.ts | 43 ++- src/commands/slash/Main/hub/delete.ts | 61 ++--- src/commands/slash/Main/hub/index.ts | 32 +-- src/commands/slash/Main/hub/invite.ts | 56 ++-- src/commands/slash/Main/hub/join.ts | 6 +- src/commands/slash/Main/hub/joined.ts | 43 +-- src/commands/slash/Main/hub/leave.ts | 45 ++-- src/commands/slash/Main/hub/logging.ts | 6 +- src/commands/slash/Main/hub/manage.ts | 284 +++++++++++++------- src/commands/slash/Main/hub/moderator.ts | 133 ++++++--- src/commands/slash/Main/hub/servers.ts | 137 ++++++++++ src/commands/slash/Main/hub/settings.ts | 97 ------- src/commands/slash/Staff/find/index.ts | 11 +- src/commands/slash/Staff/find/user.ts | 4 +- src/commands/slash/Staff/purge.ts | 4 +- src/commands/slash/Support/support/index.ts | 15 +- src/managers/CommandManager.ts | 18 +- src/managers/NetworkManager.ts | 18 +- src/scripts/hub/settings.ts | 18 +- src/utils/Constants.ts | 2 +- src/utils/CustomID.ts | 3 +- src/utils/Locale.ts | 6 +- src/utils/Utils.ts | 32 ++- 37 files changed, 810 insertions(+), 658 deletions(-) delete mode 100644 src/commands/slash/Main/hub/connections.ts create mode 100644 src/commands/slash/Main/hub/servers.ts delete mode 100644 src/commands/slash/Main/hub/settings.ts diff --git a/README.md b/README.md index 74c11adb6..376936713 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,18 @@ This repo contains the source code for the InterChat Discord bot. InterChat is a Discord bot that allows you to chat with users from other servers. -## Getting Started +# Getting Started -### Prerequisites +## Prerequisites 1. [Node.js v18.0.0](https://nodejs.org/en/download/current/), or higher for linux users 2. [Git](https://git-scm.com/downloads) 3. [MongoDB](https://www.mongodb.com/try/download/community) 4. [NPM](https://www.npmjs.com/get-npm) or [Yarn](https://yarnpkg.com/getting-started/install) (we are using npm in this guide) 5. [An Imgur API Key](https://api.imgur.com/oauth2/addclient) (optional, for setting hub icon and banner) -6. [Python 3.9.3+](https://www.python.org/downloads/release/python-2718/) & [Visual Studio Build Tools (Windows Only)](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) (for the API) -> [!NOTE] -> If you are using Windows, make sure to install the build tools from the link above and install the exact Python version v3.9.3 or you will get errors during package installation. Ignore this step if you're a linux user (I use arch btw. - dev-737) +6. [Python 2.7](https://www.python.org/downloads/release/python-2718/) & [Visual Studio Build Tools (Windows Only)](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019) (for the API) -### Setting up the bot +## Running the code 1. Clone the repository using `git clone` or download the zip file. 2. Create a file called `.env` and fill it out with the appropriate contents mentioned in the env.example file. @@ -26,29 +24,37 @@ This repo contains the source code for the InterChat Discord bot. InterChat is a 5. Register commands for your bot using `npm run register:commands --public` & `npm run register:commands --private` 6. Finally run the code using `npm run dev`, or `npm start` to run in production mode -### Setting up the database +## Database Setup 1. Create a MongoDB instance on [MongoDB Atlas](https://www.mongodb.com/cloud/atlas/register) 2. Copy the database connection string and paste it in the `.env` file as `MONGODB_URI` -### Creating new commands +## Creating Commands -To create a new command, create a new file in the `src/commands` directory with the name of the command. The file should export a **default** class that extends the `BaseCommand` class found in the `src/commands/BaseCommand.ts` file. This class is expected to include the following methods and properties: +To add a new command, follow these steps: -1. A `data` property, which should store the slash command builder or raw command JSON. -2. An `execute` method responsible for handling incoming commands. +1. Create a new file in the `src/commands` directory with the command's name. +2. In this file, export a **default** class that extends the `BaseCommand` class from `src/commands/BaseCommand.ts`. +3. The class should include: + - A `data` property to store the slash command builder or raw command JSON. + - An `execute` method to handle incoming commands. -Additionally, there are various optional methods and properties at your disposal for further customization. Have a look at `src/commands/BaseCommand.ts` for more information. +You can also explore various other methods and properties for additional customization by referring to `src/commands/BaseCommand.ts` for more details. -#### Subcommands +### Subcommands -To create a subcommand, create a new file called `index.ts` in the `src/commands//` directory. The file must export a **default class** that extends the `BaseCommand` class from the `src/commands/BaseSubCommand.ts` file like usual. Then create additional files in the same directory with the name of the subcommands. The file must export another **default class** that extends the class was just created in `./index.ts`. It is important to note that subcommand **file names must be the same as the subcommand name** registered on discord. +To add a subcommand, follow these steps: -### Adding cooldowns +1. Create a new file named `index.ts` in the `src/commands//` directory. +2. In this file, export a **default class** that extends the `BaseCommand` class found in `src/commands/BaseSubCommand.ts`. +3. Create additional files in the same directory, each named after a subcommand. +4. In each of these files, export a **default class** that extends the class created in `./index.ts`. Ensure that the subcommand file names match the registered subcommand names on Discord. + +## Adding cooldowns To add a cooldown to a command/subcommand, simply add a `cooldown` property to the class with the value being the cooldown in milliseconds. The cooldown will be applied to the user who executed the command. -### Creating Custom IDs +## Creating Custom IDs To help with handling components, we have a custom ID class that can be used to create custom IDs for components. To create a custom ID, simply call the `CustomID` method with the custom ID prefix and the custom postfix. Example: @@ -77,7 +83,7 @@ const customId = CustomID.parseCustomId(interaction.customId); */ ``` -### Handling components +## Handling components To handle components (buttons, selects, modals) create a new method called `handleComponents` or `handleModals` for modals in the command/subcommand class. Then create a decorator for the method using the `@InteractionHandler` decorator with the customId prefix of the components/modals. The method will be called when the component is triggered. Example: @@ -92,29 +98,16 @@ public async handleComponents(interaction: MessageComponentInteraction) { ## Other information -* Events are located in `src/InterChat.ts`. -* Commands are loaded automatically by calling the `loadCommandFiles` method from `src/managers/CommandManager.ts` during the bot startup. -* The `src/commands/BaseCommand.ts` file contains all the methods/properties that can be used in a command. -* We use the `interactionCreate` event for handling **all** interactions instead of using collectors. -* If you are using your own bot for testing, make sure to change the CLIENT_ID in `src/utils/Constants.ts` like so: +- Events are located in `src/InterChat.ts`. +- Commands are loaded automatically by calling the `loadCommandFiles` method from `src/managers/CommandManager.ts` during the bot startup. +- The `src/commands/BaseCommand.ts` file contains all the methods/properties that can be used in a command. +- We use the `interactionCreate` event for handling **all** interactions instead of using collectors. +- If you are using your own bot for testing, make sure to change the CLIENT_ID in `src/utils/Constants.ts` like so: ```ts export const CLIENT_ID = isDevBuild ? '' : '769921109209907241'; ``` -## Special Comments - -These are comments to show the state of a piece of code. Install -the "Todo Tree" extension to highlight them in VS-Code. - -1. `TODO` - Something that must be finished before releasing, a reminder. - -2. `REVIEW` - Review a piece of code to see if there is a better alternative. - -3. `FIXME` - To change later to avoid facing problems, a bug that must be fixed before release. - -4. `NOTE` - A note left for later, something important or something that shows how something is supposed to be used/works. - ## Contributing ### Commit Messages @@ -123,19 +116,19 @@ Use semantic commit messages in your commit messages as it will make auto-releas [Examples](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716): -* `feat`: (new feature for the user, not a new feature for build script) +- `feat`: (new feature for the user, not a new feature for build script) -* `fix`: (bug fix for the user, not a fix to a build script) +- `fix`: (bug fix for the user, not a fix to a build script) -* `docs`: (changes to the documentation) +- `docs`: (changes to the documentation) -* `style`: (formatting, missing semi colons, etc; no production code change) +- `style`: (formatting, missing semi colons, etc; no production code change) -* `refactor`: (refactoring production code, eg. renaming a variable) +- `refactor`: (refactoring production code, eg. renaming a variable) -* `test`: (adding missing tests, refactoring tests; no production code change) +- `test`: (adding missing tests, refactoring tests; no production code change) -* `chore`: (updating grunt tasks etc; no production code change) +- `chore`: (updating grunt tasks etc; no production code change) To make our lives easier by not having to remember the commit messages at all times, this repository is [commitizen](https://www.npmjs.com/package/commitizen) friendly! Commitizen is a commandline tool that guides you through the process of choosing your desired commit type. @@ -146,8 +139,15 @@ To make our lives easier by not having to remember the commit messages at all ti Run `git cz` or `cz commit` to commit using commitizen. -## Achievements & Goals +### Special Comments + +These are comments to show the state of a piece of code. Install +the "Todo Tree" extension to highlight them in VS-Code. -* [x] 1000 servers using InterChat 🎉 +1. `TODO` - Something that must be finished before releasing, a reminder. -* [x] 101 votes on topgg in a month +2. `REVIEW` - Review a piece of code to see if there is a better alternative. + +3. `FIXME` - To change later to avoid facing problems, a bug that must be fixed before release. + +4. `NOTE` - A note left for later, something important or something that shows how something is supposed to be used/works. diff --git a/locales b/locales index 3bb92c116..8eed811af 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 3bb92c1164664908c7e3a6fc1e5f84e3dc60963b +Subproject commit 8eed811af35222c71855d05e9a4727f56fa5b8b7 diff --git a/src/commands/BaseCommand.ts b/src/commands/BaseCommand.ts index e4891e120..4592784c0 100644 --- a/src/commands/BaseCommand.ts +++ b/src/commands/BaseCommand.ts @@ -9,7 +9,7 @@ import { } from 'discord.js'; import { InteractionFunction } from '../decorators/Interaction.js'; -type CommandInteraction = ChatInputCommandInteraction | ContextMenuCommandInteraction; +export type CmdInteraction = ChatInputCommandInteraction | ContextMenuCommandInteraction; export const commandsMap = new Collection(); export const interactionsMap = new Collection(); @@ -21,19 +21,10 @@ export default abstract class BaseCommand { readonly description?: string; static readonly subcommands?: Collection; - abstract execute(interaction: CommandInteraction): Promise; + abstract execute(interaction: CmdInteraction): Promise; // optional methods - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handleComponents(interaction: MessageComponentInteraction) { - /**/ - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handleModals(interaction: ModalSubmitInteraction) { - /**/ - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - autocomplete(interaction: AutocompleteInteraction) { - /**/ - } + async handleComponents?(interaction: MessageComponentInteraction): Promise; + async handleModals?(interaction: ModalSubmitInteraction): Promise; + async autocomplete?(interaction: AutocompleteInteraction): Promise; } diff --git a/src/commands/context-menu/blacklist.ts b/src/commands/context-menu/blacklist.ts index bab93cea1..85786c5ef 100644 --- a/src/commands/context-menu/blacklist.ts +++ b/src/commands/context-menu/blacklist.ts @@ -12,15 +12,15 @@ import { TextInputBuilder, TextInputStyle, } from 'discord.js'; -import BaseCommand from '../BaseCommand.js'; import db from '../../utils/Db.js'; -import { colors, emojis } from '../../utils/Constants.js'; -import { CustomID } from '../../utils/CustomID.js'; -import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; -import { errorEmbed } from '../../utils/Utils.js'; import parse from 'parse-duration'; +import BaseCommand from '../BaseCommand.js'; import NetworkLogger from '../../utils/NetworkLogger.js'; import { __ } from '../../utils/Locale.js'; +import { colors, emojis } from '../../utils/Constants.js'; +import { CustomID } from '../../utils/CustomID.js'; +import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; +import { simpleEmbed } from '../../utils/Utils.js'; export default class Blacklist extends BaseCommand { data: RESTPostAPIApplicationCommandsJSONBody = { @@ -30,6 +30,8 @@ export default class Blacklist extends BaseCommand { }; async execute(interaction: MessageContextMenuCommandInteraction) { + const locale = interaction.user.locale; + const messageInDb = await db.messageData.findFirst({ where: { channelAndMessageIds: { some: { messageId: interaction.targetId } }, @@ -45,11 +47,8 @@ export default class Blacklist extends BaseCommand { if (!messageInDb) { interaction.reply({ embeds: [ - errorEmbed( - __( - { phrase: 'errors.messageNotSentOrExpired', locale: interaction.user.locale }, - { emoji: emojis.info }, - ), + simpleEmbed( + __({ phrase: 'errors.messageNotSentOrExpired', locale }, { emoji: emojis.info }), ), ], ephemeral: true, @@ -75,7 +74,7 @@ export default class Blacklist extends BaseCommand { .addArgs('u=1') .toString(), ) - .setLabel('Blacklist User') + .setLabel(__({ phrase: 'blacklist.components.user', locale })) .setStyle(ButtonStyle.Secondary) .setEmoji('👤'), new ButtonBuilder() @@ -87,7 +86,7 @@ export default class Blacklist extends BaseCommand { .addArgs('s=1') .toString(), ) - .setLabel('Blacklist Server') + .setLabel(__({ phrase: 'blacklist.components.user', locale })) .setStyle(ButtonStyle.Secondary) .setEmoji('🏠'), ); @@ -102,7 +101,7 @@ export default class Blacklist extends BaseCommand { if (interaction.user.id !== customId.args[0]) { await interaction.reply({ embeds: [ - errorEmbed(__({ phrase: 'errors.cannotPerformAction', locale: interaction.user.locale })), + simpleEmbed(__({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -125,16 +124,24 @@ export default class Blacklist extends BaseCommand { new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('reason') - .setLabel('Reason') - .setPlaceholder('What is the reason for this blacklist?') + .setLabel( + __({ phrase: 'blacklist.modal.reason.label', locale: interaction.user.locale }), + ) + .setPlaceholder( + __({ phrase: 'blacklist.modal.reason.placeholder', locale: interaction.user.locale }), + ) .setStyle(TextInputStyle.Paragraph) .setMaxLength(500), ), new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('duration') - .setLabel('Duration') - .setPlaceholder('Duration of the blacklist. Eg. 1d 2h 3m') + .setLabel( + __({ phrase: 'blacklist.modal.duration.label', locale: interaction.user.locale }), + ) + .setPlaceholder( + __({ phrase: 'blacklist.modal.reason.placeholder', locale: interaction.user.locale }), + ) .setStyle(TextInputStyle.Short) .setMinLength(2) .setRequired(false), @@ -171,7 +178,7 @@ export default class Blacklist extends BaseCommand { const successEmbed = new EmbedBuilder().setColor('Green').addFields( { name: 'Reason', - value: reason ? reason : 'No reason provided.', + value: reason ? reason : __({ phrase: 'misc.noReason', locale: interaction.user.locale }), inline: true, }, { diff --git a/src/commands/context-menu/editMsg.ts b/src/commands/context-menu/editMsg.ts index f285592ca..d26bd69e1 100644 --- a/src/commands/context-menu/editMsg.ts +++ b/src/commands/context-menu/editMsg.ts @@ -19,7 +19,7 @@ import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; import { __ } from '../../utils/Locale.js'; -export default class DeleteMessage extends BaseCommand { +export default class EditMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { type: ApplicationCommandType.Message, name: 'Edit Message', @@ -145,7 +145,7 @@ export default class DeleteMessage extends BaseCommand { .setColor(target.member?.displayHexColor ?? 'Random') .setImage(newImageUrl || oldImageUrl || null) .addFields( - target.embeds[0].fields[0] + target.embeds[0]?.fields[0] ? [{ name: 'Replying-to', value: `${target.embeds[0].description}` }] : [], ) diff --git a/src/commands/context-menu/messageInfo.ts b/src/commands/context-menu/messageInfo.ts index 8685ce3b2..b4d21ae20 100644 --- a/src/commands/context-menu/messageInfo.ts +++ b/src/commands/context-menu/messageInfo.ts @@ -63,7 +63,7 @@ export default class MessageInfo extends BaseCommand { .setThumbnail(`https://cdn.discordapp.com/icons/${server?.id}/${server?.icon}.png`) .setColor('Random'); - const buttonsArr = MessageInfo.buildButtons(target.id); + const buttonsArr = MessageInfo.buildButtons(target.id, interaction.user.locale); if (guildConnected?.invite) { buttonsArr.push( @@ -79,7 +79,7 @@ export default class MessageInfo extends BaseCommand { await interaction.reply({ embeds: [embed], - components: MessageInfo.buildButtons(target.id), + components: MessageInfo.buildButtons(target.id, interaction.user.locale), ephemeral: true, }); } @@ -154,7 +154,9 @@ export default class MessageInfo extends BaseCommand { { phrase: 'msgInfo.server.description', locale: interaction.user.locale }, { server: server.name, - description: server.description || 'No Description.', + description: + server.description || + __({ phrase: 'misc.noDesc', locale: interaction.user.locale }), owner: `${owner.username}#${ owner.discriminator !== '0' ? `#${owner.discriminator}` : '' }`, @@ -266,27 +268,27 @@ export default class MessageInfo extends BaseCommand { buttons.components[disableElement].setDisabled(true); } - static buildButtons(messageId: string) { + static buildButtons(messageId: string, locale = 'en') { return [ new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId( new CustomID().setIdentifier('msgInfo', 'info').addArgs(messageId).toString(), ) - .setLabel('Message Info') + .setLabel(__({ phrase: 'msgInfo.buttons.message', locale })) .setStyle(ButtonStyle.Secondary) .setDisabled(true), new ButtonBuilder() .setCustomId( new CustomID().setIdentifier('msgInfo', 'serverInfo').addArgs(messageId).toString(), ) - .setLabel('Server Info') + .setLabel(__({ phrase: 'msgInfo.buttons.server', locale })) .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId( new CustomID().setIdentifier('msgInfo', 'userInfo').addArgs(messageId).toString(), ) - .setLabel('User Info') + .setLabel(__({ phrase: 'msgInfo.buttons.user', locale })) .setStyle(ButtonStyle.Secondary), ), ]; diff --git a/src/commands/context-menu/translate.ts b/src/commands/context-menu/translate.ts index 8bec00bab..33ddf674e 100644 --- a/src/commands/context-menu/translate.ts +++ b/src/commands/context-menu/translate.ts @@ -53,7 +53,7 @@ export default class Translate extends BaseCommand { const messageContent = target.content || target.embeds[0]?.description; if (!messageContent) return interaction.editReply('This message is not translatable.'); - const translatedMessage = await translator.translateText(messageContent, 'en', 'auto'); + const translatedMessage = await translator.translateText(messageContent, interaction.user.locale ?? 'en', 'auto'); const embed = new EmbedBuilder() .setDescription('### Translation Results') .setColor('Green') diff --git a/src/commands/slash/Main/blacklist/index.ts b/src/commands/slash/Main/blacklist/index.ts index bb9051249..fcdcac47a 100644 --- a/src/commands/slash/Main/blacklist/index.ts +++ b/src/commands/slash/Main/blacklist/index.ts @@ -7,6 +7,9 @@ import { } from 'discord.js'; import BaseCommand from '../../../BaseCommand.js'; import db from '../../../../utils/Db.js'; +import Logger from '../../../../utils/Logger.js'; +import { captureException } from '@sentry/node'; +import { replyWithError } from '../../../../utils/Utils.js'; export default class BlacklistCommand extends BaseCommand { // TODO: Put this in readme @@ -164,10 +167,16 @@ export default class BlacklistCommand extends BaseCommand { ], }; - async execute(interaction: ChatInputCommandInteraction): Promise { - const subCommand = interaction.options.getSubcommand(); - const isValid = BlacklistCommand.subcommands.get(subCommand); - if (isValid) return await isValid.execute(interaction); + async execute(interaction: ChatInputCommandInteraction) { + const subCommandName = interaction.options.getSubcommand(); + const subcommand = BlacklistCommand.subcommands.get(subCommandName); + + return await subcommand?.execute(interaction).catch((e) => { + Logger.error(e); + captureException(e); + // reply with an error message to the user + replyWithError(interaction, e.message); + }); } async autocomplete(interaction: AutocompleteInteraction) { diff --git a/src/commands/slash/Main/blacklist/list.ts b/src/commands/slash/Main/blacklist/list.ts index 75689b961..59e9c2724 100644 --- a/src/commands/slash/Main/blacklist/list.ts +++ b/src/commands/slash/Main/blacklist/list.ts @@ -3,7 +3,7 @@ import db from '../../../../utils/Db.js'; import BlacklistCommand from './index.js'; import { paginate } from '../../../../utils/Pagination.js'; import { colors } from '../../../../utils/Constants.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; import { __ } from '../../../../utils/Locale.js'; export default class ListBlacklists extends BlacklistCommand { @@ -25,7 +25,7 @@ export default class ListBlacklists extends BlacklistCommand { if (!hubInDb) { await interaction.editReply({ embeds: [ - errorEmbed(__({ phrase: 'errors.modUnknownHub', locale: interaction.user.locale })), + simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), ], }); return; diff --git a/src/commands/slash/Main/blacklist/server.ts b/src/commands/slash/Main/blacklist/server.ts index 2c66420d6..4094a3f79 100644 --- a/src/commands/slash/Main/blacklist/server.ts +++ b/src/commands/slash/Main/blacklist/server.ts @@ -1,6 +1,6 @@ import { captureException } from '@sentry/node'; import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; import { emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; import BlacklistCommand from './index.js'; @@ -28,11 +28,10 @@ export default class UserBlacklist extends BlacklistCommand { }); if (!hubInDb) { - return await interaction.editReply({ - embeds: [ - errorEmbed(__({ phrase: 'errors.modUnknownHub', locale: interaction.user.locale })), - ], + await interaction.editReply({ + embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], }); + return; } const blacklistManager = interaction.client.getBlacklistManager(); @@ -50,7 +49,7 @@ export default class UserBlacklist extends BlacklistCommand { if (serverInBlacklist) { return await interaction.followUp({ embeds: [ - errorEmbed( + simpleEmbed( __({ phrase: 'blacklist.server.alreadyBlacklisted', locale: interaction.user.locale, @@ -81,7 +80,7 @@ export default class UserBlacklist extends BlacklistCommand { captureException(err); interaction.followUp({ embeds: [ - errorEmbed( + simpleEmbed( __({ phrase: 'blacklist.server.unknownError', locale: interaction.user.locale, diff --git a/src/commands/slash/Main/blacklist/user.ts b/src/commands/slash/Main/blacklist/user.ts index be13386f5..eba090b62 100644 --- a/src/commands/slash/Main/blacklist/user.ts +++ b/src/commands/slash/Main/blacklist/user.ts @@ -5,7 +5,7 @@ import BlacklistManager from '../../../../managers/BlacklistManager.js'; import parse from 'parse-duration'; import { emojis } from '../../../../utils/Constants.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; import { __ } from '../../../../utils/Locale.js'; export default class Server extends BlacklistCommand { @@ -27,7 +27,7 @@ export default class Server extends BlacklistCommand { if (!hubInDb) { return await interaction.editReply({ embeds: [ - errorEmbed(__({ phrase: 'errors.modUnknownHub', locale: interaction.user.locale })), + simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), ], }); } diff --git a/src/commands/slash/Main/connection.ts b/src/commands/slash/Main/connection.ts index a82469bb8..2196d4751 100644 --- a/src/commands/slash/Main/connection.ts +++ b/src/commands/slash/Main/connection.ts @@ -26,7 +26,7 @@ import { emojis } from '../../../utils/Constants.js'; import { CustomID } from '../../../utils/CustomID.js'; import { disableComponents, - errorEmbed, + simpleEmbed, getOrCreateWebhook, setComponentExpiry, } from '../../../utils/Utils.js'; @@ -56,7 +56,7 @@ export default class Connection extends BaseCommand { if (!isInDb) { await interaction.reply({ embeds: [ - errorEmbed(__({ phrase: 'connection.notFound', locale: interaction.user.locale })), + simpleEmbed(__({ phrase: 'connection.notFound', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -164,7 +164,7 @@ export default class Connection extends BaseCommand { if (customId.args.at(1) && customId.args[1] !== interaction.user.id) { await interaction.reply({ embeds: [ - errorEmbed(__({ phrase: 'errors.cannotPerformAction', locale: interaction.user.locale })), + simpleEmbed(__({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), ], ephemeral: true, }); diff --git a/src/commands/slash/Main/hub/browse.ts b/src/commands/slash/Main/hub/browse.ts index 10ccbcdf6..05cb1bd13 100644 --- a/src/commands/slash/Main/hub/browse.ts +++ b/src/commands/slash/Main/hub/browse.ts @@ -25,12 +25,12 @@ import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; import { stripIndents } from 'common-tags'; import BlacklistManager from '../../../../managers/BlacklistManager.js'; -import i18n from 'i18n'; +import { __ } from '../../../../utils/Locale.js'; export default class Browse extends Hub { async execute(interaction: ChatInputCommandInteraction): Promise { const sortBy = interaction.options.getString('sort') as - | 'connections' + | 'servers' | 'active' | 'popular' | 'recent' @@ -58,7 +58,7 @@ export default class Browse extends Hub { orderBy: { createdAt: 'desc' }, }); break; - case 'connections': + case 'servers': sortedHubs = await db.hubs.findMany({ where: { name: hubName, private: false }, orderBy: { connections: { _count: 'desc' } }, @@ -91,7 +91,7 @@ export default class Browse extends Hub { if (!hubList || hubList.length === 0) { interaction.reply({ - content: i18n.__({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), + content: __({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -145,7 +145,7 @@ export default class Browse extends Hub { }); if (!hubDetails) { return await interaction.reply({ - content: i18n.__({ phrase: 'hub.notFound', locale: interaction.user.locale }), + content: __({ phrase: 'hub.notFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -174,7 +174,7 @@ export default class Browse extends Hub { const alreadyJoined = hubDetails.connections.find((c) => c.serverId === interaction.guildId); if (alreadyJoined) { interaction.reply({ - content: i18n.__( + content: __( { phrase: 'hub.alreadyJoined', locale: interaction.user.locale }, { hub: hubDetails.name, channel: `<#${alreadyJoined.channelId}>` }, ), @@ -215,7 +215,7 @@ export default class Browse extends Hub { // use current channel embed const embed = new EmbedBuilder() .setDescription( - i18n.__( + __( { phrase: 'hub.browse.joinConfirm', locale: interaction.user.locale, @@ -224,7 +224,9 @@ export default class Browse extends Hub { ), ) .setColor('Aqua') - .setFooter({ text: 'Want to use a different channel? Use the dropdown below.' }); + .setFooter({ + text: __({ phrase: 'hub.browse.joinFooter', locale: interaction.user.locale }), + }); await interaction.reply({ embeds: [embed], @@ -239,7 +241,7 @@ export default class Browse extends Hub { else if (customId.postfix === 'channel_select' || customId.postfix === 'confirm') { if (!hubDetails) { return await interaction.reply({ - content: i18n.__({ phrase: 'hub.notFound', locale: interaction.user.locale }), + content: __({ phrase: 'hub.notFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -252,8 +254,8 @@ export default class Browse extends Hub { ); if (userBlacklisted) { return await interaction.reply({ - content: i18n.__( - { phrase: 'hub.userBlacklisted', locale: interaction.user.locale }, + content: __( + { phrase: 'errors.userBlacklisted', locale: interaction.user.locale }, { hub: hubDetails.name }, ), ephemeral: true, @@ -266,7 +268,10 @@ export default class Browse extends Hub { ); if (serverBlacklisted) { return await interaction.reply({ - content: `This server is blacklisted from joining **${hubDetails.name}**.`, + content: __( + { phrase: 'errors.serverBlacklisted', locale: interaction.user.locale }, + { hub: hubDetails.name }, + ), ephemeral: true, }); } @@ -278,7 +283,7 @@ export default class Browse extends Hub { // for type safety if (channel?.type !== ChannelType.GuildText && !channel?.isThread()) { await interaction.reply({ - content: `${emojis.no} Only text and thread channels are supported!`, + content: __({ phrase: 'hub.invalidChannel', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -286,14 +291,20 @@ export default class Browse extends Hub { if (!interaction.guild?.members.me?.permissionsIn(channel).has(['ManageWebhooks'])) { await interaction.update( - `${emojis.no} I need to have the \`Manage Webhooks\` permission in ${channel} to connect it to a hub!`, + __( + { phrase: 'errors.missingPermissions', locale: interaction.user.locale }, + { permissions: 'Manage Webhooks' }, + ), ); return; } if (!interaction.member.permissionsIn(channel).has('ManageChannels')) { await interaction.update( - `${emojis.no} You need to have the \`Manage Channels\` permission in ${channel} to connect it to a hub!`, + __( + { phrase: 'errors.botMissingPermissions', locale: interaction.user.locale }, + { permissions: 'Manage Channels' }, + ), ); return; } @@ -304,7 +315,10 @@ export default class Browse extends Hub { if (channelConnected) { await interaction.update({ - content: 'This channel is already connected to another hub!', + content: __( + { phrase: 'hub.alreadyConnected', locale: interaction.user.locale }, + { channel: `${channel}` }, + ), embeds: [], components: [], }); @@ -324,7 +338,10 @@ export default class Browse extends Hub { } else if (onboardingCompleted === 'in-progress') { return await interaction.update({ - content: `There has already been an attempting to join a hub from ${channel}. Please cancel it or wait for it to complete.`, + content: __( + { phrase: 'onboarding.inProgress', locale: interaction.user.locale }, + { channel: `${channel}` }, + ), embeds: [], components: [], }); @@ -347,7 +364,10 @@ export default class Browse extends Hub { }); await interaction.editReply({ - content: `Successfully joined hub ${hubDetails.name} from ${channel}! Use \`/connection\` to manage your connection. And \`/hub leave\` to leave the hub.`, + content: __( + { phrase: 'hub.join.success', locale: interaction.user.locale }, + { hub: hubDetails.name, channel: `${channel}` }, + ), embeds: [], components: [], }); @@ -357,10 +377,10 @@ export default class Browse extends Hub { }); // announce a new server has joined the hub - networkManager.sendToNetwork(hubDetails.id, { + await networkManager.sendToHub(hubDetails.id, { content: stripIndents` A new server has joined us! ${emojis.clipart} - + **Server Name:** __${interaction.guild.name}__ **Member Count:** __${interaction.guild.memberCount}__ diff --git a/src/commands/slash/Main/hub/connections.ts b/src/commands/slash/Main/hub/connections.ts deleted file mode 100644 index f82ca2efd..000000000 --- a/src/commands/slash/Main/hub/connections.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { ChatInputCommandInteraction, CacheType, EmbedBuilder } from 'discord.js'; -import Hub from './index.js'; -import { stripIndent } from 'common-tags'; -import { colors, emojis } from '../../../../utils/Constants.js'; -import { paginate } from '../../../../utils/Pagination.js'; -import db from '../../../../utils/Db.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; - -export default class Connections extends Hub { - async execute(interaction: ChatInputCommandInteraction): Promise { - await interaction.deferReply(); - - const hubOpt = interaction.options.getString('hub', true); - const serverOpt = interaction.options.getString('server'); - - const hub = await db.hubs.findUnique({ where: { name: hubOpt }, include: { connections: true } }); - - if (!hub) { - return await interaction.editReply({ - embeds: [errorEmbed(`${emojis.no} Hub **${hubOpt}** doesn't exist.`)], - }); - } - else if ( - hub.ownerId !== interaction.user.id && - !hub.moderators.some((mod) => mod.userId === interaction.user.id) - ) { - return await interaction.editReply({ - embeds: [errorEmbed(`${emojis.no} You don't own or moderate **${hubOpt}**.`)], - }); - } - - - if (hub.connections.length === 0) { - return await interaction.editReply(`${emojis.no} No connected servers yet.`); - } - - if (serverOpt) { - const connection = hub.connections.find((con) => con.serverId === serverOpt); - if (!connection) { - return await interaction.editReply({ - embeds: [errorEmbed(`${emojis.no} Server **${serverOpt}** isn't connected to **${hubOpt}**.`)], - }); - } - const server = await interaction.client.guilds.fetch(serverOpt).catch(() => null); - const channel = await server?.channels.fetch(connection.channelId).catch(() => null); - const embed = new EmbedBuilder() - .setTitle(`${server?.name} \`(${connection.serverId})\``) - .setColor(colors.interchatBlue) - .setDescription(stripIndent` - Channel: #${channel?.name} \`(${connection.channelId})\` - Joined At: - Invite: ${connection.invite ? connection.invite : 'Not Set.'} - Connected: ${connection.connected ? 'Yes' : 'No'} - `); - - await interaction.editReply({ embeds: [embed] }); - return; - } - - const embeds: EmbedBuilder[] = []; - let itemsPerPage = 5; - - for (let index = 0; index < hub.connections.length; index += 5) { - const current = hub.connections?.slice(index, itemsPerPage); - - let j = index; - let l = index; - itemsPerPage += 5; - - const fields = current.map(async (connection) => { - const evalArr = await interaction.client.cluster.broadcastEval( - async (client, ctx) => { - const server = client.guilds.cache.get(ctx.connection.serverId); - - if (server) { - const channel = await server?.channels - .fetch(ctx.connection.channelId) - .catch(() => null); - return { serverName: server.name, channelName: channel?.name }; - } - }, - { context: { connection } }, - ); - - const evalRes = interaction.client.resolveEval(evalArr); - - const setup = hub.connections.find((settings) => settings.channelId === connection.channelId); - let value = stripIndent` - ServerID: ${connection.serverId} - Channel: #${evalRes?.channelName} \`(${connection.channelId}\`) - `; - if (setup) { - value += - '\n' + - stripIndent` - Joined At: - Invite: ${setup.invite ? setup.invite : 'Not Set.'} - `; - } - - return { name: `${++j}. ${evalRes?.serverName}`, value }; - }); - - embeds.push( - new EmbedBuilder() - .setDescription(`Current connected servers: ${++l}-${j} / **${hub.connections.length}**`) - .setColor(0x2f3136) - .setFields(await Promise.all(fields)), - ); - } - - return paginate(interaction, embeds); - } -} diff --git a/src/commands/slash/Main/hub/create.ts b/src/commands/slash/Main/hub/create.ts index eab19a9bb..c49318736 100644 --- a/src/commands/slash/Main/hub/create.ts +++ b/src/commands/slash/Main/hub/create.ts @@ -10,11 +10,11 @@ import { } from 'discord.js'; import Hub from './index.js'; import db from '../../../../utils/Db.js'; -import { stripIndents } from 'common-tags'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; import { HubSettingsBits } from '../../../../utils/BitFields.js'; -import { checkAndFetchImgurUrl, errorEmbed } from '../../../../utils/Utils.js'; -import { emojis } from '../../../../utils/Constants.js'; +import { checkAndFetchImgurUrl, simpleEmbed } from '../../../../utils/Utils.js'; +import { LINKS } from '../../../../utils/Constants.js'; +import { __ } from '../../../../utils/Locale.js'; export default class Create extends Hub { readonly cooldown = 60 * 60 * 1000; // 1 hour @@ -83,8 +83,7 @@ export default class Create extends Hub { // if hubName contains "discord", "clyde" "```" then return if (name.match(/discord|clyde|```/gi)) { return await interaction.followUp({ - content: - 'Hub name can not contain `discord`, `clyde` or \\`\\`\\` . Please choose another name.', + content: __({ phrase: 'hub.create.invalidName', locale: interaction.user.locale }), ephemeral: true, }); } @@ -95,7 +94,7 @@ export default class Create extends Hub { if (hubs.find((hub) => hub.name === name)) { return await interaction.followUp({ - content: `Sorry, name **${name}** is unavailable! Please choose another name.`, + content: __({ phrase: 'hub.create.nameTaken', locale: interaction.user.locale }), ephemeral: true, }); } @@ -103,8 +102,7 @@ export default class Create extends Hub { hubs.reduce((acc, hub) => (hub.ownerId === interaction.user.id ? acc + 1 : acc), 0) >= 3 ) { return await interaction.followUp({ - content: - 'You may only create a maximum of **3** hubs at the moment. Please delete one of your existing hubs before creating a new one.', + content: __({ phrase: 'hub.create.maxHubs', locale: interaction.user.locale }), ephemeral: true, }); } @@ -116,9 +114,7 @@ export default class Create extends Hub { if (iconUrl === false || bannerUrl === false) { return await interaction.followUp({ embeds: [ - errorEmbed( - `${emojis.no} Invalid icon or banner url. Make sure it is a valid imgur link and that it is not a gallery or album.`, - ), + simpleEmbed(__({ phrase: 'hub.create.invalidUrl', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -138,28 +134,19 @@ export default class Create extends Hub { }); // set cooldown after creating a hub (because a failed hub creation should not trigger the cooldown) - interaction.client.commandCooldowns.setCooldown(`${interaction.user.id}-hub-create`, 60 * 60 * 1000); // 1 hour + interaction.client.commandCooldowns.setCooldown( + `${interaction.user.id}-hub-create`, + 60 * 60 * 1000, + ); // 1 hour const successEmbed = new EmbedBuilder() .setColor('Green') .setDescription( - stripIndents` - ### Hub Created! - - Congratulations! Your private hub, **${name}**, has been successfully created. - To join, create an invite using \`/hub invite create\` and share the generated code. Then join using \`/hub join\`. - - - **Generate invite:** \`/hub invite create\` - - **Go public:** \`/hub manage\` - - **Join hub:** \`/hub join\` - - **Edit hub:** \`/hub manage\` - - **Add moderators:** \`/hub moderator add\` - - __Learn more about hubs in our [guide](https://discord-interchat.github.io/docs).__ - `, + __( + { phrase: 'hub.create.success', locale: interaction.user.locale }, + { name, support_invite: LINKS.SUPPORT_INVITE }, + ), ) - - .setFooter({ text: 'Join the support server for help!' }) .setTimestamp(); await interaction.editReply({ embeds: [successEmbed] }); diff --git a/src/commands/slash/Main/hub/delete.ts b/src/commands/slash/Main/hub/delete.ts index a93e5d0c2..11346631e 100644 --- a/src/commands/slash/Main/hub/delete.ts +++ b/src/commands/slash/Main/hub/delete.ts @@ -9,11 +9,11 @@ import { } from 'discord.js'; import db from '../../../../utils/Db.js'; import Hub from './index.js'; -import { captureException } from '@sentry/node'; -import { emojis } from '../../../../utils/Constants.js'; -import { deleteHubs, setComponentExpiry } from '../../../../utils/Utils.js'; +import { LINKS, emojis } from '../../../../utils/Constants.js'; +import { deleteHubs, simpleEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; +import { __ } from '../../../../utils/Locale.js'; export default class Delete extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -22,15 +22,17 @@ export default class Delete extends Hub { if (interaction.user.id !== hubInDb?.ownerId) { return await interaction.reply({ - content: `${emojis.info} Unable to find hub. Make sure you are the owner of the hub.`, + content: __({ phrase: 'errors.modUnownedHub', locale: interaction.user.locale }), ephemeral: true, }); } const confirmEmbed = new EmbedBuilder() - .setTitle('Are you sure?') .setDescription( - 'Are you sure you want to delete this hub? This is a destructive action that will **delete all connections** along with the hub.', + __( + { phrase: 'hub.delete.confirm', locale: interaction.user.locale }, + { hub: hubInDb.name }, + ), ) .setColor('Red'); const confirmButtons = new ActionRowBuilder().addComponents( @@ -72,13 +74,16 @@ export default class Delete extends Hub { if (interaction.user.id !== userId) { return await interaction.reply({ - content: 'Only the hub owner can delete this hub.', + embeds: [simpleEmbed(__({ phrase: 'errors.ownerOnly', locale: interaction.user.locale }))], ephemeral: true, }); } if (customId.postfix === 'cancel') { - await interaction.message.delete().catch(() => null); + await interaction.update({ + embeds: [simpleEmbed(__({ phrase: 'hub.delete.cancel', locale: interaction.user.locale }))], + components: [], + }); return; } @@ -87,33 +92,29 @@ export default class Delete extends Hub { }); if (!hubInDb) { return await interaction.update({ - content: 'That hub no longer exists.', - embeds: [], + embeds: [ + simpleEmbed( + __( + { phrase: 'errors.unknown', locale: interaction.user.locale }, + { support_invite: LINKS.SUPPORT_INVITE }, + ), + ), + ], components: [], }); } - await interaction.update( - `${emojis.loading} Deleting connections, invites, messages and the hub. Please wait...`, - ); - - try { - await deleteHubs([hubInDb.id]); - } - catch (e) { - captureException(e, { - user: { id: interaction.user.id, username: interaction.user.username }, - }); - await interaction.editReply({ - content: - 'Something went wrong while trying to delete the hub. The developers have been notified.', - }); - return; - } + await deleteHubs([hubInDb.id]); - await interaction.editReply({ - content: `${emojis.tick} The hub has been successfully deleted.`, - embeds: [], + await interaction.update({ + embeds: [ + simpleEmbed( + __( + { phrase: 'hub.delete.success', locale: interaction.user.locale }, + { emoji: emojis.tick }, + ), + ), + ], components: [], }); } diff --git a/src/commands/slash/Main/hub/index.ts b/src/commands/slash/Main/hub/index.ts index a7f218466..bf203b6e6 100644 --- a/src/commands/slash/Main/hub/index.ts +++ b/src/commands/slash/Main/hub/index.ts @@ -9,9 +9,9 @@ import { } from 'discord.js'; import BaseCommand from '../../../BaseCommand.js'; import db from '../../../../utils/Db.js'; +import { replyWithError } from '../../../../utils/Utils.js'; import Logger from '../../../../utils/Logger.js'; import { captureException } from '@sentry/node'; -import { errorEmbed, genCommandErrMsg } from '../../../../utils/Utils.js'; const hubOption: APIApplicationCommandBasicOption = { type: ApplicationCommandOptionType.String, @@ -54,8 +54,8 @@ export default class Hub extends BaseCommand { value: 'popular', }, { - name: 'Most Connections', - value: 'connections', + name: 'Most Servers', + value: 'servers', }, { name: 'Recently Created', @@ -121,8 +121,8 @@ export default class Hub extends BaseCommand { }, { type: ApplicationCommandOptionType.Subcommand, - name: 'connections', - description: '📜 List all connected servers to your hub.', + name: 'servers', + description: '📜 List all servers in your hub.', options: [ { type: ApplicationCommandOptionType.String, @@ -316,30 +316,22 @@ export default class Hub extends BaseCommand { // subcommand classes are added to this map in their respective files static readonly subcommands = new Collection(); - async execute(interaction: ChatInputCommandInteraction): Promise { - const apiSubcommandName = - interaction.options.getSubcommandGroup() || interaction.options.getSubcommand(); - const subcommand = Hub.subcommands?.get(apiSubcommandName); + async execute(interaction: ChatInputCommandInteraction) { + const subcommand = Hub.subcommands?.get( + interaction.options.getSubcommandGroup() || interaction.options.getSubcommand(), + ); - if (!subcommand) return; - await subcommand.execute(interaction).catch((e) => { + return await subcommand?.execute(interaction).catch((e) => { Logger.error(e); captureException(e); - if ('reply' in interaction) { - const method = interaction.replied ? 'editReply' : 'reply'; - - interaction[method]({ - embeds: [errorEmbed(genCommandErrMsg(interaction, e))], - ephemeral: true, - }).catch(() => null); - } + replyWithError(interaction, e.message); }); } async autocomplete(interaction: AutocompleteInteraction): Promise { const managerCmds = ['manage', 'settings', 'invite', 'moderator', 'logging']; - const modCmds = ['connections']; + const modCmds = ['servers']; const subcommand = interaction.options.getSubcommand(); const subcommandGroup = interaction.options.getSubcommandGroup(); diff --git a/src/commands/slash/Main/hub/invite.ts b/src/commands/slash/Main/hub/invite.ts index 1e1a9b8bc..961f94a2b 100644 --- a/src/commands/slash/Main/hub/invite.ts +++ b/src/commands/slash/Main/hub/invite.ts @@ -1,10 +1,11 @@ import { ChatInputCommandInteraction, CacheType, EmbedBuilder } from 'discord.js'; import Hub from './index.js'; import { captureException } from '@sentry/node'; -import { stripIndents } from 'common-tags'; import { emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; import Logger from '../../../../utils/Logger.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; export default class Invite extends Hub { readonly cooldown = 3000; // 3 seconds @@ -34,7 +35,9 @@ export default class Invite extends Hub { if (!hubInDb) { await interaction.reply({ - content: `${emojis.no} Invalid Hub Provided. Make sure you are a owner/manager of the hub and that hub is private.`, + embeds: [ + simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), + ], ephemeral: true, }); return; @@ -47,16 +50,14 @@ export default class Invite extends Hub { }); const embed = new EmbedBuilder() - .setTitle('Invite Created') .setDescription( - stripIndents` - Give this code to someone who wishes to join the hub. This invite has unlimited uses. - - Join using: \`/hub join invite:${createdInvite.code}\` - - **Code:** \`${createdInvite.code}\` - **Expiry ** - `, + __( + { phrase: 'hub.invite.create.success', locale: interaction.user.locale }, + { + inviteCode: createdInvite.code, + expiry: ``, + }, + ), ) .setColor('Green') .setTimestamp(); @@ -84,7 +85,10 @@ export default class Invite extends Hub { if (!inviteInDb) { await interaction.reply({ - content: `${emojis.no} Invalid Invite Code.`, + content: __({ + phrase: 'hub.invite.revoke.invalidCode', + locale: interaction.user.locale, + }), ephemeral: true, }); return; @@ -93,7 +97,14 @@ export default class Invite extends Hub { try { await db.hubInvites.delete({ where: { code } }); await interaction.reply({ - content: `Successfully revoked invite \`${code}\`!`, + embeds: [ + simpleEmbed( + __( + { phrase: 'hub.invite.revoke.success', locale: interaction.user.locale }, + { emoji: emojis.yes, inviteCode: code }, + ), + ), + ], ephemeral: true, }); } @@ -102,8 +113,9 @@ export default class Invite extends Hub { captureException(e); await interaction .reply({ - content: - 'An error occoured while trying to revoke invite! The developers have been notified.', + embeds: [ + simpleEmbed(__({ phrase: 'errors.unknown', locale: interaction.user.locale })), + ], ephemeral: true, }) .catch(() => null); @@ -126,7 +138,11 @@ export default class Invite extends Hub { if (!hubInDb?.private) { await interaction.reply({ - content: 'You can only view invite codes for private hubs.', + embeds: [ + simpleEmbed( + __({ phrase: 'hub.invite.list.notPrivate', locale: interaction.user.locale }), + ), + ], ephemeral: true, }); return; @@ -135,7 +151,11 @@ export default class Invite extends Hub { const invitesInDb = await db.hubInvites.findMany({ where: { hubId: hubInDb.id } }); if (invitesInDb.length === 0) { await interaction.reply({ - content: `${emojis.yes} There are no invites to this hub yet.`, + embeds: [ + simpleEmbed( + __({ phrase: 'hub.invite.list.noInvites', locale: interaction.user.locale }), + ), + ], ephemeral: true, }); return; @@ -147,7 +167,7 @@ export default class Invite extends Hub { ); const inviteEmbed = new EmbedBuilder() - .setTitle('Invite Codes') + .setTitle(__({ phrase: 'hub.invite.list.title', locale: interaction.user.locale })) .setDescription(inviteArr.join('\n')) .setColor('Yellow') .setTimestamp(); diff --git a/src/commands/slash/Main/hub/join.ts b/src/commands/slash/Main/hub/join.ts index 25a8c1aa3..6e79fe3b9 100644 --- a/src/commands/slash/Main/hub/join.ts +++ b/src/commands/slash/Main/hub/join.ts @@ -4,7 +4,7 @@ import Hub from './index.js'; import db from '../../../../utils/Db.js'; import BlacklistManager from '../../../../managers/BlacklistManager.js'; import { hubs } from '@prisma/client'; -import { errorEmbed, getOrCreateWebhook } from '../../../../utils/Utils.js'; +import { simpleEmbed, getOrCreateWebhook } from '../../../../utils/Utils.js'; import { showOnboarding } from '../../../../scripts/network/onboarding.js'; import { stripIndents } from 'common-tags'; @@ -101,7 +101,7 @@ export default class JoinSubCommand extends Hub { if (!webhook) { await interaction.editReply({ embeds: [ - errorEmbed( + simpleEmbed( `${emojis.no} I could not create a webhook in ${channel}. Please make sure I have the \`Manage Webhooks\` permission in that channel.`, ), ], @@ -133,7 +133,7 @@ export default class JoinSubCommand extends Hub { }); // announce a new server has joined the hub - networkManager.sendToNetwork(hub.id, { + networkManager.sendToHub(hub.id, { content: stripIndents` A new server has joined us! ${emojis.clipart} diff --git a/src/commands/slash/Main/hub/joined.ts b/src/commands/slash/Main/hub/joined.ts index e16aecdbf..3b73f72bd 100644 --- a/src/commands/slash/Main/hub/joined.ts +++ b/src/commands/slash/Main/hub/joined.ts @@ -1,8 +1,10 @@ -import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; -import { paginate } from '../../../../utils/Pagination.js'; import Hub from './index.js'; -import { emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; +import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; +import { paginate } from '../../../../utils/Pagination.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; +import { colors } from '../../../../utils/Constants.js'; export default class Joined extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -11,7 +13,11 @@ export default class Joined extends Hub { include: { hub: true }, }); if (connections.length === 0) { - return await interaction.reply(`${emojis.no} You have not joined any hubs yet!`); + return await interaction.reply({ + embeds: [ + simpleEmbed(__({ phrase: 'hub.joined.noJoinedHubs', locale: interaction.user.locale })), + ], + }); } const allFields = connections.map((con) => ({ @@ -27,14 +33,16 @@ export default class Joined extends Hub { // Split the fields into multiple embeds allFields.forEach((field, index) => { if (index % 25 === 0) { - // Start a new embed + // Start a new embed currentEmbed = new EmbedBuilder() - .setTitle('Joined hubs') - .setDescription(`This server is a part of **${connections.length}** hub(s).`) - .setColor('Blue') - .setFooter({ - text: 'Use /hub leave to leave a hub.', - }); + .setDescription( + __( + { phrase: 'hub.joined.joinedHubs', locale: interaction.user.locale }, + { total: `${allFields.length}` }, + ), + ) + .setColor(colors.interchatBlue); + paginateEmbeds.push(currentEmbed); } @@ -49,13 +57,14 @@ export default class Joined extends Hub { } const embed = new EmbedBuilder() - .setTitle('Joined hubs') - .setDescription(`This server is a part of **${connections.length}** hub(s).`) + .setDescription( + __( + { phrase: 'hub.joined.joinedHubs', locale: interaction.user.locale }, + { total: `${allFields.length}` }, + ), + ) .setFields(allFields) - .setColor('Blue') - .setFooter({ - text: 'Use /hub leave to leave a hub.', - }); + .setColor(colors.interchatBlue); await interaction.reply({ embeds: [embed] }); } diff --git a/src/commands/slash/Main/hub/leave.ts b/src/commands/slash/Main/hub/leave.ts index d19a85b73..8169f7a90 100644 --- a/src/commands/slash/Main/hub/leave.ts +++ b/src/commands/slash/Main/hub/leave.ts @@ -7,36 +7,38 @@ import { ButtonStyle, EmbedBuilder, } from 'discord.js'; +import db from '../../../../utils/Db.js'; import Hub from './index.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; import { CustomID } from '../../../../utils/CustomID.js'; import { emojis } from '../../../../utils/Constants.js'; -import { errorEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; -import db from '../../../../utils/Db.js'; +import { simpleEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; export default class Leave extends Hub { async execute(interaction: ChatInputCommandInteraction) { + if (!interaction.inCachedGuild()) return; + const channelId = interaction.options.getString('hub', true); - const isChannelConnected = await db.connectedList.findFirst({ where: { channelId } }); + const isChannelConnected = await db.connectedList.findFirst({ + where: { channelId }, + include: { hub: true }, + }); - if (!interaction.inCachedGuild()) { + if (!isChannelConnected) { return await interaction.reply({ - embeds: [errorEmbed(`${emojis.no} This command can only be run in a server.`)], + embeds: [simpleEmbed(__({ phrase: 'hub.leave.noHub', locale: interaction.user.locale }))], ephemeral: true, }); } - else if (!isChannelConnected) { - return await interaction.reply({ - embeds: [ - errorEmbed(`${emojis.no} The channel <#${channelId}> does not have any networks.`), - ], - }); - } else if (!interaction.member.permissions.has('ManageChannels', true)) { return await interaction.reply({ embeds: [ - errorEmbed( - `${emojis.no} You must have the \`Manage Channels\` permission in this server to leave a hub.`, + simpleEmbed( + __( + { phrase: 'errors.missingPermissions', locale: interaction.user.locale }, + { permission: 'Manage Channels' }, + ), ), ], ephemeral: true, @@ -55,12 +57,16 @@ export default class Leave extends Hub { ]); const resetConfirmEmbed = new EmbedBuilder() - .setTitle('Delete Network Connection') .setDescription( - 'Are you sure? You will have to rejoin the hub to use the network again! All previous connection data will be lost.', + __( + { phrase: 'hub.leave.confirm', locale: interaction.user.locale }, + { channel: `<#${channelId}>`, hub: `${isChannelConnected.hub?.name}` }, + ), ) .setColor('Red') - .setFooter({ text: 'Confirm within the next 10 seconds.' }); + .setFooter({ + text: __({ phrase: 'hub.leave.confirmFooter', locale: interaction.user.locale }), + }); await interaction.reply({ embeds: [resetConfirmEmbed], @@ -83,7 +89,10 @@ export default class Leave extends Hub { await db.connectedList.delete({ where: { channelId } }); await interaction.update({ - content: `${emojis.yes} Deleted network connection from <#${channelId}> and left the hub!`, + content: __( + { phrase: 'hub.leave.success', locale: interaction.user.locale }, + { channel: `<#${channelId}>`, emoji: emojis.yes }, + ), embeds: [], components: [], }); diff --git a/src/commands/slash/Main/hub/logging.ts b/src/commands/slash/Main/hub/logging.ts index 65f4baafd..f256d92b4 100644 --- a/src/commands/slash/Main/hub/logging.ts +++ b/src/commands/slash/Main/hub/logging.ts @@ -2,7 +2,7 @@ import { ChatInputCommandInteraction, CacheType, EmbedBuilder, ChannelType } fro import db from '../../../../utils/Db.js'; import Hub from './index.js'; import { colors, emojis } from '../../../../utils/Constants.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; import { Prisma } from '@prisma/client'; import { stripIndents } from 'common-tags'; @@ -23,7 +23,7 @@ export default class Logging extends Hub { if (!hubInDb) { return await interaction.reply({ - embeds: [errorEmbed(`${emojis.no} This hub does not exist.`)], + embeds: [simpleEmbed(`${emojis.no} This hub does not exist.`)], }); } if ( @@ -34,7 +34,7 @@ export default class Logging extends Hub { ) { return await interaction.reply({ embeds: [ - errorEmbed(`${emojis.no} You are not allowed to perform this action on this hub.`), + simpleEmbed(`${emojis.no} You are not allowed to perform this action on this hub.`), ], }); } diff --git a/src/commands/slash/Main/hub/manage.ts b/src/commands/slash/Main/hub/manage.ts index 3206c5276..a3145ced5 100644 --- a/src/commands/slash/Main/hub/manage.ts +++ b/src/commands/slash/Main/hub/manage.ts @@ -1,23 +1,28 @@ +import db from '../../../../utils/Db.js'; +import Hub from './index.js'; import { ActionRowBuilder, + ButtonBuilder, + ButtonStyle, CacheType, ChatInputCommandInteraction, EmbedBuilder, + MessageComponentInteraction, ModalBuilder, ModalSubmitInteraction, StringSelectMenuBuilder, - StringSelectMenuInteraction, TextInputBuilder, TextInputStyle, } from 'discord.js'; -import db from '../../../../utils/Db.js'; -import Hub from './index.js'; -import { hubs, connectedList } from '@prisma/client'; +import { __ } from '../../../../utils/Locale.js'; +import { CustomID } from '../../../../utils/CustomID.js'; import { stripIndents } from 'common-tags'; -import { emojis } from '../../../../utils/Constants.js'; +import { colors, emojis } from '../../../../utils/Constants.js'; +import { hubs, connectedList } from '@prisma/client'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; -import { CustomID } from '../../../../utils/CustomID.js'; -import { checkAndFetchImgurUrl, errorEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; +import { buildSettingsEmbed, buildSettingsMenu } from '../../../../scripts/hub/settings.js'; +import { HubSettingsBitField, HubSettingsString } from '../../../../utils/BitFields.js'; +import { checkAndFetchImgurUrl, simpleEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; export default class Manage extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -36,18 +41,28 @@ export default class Manage extends Hub { if (!hubInDb) { await interaction.reply({ - embeds: [ - errorEmbed( - `${emojis.no} Hub not found. Make sure there are no typos in the name and that own or moderate the hub.`, - ), - ], + embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], }); return; } + const button = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setLabel('Settings') + .setStyle(ButtonStyle.Secondary) + .setEmoji(emojis.settings) + .setCustomId( + new CustomID() + .setIdentifier('hub_manage', 'settingsBtn') + .addArgs(interaction.user.id) + .addArgs(hubInDb.name) + .toString(), + ), + ); + await interaction.reply({ embeds: [await Manage.hubEmbed(hubInDb)], - components: [Manage.actionsSelect(hubInDb.name, interaction.user.id)], + components: [Manage.actionsSelect(hubInDb.name, interaction.user.id), button], }); // disable components after 5 minutes @@ -59,12 +74,14 @@ export default class Manage extends Hub { } @RegisterInteractionHandler('hub_manage') - async handleComponents(interaction: StringSelectMenuInteraction) { + async handleComponents(interaction: MessageComponentInteraction) { const customId = CustomID.parseCustomId(interaction.customId); if (customId.args[0] !== interaction.user.id) { await interaction.reply({ - embeds: [errorEmbed('This dropdown is not for you!')], + embeds: [ + simpleEmbed(__({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), + ], ephemeral: true, }); return; @@ -76,104 +93,166 @@ export default class Manage extends Hub { }); if (!hubInDb) { - await interaction.reply({ content: 'This hub no longer exists!', ephemeral: true }); + await interaction.reply({ + embeds: [simpleEmbed(__({ phrase: 'hub.notFound', locale: interaction.user.locale }))], + ephemeral: true, + }); return; } - switch (interaction.values[0]) { - case 'icon': { - const modal = new ModalBuilder() - .setCustomId( - new CustomID() - .setIdentifier('hub_manage_modal', 'icon') - .addArgs(hubInDb.name) - .toString(), - ) - .setTitle('Change Hub Icon') - .addComponents( - new ActionRowBuilder().addComponents( - new TextInputBuilder() - .setLabel('Enter Icon URL') - .setPlaceholder('Enter a valid imgur image URL.') - .setStyle(TextInputStyle.Short) - .setCustomId('icon'), - ), - ); - - await interaction.showModal(modal); - break; - } + // settings button + if (interaction.isButton()) { + if (customId.postfix === 'settingsBtn') { + const { name, iconUrl, settings } = hubInDb; + const embed = buildSettingsEmbed(name, iconUrl, settings); + const selects = buildSettingsMenu(settings, name, customId.args[0]); - case 'description': { - const modal = new ModalBuilder() - .setCustomId( - new CustomID() - .setIdentifier('hub_manage_modal', 'description') - .addArgs(hubInDb.name) - .toString(), - ) - .setTitle('Edit Hub Description') - .addComponents( - new ActionRowBuilder().addComponents( - new TextInputBuilder() - .setLabel('Enter Description') - .setPlaceholder('A detailed description about the hub.') - .setMaxLength(1024) - .setStyle(TextInputStyle.Paragraph) - .setCustomId('description'), - ), - ); - - await interaction.showModal(modal); - break; + await interaction.reply({ embeds: [embed], components: [selects], ephemeral: true }); } + } - case 'banner': { - const modal = new ModalBuilder() - .setCustomId( - new CustomID() - .setIdentifier('hub_manage_modal', 'banner') - .addArgs(hubInDb.name) - .toString(), - ) - .setTitle('Set Hub Banner') - .addComponents( - new ActionRowBuilder().addComponents( - new TextInputBuilder() - .setLabel('Enter Banner URL') - .setPlaceholder('Enter a valid imgur URL. Leave blank to remove.') - .setStyle(TextInputStyle.Short) - .setRequired(false) - .setCustomId('banner'), - ), - ); - - await interaction.showModal(modal); - break; + // hub manage selects/toggle settings menu + else if (interaction.isStringSelectMenu()) { + if (customId.postfix === 'actions') { + switch (interaction.values[0]) { + case 'icon': { + const modal = new ModalBuilder() + .setCustomId( + new CustomID() + .setIdentifier('hub_manage_modal', 'icon') + .addArgs(hubInDb.name) + .toString(), + ) + .setTitle('Change Hub Icon') + .addComponents( + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setLabel('Enter Icon URL') + .setPlaceholder('Enter a valid Imgur image URL.') + .setStyle(TextInputStyle.Short) + .setCustomId('icon'), + ), + ); + + await interaction.showModal(modal); + break; + } + + case 'description': { + const modal = new ModalBuilder() + .setCustomId( + new CustomID() + .setIdentifier('hub_manage_modal', 'description') + .addArgs(hubInDb.name) + .toString(), + ) + .setTitle('Edit Hub Description') + .addComponents( + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setLabel('Enter Description') + .setPlaceholder('A detailed description about the hub.') + .setMaxLength(1024) + .setStyle(TextInputStyle.Paragraph) + .setCustomId('description'), + ), + ); + + await interaction.showModal(modal); + break; + } + + case 'banner': { + const modal = new ModalBuilder() + .setCustomId( + new CustomID() + .setIdentifier('hub_manage_modal', 'banner') + .addArgs(hubInDb.name) + .toString(), + ) + .setTitle('Set Hub Banner') + .addComponents( + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setLabel('Enter Banner URL') + .setPlaceholder('Enter a valid imgur URL. Leave blank to remove.') + .setStyle(TextInputStyle.Short) + .setRequired(false) + .setCustomId('banner'), + ), + ); + + await interaction.showModal(modal); + break; + } + + case 'visibility': { + const updatedHub = await db.hubs.update({ + where: { id: hubInDb?.id }, + data: { private: !hubInDb?.private }, + include: { connections: true }, + }); + + await interaction.reply({ + content: `Successfully set hub visibility to **${ + updatedHub?.private ? 'Private' : 'Public' + }**.`, + ephemeral: true, + }); + + await interaction.message + .edit({ embeds: [await Manage.hubEmbed(updatedHub)] }) + .catch(() => null); + break; + } + + default: + break; + } } - case 'visibility': { - const updatedHub = await db.hubs.update({ - where: { id: hubInDb?.id }, - data: { private: !hubInDb?.private }, - include: { connections: true }, - }); + // settings menu + else if (customId.postfix === 'settingsToggle') { + // respond to select menu + const selected = interaction.values[0] as HubSettingsString; + + // TODO: implement BlockNSFW, only allow hubs that are explicitly marked as NSFW to have this setting + // & only allow network channels to be marked as NSFW + if (selected === 'BlockNSFW') { + return interaction.reply({ + embeds: [ + simpleEmbed( + `${emojis.no} This setting cannot be changed yet. Please wait for the next update.`, + ), + ], + ephemeral: true, + }); + } - await interaction.reply({ - content: `Successfully set hub visibility to **${ - updatedHub?.private ? 'Private' : 'Public' - }**.`, - ephemeral: true, + const hubSettings = new HubSettingsBitField(hubInDb.settings); + const updHub = await db.hubs.update({ + where: { id: hubInDb.id }, + data: { settings: hubSettings.toggle(selected).bitfield }, // toggle the setting }); - await interaction.message - .edit({ embeds: [await Manage.hubEmbed(updatedHub)] }) - .catch(() => null); - break; - } + if (!updHub) { + await interaction.reply({ + embeds: [simpleEmbed(__({ phrase: 'errors.unknown', locale: 'en' }))], + ephemeral: true, + }); + return; + } - default: - break; + const { name, iconUrl, settings } = updHub; + + const embed = buildSettingsEmbed(name, iconUrl, settings); + const selects = buildSettingsMenu(settings, name, customId.args[0]); + + await interaction.update({ + embeds: [embed], + components: [selects], + }); + } } } @@ -346,7 +425,7 @@ export default class Manage extends Hub { return new EmbedBuilder() .setTitle(hub.name) - .setColor('Random') + .setColor(colors.interchatBlue) .setDescription( stripIndents` ${hub.description} @@ -383,7 +462,6 @@ export default class Manage extends Hub { - Mod Logs: ${hub?.logChannels?.modLogs ? `<#${hub?.logChannels?.modLogs}>` : emojis.no} - Reports: ${hub?.logChannels?.reports ? `<#${hub?.logChannels?.reports}>` : emojis.no} `, - inline: true, }, ); } diff --git a/src/commands/slash/Main/hub/moderator.ts b/src/commands/slash/Main/hub/moderator.ts index fd64b1f9c..1e3da8ad4 100644 --- a/src/commands/slash/Main/hub/moderator.ts +++ b/src/commands/slash/Main/hub/moderator.ts @@ -1,6 +1,9 @@ import { ChatInputCommandInteraction, CacheType, EmbedBuilder } from 'discord.js'; import Hub from './index.js'; import db from '../../../../utils/Db.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; +import { emojis } from '../../../../utils/Constants.js'; export default class Moderator extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -17,7 +20,7 @@ export default class Moderator extends Hub { if (!hub) { return await interaction.reply({ - content: 'Invalid hub input. Make sure the hub exists and that you are a owner/manager of the hub.', + embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], ephemeral: true, }); } @@ -28,7 +31,14 @@ export default class Moderator extends Hub { if (hub.moderators.find((mod) => mod.userId === user.id)) { return interaction.reply({ - content: `User ${user} is already a moderator for **${hub.name}**!`, + embeds: [ + simpleEmbed( + __( + { phrase: 'hub.moderator.add.alreadyModerator', locale: interaction.user.locale }, + { user: user.toString() }, + ), + ), + ], ephemeral: true, }); } @@ -38,7 +48,13 @@ export default class Moderator extends Hub { where: { id: hub.id }, data: { moderators: { push: { userId: user.id, position } } }, }); - interaction.reply(`Added ${user} as a hub moderator for **${hub.name}**!`); + + await interaction.reply({ + content: __( + { phrase: 'hub.moderator.add.success', locale: interaction.user.locale }, + { user: user.toString(), position, emoji: emojis.yes }, + ), + }); break; } @@ -46,27 +62,30 @@ export default class Moderator extends Hub { const user = interaction.options.getUser('user', true); if (!hub.moderators.find((mod) => mod.userId === user.id)) { - return interaction.reply({ - content: `User ${user} is not a moderator for **${hub.name}**!`, + await interaction.reply({ + content: __( + { phrase: 'hub.moderator.remove.notModerator', locale: interaction.user.locale }, + { user: user.toString() }, + ), ephemeral: true, }); + return; } - if (hub.ownerId !== interaction.user.id) { - if (user.id === interaction.user.id) { - return interaction.reply({ - content: 'I don\'t know why you would want to do that, but only the owner of the hub can remove you!', - ephemeral: true, - }); - } - - if (hub.moderators.find(m => m.position === 'manager' && m.userId === user.id)) { - return interaction.reply({ - content: 'Only the owner of the hub can remove a manager!', - ephemeral: true, - }); - } + if ( + (hub.ownerId !== interaction.user.id && user.id === interaction.user.id) || + hub.moderators.find((m) => m.position === 'manager' && m.userId === user.id) + ) { + await interaction.reply({ + content: __({ + phrase: 'hub.moderator.remove.notOwner', + locale: interaction.user.locale, + }), + ephemeral: true, + }); + return; } + await db.hubs.update({ where: { id: hub.id }, data: { @@ -74,26 +93,69 @@ export default class Moderator extends Hub { }, }); - await interaction.reply(`Removed hub moderator ${user} from **${hub.name}**!`); + await interaction.reply( + __( + { phrase: 'hub.moderator.remove.success', locale: interaction.user.locale }, + { user: user.toString(), emoji: emojis.yes }, + ), + ); break; } case 'update': { const user = interaction.options.getUser('user', true); const position = interaction.options.getString('position', true); + const isUserMod = hub.moderators.find((mod) => mod.userId === user.id); + const isExecutorMod = hub.moderators.find( + (mod) => + (mod.userId === interaction.user.id && mod.position === 'manager') || + hub.ownerId === interaction.user.id, + ); - if (!hub.moderators.find((mod) => mod.userId === user.id)) { - return interaction.reply({ - content: `User ${user} is not a moderator for **${hub.name}**!`, + if (!isExecutorMod) { + await interaction.reply({ + embeds: [ + simpleEmbed( + __({ phrase: 'hub.moderator.update.notAllowed', locale: interaction.user.locale }), + ), + ], ephemeral: true, }); + return; } - - if (hub.ownerId !== interaction.user.id && user.id === interaction.user.id) { - return interaction.reply({ - content: 'Only the owner of the hub can update your role!', + else if (!isUserMod) { + await interaction.reply({ + embeds: [ + simpleEmbed( + __( + { + phrase: 'hub.moderator.update.nodModerator', + locale: interaction.user.locale, + }, + { user: user.toString() }, + ), + ), + ], ephemeral: true, }); + return; + } + else if ( + (hub.ownerId !== interaction.user.id && user.id === interaction.user.id) || + isUserMod.position === 'manager' + ) { + await interaction.reply({ + embeds: [ + simpleEmbed( + __({ + phrase: 'hub.moderator.update.notOwner', + locale: interaction.user.locale, + }), + ), + ], + ephemeral: true, + }); + return; } await db.hubs.update({ @@ -104,7 +166,13 @@ export default class Moderator extends Hub { }, }, }); - interaction.reply(`Sucessfully moved ${user} to the role of \`${position}\` for **${hub.name}**!`); + + await interaction.reply( + __( + { phrase: 'hub.moderator.update.success', locale: interaction.user.locale }, + { user: user.toString(), position, emoji: emojis.yes }, + ), + ); break; } @@ -116,9 +184,14 @@ export default class Moderator extends Hub { .setDescription( hub.moderators.length > 0 ? hub.moderators - .map((mod, index) => `${index + 1}. <@${mod.userId}> - ${mod.position === 'network_mod' ? 'Network Moderator' : 'Hub Manager'}`) + .map( + (mod, index) => + `${index + 1}. <@${mod.userId}> - ${ + mod.position === 'network_mod' ? 'Network Moderator' : 'Hub Manager' + }`, + ) .join('\n') - : 'There are no moderators for this hub yet.', + : __({ phrase: 'hub.moderator.noModerators', locale: interaction.user.locale }), ) .setColor('Aqua') .setTimestamp(), diff --git a/src/commands/slash/Main/hub/servers.ts b/src/commands/slash/Main/hub/servers.ts new file mode 100644 index 000000000..f2df6bbb2 --- /dev/null +++ b/src/commands/slash/Main/hub/servers.ts @@ -0,0 +1,137 @@ +import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; +import Hub from './index.js'; +import { colors } from '../../../../utils/Constants.js'; +import { paginate } from '../../../../utils/Pagination.js'; +import db from '../../../../utils/Db.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; +import { __ } from '../../../../utils/Locale.js'; + +export default class Servers extends Hub { + async execute(interaction: ChatInputCommandInteraction) { + const hubOpt = interaction.options.getString('hub', true); + const serverOpt = interaction.options.getString('server'); + const locale = interaction.user.locale; + + const hub = await db.hubs.findUnique({ + where: { name: hubOpt }, + include: { connections: true }, + }); + + if (!hub) { + await interaction.reply({ + embeds: [simpleEmbed(__({ phrase: 'hub.notFound', locale }))], + ephemeral: true, + }); + return; + } + else if ( + hub.ownerId !== interaction.user.id && + !hub.moderators.some((mod) => mod.userId === interaction.user.id) + ) { + await interaction.reply({ + embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale }))], + ephemeral: true, + }); + return; + } + + if (hub.connections.length === 0) { + await interaction.reply({ + embeds: [simpleEmbed(__({ phrase: 'hub.servers.noConnections', locale }))], + ephemeral: true, + }); + return; + } + + if (serverOpt) { + const connection = hub.connections.find((con) => con.serverId === serverOpt); + if (!connection) { + return await interaction.reply({ + embeds: [simpleEmbed(__({ phrase: 'hub.servers.notConnected', locale }))], + ephemeral: true, + }); + } + const server = await interaction.client.guilds.fetch(serverOpt).catch(() => null); + const channel = await server?.channels.fetch(connection.channelId).catch(() => null); + const embed = new EmbedBuilder() + .setTitle(`${server?.name} \`(${connection.serverId})\``) + .setColor(colors.interchatBlue) + .setDescription( + __( + { phrase: 'hub.servers.connectionInfo', locale }, + { + channelName: `${channel?.name}`, + channelId: connection.channelId, + joinedAt: ``, + invite: connection.invite ? connection.invite : 'Not Set.', + connected: connection.connected ? 'Yes' : 'No', + }, + ), + ); + + await interaction.reply({ embeds: [embed] }); + return; + } + + const embeds: EmbedBuilder[] = []; + let itemsPerPage = 5; + + for (let index = 0; index < hub.connections.length; index += 5) { + const current = hub.connections?.slice(index, itemsPerPage); + + let j = index; + let l = index; + itemsPerPage += 5; + + const fields = current.map(async (connection) => { + const evalArr = await interaction.client.cluster.broadcastEval( + async (client, ctx) => { + const server = client.guilds.cache.get(ctx.connection.serverId); + + if (server) { + const channel = await server?.channels + .fetch(ctx.connection.channelId) + .catch(() => null); + return { serverName: server.name, channelName: channel?.name }; + } + }, + { context: { connection } }, + ); + + const evalRes = interaction.client.resolveEval(evalArr); + + const value = __( + { phrase: 'hub.servers.connectionInfo', locale }, + { + total: `${hub.connections.length}`, + channelName: `${evalRes?.channelName}`, + channelId: connection.channelId, + joinedAt: ``, + invite: connection.invite ? connection.invite : 'Not Set.', + connected: connection.connected ? 'Yes' : 'No', + }, + ); + + return { name: `${++j}. ${evalRes?.serverName}`, value }; + }); + + embeds.push( + new EmbedBuilder() + .setDescription( + __( + { phrase: 'hub.servers.total', locale }, + { + from: `${++l}`, + to: `${j}`, + total: `${hub.connections.length}`, + }, + ), + ) + .setColor(0x2f3136) + .setFields(await Promise.all(fields)), + ); + } + + return paginate(interaction, embeds); + } +} diff --git a/src/commands/slash/Main/hub/settings.ts b/src/commands/slash/Main/hub/settings.ts deleted file mode 100644 index 57f0665de..000000000 --- a/src/commands/slash/Main/hub/settings.ts +++ /dev/null @@ -1,97 +0,0 @@ -import db from '../../../../utils/Db.js'; -import Hub from './index.js'; -import { ChatInputCommandInteraction, CacheType } from 'discord.js'; -import { HubSettingsBitField, HubSettingsString } from '../../../../utils/BitFields.js'; -import { emojis } from '../../../../utils/Constants.js'; -import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; -import { CustomID } from '../../../../utils/CustomID.js'; -import { StringSelectMenuInteraction } from 'discord.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; -import { buildSettingsEmbed, buildSettingsMenu } from '../../../../scripts/hub/settings.js'; - -export default class Settings extends Hub { - async execute(interaction: ChatInputCommandInteraction): Promise { - const hubName = interaction.options.getString('hub', true); - const hub = await db.hubs.findUnique({ - where: { - name: hubName, - OR: [ - { - moderators: { some: { userId: interaction.user.id, position: 'manager' } }, - }, - { ownerId: interaction.user.id }, - ], - }, - }); - - if (!hub) { - return interaction.reply({ - embeds: [ - errorEmbed( - 'Hub not found. Make sure there are no typos in the name and that own or moderate the hub.', - ), - ], - ephemeral: true, - }); - } - - const hubSettings = new HubSettingsBitField(hub.settings); - const embed = buildSettingsEmbed(hub); - const selects = buildSettingsMenu(hubSettings, hubName, interaction.user.id); - - await interaction.reply({ embeds: [embed], components: [selects] }); - } - - @RegisterInteractionHandler('hub_settings') - async handleComponents(interaction: StringSelectMenuInteraction) { - const customId = CustomID.parseCustomId(interaction.customId); - const hubName = customId.args[0]; - - if (interaction.user.id !== customId.args[1]) { - return interaction.reply({ - embeds: [ - errorEmbed(`${emojis.no} Sorry, you can't perform this action. Please run the command yourself.`), - ], - ephemeral: true, - }); - } - - // respond to select menu - const selected = interaction.values[0] as HubSettingsString; - - // TODO: implement BlockNSFW, only allow hubs that are explicitly marked as NSFW to have this setting - // & only allow network channels to be marked as NSFW - if (selected === 'BlockNSFW') { - return interaction.reply({ - embeds: [ - errorEmbed( - `${emojis.no} This setting cannot be changed yet. Please wait for the next update.`, - ), - ], - ephemeral: true, - }); - } - - let hub = await db.hubs.findFirst({ where: { name: hubName } }); - if (!hub) { - return interaction.reply({ - embeds: [errorEmbed('Hub not found.')], - ephemeral: true, - }); - } - - const hubSettings = new HubSettingsBitField(hub.settings); - hub = await db.hubs.update({ - where: { name: hubName }, - data: { settings: hubSettings.toggle(selected).bitfield }, // toggle the setting - }); - - const embed = buildSettingsEmbed(hub); - const selects = buildSettingsMenu(hubSettings, hub.name, interaction.user.id); - - await interaction.update({ - embeds: [embed], - components: [selects], - }); - } -} diff --git a/src/commands/slash/Staff/find/index.ts b/src/commands/slash/Staff/find/index.ts index 142af7ada..00cdb1881 100644 --- a/src/commands/slash/Staff/find/index.ts +++ b/src/commands/slash/Staff/find/index.ts @@ -6,6 +6,9 @@ import { RESTPostAPIApplicationCommandsJSONBody, } from 'discord.js'; import BaseCommand from '../../../BaseCommand.js'; +import Logger from '../../../../utils/Logger.js'; +import { captureException } from '@sentry/node'; +import { replyWithError } from '../../../../utils/Utils.js'; export default class Find extends BaseCommand { staffOnly = true; @@ -60,7 +63,13 @@ export default class Find extends BaseCommand { async execute(interaction: ChatInputCommandInteraction) { const subcommand = Find.subcommands?.get(interaction.options.getSubcommand()); - if (subcommand) return await subcommand.execute(interaction); + + return await subcommand?.execute(interaction).catch((e) => { + Logger.error(e); + captureException(e); + // reply with an error message to the user + replyWithError(interaction, e.message); + }); } async autocomplete(interaction: AutocompleteInteraction) { const subcommand = interaction.options.getSubcommand(); diff --git a/src/commands/slash/Staff/find/user.ts b/src/commands/slash/Staff/find/user.ts index 0954667bf..ebfe861a8 100644 --- a/src/commands/slash/Staff/find/user.ts +++ b/src/commands/slash/Staff/find/user.ts @@ -3,7 +3,7 @@ import Find from './index.js'; import { stripIndents } from 'common-tags'; import { colors, emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; -import { errorEmbed } from '../../../../utils/Utils.js'; +import { simpleEmbed } from '../../../../utils/Utils.js'; export default class Server extends Find { async execute(interaction: ChatInputCommandInteraction) { @@ -13,7 +13,7 @@ export default class Server extends Find { if (!user) { return interaction.reply({ embeds: [ - errorEmbed( + simpleEmbed( `${emojis.no} Unknown user. Try using user\`s ID instead if you used username.`, ), ], diff --git a/src/commands/slash/Staff/purge.ts b/src/commands/slash/Staff/purge.ts index f65c8fb51..b2ead0295 100644 --- a/src/commands/slash/Staff/purge.ts +++ b/src/commands/slash/Staff/purge.ts @@ -12,7 +12,7 @@ import { captureException } from '@sentry/node'; import { stripIndents } from 'common-tags'; import { emojis } from '../../../utils/Constants.js'; import { messageData as messageDataCol } from '@prisma/client'; -import { errorEmbed, msToReadable } from '../../../utils/Utils.js'; +import { simpleEmbed, msToReadable } from '../../../utils/Utils.js'; import Logger from '../../../utils/Logger.js'; const limitOpt: APIApplicationCommandBasicOption = { @@ -120,7 +120,7 @@ export default class Purge extends BaseCommand { if (!isMod) { return await interaction.reply({ - embeds: [errorEmbed(`${emojis.no} You must be a moderator or owner of this hub to use this command.`)], + embeds: [simpleEmbed(`${emojis.no} You must be a moderator or owner of this hub to use this command.`)], ephemeral: true, }); } diff --git a/src/commands/slash/Support/support/index.ts b/src/commands/slash/Support/support/index.ts index 0205873d0..fab4dcc77 100644 --- a/src/commands/slash/Support/support/index.ts +++ b/src/commands/slash/Support/support/index.ts @@ -6,6 +6,9 @@ import { RESTPostAPIApplicationCommandsJSONBody, } from 'discord.js'; import BaseCommand from '../../../BaseCommand.js'; +import Logger from '../../../../utils/Logger.js'; +import { captureException } from '@sentry/node'; +import { replyWithError } from '../../../../utils/Utils.js'; export default class Support extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -44,8 +47,14 @@ export default class Support extends BaseCommand { static readonly subcommands = new Collection(); async execute(interaction: ChatInputCommandInteraction) { - const subCommand = interaction.options.getSubcommand(); - const isValid = Support.subcommands?.get(subCommand); - if (isValid) return await isValid.execute(interaction); + const subCommandName = interaction.options.getSubcommand(); + const subcommand = Support.subcommands?.get(subCommandName); + + await subcommand?.execute(interaction).catch((e) => { + Logger.error(e); + captureException(e); + // reply with an error message to the user + replyWithError(interaction, e.message); + }); } } diff --git a/src/managers/CommandManager.ts b/src/managers/CommandManager.ts index b7853e2b9..7ac85f323 100644 --- a/src/managers/CommandManager.ts +++ b/src/managers/CommandManager.ts @@ -6,7 +6,7 @@ import { emojis } from '../utils/Constants.js'; import { CustomID } from '../utils/CustomID.js'; import { Interaction } from 'discord.js'; import { captureException } from '@sentry/node'; -import { errorEmbed, genCommandErrMsg } from '../utils/Utils.js'; +import { simpleEmbed, replyWithError } from '../utils/Utils.js'; import db from '../utils/Db.js'; import Logger from '../utils/Logger.js'; @@ -22,7 +22,7 @@ export default class CommandManager extends Factory { async handleInteraction(interaction: Interaction): Promise { try { const userData = await db.userData.findFirst({ where: { userId: interaction.user.id } }); - interaction.user.locale = userData?.locale ?? 'en'; + interaction.user.locale = userData?.locale || 'en'; if (interaction.isAutocomplete()) { const command = this.client.commands.get(interaction.commandName); @@ -103,7 +103,7 @@ export default class CommandManager extends Factory { if (!handler || (customId.expiry && customId.expiry < Date.now())) { await interaction.reply({ - embeds: [errorEmbed(`${emojis.no} This is no longer usable.`)], + embeds: [simpleEmbed(`${emojis.no} This is no longer usable.`)], ephemeral: true, }); return; @@ -117,15 +117,9 @@ export default class CommandManager extends Factory { Logger.error(e); captureException(e); - if ('reply' in interaction) { - const errFormat = { - embeds: [errorEmbed(genCommandErrMsg(interaction, e))], - ephemeral: true, - }; - - interaction.replied || interaction.deferred - ? await interaction.editReply(errFormat) - : await interaction.reply(errFormat); + // reply with an error message to the user + if (interaction.isChatInputCommand() || interaction.isContextMenuCommand()) { + replyWithError(interaction, e); } } } diff --git a/src/managers/NetworkManager.ts b/src/managers/NetworkManager.ts index c2fdbd16d..abd33fc17 100644 --- a/src/managers/NetworkManager.ts +++ b/src/managers/NetworkManager.ts @@ -61,7 +61,9 @@ export default class NetworkManager extends Factory { if (!isNetworkMessage?.hub) return; // FIXME remove later - Logger.info(`[Network Debug] ${message.author.tag} sent a message in ${isNetworkMessage.hub.name}.`); + Logger.info( + `[Network Debug] ${message.author.tag} sent a message in ${isNetworkMessage.hub.name}.`, + ); Logger.info(`[Network Debug] ${message.client.webhooks.size} webhooks cached.`); const settings = new HubSettingsBitField(isNetworkMessage.hub.settings); @@ -249,13 +251,7 @@ export default class NetworkManager extends Factory { name: 'Welcome to the Network!', iconURL: 'https://i.imgur.com/jlCtQGs.gif', }) - .setDescription( - stripIndents` - Messages you send here will be transmitted to multiple other servers that are connected to this hub called **${isNetworkMessage.hub.name}**, and messages from those servers will also be relayed here. - - You can also send images, gifs, reply and even react to messages from other servers! But remember, keep it casual—don't share personal or sensitive info. Have fun chatting with people from other servers right from here! Go wild! ${emojis.tada} - `, - ) + .setDescription('Welcome to the network for {hub name}! Messages sent to this channel will be sent across to multiple other servers, enabling seamless communication with them. You can share images & GIFs, reply, react and with messages just as you would normally. Just remember to avoid sharing personal info (check /rules for more). Go ahead and send your first message!') .setFooter({ text: `Sent for: ${message.author.username}`, iconURL: message.author.displayAvatarURL(), @@ -280,7 +276,9 @@ export default class NetworkManager extends Factory { .setURL(LINKS.DOCS), ); - await message.reply({ embeds: [welcomeEmbed], components: [linkButtons] }).catch(() => null); + await message.channel + .send({ content: `${message.author}`, embeds: [welcomeEmbed], components: [linkButtons] }) + .catch(() => null); } // only delete the message if there is no attachment or if the user has already viewed the welcome message @@ -671,7 +669,7 @@ export default class NetworkManager extends Factory { * @param message The message to send. Can be a string or a MessageCreateOptions object. * @returns A array of the responses from each connection's webhook. */ - async sendToNetwork(hubId: string, message: string | MessageCreateOptions) { + async sendToHub(hubId: string, message: string | MessageCreateOptions) { const connections = await this.fetchHubNetworks({ hubId }); const res = connections diff --git a/src/scripts/hub/settings.ts b/src/scripts/hub/settings.ts index 9dad81c5f..cac1c28eb 100644 --- a/src/scripts/hub/settings.ts +++ b/src/scripts/hub/settings.ts @@ -1,11 +1,10 @@ import { ActionRowBuilder, EmbedBuilder, Snowflake, StringSelectMenuBuilder } from 'discord.js'; import { HubSettingsBitField, HubSettingsString } from '../../utils/BitFields.js'; import { emojis, colors } from '../../utils/Constants.js'; -import { hubs } from '@prisma/client'; import { CustomID } from '../../utils/CustomID.js'; -export function buildSettingsEmbed(hub: hubs) { - const settings = new HubSettingsBitField(hub.settings); +export function buildSettingsEmbed(name: string, iconURL: string, rawSettings: number) { + const settings = new HubSettingsBitField(rawSettings); const settingDescriptions = { Reactions: '**Reactions** - Allow users to react to messages.', HideLinks: '**Hide Links** - Redact links sent by users.', @@ -16,7 +15,7 @@ export function buildSettingsEmbed(hub: hubs) { }; return new EmbedBuilder() - .setAuthor({ name: `${hub.name} Settings`, iconURL: hub.iconUrl }) + .setAuthor({ name: `${name} Settings`, iconURL }) .setDescription( Object.entries(settingDescriptions) .map(([key, value]) => { @@ -30,14 +29,19 @@ export function buildSettingsEmbed(hub: hubs) { .setTimestamp(); } -export function buildSettingsMenu(hubSettings: HubSettingsBitField, hubName: string, userId: Snowflake) { +export function buildSettingsMenu( + rawSettings: number, + hubName: string, + userId: Snowflake, +) { + const hubSettings = new HubSettingsBitField(rawSettings); return new ActionRowBuilder().addComponents( new StringSelectMenuBuilder() .setCustomId( new CustomID() - .setIdentifier('hub_settings', 'settings') - .addArgs(hubName) + .setIdentifier('hub_manage', 'settingsToggle') .addArgs(userId) + .addArgs(hubName) .toString(), ) .setPlaceholder('Select an option') diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 6e50ac049..7f9450435 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -54,7 +54,7 @@ export const channels = { networklogs: '1156144879869632553', modlogs: '1042265633896796231', reports: '1158773603551162398', - goal: '906460473065615403', + goal: '1184062391395303445', suggestions: '1021256657528954900', } as const; diff --git a/src/utils/CustomID.ts b/src/utils/CustomID.ts index 865b83dba..9485608c2 100644 --- a/src/utils/CustomID.ts +++ b/src/utils/CustomID.ts @@ -89,8 +89,7 @@ export class CustomID { toString() { let str = `${this.customId}`; if (this.data.length > 0) this.data.forEach((element) => (str += `&${element}`)); - - if (str.length > 100) throw new TypeError('Custom ID cannot be longer than 100 characters.'); + if (str.length > 100) throw new Error('CustomID is too long.'); return str; } diff --git a/src/utils/Locale.ts b/src/utils/Locale.ts index acba7ff0d..7d81c4414 100644 --- a/src/utils/Locale.ts +++ b/src/utils/Locale.ts @@ -4,10 +4,12 @@ import Logger from './Logger.js'; const { configure, __ } = new I18n(); -export function initI18n(locale = 'en') { +export function initI18n() { configure({ directory: './locales', - fallbacks: { '*': locale }, + defaultLocale: 'en', + fallbacks: { '*': 'en' }, + retryInDefaultLocale: true, objectNotation: true, parser: YAML, extension: '.yml', diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 306081643..bb3094c2e 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -1,3 +1,7 @@ +import db from './Db.js'; +import toLower from 'lodash/toLower.js'; +import Scheduler from '../services/SchedulerService.js'; +import startCase from 'lodash/startCase.js'; import { ActionRow, ButtonStyle, @@ -5,7 +9,6 @@ import { ColorResolvable, ComponentType, EmbedBuilder, - Interaction, Message, MessageActionRowComponent, NewsChannel, @@ -15,12 +18,9 @@ import { } from 'discord.js'; import { DeveloperIds, REGEX, StaffIds, SupporterIds, LINKS, colors, emojis } from './Constants.js'; import { randomBytes } from 'crypto'; -import Scheduler from '../services/SchedulerService.js'; -import db from './Db.js'; -import startCase from 'lodash/startCase.js'; -import toLower from 'lodash/toLower.js'; -import 'dotenv/config'; import { __ } from './Locale.js'; +import 'dotenv/config'; +import { CmdInteraction } from '../commands/BaseCommand.js'; /** Convert milliseconds to a human readable time (eg: 1d 2h 3m 4s) */ export function msToReadable(milliseconds: number): string { @@ -165,7 +165,7 @@ export function replaceLinks(string: string, replaceText = '`[LINK HIDDEN]`') { return string.replaceAll(REGEX.LINKS, replaceText); } -export function errorEmbed(description: string, color: ColorResolvable = colors.invisible) { +export function simpleEmbed(description: string, color: ColorResolvable = colors.invisible) { return new EmbedBuilder().setColor(color).setDescription(description.toString()); } @@ -207,9 +207,23 @@ export function toTitleCase(str: string) { return startCase(toLower(str)); } -export function genCommandErrMsg(interaction: Interaction, error: string) { +export function genCommandErrMsg(locale: string, error: string) { return __( - { phrase: 'errors.commandError', locale: interaction.user.locale }, + { phrase: 'errors.commandError', locale }, { error, emoji: emojis.no, support_invite: LINKS.SUPPORT_INVITE }, ); } + +/** + Invoke this method to handle errors that occur during command execution. + It will send an error message to the user and log the error to the system. + */ +export async function replyWithError(interaction: CmdInteraction, e: string) { + const method = interaction.replied || interaction.deferred ? 'followUp' : 'reply'; + + // reply with an error message if the command failed + return await interaction[method]({ + embeds: [simpleEmbed(genCommandErrMsg(interaction.user.locale || 'en', e))], + ephemeral: true, + }).catch(() => null); +} From ad63edb5f491491c2e9426780958867a32e1b4cf Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sat, 16 Dec 2023 12:51:51 +0530 Subject: [PATCH 11/15] fix: better error handling and add `/hub manage` translations --- locales | 2 +- src/InterChat.ts | 8 +- src/api/index.ts | 3 +- src/commands/context-menu/blacklist.ts | 26 ++--- src/commands/context-menu/deleteMsg.ts | 8 +- src/commands/context-menu/editMsg.ts | 18 +-- src/commands/context-menu/messageInfo.ts | 26 ++--- src/commands/context-menu/translate.ts | 8 +- src/commands/slash/Information/invite.ts | 4 +- src/commands/slash/Information/rules.ts | 4 +- src/commands/slash/Information/vote.ts | 4 +- src/commands/slash/Main/blacklist/list.ts | 8 +- src/commands/slash/Main/blacklist/server.ts | 16 +-- src/commands/slash/Main/blacklist/user.ts | 16 +-- src/commands/slash/Main/connection.ts | 32 +++--- src/commands/slash/Main/hub/browse.ts | 30 ++--- src/commands/slash/Main/hub/create.ts | 12 +- src/commands/slash/Main/hub/delete.ts | 14 +-- src/commands/slash/Main/hub/invite.ts | 18 +-- src/commands/slash/Main/hub/joined.ts | 8 +- src/commands/slash/Main/hub/leave.ts | 12 +- src/commands/slash/Main/hub/manage.ts | 109 +++++++++++++------ src/commands/slash/Main/hub/moderator.ts | 24 ++-- src/commands/slash/Main/hub/servers.ts | 16 +-- src/commands/slash/Support/support/report.ts | 109 ++++++++++--------- src/scripts/network/onboarding.ts | 10 +- src/updater/ReactionUpdater.ts | 2 +- src/utils/Constants.ts | 6 +- src/utils/JSON/profanity.json | 2 +- src/utils/Locale.ts | 2 +- src/utils/Utils.ts | 4 +- 31 files changed, 304 insertions(+), 257 deletions(-) diff --git a/locales b/locales index 8eed811af..504b6d69b 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 8eed811af35222c71855d05e9a4727f56fa5b8b7 +Subproject commit 504b6d69bdd4007e4b356f0b90dfd3b102017bb3 diff --git a/src/InterChat.ts b/src/InterChat.ts index 0a1f04468..7fa778351 100644 --- a/src/InterChat.ts +++ b/src/InterChat.ts @@ -90,7 +90,7 @@ class InterChat extends SuperClient { // .setTitle(`Thank you for inviting me! ${emojis.tada}`) .setDescription( stripIndents` - 👋 Hey there! Step into the world of ${guild.client.user.username}, where chatting across servers is a delightful breeze! 🚀 Explore public hubs, connect with multiple servers, and add a splash of excitement to your server experience! ${emojis.clipart} + 👋 Hey there! Step into the world of ${this.user?.username}, where chatting across servers is a delightful breeze! 🚀 Explore public hubs, connect with multiple servers, and add a splash of excitement to your server experience! ${emojis.clipart} ### Let's make this journey even more enjoyable: - Discover your perfect hub with . @@ -126,7 +126,7 @@ class InterChat extends SuperClient { const profaneErrorEmbed = new EmbedBuilder() .setTitle('Leave Notice 👋') .setDescription( - `${emojis.no} Your server name contains profanity and or sensitive content. Please change it before using InterChat.`, + `${emojis.no} Your server name contains profanity or sensitive content. Please change it before using InterChat.`, ) .setColor(colors.invisible) .setFooter({ text: `Sent for: ${guild.name}`, iconURL: guild.iconURL() || undefined }); @@ -220,9 +220,7 @@ class InterChat extends SuperClient { ); // handle network reactions - this.on('messageReactionAdd', (reaction, user) => - this.getReactionUpdater().listenForReactions(reaction, user), - ); + this.on('messageReactionAdd', (react, usr) => this.getReactionUpdater().listen(react, usr)); // handle messages this.on('messageCreate', async (message) => { diff --git a/src/api/index.ts b/src/api/index.ts index 3e4602023..4ac8c0e1d 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -32,8 +32,7 @@ export default function start() { try { const imageBuffer = await (await fetch(imageUrl)).arrayBuffer(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const imageTensor = (await node.decodeImage(Buffer.from(imageBuffer), 3)) as any; + const imageTensor = (await node.decodeImage(Buffer.from(imageBuffer), 3)) as any; // eslint-disable-line @typescript-eslint/no-explicit-any const predictions = await model.classify(imageTensor); imageTensor.dispose(); diff --git a/src/commands/context-menu/blacklist.ts b/src/commands/context-menu/blacklist.ts index 85786c5ef..36206f050 100644 --- a/src/commands/context-menu/blacklist.ts +++ b/src/commands/context-menu/blacklist.ts @@ -16,7 +16,7 @@ import db from '../../utils/Db.js'; import parse from 'parse-duration'; import BaseCommand from '../BaseCommand.js'; import NetworkLogger from '../../utils/NetworkLogger.js'; -import { __ } from '../../utils/Locale.js'; +import { t } from '../../utils/Locale.js'; import { colors, emojis } from '../../utils/Constants.js'; import { CustomID } from '../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; @@ -48,7 +48,7 @@ export default class Blacklist extends BaseCommand { interaction.reply({ embeds: [ simpleEmbed( - __({ phrase: 'errors.messageNotSentOrExpired', locale }, { emoji: emojis.info }), + t({ phrase: 'errors.messageNotSentOrExpired', locale }, { emoji: emojis.info }), ), ], ephemeral: true, @@ -74,7 +74,7 @@ export default class Blacklist extends BaseCommand { .addArgs('u=1') .toString(), ) - .setLabel(__({ phrase: 'blacklist.components.user', locale })) + .setLabel(t({ phrase: 'blacklist.components.user', locale })) .setStyle(ButtonStyle.Secondary) .setEmoji('👤'), new ButtonBuilder() @@ -86,7 +86,7 @@ export default class Blacklist extends BaseCommand { .addArgs('s=1') .toString(), ) - .setLabel(__({ phrase: 'blacklist.components.user', locale })) + .setLabel(t({ phrase: 'blacklist.components.user', locale })) .setStyle(ButtonStyle.Secondary) .setEmoji('🏠'), ); @@ -101,7 +101,7 @@ export default class Blacklist extends BaseCommand { if (interaction.user.id !== customId.args[0]) { await interaction.reply({ embeds: [ - simpleEmbed(__({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -125,10 +125,10 @@ export default class Blacklist extends BaseCommand { new TextInputBuilder() .setCustomId('reason') .setLabel( - __({ phrase: 'blacklist.modal.reason.label', locale: interaction.user.locale }), + t({ phrase: 'blacklist.modal.reason.label', locale: interaction.user.locale }), ) .setPlaceholder( - __({ phrase: 'blacklist.modal.reason.placeholder', locale: interaction.user.locale }), + t({ phrase: 'blacklist.modal.reason.placeholder', locale: interaction.user.locale }), ) .setStyle(TextInputStyle.Paragraph) .setMaxLength(500), @@ -137,10 +137,10 @@ export default class Blacklist extends BaseCommand { new TextInputBuilder() .setCustomId('duration') .setLabel( - __({ phrase: 'blacklist.modal.duration.label', locale: interaction.user.locale }), + t({ phrase: 'blacklist.modal.duration.label', locale: interaction.user.locale }), ) .setPlaceholder( - __({ phrase: 'blacklist.modal.reason.placeholder', locale: interaction.user.locale }), + t({ phrase: 'blacklist.modal.reason.placeholder', locale: interaction.user.locale }), ) .setStyle(TextInputStyle.Short) .setMinLength(2) @@ -165,7 +165,7 @@ export default class Blacklist extends BaseCommand { if (!messageInDb?.hubId) { await interaction.reply({ - content: __({ phrase: 'errors.networkMessageExpired', locale: interaction.user.locale }), + content: t({ phrase: 'errors.networkMessageExpired', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -178,7 +178,7 @@ export default class Blacklist extends BaseCommand { const successEmbed = new EmbedBuilder().setColor('Green').addFields( { name: 'Reason', - value: reason ? reason : __({ phrase: 'misc.noReason', locale: interaction.user.locale }), + value: reason ? reason : t({ phrase: 'misc.noReason', locale: interaction.user.locale }), inline: true, }, { @@ -194,7 +194,7 @@ export default class Blacklist extends BaseCommand { if (blacklistType.startsWith('u=')) { const user = await interaction.client.users.fetch(messageInDb.authorId).catch(() => null); successEmbed.setDescription( - __( + t( { phrase: 'blacklist.user.success', locale: interaction.user.locale }, { username: user?.username ?? 'Unknown User', emoji: emojis.tick }, ), @@ -227,7 +227,7 @@ export default class Blacklist extends BaseCommand { const server = interaction.client.guilds.cache.get(messageInDb.serverId); successEmbed.setDescription( - __( + t( { phrase: 'blacklist.server.success', locale: interaction.user.locale }, { username: server?.name ?? 'Unknown Server', emoji: emojis.tick }, ), diff --git a/src/commands/context-menu/deleteMsg.ts b/src/commands/context-menu/deleteMsg.ts index 9965719a4..0e61cfe78 100644 --- a/src/commands/context-menu/deleteMsg.ts +++ b/src/commands/context-menu/deleteMsg.ts @@ -8,7 +8,7 @@ import BaseCommand from '../BaseCommand.js'; import { checkIfStaff } from '../../utils/Utils.js'; import { emojis } from '../../utils/Constants.js'; import db from '../../utils/Db.js'; -import { __ } from '../../utils/Locale.js'; +import { t } from '../../utils/Locale.js'; export default class DeleteMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -27,7 +27,7 @@ export default class DeleteMessage extends BaseCommand { if (!messageInDb) { return await interaction.editReply( - __({ + t({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale, }), @@ -42,7 +42,7 @@ export default class DeleteMessage extends BaseCommand { interaction.user.id !== messageInDb.authorId ) { return await interaction.editReply( - __({ + t({ phrase: 'errors.notMessageAuthor', locale: interaction.user.locale, }), @@ -75,7 +75,7 @@ export default class DeleteMessage extends BaseCommand { const deleted = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); await interaction .editReply( - __( + t( { phrase: 'network.deleteSuccess', locale: interaction.user.locale, diff --git a/src/commands/context-menu/editMsg.ts b/src/commands/context-menu/editMsg.ts index d26bd69e1..ad6bef008 100644 --- a/src/commands/context-menu/editMsg.ts +++ b/src/commands/context-menu/editMsg.ts @@ -17,7 +17,7 @@ import { checkIfStaff, hasVoted, replaceLinks } from '../../utils/Utils.js'; import { censor } from '../../utils/Profanity.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; -import { __ } from '../../utils/Locale.js'; +import { t } from '../../utils/Locale.js'; export default class EditMessage extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -31,7 +31,7 @@ export default class EditMessage extends BaseCommand { if (!checkIfStaff(interaction.user.id) && !(await hasVoted(interaction.user.id))) { await interaction.reply({ - content: __({ phrase: 'errors.mustVote', locale: interaction.user.locale }), + content: t({ phrase: 'errors.mustVote', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -44,8 +44,8 @@ export default class EditMessage extends BaseCommand { if (!messageInDb) { await interaction.reply({ - content: __( - __({ + content: t( + t({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale, }), @@ -56,7 +56,7 @@ export default class EditMessage extends BaseCommand { } else if (interaction.user.id != messageInDb?.authorId) { await interaction.reply({ - content: __({ phrase: 'errors.notMessageAuthor', locale: interaction.user.locale }), + content: t({ phrase: 'errors.notMessageAuthor', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -93,7 +93,7 @@ export default class EditMessage extends BaseCommand { const target = await interaction.channel?.messages.fetch(messageId).catch(() => null); if (!target) { - return await interaction.reply(__({ phrase: 'errors.unknownNetworkMessage' })); + return await interaction.reply(t({ phrase: 'errors.unknownNetworkMessage' })); } const messageInDb = await db.messageData.findFirst({ @@ -102,7 +102,7 @@ export default class EditMessage extends BaseCommand { }); if (!messageInDb?.hub) { return await interaction.reply( - __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), + t({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), ); } @@ -122,7 +122,7 @@ export default class EditMessage extends BaseCommand { newMessage.includes('dsc.gg')) ) { await interaction.editReply( - __({ phrase: 'errors.inviteLinks', locale: interaction.user.locale }), + t({ phrase: 'errors.inviteLinks', locale: interaction.user.locale }), ); return; } @@ -207,7 +207,7 @@ export default class EditMessage extends BaseCommand { const resultsArray = await Promise.all(results); const edited = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); await interaction.editReply( - __( + t( { phrase: 'editMsg.editSuccess', locale: interaction.user.locale }, { edited: `${edited}`, total: `${resultsArray.length}` }, ), diff --git a/src/commands/context-menu/messageInfo.ts b/src/commands/context-menu/messageInfo.ts index b4d21ae20..9d4e616df 100644 --- a/src/commands/context-menu/messageInfo.ts +++ b/src/commands/context-menu/messageInfo.ts @@ -17,7 +17,7 @@ import { colors, emojis } from '../../utils/Constants.js'; import BaseCommand from '../BaseCommand.js'; import { CustomID } from '../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; -import { __ } from '../../utils/Locale.js'; +import { t } from '../../utils/Locale.js'; export default class MessageInfo extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -35,7 +35,7 @@ export default class MessageInfo extends BaseCommand { if (!networkMessage) { await interaction.reply({ - content: __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), + content: t({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -48,7 +48,7 @@ export default class MessageInfo extends BaseCommand { const embed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'msgInfo.message.description', locale: interaction.user.locale }, { emoji: emojis.clipart, @@ -96,7 +96,7 @@ export default class MessageInfo extends BaseCommand { }); if (!networkMessage) { return await interaction.update({ - content: __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), + content: t({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), embeds: [], components: [], }); @@ -128,7 +128,7 @@ export default class MessageInfo extends BaseCommand { case 'serverInfo': { if (!server) { return await interaction.update({ - content: __({ phrase: 'errors.unknownServer', locale: interaction.user.locale }), + content: t({ phrase: 'errors.unknownServer', locale: interaction.user.locale }), embeds: [], components: [], }); @@ -150,13 +150,13 @@ export default class MessageInfo extends BaseCommand { .setThumbnail(iconUrl) .setImage(bannerUrL) .setDescription( - __( + t( { phrase: 'msgInfo.server.description', locale: interaction.user.locale }, { server: server.name, description: server.description || - __({ phrase: 'misc.noDesc', locale: interaction.user.locale }), + t({ phrase: 'misc.noDesc', locale: interaction.user.locale }), owner: `${owner.username}#${ owner.discriminator !== '0' ? `#${owner.discriminator}` : '' }`, @@ -185,7 +185,7 @@ export default class MessageInfo extends BaseCommand { .setColor('Random') .setImage(author.bannerURL() ?? null) .setDescription( - __( + t( { phrase: 'msgInfo.user.description', locale: interaction.user.locale }, { user: author.discriminator !== '0' ? author.tag : author.username, @@ -222,7 +222,7 @@ export default class MessageInfo extends BaseCommand { if (!message) { await interaction.update({ - content: __({ phrase: 'errors.unknownMessage', locale: interaction.user.locale }), + content: t({ phrase: 'errors.unknownMessage', locale: interaction.user.locale }), embeds: [], components: [], }); @@ -231,7 +231,7 @@ export default class MessageInfo extends BaseCommand { const embed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'msgInfo.message.description', locale: interaction.user.locale }, { emoji: emojis.clipart, @@ -275,20 +275,20 @@ export default class MessageInfo extends BaseCommand { .setCustomId( new CustomID().setIdentifier('msgInfo', 'info').addArgs(messageId).toString(), ) - .setLabel(__({ phrase: 'msgInfo.buttons.message', locale })) + .setLabel(t({ phrase: 'msgInfo.buttons.message', locale })) .setStyle(ButtonStyle.Secondary) .setDisabled(true), new ButtonBuilder() .setCustomId( new CustomID().setIdentifier('msgInfo', 'serverInfo').addArgs(messageId).toString(), ) - .setLabel(__({ phrase: 'msgInfo.buttons.server', locale })) + .setLabel(t({ phrase: 'msgInfo.buttons.server', locale })) .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId( new CustomID().setIdentifier('msgInfo', 'userInfo').addArgs(messageId).toString(), ) - .setLabel(__({ phrase: 'msgInfo.buttons.user', locale })) + .setLabel(t({ phrase: 'msgInfo.buttons.user', locale })) .setStyle(ButtonStyle.Secondary), ), ]; diff --git a/src/commands/context-menu/translate.ts b/src/commands/context-menu/translate.ts index 33ddf674e..10dd583ae 100644 --- a/src/commands/context-menu/translate.ts +++ b/src/commands/context-menu/translate.ts @@ -20,7 +20,7 @@ import { RegisterInteractionHandler } from '../../decorators/Interaction.js'; import { CustomID } from '../../utils/CustomID.js'; import { supportedLanguages } from '@translate-tools/core/translators/GoogleTranslator/index.js'; import translator from '../../utils/Translator.cjs'; -import { __ } from '../../utils/Locale.js'; +import { t } from '../../utils/Locale.js'; export default class Translate extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -34,7 +34,7 @@ export default class Translate extends BaseCommand { if (!(await hasVoted(interaction.user.id))) { return await interaction.editReply( - __({ phrase: 'errors.mustVote', locale: interaction.user.locale }), + t({ phrase: 'errors.mustVote', locale: interaction.user.locale }), ); } @@ -46,7 +46,7 @@ export default class Translate extends BaseCommand { if (!messageInDb) { return interaction.editReply( - __({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), + t({ phrase: 'errors.unknownNetworkMessage', locale: interaction.user.locale }), ); } @@ -125,7 +125,7 @@ export default class Translate extends BaseCommand { const from = interaction.fields.getTextInputValue('from'); if (!supportedLanguages.includes(from) || !supportedLanguages.includes(to)) { await interaction.reply({ - content: __({ phrase: 'errors.invalidLangCode', locale: interaction.user.locale }), + content: t({ phrase: 'errors.invalidLangCode', locale: interaction.user.locale }), ephemeral: true, }); return; diff --git a/src/commands/slash/Information/invite.ts b/src/commands/slash/Information/invite.ts index d6c4a30b8..9ecac2cc0 100644 --- a/src/commands/slash/Information/invite.ts +++ b/src/commands/slash/Information/invite.ts @@ -7,7 +7,7 @@ import { } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { LINKS, emojis } from '../../../utils/Constants.js'; -import { __ } from '../../../utils/Locale.js'; +import { t } from '../../../utils/Locale.js'; export default class Invite extends BaseCommand { readonly data = { @@ -30,7 +30,7 @@ export default class Invite extends BaseCommand { .setDisabled(false), ]); await interaction.reply({ - content: __( + content: t( { phrase: 'invite', locale: interaction.user.locale }, { support: LINKS.SUPPORT_INVITE }, ), diff --git a/src/commands/slash/Information/rules.ts b/src/commands/slash/Information/rules.ts index fd860f2d1..2f59570dc 100644 --- a/src/commands/slash/Information/rules.ts +++ b/src/commands/slash/Information/rules.ts @@ -1,7 +1,7 @@ import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { LINKS, colors } from '../../../utils/Constants.js'; -import { __ } from '../../../utils/Locale.js'; +import { t } from '../../../utils/Locale.js'; export default class Rules extends BaseCommand { readonly data = { name: 'rules', @@ -10,7 +10,7 @@ export default class Rules extends BaseCommand { async execute(interaction: ChatInputCommandInteraction) { const rulesEmbed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'rules', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), diff --git a/src/commands/slash/Information/vote.ts b/src/commands/slash/Information/vote.ts index 08ae1a9c5..0999775db 100644 --- a/src/commands/slash/Information/vote.ts +++ b/src/commands/slash/Information/vote.ts @@ -7,7 +7,7 @@ import { } from 'discord.js'; import BaseCommand from '../../BaseCommand.js'; import { colors } from '../../../utils/Constants.js'; -import { __ } from '../../../utils/Locale.js'; +import { t } from '../../../utils/Locale.js'; export default class Vote extends BaseCommand { readonly data = { @@ -17,7 +17,7 @@ export default class Vote extends BaseCommand { async execute(interaction: ChatInputCommandInteraction) { const embed = new EmbedBuilder() .setDescription( - __({ phrase: 'vote', locale: interaction.user.locale }), + t({ phrase: 'vote', locale: interaction.user.locale }), ) .setColor(colors.interchatBlue); diff --git a/src/commands/slash/Main/blacklist/list.ts b/src/commands/slash/Main/blacklist/list.ts index 59e9c2724..eeac95239 100644 --- a/src/commands/slash/Main/blacklist/list.ts +++ b/src/commands/slash/Main/blacklist/list.ts @@ -4,7 +4,7 @@ import BlacklistCommand from './index.js'; import { paginate } from '../../../../utils/Pagination.js'; import { colors } from '../../../../utils/Constants.js'; import { simpleEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class ListBlacklists extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -25,7 +25,7 @@ export default class ListBlacklists extends BlacklistCommand { if (!hubInDb) { await interaction.editReply({ embeds: [ - simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), ], }); return; @@ -58,7 +58,7 @@ export default class ListBlacklists extends BlacklistCommand { fields.push({ name: data.serverName, - value: __( + value: t( { phrase: 'blacklist.list.server', locale: interaction.user.locale }, { serverId: data.serverId, @@ -102,7 +102,7 @@ export default class ListBlacklists extends BlacklistCommand { fields.push({ name: data.username, - value: __( + value: t( { phrase: 'blacklist.list.user', locale: interaction.user.locale }, { userId: data.userId, diff --git a/src/commands/slash/Main/blacklist/server.ts b/src/commands/slash/Main/blacklist/server.ts index 4094a3f79..b4f126fa8 100644 --- a/src/commands/slash/Main/blacklist/server.ts +++ b/src/commands/slash/Main/blacklist/server.ts @@ -8,7 +8,7 @@ import BlacklistManager from '../../../../managers/BlacklistManager.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; import parse from 'parse-duration'; import Logger from '../../../../utils/Logger.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class UserBlacklist extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -29,7 +29,7 @@ export default class UserBlacklist extends BlacklistCommand { if (!hubInDb) { await interaction.editReply({ - embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], }); return; } @@ -50,7 +50,7 @@ export default class UserBlacklist extends BlacklistCommand { return await interaction.followUp({ embeds: [ simpleEmbed( - __({ + t({ phrase: 'blacklist.server.alreadyBlacklisted', locale: interaction.user.locale, }), @@ -62,7 +62,7 @@ export default class UserBlacklist extends BlacklistCommand { const server = await interaction.client.guilds.fetch(serverOpt).catch(() => null); if (!server) { return await interaction.followUp( - __({ phrase: 'errors.unknownServer', locale: interaction.user.locale }), + t({ phrase: 'errors.unknownServer', locale: interaction.user.locale }), ); } @@ -81,7 +81,7 @@ export default class UserBlacklist extends BlacklistCommand { interaction.followUp({ embeds: [ simpleEmbed( - __({ + t({ phrase: 'blacklist.server.unknownError', locale: interaction.user.locale, }), @@ -97,7 +97,7 @@ export default class UserBlacklist extends BlacklistCommand { const successEmbed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'blacklist.server.success', locale: interaction.user.locale }, { emoji: emojis.tick, server: server.name }, ), @@ -131,13 +131,13 @@ export default class UserBlacklist extends BlacklistCommand { const result = await blacklistManager.removeBlacklist('server', hubInDb.id, serverOpt); if (!result) { return await interaction.followUp( - __({ phrase: 'errors.serverNotBlacklisted', locale: interaction.user.locale }), + t({ phrase: 'errors.serverNotBlacklisted', locale: interaction.user.locale }), ); } // Using name from DB since the bot can't access server through API. await interaction.followUp( - __( + t( { phrase: 'blacklist.server.removed', locale: interaction.user.locale }, { emoji: emojis.delete, server: result.serverName }, ), diff --git a/src/commands/slash/Main/blacklist/user.ts b/src/commands/slash/Main/blacklist/user.ts index eba090b62..aacac8ddd 100644 --- a/src/commands/slash/Main/blacklist/user.ts +++ b/src/commands/slash/Main/blacklist/user.ts @@ -6,7 +6,7 @@ import parse from 'parse-duration'; import { emojis } from '../../../../utils/Constants.js'; import NetworkLogger from '../../../../utils/NetworkLogger.js'; import { simpleEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Server extends BlacklistCommand { async execute(interaction: ChatInputCommandInteraction) { @@ -27,7 +27,7 @@ export default class Server extends BlacklistCommand { if (!hubInDb) { return await interaction.editReply({ embeds: [ - simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), ], }); } @@ -51,7 +51,7 @@ export default class Server extends BlacklistCommand { if (!user) { return interaction.followUp( - __({ phrase: 'errors.userNotFound', locale: interaction.user.locale }), + t({ phrase: 'errors.userNotFound', locale: interaction.user.locale }), ); } @@ -60,7 +60,7 @@ export default class Server extends BlacklistCommand { // } else if (user.id === interaction.client.user?.id) { return interaction.followUp( - __({ + t({ phrase: 'blacklist.easterEggs.blacklistBot', locale: interaction.user.locale, }), @@ -70,7 +70,7 @@ export default class Server extends BlacklistCommand { const userInBlacklist = await BlacklistManager.fetchUserBlacklist(hubInDb.id, userOpt); if (userInBlacklist) { await interaction.followUp( - __({ phrase: 'blacklist.user.alreadyBlacklisted', locale: interaction.user.locale }), + t({ phrase: 'blacklist.user.alreadyBlacklisted', locale: interaction.user.locale }), ); return; } @@ -87,7 +87,7 @@ export default class Server extends BlacklistCommand { blacklistManager.notifyBlacklist('user', user.id, hubInDb.id, expires, reason); const successEmbed = new EmbedBuilder() - .setDescription(__({ phrase: 'blacklist.user.success', locale: interaction.user.locale })) + .setDescription(t({ phrase: 'blacklist.user.success', locale: interaction.user.locale })) .setColor('Green') .addFields( { @@ -112,12 +112,12 @@ export default class Server extends BlacklistCommand { const result = await blacklistManager.removeBlacklist('user', hubInDb.id, userId); if (!result) { return await interaction.followUp( - __({ phrase: 'errors.userNotBlacklisted', locale: interaction.user.locale }), + t({ phrase: 'errors.userNotBlacklisted', locale: interaction.user.locale }), ); } const user = await interaction.client.users.fetch(userId).catch(() => null); await interaction.followUp( - __( + t( { phrase: 'blacklist.user.removed', locale: interaction.user.locale }, { emoji: emojis.delete, server: result.username }, ), diff --git a/src/commands/slash/Main/connection.ts b/src/commands/slash/Main/connection.ts index 2196d4751..d00d4ebf3 100644 --- a/src/commands/slash/Main/connection.ts +++ b/src/commands/slash/Main/connection.ts @@ -30,7 +30,7 @@ import { getOrCreateWebhook, setComponentExpiry, } from '../../../utils/Utils.js'; -import { __ } from '../../../utils/Locale.js'; +import { t } from '../../../utils/Locale.js'; export default class Connection extends BaseCommand { readonly data: RESTPostAPIApplicationCommandsJSONBody = { @@ -56,7 +56,7 @@ export default class Connection extends BaseCommand { if (!isInDb) { await interaction.reply({ embeds: [ - simpleEmbed(__({ phrase: 'connection.notFound', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'connection.notFound', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -115,7 +115,7 @@ export default class Connection extends BaseCommand { { connected: !isInDb.connected }, ); await interaction.followUp({ - content: __({ phrase: 'connection.channelNotFound', locale: interaction.user.locale }), + content: t({ phrase: 'connection.channelNotFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -164,7 +164,7 @@ export default class Connection extends BaseCommand { if (customId.args.at(1) && customId.args[1] !== interaction.user.id) { await interaction.reply({ embeds: [ - simpleEmbed(__({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -175,7 +175,7 @@ export default class Connection extends BaseCommand { const isInDb = await networkManager.fetchConnection({ channelId }); if (!isInDb || !channelId) { await interaction.reply({ - content: __({ phrase: 'connection.channelNotFound', locale: interaction.user.locale }), + content: t({ phrase: 'connection.channelNotFound', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -250,7 +250,7 @@ export default class Connection extends BaseCommand { ); await interaction.reply({ - content: __( + content: t( { phrase: 'connection.switchChannel', locale: interaction.user.locale }, { emoji: emojis.info }, ), @@ -260,7 +260,7 @@ export default class Connection extends BaseCommand { // current interaction will become outdated due to new channelId await interaction.message.edit({ - content: __( + content: t( { phrase: 'connection.switchCalled', locale: interaction.user.locale }, { emoji: emojis.info }, ), @@ -307,12 +307,14 @@ export default class Connection extends BaseCommand { // channel select menu interactions else if (interaction.isChannelSelectMenu()) { if (customId.postfix !== 'change_channel') return; + await interaction.deferUpdate(); + const newChannel = interaction.channels.first(); const channelInHub = await networkManager.fetchConnection({ channelId: newChannel?.id }); if (channelInHub) { await interaction.reply({ - content: __( + content: t( { phrase: 'connection.alreadyConnected', locale: interaction.user.locale }, { channel: `${newChannel}` }, ), @@ -327,8 +329,8 @@ export default class Connection extends BaseCommand { { channelId: newChannel?.id, webhookURL: newWebhook?.url }, ); - await interaction.update({ - content: __( + await interaction.editReply({ + content: t( { phrase: 'connection.switchSuccess', locale: interaction.user.locale }, { channel: `${newChannel}`, emoji: emojis.yes }, ), @@ -348,7 +350,7 @@ export default class Connection extends BaseCommand { if (!invite) { await networkManager.updateConnection({ channelId }, { invite: { unset: true } }); await interaction.reply({ - content: __( + content: t( { phrase: 'connection.inviteRemoved', locale: interaction.user.locale }, { emoji: emojis.yes }, ), @@ -361,7 +363,7 @@ export default class Connection extends BaseCommand { if (isValid?.guild?.id !== interaction.guildId) { await interaction.reply({ - content: __({ phrase: 'connection.inviteInvalid', locale: interaction.user.locale }), + content: t({ phrase: 'connection.inviteInvalid', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -370,7 +372,7 @@ export default class Connection extends BaseCommand { await networkManager.updateConnection({ channelId }, { invite }); await interaction.reply({ - content: __( + content: t( { phrase: 'connection.inviteAdded', locale: interaction.user.locale }, { emoji: emojis.yes }, ), @@ -383,7 +385,7 @@ export default class Connection extends BaseCommand { const hex_regex = /^#[0-9A-F]{6}$/i; if (embedColor && !hex_regex.test(embedColor)) { interaction.reply({ - content: __({ phrase: 'connection.emColorInvalid', locale: interaction.user.locale }), + content: t({ phrase: 'connection.emColorInvalid', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -395,7 +397,7 @@ export default class Connection extends BaseCommand { }); await interaction.reply({ - content: __( + content: t( { phrase: 'connection.emColorChange', locale: interaction.user.locale }, { action: embedColor ? `set to \`${embedColor}\`!` : 'unset', emoji: emojis.yes }, ), diff --git a/src/commands/slash/Main/hub/browse.ts b/src/commands/slash/Main/hub/browse.ts index 05cb1bd13..f9a16b877 100644 --- a/src/commands/slash/Main/hub/browse.ts +++ b/src/commands/slash/Main/hub/browse.ts @@ -25,7 +25,7 @@ import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; import { stripIndents } from 'common-tags'; import BlacklistManager from '../../../../managers/BlacklistManager.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Browse extends Hub { async execute(interaction: ChatInputCommandInteraction): Promise { @@ -91,7 +91,7 @@ export default class Browse extends Hub { if (!hubList || hubList.length === 0) { interaction.reply({ - content: __({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), + content: t({ phrase: 'hub.browse.noHubs', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -145,7 +145,7 @@ export default class Browse extends Hub { }); if (!hubDetails) { return await interaction.reply({ - content: __({ phrase: 'hub.notFound', locale: interaction.user.locale }), + content: t({ phrase: 'hub.notFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -174,7 +174,7 @@ export default class Browse extends Hub { const alreadyJoined = hubDetails.connections.find((c) => c.serverId === interaction.guildId); if (alreadyJoined) { interaction.reply({ - content: __( + content: t( { phrase: 'hub.alreadyJoined', locale: interaction.user.locale }, { hub: hubDetails.name, channel: `<#${alreadyJoined.channelId}>` }, ), @@ -215,7 +215,7 @@ export default class Browse extends Hub { // use current channel embed const embed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.browse.joinConfirm', locale: interaction.user.locale, @@ -225,7 +225,7 @@ export default class Browse extends Hub { ) .setColor('Aqua') .setFooter({ - text: __({ phrase: 'hub.browse.joinFooter', locale: interaction.user.locale }), + text: t({ phrase: 'hub.browse.joinFooter', locale: interaction.user.locale }), }); await interaction.reply({ @@ -241,7 +241,7 @@ export default class Browse extends Hub { else if (customId.postfix === 'channel_select' || customId.postfix === 'confirm') { if (!hubDetails) { return await interaction.reply({ - content: __({ phrase: 'hub.notFound', locale: interaction.user.locale }), + content: t({ phrase: 'hub.notFound', locale: interaction.user.locale }), ephemeral: true, }); } @@ -254,7 +254,7 @@ export default class Browse extends Hub { ); if (userBlacklisted) { return await interaction.reply({ - content: __( + content: t( { phrase: 'errors.userBlacklisted', locale: interaction.user.locale }, { hub: hubDetails.name }, ), @@ -268,7 +268,7 @@ export default class Browse extends Hub { ); if (serverBlacklisted) { return await interaction.reply({ - content: __( + content: t( { phrase: 'errors.serverBlacklisted', locale: interaction.user.locale }, { hub: hubDetails.name }, ), @@ -283,7 +283,7 @@ export default class Browse extends Hub { // for type safety if (channel?.type !== ChannelType.GuildText && !channel?.isThread()) { await interaction.reply({ - content: __({ phrase: 'hub.invalidChannel', locale: interaction.user.locale }), + content: t({ phrase: 'hub.invalidChannel', locale: interaction.user.locale }), ephemeral: true, }); return; @@ -291,7 +291,7 @@ export default class Browse extends Hub { if (!interaction.guild?.members.me?.permissionsIn(channel).has(['ManageWebhooks'])) { await interaction.update( - __( + t( { phrase: 'errors.missingPermissions', locale: interaction.user.locale }, { permissions: 'Manage Webhooks' }, ), @@ -301,7 +301,7 @@ export default class Browse extends Hub { if (!interaction.member.permissionsIn(channel).has('ManageChannels')) { await interaction.update( - __( + t( { phrase: 'errors.botMissingPermissions', locale: interaction.user.locale }, { permissions: 'Manage Channels' }, ), @@ -315,7 +315,7 @@ export default class Browse extends Hub { if (channelConnected) { await interaction.update({ - content: __( + content: t( { phrase: 'hub.alreadyConnected', locale: interaction.user.locale }, { channel: `${channel}` }, ), @@ -338,7 +338,7 @@ export default class Browse extends Hub { } else if (onboardingCompleted === 'in-progress') { return await interaction.update({ - content: __( + content: t( { phrase: 'onboarding.inProgress', locale: interaction.user.locale }, { channel: `${channel}` }, ), @@ -364,7 +364,7 @@ export default class Browse extends Hub { }); await interaction.editReply({ - content: __( + content: t( { phrase: 'hub.join.success', locale: interaction.user.locale }, { hub: hubDetails.name, channel: `${channel}` }, ), diff --git a/src/commands/slash/Main/hub/create.ts b/src/commands/slash/Main/hub/create.ts index c49318736..e2f2b93fc 100644 --- a/src/commands/slash/Main/hub/create.ts +++ b/src/commands/slash/Main/hub/create.ts @@ -14,7 +14,7 @@ import { RegisterInteractionHandler } from '../../../../decorators/Interaction.j import { HubSettingsBits } from '../../../../utils/BitFields.js'; import { checkAndFetchImgurUrl, simpleEmbed } from '../../../../utils/Utils.js'; import { LINKS } from '../../../../utils/Constants.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Create extends Hub { readonly cooldown = 60 * 60 * 1000; // 1 hour @@ -83,7 +83,7 @@ export default class Create extends Hub { // if hubName contains "discord", "clyde" "```" then return if (name.match(/discord|clyde|```/gi)) { return await interaction.followUp({ - content: __({ phrase: 'hub.create.invalidName', locale: interaction.user.locale }), + content: t({ phrase: 'hub.create.invalidName', locale: interaction.user.locale }), ephemeral: true, }); } @@ -94,7 +94,7 @@ export default class Create extends Hub { if (hubs.find((hub) => hub.name === name)) { return await interaction.followUp({ - content: __({ phrase: 'hub.create.nameTaken', locale: interaction.user.locale }), + content: t({ phrase: 'hub.create.nameTaken', locale: interaction.user.locale }), ephemeral: true, }); } @@ -102,7 +102,7 @@ export default class Create extends Hub { hubs.reduce((acc, hub) => (hub.ownerId === interaction.user.id ? acc + 1 : acc), 0) >= 3 ) { return await interaction.followUp({ - content: __({ phrase: 'hub.create.maxHubs', locale: interaction.user.locale }), + content: t({ phrase: 'hub.create.maxHubs', locale: interaction.user.locale }), ephemeral: true, }); } @@ -114,7 +114,7 @@ export default class Create extends Hub { if (iconUrl === false || bannerUrl === false) { return await interaction.followUp({ embeds: [ - simpleEmbed(__({ phrase: 'hub.create.invalidUrl', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'hub.invalidImgurUrl', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -142,7 +142,7 @@ export default class Create extends Hub { const successEmbed = new EmbedBuilder() .setColor('Green') .setDescription( - __( + t( { phrase: 'hub.create.success', locale: interaction.user.locale }, { name, support_invite: LINKS.SUPPORT_INVITE }, ), diff --git a/src/commands/slash/Main/hub/delete.ts b/src/commands/slash/Main/hub/delete.ts index 11346631e..d934b3d34 100644 --- a/src/commands/slash/Main/hub/delete.ts +++ b/src/commands/slash/Main/hub/delete.ts @@ -13,7 +13,7 @@ import { LINKS, emojis } from '../../../../utils/Constants.js'; import { deleteHubs, simpleEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Delete extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -22,14 +22,14 @@ export default class Delete extends Hub { if (interaction.user.id !== hubInDb?.ownerId) { return await interaction.reply({ - content: __({ phrase: 'errors.modUnownedHub', locale: interaction.user.locale }), + content: t({ phrase: 'errors.modUnownedHub', locale: interaction.user.locale }), ephemeral: true, }); } const confirmEmbed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.delete.confirm', locale: interaction.user.locale }, { hub: hubInDb.name }, ), @@ -74,14 +74,14 @@ export default class Delete extends Hub { if (interaction.user.id !== userId) { return await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'errors.ownerOnly', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'errors.ownerOnly', locale: interaction.user.locale }))], ephemeral: true, }); } if (customId.postfix === 'cancel') { await interaction.update({ - embeds: [simpleEmbed(__({ phrase: 'hub.delete.cancel', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.delete.cancel', locale: interaction.user.locale }))], components: [], }); return; @@ -94,7 +94,7 @@ export default class Delete extends Hub { return await interaction.update({ embeds: [ simpleEmbed( - __( + t( { phrase: 'errors.unknown', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), @@ -109,7 +109,7 @@ export default class Delete extends Hub { await interaction.update({ embeds: [ simpleEmbed( - __( + t( { phrase: 'hub.delete.success', locale: interaction.user.locale }, { emoji: emojis.tick }, ), diff --git a/src/commands/slash/Main/hub/invite.ts b/src/commands/slash/Main/hub/invite.ts index 961f94a2b..18688bc22 100644 --- a/src/commands/slash/Main/hub/invite.ts +++ b/src/commands/slash/Main/hub/invite.ts @@ -5,7 +5,7 @@ import { emojis } from '../../../../utils/Constants.js'; import db from '../../../../utils/Db.js'; import Logger from '../../../../utils/Logger.js'; import { simpleEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Invite extends Hub { readonly cooldown = 3000; // 3 seconds @@ -36,7 +36,7 @@ export default class Invite extends Hub { if (!hubInDb) { await interaction.reply({ embeds: [ - simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), ], ephemeral: true, }); @@ -51,7 +51,7 @@ export default class Invite extends Hub { const embed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.invite.create.success', locale: interaction.user.locale }, { inviteCode: createdInvite.code, @@ -85,7 +85,7 @@ export default class Invite extends Hub { if (!inviteInDb) { await interaction.reply({ - content: __({ + content: t({ phrase: 'hub.invite.revoke.invalidCode', locale: interaction.user.locale, }), @@ -99,7 +99,7 @@ export default class Invite extends Hub { await interaction.reply({ embeds: [ simpleEmbed( - __( + t( { phrase: 'hub.invite.revoke.success', locale: interaction.user.locale }, { emoji: emojis.yes, inviteCode: code }, ), @@ -114,7 +114,7 @@ export default class Invite extends Hub { await interaction .reply({ embeds: [ - simpleEmbed(__({ phrase: 'errors.unknown', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'errors.unknown', locale: interaction.user.locale })), ], ephemeral: true, }) @@ -140,7 +140,7 @@ export default class Invite extends Hub { await interaction.reply({ embeds: [ simpleEmbed( - __({ phrase: 'hub.invite.list.notPrivate', locale: interaction.user.locale }), + t({ phrase: 'hub.invite.list.notPrivate', locale: interaction.user.locale }), ), ], ephemeral: true, @@ -153,7 +153,7 @@ export default class Invite extends Hub { await interaction.reply({ embeds: [ simpleEmbed( - __({ phrase: 'hub.invite.list.noInvites', locale: interaction.user.locale }), + t({ phrase: 'hub.invite.list.noInvites', locale: interaction.user.locale }), ), ], ephemeral: true, @@ -167,7 +167,7 @@ export default class Invite extends Hub { ); const inviteEmbed = new EmbedBuilder() - .setTitle(__({ phrase: 'hub.invite.list.title', locale: interaction.user.locale })) + .setTitle(t({ phrase: 'hub.invite.list.title', locale: interaction.user.locale })) .setDescription(inviteArr.join('\n')) .setColor('Yellow') .setTimestamp(); diff --git a/src/commands/slash/Main/hub/joined.ts b/src/commands/slash/Main/hub/joined.ts index 3b73f72bd..ccb2a57fa 100644 --- a/src/commands/slash/Main/hub/joined.ts +++ b/src/commands/slash/Main/hub/joined.ts @@ -3,7 +3,7 @@ import db from '../../../../utils/Db.js'; import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; import { paginate } from '../../../../utils/Pagination.js'; import { simpleEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; import { colors } from '../../../../utils/Constants.js'; export default class Joined extends Hub { @@ -15,7 +15,7 @@ export default class Joined extends Hub { if (connections.length === 0) { return await interaction.reply({ embeds: [ - simpleEmbed(__({ phrase: 'hub.joined.noJoinedHubs', locale: interaction.user.locale })), + simpleEmbed(t({ phrase: 'hub.joined.noJoinedHubs', locale: interaction.user.locale })), ], }); } @@ -36,7 +36,7 @@ export default class Joined extends Hub { // Start a new embed currentEmbed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.joined.joinedHubs', locale: interaction.user.locale }, { total: `${allFields.length}` }, ), @@ -58,7 +58,7 @@ export default class Joined extends Hub { const embed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.joined.joinedHubs', locale: interaction.user.locale }, { total: `${allFields.length}` }, ), diff --git a/src/commands/slash/Main/hub/leave.ts b/src/commands/slash/Main/hub/leave.ts index 8169f7a90..f15a27977 100644 --- a/src/commands/slash/Main/hub/leave.ts +++ b/src/commands/slash/Main/hub/leave.ts @@ -13,7 +13,7 @@ import { RegisterInteractionHandler } from '../../../../decorators/Interaction.j import { CustomID } from '../../../../utils/CustomID.js'; import { emojis } from '../../../../utils/Constants.js'; import { simpleEmbed, setComponentExpiry } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Leave extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -27,7 +27,7 @@ export default class Leave extends Hub { if (!isChannelConnected) { return await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.leave.noHub', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.leave.noHub', locale: interaction.user.locale }))], ephemeral: true, }); } @@ -35,7 +35,7 @@ export default class Leave extends Hub { return await interaction.reply({ embeds: [ simpleEmbed( - __( + t( { phrase: 'errors.missingPermissions', locale: interaction.user.locale }, { permission: 'Manage Channels' }, ), @@ -58,14 +58,14 @@ export default class Leave extends Hub { const resetConfirmEmbed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.leave.confirm', locale: interaction.user.locale }, { channel: `<#${channelId}>`, hub: `${isChannelConnected.hub?.name}` }, ), ) .setColor('Red') .setFooter({ - text: __({ phrase: 'hub.leave.confirmFooter', locale: interaction.user.locale }), + text: t({ phrase: 'hub.leave.confirmFooter', locale: interaction.user.locale }), }); await interaction.reply({ @@ -89,7 +89,7 @@ export default class Leave extends Hub { await db.connectedList.delete({ where: { channelId } }); await interaction.update({ - content: __( + content: t( { phrase: 'hub.leave.success', locale: interaction.user.locale }, { channel: `<#${channelId}>`, emoji: emojis.yes }, ), diff --git a/src/commands/slash/Main/hub/manage.ts b/src/commands/slash/Main/hub/manage.ts index a3145ced5..4fc45ebfe 100644 --- a/src/commands/slash/Main/hub/manage.ts +++ b/src/commands/slash/Main/hub/manage.ts @@ -14,7 +14,7 @@ import { TextInputBuilder, TextInputStyle, } from 'discord.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; import { CustomID } from '../../../../utils/CustomID.js'; import { stripIndents } from 'common-tags'; import { colors, emojis } from '../../../../utils/Constants.js'; @@ -41,7 +41,7 @@ export default class Manage extends Hub { if (!hubInDb) { await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], }); return; } @@ -62,7 +62,10 @@ export default class Manage extends Hub { await interaction.reply({ embeds: [await Manage.hubEmbed(hubInDb)], - components: [Manage.actionsSelect(hubInDb.name, interaction.user.id), button], + components: [ + Manage.actionsSelect(hubInDb.name, interaction.user.id, interaction.user.locale), + button, + ], }); // disable components after 5 minutes @@ -76,12 +79,11 @@ export default class Manage extends Hub { @RegisterInteractionHandler('hub_manage') async handleComponents(interaction: MessageComponentInteraction) { const customId = CustomID.parseCustomId(interaction.customId); + const locale = interaction.user.locale; if (customId.args[0] !== interaction.user.id) { await interaction.reply({ - embeds: [ - simpleEmbed(__({ phrase: 'errors.notYourAction', locale: interaction.user.locale })), - ], + embeds: [simpleEmbed(t({ phrase: 'errors.notYourAction', locale }))], ephemeral: true, }); return; @@ -94,7 +96,7 @@ export default class Manage extends Hub { if (!hubInDb) { await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.notFound', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound', locale }))], ephemeral: true, }); return; @@ -123,12 +125,17 @@ export default class Manage extends Hub { .addArgs(hubInDb.name) .toString(), ) - .setTitle('Change Hub Icon') + .setTitle(t({ phrase: 'hub.manage.icon.modal.title', locale })) .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('Enter Icon URL') - .setPlaceholder('Enter a valid Imgur image URL.') + .setLabel(t({ phrase: 'hub.manage.icon.modal.label', locale })) + .setPlaceholder( + t({ + phrase: 'hub.manage.enterImgurUrl', + locale, + }), + ) .setStyle(TextInputStyle.Short) .setCustomId('icon'), ), @@ -146,12 +153,27 @@ export default class Manage extends Hub { .addArgs(hubInDb.name) .toString(), ) - .setTitle('Edit Hub Description') + .setTitle( + t({ + phrase: 'hub.manage.description.modal.title', + locale, + }), + ) .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('Enter Description') - .setPlaceholder('A detailed description about the hub.') + .setLabel( + t({ + phrase: 'hub.manage.description.modal.label', + locale, + }), + ) + .setPlaceholder( + t({ + phrase: 'hub.manage.description.modal.placeholder', + locale, + }), + ) .setMaxLength(1024) .setStyle(TextInputStyle.Paragraph) .setCustomId('description'), @@ -174,8 +196,13 @@ export default class Manage extends Hub { .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('Enter Banner URL') - .setPlaceholder('Enter a valid imgur URL. Leave blank to remove.') + .setLabel( + t({ + phrase: 'hub.manage.banner.modal.label', + locale, + }), + ) + .setPlaceholder(t({ phrase: 'hub.manage.enterImgurUrl', locale })) .setStyle(TextInputStyle.Short) .setRequired(false) .setCustomId('banner'), @@ -194,9 +221,13 @@ export default class Manage extends Hub { }); await interaction.reply({ - content: `Successfully set hub visibility to **${ - updatedHub?.private ? 'Private' : 'Public' - }**.`, + content: t( + { phrase: 'hub.manage.visibility.success', locale }, + { + emoji: updatedHub.private ? '🔒' : '🔓', + visibility: updatedHub.private ? 'private' : 'public', + }, + ), ephemeral: true, }); @@ -237,7 +268,7 @@ export default class Manage extends Hub { if (!updHub) { await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'errors.unknown', locale: 'en' }))], + embeds: [simpleEmbed(t({ phrase: 'errors.unknown', locale: 'en' }))], ephemeral: true, }); return; @@ -260,6 +291,7 @@ export default class Manage extends Hub { async handleModals(interaction: ModalSubmitInteraction) { const customId = CustomID.parseCustomId(interaction.customId); const hubName = customId.args[0]; + const locale = interaction.user.locale || 'en'; let hubInDb = await db.hubs.findFirst({ where: { @@ -274,8 +306,7 @@ export default class Manage extends Hub { if (!hubInDb) { await interaction.reply({ - content: - 'This hub no longer exists or you no longer have permissions to perform this action!', + content: t({ phrase: 'hub.notFound_mod', locale }), ephemeral: true, }); return; @@ -291,7 +322,7 @@ export default class Manage extends Hub { }); await interaction.reply({ - content: 'Successfully updated hub description.', + content: t({ phrase: 'hub.manage.description.changed', locale }), ephemeral: true, }); break; @@ -305,7 +336,7 @@ export default class Manage extends Hub { const iconUrl = await checkAndFetchImgurUrl(newIcon); if (!iconUrl) { await interaction.reply({ - content: 'Invalid icon URL. Please make sure it is a valid imgur image URL.', + content: t({ phrase: 'hub.invalidImgurUrl', locale }), ephemeral: true, }); return; @@ -317,7 +348,7 @@ export default class Manage extends Hub { }); await interaction.reply({ - content: 'Successfully updated icon!', + content: t({ phrase: 'hub.manage.icon.changed', locale }), ephemeral: true, }); break; @@ -333,7 +364,10 @@ export default class Manage extends Hub { data: { bannerUrl: { unset: true } }, }); - await interaction.reply({ content: 'Successfully removed banner!', ephemeral: true }); + await interaction.reply({ + content: t({ phrase: 'hub.manage.banner.removed', locale }), + ephemeral: true, + }); break; } @@ -342,7 +376,7 @@ export default class Manage extends Hub { // if banner is not a valid imgur link if (!bannerUrl) { await interaction.reply({ - content: 'Invalid banner URL. Please make sure it is a valid imgur image URL.', + content: t({ phrase: 'hub.invalidImgurUrl', locale }), ephemeral: true, }); return; @@ -353,7 +387,10 @@ export default class Manage extends Hub { data: { bannerUrl }, }); - await interaction.reply({ content: 'Successfully updated banner!', ephemeral: true }); + await interaction.reply({ + content: t({ phrase: 'hub.manage.banner.changed', locale }), + ephemeral: true, + }); break; } @@ -376,7 +413,7 @@ export default class Manage extends Hub { } // utility methods - static actionsSelect(hubName: string, userId: string) { + static actionsSelect(hubName: string, userId: string, locale = 'en') { return new ActionRowBuilder().addComponents( new StringSelectMenuBuilder() .setCustomId( @@ -388,27 +425,27 @@ export default class Manage extends Hub { ) .addOptions([ { - label: 'Edit Description', + label: t({ phrase: 'hub.manage.description.selects.label', locale }), value: 'description', - description: 'Edit the hub description.', + description: t({ phrase: 'hub.manage.description.selects.description', locale }), emoji: '✏️', }, { - label: 'Toggle Visibility', + label: t({ phrase: 'hub.manage.visibility.selects.label', locale }), value: 'visibility', - description: 'Toggle the hub visibility between public and private.', + description: t({ phrase: 'hub.manage.visibility.selects.description', locale }), emoji: '🔒', }, { - label: 'Set Icon', + label: t({ phrase: 'hub.manage.icon.selects.label', locale }), value: 'icon', - description: 'Set the hub icon.', + description: t({ phrase: 'hub.manage.icon.selects.description', locale }), emoji: '🖼️', }, { - label: 'Set Banner', + label: t({ phrase: 'hub.manage.banner.selects.label', locale }), value: 'banner', - description: 'Set the hub banner.', + description: t({ phrase: 'hub.manage.banner.selects.description', locale }), emoji: '🎨', }, ]), diff --git a/src/commands/slash/Main/hub/moderator.ts b/src/commands/slash/Main/hub/moderator.ts index 1e3da8ad4..da1ef52e6 100644 --- a/src/commands/slash/Main/hub/moderator.ts +++ b/src/commands/slash/Main/hub/moderator.ts @@ -2,7 +2,7 @@ import { ChatInputCommandInteraction, CacheType, EmbedBuilder } from 'discord.js import Hub from './index.js'; import db from '../../../../utils/Db.js'; import { simpleEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; import { emojis } from '../../../../utils/Constants.js'; export default class Moderator extends Hub { @@ -20,7 +20,7 @@ export default class Moderator extends Hub { if (!hub) { return await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], ephemeral: true, }); } @@ -33,7 +33,7 @@ export default class Moderator extends Hub { return interaction.reply({ embeds: [ simpleEmbed( - __( + t( { phrase: 'hub.moderator.add.alreadyModerator', locale: interaction.user.locale }, { user: user.toString() }, ), @@ -50,7 +50,7 @@ export default class Moderator extends Hub { }); await interaction.reply({ - content: __( + content: t( { phrase: 'hub.moderator.add.success', locale: interaction.user.locale }, { user: user.toString(), position, emoji: emojis.yes }, ), @@ -63,7 +63,7 @@ export default class Moderator extends Hub { if (!hub.moderators.find((mod) => mod.userId === user.id)) { await interaction.reply({ - content: __( + content: t( { phrase: 'hub.moderator.remove.notModerator', locale: interaction.user.locale }, { user: user.toString() }, ), @@ -77,7 +77,7 @@ export default class Moderator extends Hub { hub.moderators.find((m) => m.position === 'manager' && m.userId === user.id) ) { await interaction.reply({ - content: __({ + content: t({ phrase: 'hub.moderator.remove.notOwner', locale: interaction.user.locale, }), @@ -94,7 +94,7 @@ export default class Moderator extends Hub { }); await interaction.reply( - __( + t( { phrase: 'hub.moderator.remove.success', locale: interaction.user.locale }, { user: user.toString(), emoji: emojis.yes }, ), @@ -116,7 +116,7 @@ export default class Moderator extends Hub { await interaction.reply({ embeds: [ simpleEmbed( - __({ phrase: 'hub.moderator.update.notAllowed', locale: interaction.user.locale }), + t({ phrase: 'hub.moderator.update.notAllowed', locale: interaction.user.locale }), ), ], ephemeral: true, @@ -127,7 +127,7 @@ export default class Moderator extends Hub { await interaction.reply({ embeds: [ simpleEmbed( - __( + t( { phrase: 'hub.moderator.update.nodModerator', locale: interaction.user.locale, @@ -147,7 +147,7 @@ export default class Moderator extends Hub { await interaction.reply({ embeds: [ simpleEmbed( - __({ + t({ phrase: 'hub.moderator.update.notOwner', locale: interaction.user.locale, }), @@ -168,7 +168,7 @@ export default class Moderator extends Hub { }); await interaction.reply( - __( + t( { phrase: 'hub.moderator.update.success', locale: interaction.user.locale }, { user: user.toString(), position, emoji: emojis.yes }, ), @@ -191,7 +191,7 @@ export default class Moderator extends Hub { }`, ) .join('\n') - : __({ phrase: 'hub.moderator.noModerators', locale: interaction.user.locale }), + : t({ phrase: 'hub.moderator.noModerators', locale: interaction.user.locale }), ) .setColor('Aqua') .setTimestamp(), diff --git a/src/commands/slash/Main/hub/servers.ts b/src/commands/slash/Main/hub/servers.ts index f2df6bbb2..85b52afba 100644 --- a/src/commands/slash/Main/hub/servers.ts +++ b/src/commands/slash/Main/hub/servers.ts @@ -4,7 +4,7 @@ import { colors } from '../../../../utils/Constants.js'; import { paginate } from '../../../../utils/Pagination.js'; import db from '../../../../utils/Db.js'; import { simpleEmbed } from '../../../../utils/Utils.js'; -import { __ } from '../../../../utils/Locale.js'; +import { t } from '../../../../utils/Locale.js'; export default class Servers extends Hub { async execute(interaction: ChatInputCommandInteraction) { @@ -19,7 +19,7 @@ export default class Servers extends Hub { if (!hub) { await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.notFound', locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound', locale }))], ephemeral: true, }); return; @@ -29,7 +29,7 @@ export default class Servers extends Hub { !hub.moderators.some((mod) => mod.userId === interaction.user.id) ) { await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.notFound_mod', locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound_mod', locale }))], ephemeral: true, }); return; @@ -37,7 +37,7 @@ export default class Servers extends Hub { if (hub.connections.length === 0) { await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.servers.noConnections', locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.servers.noConnections', locale }))], ephemeral: true, }); return; @@ -47,7 +47,7 @@ export default class Servers extends Hub { const connection = hub.connections.find((con) => con.serverId === serverOpt); if (!connection) { return await interaction.reply({ - embeds: [simpleEmbed(__({ phrase: 'hub.servers.notConnected', locale }))], + embeds: [simpleEmbed(t({ phrase: 'hub.servers.notConnected', locale }))], ephemeral: true, }); } @@ -57,7 +57,7 @@ export default class Servers extends Hub { .setTitle(`${server?.name} \`(${connection.serverId})\``) .setColor(colors.interchatBlue) .setDescription( - __( + t( { phrase: 'hub.servers.connectionInfo', locale }, { channelName: `${channel?.name}`, @@ -100,7 +100,7 @@ export default class Servers extends Hub { const evalRes = interaction.client.resolveEval(evalArr); - const value = __( + const value = t( { phrase: 'hub.servers.connectionInfo', locale }, { total: `${hub.connections.length}`, @@ -118,7 +118,7 @@ export default class Servers extends Hub { embeds.push( new EmbedBuilder() .setDescription( - __( + t( { phrase: 'hub.servers.total', locale }, { from: `${++l}`, diff --git a/src/commands/slash/Support/support/report.ts b/src/commands/slash/Support/support/report.ts index 48ff6c513..289b3fde2 100644 --- a/src/commands/slash/Support/support/report.ts +++ b/src/commands/slash/Support/support/report.ts @@ -1,34 +1,34 @@ +import Support from './index.js'; import { ActionRowBuilder, CacheType, ChatInputCommandInteraction, EmbedBuilder, - ForumChannel, MessageComponentInteraction, ModalBuilder, ModalSubmitInteraction, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, + TextChannel, TextInputBuilder, TextInputStyle, ThreadChannel, } from 'discord.js'; -import { stripIndents } from 'common-tags'; import { LINKS, channels, colors, emojis } from '../../../../utils/Constants.js'; -import Support from './index.js'; import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; +import { t } from '../../../../utils/Locale.js'; export default class Report extends Support { static readonly reportModal = new ModalBuilder() - .setTitle('New Report') + .setTitle(t({ phrase: 'report.modal.title', locale: 'en' })) .setCustomId(new CustomID().setIdentifier('report_modal').toString()) .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('description') - .setLabel('Report Details') - .setPlaceholder('A detailed description of the report.') + .setLabel(t({ phrase: 'report.modal.other.label', locale: 'en' })) + .setPlaceholder(t({ phrase: 'report.modal.other.placeholder', locale: 'en' })) .setStyle(TextInputStyle.Paragraph) .setMinLength(10) .setMaxLength(950), @@ -64,9 +64,9 @@ export default class Report extends Support { ); const bugEmbed = new EmbedBuilder() - .setTitle('Affected Components') - .setDescription('Please choose what component of the bot you are facing issues with.') - .setColor('Random'); + .setTitle(t({ phrase: 'report.bug.affected', locale: interaction.user.locale })) + .setDescription(t({ phrase: 'report.bug.description', locale: interaction.user.locale })) + .setColor(colors.interchatBlue); await interaction.reply({ embeds: [bugEmbed], @@ -75,14 +75,21 @@ export default class Report extends Support { }); } else if (reportType === 'server' || reportType === 'user' || reportType === 'other') { - const modal = new ModalBuilder(Report.reportModal) + const modal = ModalBuilder.from(Report.reportModal.components[0]) .setCustomId(new CustomID().setIdentifier('report_modal', reportType).toString()) .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('id') - .setLabel('User/Server ID') - .setPlaceholder('The IDs of the user/server you are reporting.') + .setLabel( + t({ phrase: 'report.modal.userOrServer.label', locale: interaction.user.locale }), + ) + .setPlaceholder( + t({ + phrase: 'report.modal.userOrServer.placeholder', + locale: interaction.user.locale, + }), + ) .setStyle(TextInputStyle.Short) .setMinLength(17) .setMaxLength(20), @@ -103,21 +110,33 @@ export default class Report extends Support { .addArgs(interaction.values.join(', ')) .toString(), ) - .setTitle('New Bug Report') + .setTitle(t({ phrase: 'report.bug.title', locale: interaction.user.locale })) .setComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('summary') - .setLabel('Whats the bug about?') - .setPlaceholder('Frequent interaction failures...') + .setLabel( + t({ phrase: 'report.modal.bug.input1.label', locale: interaction.user.locale }), + ) + .setPlaceholder( + t({ + phrase: 'report.modal.bug.input1.placeholder', + locale: interaction.user.locale, + }), + ) .setStyle(TextInputStyle.Short), ), new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('description') - .setLabel('Detailed Description (OPTIONAL)') + .setLabel( + t({ phrase: 'report.modal.bug.input2.label', locale: interaction.user.locale }), + ) .setPlaceholder( - 'Please describe the steps to reproduce the issue, include any unexpected behavior.', + t({ + phrase: 'report.modal.bug.input1.placeholder', + locale: interaction.user.locale, + }), ) .setStyle(TextInputStyle.Paragraph) .setRequired(false) @@ -134,7 +153,7 @@ export default class Report extends Support { async handleModals(interaction: ModalSubmitInteraction) { const customId = CustomID.parseCustomId(interaction.customId); const affected = customId.args[0]; - const reportType = customId.args[0]; + const reportType = customId.postfix; if (reportType === 'bug') { const summary = interaction.fields.getTextInputValue('summary'); @@ -157,30 +176,23 @@ export default class Report extends Support { // send the bug report to ic central await interaction.client.cluster.broadcastEval( async (client, ctx) => { - const bugReportChannel = (await client.channels - .fetch(ctx.bugsChannel) - .catch(() => null)) as ForumChannel | null; + const devChat = (await client.channels + .fetch(ctx.devChannel) + .catch(() => null)) as TextChannel | null; - if (!bugReportChannel) return; - - const appliedTags = bugReportChannel.availableTags - .map((tag) => { - if (ctx.affected.includes(tag.name)) return tag.id; - }) - .filter((tag) => tag !== undefined) as string[]; + if (!devChat) return; // finally make the post in ic central - await bugReportChannel.threads.create({ - name: summary, - message: { embeds: [bugReportEmbed] }, - appliedTags, - }); + await devChat.send({ embeds: [bugReportEmbed] }); }, - { context: { affected, bugsChannel: channels.bugs } }, + { context: { affected, devChannel: channels.devChat } }, ); await interaction.reply({ - content: `${emojis.yes} Successfully submitted report. Join the to view and/or attach screenshots to it.`, + content: t( + { phrase: 'report.bug.submitted', locale: interaction.user.locale }, + { emoji: emojis.yes }, + ), ephemeral: true, }); } @@ -197,11 +209,10 @@ export default class Report extends Support { const reportedUser = await interaction.client.users.fetch(Ids).catch(() => null); if (!reportedUser) { await interaction.reply({ - content: stripIndents` - ${emojis.no} I couldn't find a user with that ID.\n\n - **To find a user's ID within the network, please follow these instructions:** - ${emojis.dotYellow} Right click on a message sent from the user in question select \`Apps > Message Info\`. Please double-check the ID and try again. - `, + content: t( + { phrase: 'report.serverOrUser.invalidUser', locale: interaction.user.locale }, + { dot: emojis.dotYellow }, + ), ephemeral: true, }); return; @@ -226,11 +237,10 @@ export default class Report extends Support { const reportedServer = await interaction.client.fetchGuild(id).catch(() => null); if (!reportedServer) { await interaction.reply({ - content: stripIndents` - ${emojis.no} I couldn't find a server with that ID.\n - **To find a server ID within the network, please follow these instructions:** - ${emojis.dotYellow} Right click on a message sent by the server in question and select \`Apps > Message Info\`. Please double-check the ID and try again. - `, + content: t( + { phrase: 'report.serverOrUser.invalidUser', locale: interaction.user.locale }, + { dot: emojis.dotYellow }, + ), ephemeral: true, }); return; @@ -239,9 +249,7 @@ export default class Report extends Support { const serverReport = new EmbedBuilder() .setColor('Red') .setTitle('New Server Report') - .setDescription( - `Server Name: ${reportedServer.name}\nServer Id: ${reportedServer.members}`, - ) + .setDescription(`Server Name: ${reportedServer.name}\nServer Id: ${reportedServer.id}`) .setFields({ name: 'Reason for report', value: reportDescription }) .setThumbnail( `https://cdn.discordapp.com/icons/${reportedServer.id}/${reportedServer.icon}.png?size=2048`, @@ -274,7 +282,10 @@ export default class Report extends Support { } } await interaction.reply({ - content: `Report submitted. Join the [**support server**](${LINKS.SUPPORT_INVITE}) to get updates on your report.`, + content: t( + { phrase: 'report.submitted', locale: interaction.user.locale }, + { support_invite: LINKS.SUPPORT_INVITE }, + ), ephemeral: true, }); } diff --git a/src/scripts/network/onboarding.ts b/src/scripts/network/onboarding.ts index a478c5094..7eec004a7 100644 --- a/src/scripts/network/onboarding.ts +++ b/src/scripts/network/onboarding.ts @@ -10,7 +10,7 @@ import { Collection, } from 'discord.js'; import { LINKS, colors } from '../../utils/Constants.js'; -import { __ } from '../../utils/Locale.js'; +import { t } from '../../utils/Locale.js'; const onboardingInProgress = new Collection(); @@ -36,20 +36,20 @@ export async function showOnboarding( const embed = new EmbedBuilder() .setTitle( - __( + t( { phrase: 'network.onboarding.embed.title', locale: interaction.user.locale }, { hubName }, ), ) .setDescription( - __( + t( { phrase: 'network.onboarding.embed.description', locale: interaction.user.locale }, { hubName }, ), ) .setColor(colors.interchatBlue) .setFooter({ - text: __( + text: t( { phrase: 'network.onboarding.embed.footer', locale: interaction.user.locale }, { version: interaction.client.version }, ), @@ -101,7 +101,7 @@ export async function showOnboarding( const rulesEmbed = new EmbedBuilder() .setDescription( - __( + t( { phrase: 'rules', locale: interaction.user.locale }, { support_invite: LINKS.SUPPORT_INVITE }, ), diff --git a/src/updater/ReactionUpdater.ts b/src/updater/ReactionUpdater.ts index 0f57e28e0..fa847f83b 100644 --- a/src/updater/ReactionUpdater.ts +++ b/src/updater/ReactionUpdater.ts @@ -29,7 +29,7 @@ export default class ReactionUpdater extends Factory { /** * Listens for reactions on a message and updates the database with the new reaction data. */ - public async listenForReactions( + public async listen( reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser, ): Promise { diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 7f9450435..5dd62aeb0 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -12,7 +12,7 @@ const badwords = require('./JSON/profanity.json') as typeof badwordsType; export const isDevBuild = process.env.NODE_ENV === 'development'; -export const CLIENT_ID = isDevBuild ? '1075667060241211423' : '769921109209907241'; +export const CLIENT_ID = isDevBuild ? '798748015435055134' : '769921109209907241'; export const SUPPORT_SERVER_ID = '770256165300338709'; export const emojis: typeof normal = emotes.normal; @@ -50,11 +50,11 @@ export const LINKS = { } as const; export const channels = { - bugs: '1035135196053393418', + devChat: '770488420521738250', networklogs: '1156144879869632553', modlogs: '1042265633896796231', reports: '1158773603551162398', - goal: '1184062391395303445', + goal: '906460473065615403', suggestions: '1021256657528954900', } as const; diff --git a/src/utils/JSON/profanity.json b/src/utils/JSON/profanity.json index 50fdd4392..8b9464555 100644 --- a/src/utils/JSON/profanity.json +++ b/src/utils/JSON/profanity.json @@ -1,5 +1,5 @@ { - "slurs": ["nigga", "nigg", "nigger", "n1gger", "n1gg3r", "niger", "kys", "nig", "fag"], + "slurs": ["nigga", "nigg", "niggas", "nigger", "n1gger", "n1gg3r", "niger", "kys", "nig", "fag"], "profanity": [ "4r5e", diff --git a/src/utils/Locale.ts b/src/utils/Locale.ts index 7d81c4414..d7f69bd60 100644 --- a/src/utils/Locale.ts +++ b/src/utils/Locale.ts @@ -20,4 +20,4 @@ export function initI18n() { } // i18n is cjs :( -export { __ }; +export { __ as t }; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index bb3094c2e..b6986cbc0 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -18,7 +18,7 @@ import { } from 'discord.js'; import { DeveloperIds, REGEX, StaffIds, SupporterIds, LINKS, colors, emojis } from './Constants.js'; import { randomBytes } from 'crypto'; -import { __ } from './Locale.js'; +import { t } from './Locale.js'; import 'dotenv/config'; import { CmdInteraction } from '../commands/BaseCommand.js'; @@ -208,7 +208,7 @@ export function toTitleCase(str: string) { } export function genCommandErrMsg(locale: string, error: string) { - return __( + return t( { phrase: 'errors.commandError', locale }, { error, emoji: emojis.no, support_invite: LINKS.SUPPORT_INVITE }, ); From af4061bbbd717b9dc2ce5c156c395fb183f21161 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sat, 16 Dec 2023 15:45:42 +0530 Subject: [PATCH 12/15] chore: add more english translations --- locales | 2 +- src/SuperClient.ts | 9 +++- src/commands/slash/Main/connection.ts | 2 +- src/commands/slash/Main/hub/browse.ts | 23 +++++---- src/commands/slash/Main/hub/join.ts | 17 +++---- src/commands/slash/Main/hub/manage.ts | 2 +- src/commands/slash/Main/setlanguage.ts | 49 +++++++++++++++++++ src/commands/slash/Support/support/report.ts | 30 ++++++------ src/managers/CommandManager.ts | 4 +- src/managers/NetworkManager.ts | 50 +++++++------------- src/scripts/network/buildEmbed.ts | 39 ++++++++++----- src/typings/index.d.ts | 1 + 12 files changed, 146 insertions(+), 82 deletions(-) create mode 100644 src/commands/slash/Main/setlanguage.ts diff --git a/locales b/locales index 504b6d69b..f875ca216 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 504b6d69bdd4007e4b356f0b90dfd3b102017bb3 +Subproject commit f875ca2160b642403030c9dfe16918426a3b1c83 diff --git a/src/SuperClient.ts b/src/SuperClient.ts index a330a6d6e..f9088479b 100644 --- a/src/SuperClient.ts +++ b/src/SuperClient.ts @@ -22,13 +22,14 @@ import { RemoveMethods } from './typings/index.js'; import { isDevBuild } from './utils/Constants.js'; import { ActivityType } from 'discord.js'; import 'dotenv/config'; +import db from './utils/Db.js'; export default abstract class SuperClient extends Client { readonly description = 'The only cross-server chatting bot you\'ll ever need.'; readonly version = process.env.npm_package_version ?? 'Unknown'; readonly commands = commandsMap; readonly interactions = interactionsMap; - readonly webhooks = new Collection; + readonly webhooks = new Collection(); readonly commandCooldowns = new CooldownService(); readonly reactionCooldowns = new Collection(); @@ -125,6 +126,12 @@ export default abstract class SuperClient extends Client { return fetch ? this.resolveEval(fetch) : undefined; } + async getUserLocale(userId: Snowflake): Promise { + const fetch = await db.userData.findFirst({ where: { userId } }); + + return fetch?.locale || 'en'; + } + getCommandManager(): CommandManager { return this.commandHandler; } diff --git a/src/commands/slash/Main/connection.ts b/src/commands/slash/Main/connection.ts index d00d4ebf3..ff69de34a 100644 --- a/src/commands/slash/Main/connection.ts +++ b/src/commands/slash/Main/connection.ts @@ -102,7 +102,7 @@ export default class Connection extends BaseCommand { new StringSelectMenuOptionBuilder() .setLabel('Embed Color') .setEmoji('🎨') - .setDescription('Set the color of the embeds sent in the network.') + .setDescription('Set the color of embeds sent by this server.') .setValue('embed_color'), ), ]); diff --git a/src/commands/slash/Main/hub/browse.ts b/src/commands/slash/Main/hub/browse.ts index f9a16b877..68eff8e33 100644 --- a/src/commands/slash/Main/hub/browse.ts +++ b/src/commands/slash/Main/hub/browse.ts @@ -19,7 +19,11 @@ import Hub from './index.js'; import { hubs } from '@prisma/client'; import { emojis } from '../../../../utils/Constants.js'; import { paginate } from '../../../../utils/Pagination.js'; -import { calculateAverageRating, getOrCreateWebhook } from '../../../../utils/Utils.js'; +import { + calculateAverageRating, + getOrCreateWebhook, + simpleEmbed, +} from '../../../../utils/Utils.js'; import { showOnboarding } from '../../../../scripts/network/onboarding.js'; import { CustomID } from '../../../../utils/CustomID.js'; import { RegisterInteractionHandler } from '../../../../decorators/Interaction.js'; @@ -376,15 +380,16 @@ export default class Browse extends Hub { where: { hubId: hubDetails.id, connected: true }, }); - // announce a new server has joined the hub + // announce await networkManager.sendToHub(hubDetails.id, { + username: `InterChat | ${hubDetails.name}`, content: stripIndents` - A new server has joined us! ${emojis.clipart} + A new server has joined the hub! ${emojis.clipart} **Server Name:** __${interaction.guild.name}__ **Member Count:** __${interaction.guild.memberCount}__ - We now have **${totalConnections}** servers in the hub! + We now have **${totalConnections}** servers with us! `, }); } @@ -397,7 +402,9 @@ export default class Browse extends Hub { const rating = parseInt(interaction.fields.getTextInputValue('rating')); if (isNaN(rating) || rating < 1 || rating > 5) { return await interaction.reply({ - content: 'Invalid rating. You must enter a number between 1 and 5.', + embeds: [ + simpleEmbed(t({ phrase: 'hub.browse.rating.invalid', locale: interaction.user.locale })), + ], ephemeral: true, }); } @@ -406,7 +413,7 @@ export default class Browse extends Hub { const hub = await db.hubs.findFirst({ where: { id: hubId } }); if (!hub) { interaction.reply({ - content: 'Hub not found.', + embeds: [simpleEmbed(t({ phrase: 'hub.notFound', locale: interaction.user.locale }))], ephemeral: true, }); return; @@ -424,7 +431,7 @@ export default class Browse extends Hub { }); await interaction.reply({ - content: 'Rating submitted. Thank you!', + content: t({ phrase: 'hub.browse.rating.success', locale: interaction.user.locale }), ephemeral: true, }); } @@ -449,7 +456,7 @@ export default class Browse extends Hub { { name: 'Information', value: stripIndents` - ${emojis.connect_icon} **Connections:** ${connections ?? 'Unknown.'} + ${emojis.connect_icon} **Servers:** ${connections ?? 'Unknown.'} ${emojis.clock_icon} **Created At:** ${emojis.chat_icon} **Last Message:** ${lastMessageStr} `, diff --git a/src/commands/slash/Main/hub/join.ts b/src/commands/slash/Main/hub/join.ts index 6e79fe3b9..6949c986a 100644 --- a/src/commands/slash/Main/hub/join.ts +++ b/src/commands/slash/Main/hub/join.ts @@ -132,16 +132,17 @@ export default class JoinSubCommand extends Hub { where: { hubId: hub.id, connected: true }, }); - // announce a new server has joined the hub - networkManager.sendToHub(hub.id, { + // announce + await networkManager.sendToHub(hub.id, { + username: `InterChat | ${hub.name}`, content: stripIndents` - A new server has joined us! ${emojis.clipart} - - **Server Name:** __${interaction.guild.name}__ - **Member Count:** __${interaction.guild.memberCount}__ + A new server has joined the hub! ${emojis.clipart} - We now have **${totalConnections}** servers in the hub! - `, + **Server Name:** __${interaction.guild.name}__ + **Member Count:** __${interaction.guild.memberCount}__ + + We now have **${totalConnections}** servers with us! + `, }); } } diff --git a/src/commands/slash/Main/hub/manage.ts b/src/commands/slash/Main/hub/manage.ts index 4fc45ebfe..c536cb03a 100644 --- a/src/commands/slash/Main/hub/manage.ts +++ b/src/commands/slash/Main/hub/manage.ts @@ -268,7 +268,7 @@ export default class Manage extends Hub { if (!updHub) { await interaction.reply({ - embeds: [simpleEmbed(t({ phrase: 'errors.unknown', locale: 'en' }))], + embeds: [simpleEmbed(t({ phrase: 'errors.unknown', locale: interaction.user.locale }))], ephemeral: true, }); return; diff --git a/src/commands/slash/Main/setlanguage.ts b/src/commands/slash/Main/setlanguage.ts new file mode 100644 index 000000000..d1765dd11 --- /dev/null +++ b/src/commands/slash/Main/setlanguage.ts @@ -0,0 +1,49 @@ +import { + ApplicationCommandOptionType, + ChatInputCommandInteraction, + RESTPostAPIApplicationCommandsJSONBody, +} from 'discord.js'; +import BaseCommand from '../../BaseCommand.js'; +import db from '../../../utils/Db.js'; +import { emojis } from '../../../utils/Constants.js'; + +export default class SetLanguage extends BaseCommand { + data: RESTPostAPIApplicationCommandsJSONBody = { + name: 'setlanguage', + description: 'Set my language for when I respond to you', + options: [ + { + name: 'lang', + description: 'The language to set', + type: ApplicationCommandOptionType.String, + required: true, + choices: [ + { name: '🇺🇸 English', value: 'en' }, + { name: '🇹🇷 Turkish', value: 'tr' }, + ], + }, + ], + }; + + async execute(interaction: ChatInputCommandInteraction) { + const language = interaction.options.getString('lang', true); + await db.userData.upsert({ + where: { userId: interaction.user.id }, + create: { + userId: interaction.user.id, + locale: language, + username: interaction.user.username, + }, + update: { + locale: language, + }, + }); + + await interaction.reply({ + content: `${emojis.yes} Language set! I will now respond to you in **${ + language === 'en' ? '🇺🇸 English' : '🇹🇷 Turkish' + }**.`, + ephemeral: true, + }); + } +} diff --git a/src/commands/slash/Support/support/report.ts b/src/commands/slash/Support/support/report.ts index 289b3fde2..22481be82 100644 --- a/src/commands/slash/Support/support/report.ts +++ b/src/commands/slash/Support/support/report.ts @@ -20,21 +20,6 @@ import { RegisterInteractionHandler } from '../../../../decorators/Interaction.j import { t } from '../../../../utils/Locale.js'; export default class Report extends Support { - static readonly reportModal = new ModalBuilder() - .setTitle(t({ phrase: 'report.modal.title', locale: 'en' })) - .setCustomId(new CustomID().setIdentifier('report_modal').toString()) - .addComponents( - new ActionRowBuilder().addComponents( - new TextInputBuilder() - .setCustomId('description') - .setLabel(t({ phrase: 'report.modal.other.label', locale: 'en' })) - .setPlaceholder(t({ phrase: 'report.modal.other.placeholder', locale: 'en' })) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(10) - .setMaxLength(950), - ), - ); - async execute(interaction: ChatInputCommandInteraction) { const reportType = interaction.options.getString('type', true) as | 'user' @@ -75,9 +60,22 @@ export default class Report extends Support { }); } else if (reportType === 'server' || reportType === 'user' || reportType === 'other') { - const modal = ModalBuilder.from(Report.reportModal.components[0]) + const modal = new ModalBuilder() + .setTitle(t({ phrase: 'report.modal.title', locale: interaction.user.locale })) .setCustomId(new CustomID().setIdentifier('report_modal', reportType).toString()) + .addComponents( + new ActionRowBuilder().addComponents( + new TextInputBuilder() + .setCustomId('description') + .setLabel(t({ phrase: 'report.modal.other.label', locale: interaction.user.locale })) + .setPlaceholder( + t({ phrase: 'report.modal.other.placeholder', locale: interaction.user.locale }), + ) + .setStyle(TextInputStyle.Paragraph) + .setMinLength(10) + .setMaxLength(950), + ), new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('id') diff --git a/src/managers/CommandManager.ts b/src/managers/CommandManager.ts index 7ac85f323..5d7050b4b 100644 --- a/src/managers/CommandManager.ts +++ b/src/managers/CommandManager.ts @@ -7,7 +7,6 @@ import { CustomID } from '../utils/CustomID.js'; import { Interaction } from 'discord.js'; import { captureException } from '@sentry/node'; import { simpleEmbed, replyWithError } from '../utils/Utils.js'; -import db from '../utils/Db.js'; import Logger from '../utils/Logger.js'; const __filename = new URL(import.meta.url).pathname; @@ -21,8 +20,7 @@ export default class CommandManager extends Factory { /** Handle interactions from the `InteractionCreate` event */ async handleInteraction(interaction: Interaction): Promise { try { - const userData = await db.userData.findFirst({ where: { userId: interaction.user.id } }); - interaction.user.locale = userData?.locale || 'en'; + interaction.user.locale = await interaction.client.getUserLocale(interaction.user.id); if (interaction.isAutocomplete()) { const command = this.client.commands.get(interaction.commandName); diff --git a/src/managers/NetworkManager.ts b/src/managers/NetworkManager.ts index abd33fc17..b43981849 100644 --- a/src/managers/NetworkManager.ts +++ b/src/managers/NetworkManager.ts @@ -7,7 +7,6 @@ import { EmbedBuilder, HexColorString, Message, - MessageCreateOptions, User, WebhookClient, WebhookMessageCreateOptions, @@ -17,11 +16,10 @@ import db from '../utils/Db.js'; import { Prisma, connectedList, hubs, messageData } from '@prisma/client'; import { LINKS, REGEX, emojis } from '../utils/Constants.js'; import { check as checkProfanity, censor } from '../utils/Profanity.js'; -import { stripIndents } from 'common-tags'; import { HubSettingsBitField } from '../utils/BitFields.js'; import { replaceLinks } from '../utils/Utils.js'; import NetworkLogger from '../utils/NetworkLogger.js'; -import Logger from '../utils/Logger.js'; +import { t } from '../utils/Locale.js'; export interface NetworkMessage extends Message { censoredContent: string; @@ -52,6 +50,8 @@ export default class NetworkManager extends Factory { * @param message The network message to handle. */ public async handleNetworkMessage(message: NetworkMessage) { + const locale = await message.client.getUserLocale(message.author.id); + const isNetworkMessage = await db.connectedList.findFirst({ where: { channelId: message.channel.id, connected: true }, include: { hub: true }, @@ -60,12 +60,6 @@ export default class NetworkManager extends Factory { // check if the message was sent in a network channel if (!isNetworkMessage?.hub) return; - // FIXME remove later - Logger.info( - `[Network Debug] ${message.author.tag} sent a message in ${isNetworkMessage.hub.name}.`, - ); - Logger.info(`[Network Debug] ${message.client.webhooks.size} webhooks cached.`); - const settings = new HubSettingsBitField(isNetworkMessage.hub.settings); const checksPassed = await this.runChecks(message, settings, isNetworkMessage.hubId); if (!checksPassed) return; @@ -88,17 +82,15 @@ export default class NetworkManager extends Factory { if (predictions && nsfwDetector.isUnsafeContent(predictions)) { const nsfwEmbed = new EmbedBuilder() - .setTitle('NSFW Image Detected') + .setTitle(t({ phrase: 'nsfw.title', locale: message.author.locale })) .setDescription( - stripIndents` - I have identified this image as NSFW (Not Safe For Work). Sharing NSFW content is against our network guidelines. Refrain from posting such content here. - - **Detected NSFW:** ${predictions[0].className} ${Math.round( - predictions[0].probability * 100, -)}%`, + t( + { phrase: 'nsfw.description', locale: message.author.locale }, + { confidence: `${Math.round(predictions[0].probability * 100)}` }, + ), ) .setFooter({ - text: 'Please be aware that AI predictions can be inaccurate at times, and we cannot guarantee perfect accuracy in all cases. 😔', + text: t({ phrase: 'nsfw.footer', locale: message.author.locale }), iconURL: 'https://i.imgur.com/625Zy9W.png', }) .setColor('Red'); @@ -246,18 +238,6 @@ export default class NetworkManager extends Factory { update: { viewedNetworkWelcome: true }, }); - const welcomeEmbed = new EmbedBuilder() - .setAuthor({ - name: 'Welcome to the Network!', - iconURL: 'https://i.imgur.com/jlCtQGs.gif', - }) - .setDescription('Welcome to the network for {hub name}! Messages sent to this channel will be sent across to multiple other servers, enabling seamless communication with them. You can share images & GIFs, reply, react and with messages just as you would normally. Just remember to avoid sharing personal info (check /rules for more). Go ahead and send your first message!') - .setFooter({ - text: `Sent for: ${message.author.username}`, - iconURL: message.author.displayAvatarURL(), - }) - .setColor('#A0C2EC'); - const linkButtons = new ActionRowBuilder().addComponents( new ButtonBuilder() .setStyle(ButtonStyle.Link) @@ -272,12 +252,18 @@ export default class NetworkManager extends Factory { new ButtonBuilder() .setStyle(ButtonStyle.Link) .setEmoji(emojis.docs_icon) - .setLabel('Documentation') + .setLabel('How-To Guide') .setURL(LINKS.DOCS), ); await message.channel - .send({ content: `${message.author}`, embeds: [welcomeEmbed], components: [linkButtons] }) + .send({ + content: t( + { phrase: 'network.welcome', locale }, + { user: `${message.author}`, hub: isNetworkMessage.hub.name }, + ), + components: [linkButtons], + }) .catch(() => null); } @@ -669,7 +655,7 @@ export default class NetworkManager extends Factory { * @param message The message to send. Can be a string or a MessageCreateOptions object. * @returns A array of the responses from each connection's webhook. */ - async sendToHub(hubId: string, message: string | MessageCreateOptions) { + async sendToHub(hubId: string, message: string | WebhookMessageCreateOptions) { const connections = await this.fetchHubNetworks({ hubId }); const res = connections diff --git a/src/scripts/network/buildEmbed.ts b/src/scripts/network/buildEmbed.ts index 4ce1fc28e..1cf0a9703 100644 --- a/src/scripts/network/buildEmbed.ts +++ b/src/scripts/network/buildEmbed.ts @@ -2,6 +2,7 @@ import { Interaction, EmbedBuilder } from 'discord.js'; import { emojis, colors } from '../../utils/Constants.js'; import { yesOrNoEmoji } from '../../utils/Utils.js'; import db from '../../utils/Db.js'; +import { t } from '../../utils/Locale.js'; export async function buildEmbed(interaction: Interaction, channelId: string) { const networkData = await db.connectedList.findFirst({ @@ -13,33 +14,49 @@ export async function buildEmbed(interaction: Interaction, channelId: string) { const invite = networkData?.invite ? `Code: [\`${networkData.invite.replace('https://discord.gg/', '')}\`](${networkData.invite})` : 'Not Set.'; + const locale = interaction.user.locale; return new EmbedBuilder() - .setTitle('Edit Settings') - .setDescription(`Showing network settings for <#${channelId}>.`) + .setTitle(t({ phrase: 'connection.embed.title', locale })) + .setDescription(t({ phrase: 'connection.embed.description', locale })) .addFields([ - { name: 'Channel', value: `<#${channelId}>`, inline: true }, - { name: 'Hub', value: `${networkData?.hub?.name}`, inline: true }, - { name: 'Invite', value: invite, inline: true }, - { name: 'Connected', value: yesOrNoEmoji(networkData?.connected, yes, no), inline: true }, { - name: 'Compact', + name: t({ phrase: 'connection.embed.fields.hub', locale }), + value: `${networkData?.hub?.name}`, + inline: true, + }, + { + name: t({ phrase: 'connection.embed.fields.channel', locale }), + value: `<#${channelId}>`, + inline: true, + }, + { + name: t({ phrase: 'connection.embed.fields.invite', locale }), + value: invite, + inline: true, + }, + { + name: t({ phrase: 'connection.embed.fields.connected', locale }), + value: yesOrNoEmoji(networkData?.connected, yes, no), + inline: true, + }, + { + name: t({ phrase: 'connection.embed.fields.compact', locale }), value: yesOrNoEmoji(networkData?.compact, enabled, disabled), inline: true, }, { - name: 'Profanity Filter', + name: t({ phrase: 'connection.embed.fields.profanity', locale }), value: yesOrNoEmoji(networkData?.profFilter, enabled, disabled), inline: true, }, { - name: 'Embed Color', + name: t({ phrase: 'connection.embed.fields.emColor', locale }), value: networkData?.embedColor ? `\`${networkData?.embedColor}\`` : no, inline: true, }, ]) .setColor(colors.interchatBlue) .setThumbnail(interaction.guild?.iconURL() || interaction.client.user.avatarURL()) - .setTimestamp() - .setFooter({ text: 'Use to menu below to edit.' }); + .setFooter({ text: t({ phrase: 'connection.embed.footer', locale }) }); } diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index d66bee81a..8281d974a 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -23,6 +23,7 @@ declare module 'discord.js' { resolveEval: (value: T[]) => T | undefined; + getUserLocale(userId: Snowflake): Promise fetchGuild(guildId: Snowflake): Promise | undefined>; getScheduler(): Scheduler; getCommandManager(): CommandManager; From 02de634a4043b4b6dc8a318abae68137e5ba7e70 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sat, 16 Dec 2023 16:53:53 +0530 Subject: [PATCH 13/15] chore: add final bit of translations --- .env.example | 4 +--- README.md | 6 ++--- src/commands/slash/Main/hub/create.ts | 20 +++++++++-------- src/commands/slash/Main/hub/join.ts | 31 +++++++++++++++++++------- src/commands/slash/Main/setlanguage.ts | 13 ++++++----- src/managers/CommandManager.ts | 21 ++++++++--------- src/managers/NetworkManager.ts | 8 +++++-- src/updater/ReactionUpdater.ts | 10 ++++----- 8 files changed, 66 insertions(+), 47 deletions(-) diff --git a/.env.example b/.env.example index 3198d0793..1ca7d8cb3 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,7 @@ TOKEN="discord bot token here" -MONGODB_URI="mongodb connection url here" # Format: mongodb+srv://:@uri/dbname +DATABASE_URL="mongodb connection url here" # get it from mongodb atlas or use local mongodb TOPGG_API_KEY="your topgg bot access token>" # not required if you don't have a bot on top.gg. TENOR_KEY="tenor api key" # required for posting gifs in global chat. -SENTRY_DSN="your sentry dsn link" # get it from sentry.io SENTRY_AUTH_TOKEN="your sentry auth token" # get it from devoid NODE_ENV=development # change to production when deploying IMGUR_CLIENT_ID="imgur client id" # get it from imgur -IMGUR_CLIENT_SECRET="imgur client secret" # get it from imgur \ No newline at end of file diff --git a/README.md b/README.md index 376936713..695ecb3af 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ const customId = CustomID.parseCustomId(interaction.customId); /* { prefix: string, postfix: string, - expiry: Date, + expiry?: Date, args?: string[], }; */ @@ -92,7 +92,7 @@ To handle components (buttons, selects, modals) create a new method called `hand @InteractionHandler('cool_button_') public async handleComponents(interaction: MessageComponentInteraction) { const customId = CustomID.parseCustomId(interaction.customId); - // handle component logic, same as how its with collectors + // handle component logic, same as how it is with collectors } ``` @@ -102,7 +102,7 @@ public async handleComponents(interaction: MessageComponentInteraction) { - Commands are loaded automatically by calling the `loadCommandFiles` method from `src/managers/CommandManager.ts` during the bot startup. - The `src/commands/BaseCommand.ts` file contains all the methods/properties that can be used in a command. - We use the `interactionCreate` event for handling **all** interactions instead of using collectors. -- If you are using your own bot for testing, make sure to change the CLIENT_ID in `src/utils/Constants.ts` like so: +- If you are using your own bot for testing, make sure to change the CLIENT_ID in `src/utils/Constants.ts` like so (don't commit this change): ```ts export const CLIENT_ID = isDevBuild ? '' : '769921109209907241'; diff --git a/src/commands/slash/Main/hub/create.ts b/src/commands/slash/Main/hub/create.ts index e2f2b93fc..29a2b2193 100644 --- a/src/commands/slash/Main/hub/create.ts +++ b/src/commands/slash/Main/hub/create.ts @@ -20,14 +20,16 @@ export default class Create extends Hub { readonly cooldown = 60 * 60 * 1000; // 1 hour async execute(interaction: ChatInputCommandInteraction) { + const locale = interaction.user.locale; + const modal = new ModalBuilder() - .setTitle('Create a hub') + .setTitle(t({ phrase: 'hub.create.modal.title', locale })) .setCustomId('hub_create_modal') .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('Name') - .setPlaceholder('Give your hub a name.') + .setLabel(t({ phrase: 'hub.create.modal.name.label', locale })) + .setPlaceholder(t({ phrase: 'hub.create.modal.name.placeholder', locale })) .setMinLength(2) .setMaxLength(100) .setStyle(TextInputStyle.Short) @@ -35,16 +37,16 @@ export default class Create extends Hub { ), new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('What is the hub about?') - .setPlaceholder('A detailed description about your hub.') + .setLabel(t({ phrase: 'hub.create.modal.description.label', locale })) + .setPlaceholder(t({ phrase: 'hub.create.modal.description.placeholder', locale })) .setMaxLength(1024) .setStyle(TextInputStyle.Paragraph) .setCustomId('description'), ), new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('Icon') - .setPlaceholder('Set a custom icon for your hub. Must be a imgur link.') + .setLabel(t({ phrase: 'hub.create.modal.icon.label', locale })) + .setPlaceholder(t({ phrase: 'hub.create.modal.icon.placeholder', locale })) .setMaxLength(300) .setStyle(TextInputStyle.Short) .setRequired(false) @@ -52,8 +54,8 @@ export default class Create extends Hub { ), new ActionRowBuilder().addComponents( new TextInputBuilder() - .setLabel('Banner') - .setPlaceholder('Set a custom banner for your hub. Must be a imgur link.') + .setLabel(t({ phrase: 'hub.create.modal.banner.label', locale })) + .setPlaceholder(t({ phrase: 'hub.create.modal.description.placeholder', locale })) .setMaxLength(300) .setStyle(TextInputStyle.Short) .setRequired(false) diff --git a/src/commands/slash/Main/hub/join.ts b/src/commands/slash/Main/hub/join.ts index 6949c986a..facf99a69 100644 --- a/src/commands/slash/Main/hub/join.ts +++ b/src/commands/slash/Main/hub/join.ts @@ -7,11 +7,13 @@ import { hubs } from '@prisma/client'; import { simpleEmbed, getOrCreateWebhook } from '../../../../utils/Utils.js'; import { showOnboarding } from '../../../../scripts/network/onboarding.js'; import { stripIndents } from 'common-tags'; +import { t } from '../../../../utils/Locale.js'; export default class JoinSubCommand extends Hub { async execute(interaction: ChatInputCommandInteraction): Promise { if (!interaction.inCachedGuild()) return; + const locale = interaction.user.locale; const networkManager = interaction.client.getNetworkManager(); const hubName = interaction.options.getString('hub') ?? 'InterChat Central'; const invite = interaction.options.getString('invite'); @@ -24,7 +26,9 @@ export default class JoinSubCommand extends Hub { const channelInHub = await networkManager.fetchConnection({ channelId: channel.id }); if (channelInHub) { return await interaction.reply({ - content: `${emojis.no} You are already connected to a hub from ${channel}.`, + embeds: [ + simpleEmbed(t({ phrase: 'hub.alreadyJoined', locale }, { channel: `${channel}` })), + ], ephemeral: true, }); } @@ -40,7 +44,7 @@ export default class JoinSubCommand extends Hub { if (!fetchedInvite) { return await interaction.reply({ - content: `${emojis.no} That invite code is invalid.`, + embeds: [simpleEmbed(t({ phrase: 'hub.invite.revoke.invalidCode', locale }))], ephemeral: true, }); } @@ -52,7 +56,7 @@ export default class JoinSubCommand extends Hub { if (!hub) { return await interaction.reply({ - content: `${emojis.no} That hub does not exist.`, + embeds: [simpleEmbed(t({ phrase: 'hub.notFound', locale }))], ephemeral: true, }); } @@ -66,7 +70,11 @@ export default class JoinSubCommand extends Hub { if (alreadyInHub) { return await interaction.reply({ - content: `${emojis.no} You are already connected to this hub from <#${alreadyInHub.channelId}>.`, + embeds: [ + simpleEmbed( + t({ phrase: 'hub.alreadyJoined', locale }, { channel: `<#${alreadyInHub.channelId}>` }), + ), + ], ephemeral: true, }); } @@ -79,7 +87,7 @@ export default class JoinSubCommand extends Hub { if (userBlacklisted || serverBlacklisted) { return await interaction.reply({ - content: `${emojis.no} You or this server is blacklisted from this hub.`, + embeds: [simpleEmbed(t({ phrase: 'errors.blacklisted', locale }))], ephemeral: true, }); } @@ -92,7 +100,11 @@ export default class JoinSubCommand extends Hub { } else if (onboardingCompleted === 'in-progress') { return await interaction.reply({ - content: `${emojis.no} An attempt to join a hub in <#${channel.id}> is currently in progress. Please wait for it to complete before making another attempt.`, + embeds: [ + simpleEmbed( + t({ phrase: 'hub.onboarding.inProgress', locale }, { channel: `${channel}` }), + ), + ], ephemeral: true, }); } @@ -102,7 +114,10 @@ export default class JoinSubCommand extends Hub { await interaction.editReply({ embeds: [ simpleEmbed( - `${emojis.no} I could not create a webhook in ${channel}. Please make sure I have the \`Manage Webhooks\` permission in that channel.`, + t( + { phrase: 'errors.botMissingPermissions', locale }, + { permissions: 'Manage Webhooks' }, + ), ), ], components: [], @@ -123,7 +138,7 @@ export default class JoinSubCommand extends Hub { }); await interaction.editReply({ - content: `${emojis.yes} You have successfully joined **${hub.name}** from ${channel}. Use \`/connection\` to configure your connection.`, + content: t({ phrase: 'hub.join.success', locale }, { channel: `${channel}`, hub: hub.name }), embeds: [], components: [], }); diff --git a/src/commands/slash/Main/setlanguage.ts b/src/commands/slash/Main/setlanguage.ts index d1765dd11..ef5020479 100644 --- a/src/commands/slash/Main/setlanguage.ts +++ b/src/commands/slash/Main/setlanguage.ts @@ -6,6 +6,7 @@ import { import BaseCommand from '../../BaseCommand.js'; import db from '../../../utils/Db.js'; import { emojis } from '../../../utils/Constants.js'; +import { t } from '../../../utils/Locale.js'; export default class SetLanguage extends BaseCommand { data: RESTPostAPIApplicationCommandsJSONBody = { @@ -26,23 +27,23 @@ export default class SetLanguage extends BaseCommand { }; async execute(interaction: ChatInputCommandInteraction) { - const language = interaction.options.getString('lang', true); + const locale = interaction.options.getString('lang', true); await db.userData.upsert({ where: { userId: interaction.user.id }, create: { userId: interaction.user.id, - locale: language, + locale, username: interaction.user.username, }, update: { - locale: language, + locale, }, }); + const lang = locale === 'en' ? '🇺🇸 English' : '🇹🇷 Turkish'; + await interaction.reply({ - content: `${emojis.yes} Language set! I will now respond to you in **${ - language === 'en' ? '🇺🇸 English' : '🇹🇷 Turkish' - }**.`, + content: emojis.yes + t({ phrase: 'language.set', locale }, { lang }), ephemeral: true, }); } diff --git a/src/managers/CommandManager.ts b/src/managers/CommandManager.ts index 5d7050b4b..868dbf508 100644 --- a/src/managers/CommandManager.ts +++ b/src/managers/CommandManager.ts @@ -1,13 +1,13 @@ -import { access, constants, readdirSync, statSync } from 'fs'; +import { t } from '../utils/Locale.js'; import { join, dirname } from 'path'; -import Factory from '../Factory.js'; -import BaseCommand, { commandsMap } from '../commands/BaseCommand.js'; -import { emojis } from '../utils/Constants.js'; import { CustomID } from '../utils/CustomID.js'; import { Interaction } from 'discord.js'; import { captureException } from '@sentry/node'; import { simpleEmbed, replyWithError } from '../utils/Utils.js'; +import { access, constants, readdirSync, statSync } from 'fs'; import Logger from '../utils/Logger.js'; +import Factory from '../Factory.js'; +import BaseCommand, { commandsMap } from '../commands/BaseCommand.js'; const __filename = new URL(import.meta.url).pathname; const __dirname = dirname(__filename); @@ -74,11 +74,10 @@ export default class CommandManager extends Factory { // check if command is in cooldown for the user if (remainingCooldown) { await interaction.reply({ - content: `${ - emojis.timeout - } This command is on a cooldown! You can use it again: .`, + content: t( + { phrase: 'errors.cooldown', locale: interaction.user.locale }, + { time: `` }, + ), ephemeral: true, }); return; @@ -101,7 +100,9 @@ export default class CommandManager extends Factory { if (!handler || (customId.expiry && customId.expiry < Date.now())) { await interaction.reply({ - embeds: [simpleEmbed(`${emojis.no} This is no longer usable.`)], + embeds: [ + simpleEmbed(t({ phrase: 'errors.notUsable', locale: interaction.user.locale })), + ], ephemeral: true, }); return; diff --git a/src/managers/NetworkManager.ts b/src/managers/NetworkManager.ts index b43981849..42677191d 100644 --- a/src/managers/NetworkManager.ts +++ b/src/managers/NetworkManager.ts @@ -260,7 +260,11 @@ export default class NetworkManager extends Factory { .send({ content: t( { phrase: 'network.welcome', locale }, - { user: `${message.author}`, hub: isNetworkMessage.hub.name }, + { + user: `${message.author}`, + hub: isNetworkMessage.hub.name, + channel: `${message.channel}`, + }, ), components: [linkButtons], }) @@ -269,7 +273,7 @@ export default class NetworkManager extends Factory { // only delete the message if there is no attachment or if the user has already viewed the welcome message // deleting attachments will make the image not show up in the embed (discord removes it from its cdn) - if (!attachment && userData) message.delete().catch(() => null); + if (!attachment) message.delete().catch(() => null); // store the message in the db await this.storeMessageData( diff --git a/src/updater/ReactionUpdater.ts b/src/updater/ReactionUpdater.ts index fa847f83b..b986929e9 100644 --- a/src/updater/ReactionUpdater.ts +++ b/src/updater/ReactionUpdater.ts @@ -24,6 +24,7 @@ import { CustomID } from '../utils/CustomID.js'; import { RegisterInteractionHandler } from '../decorators/Interaction.js'; import { emojis } from '../utils/Constants.js'; import { stripIndents } from 'common-tags'; +import { t } from '../utils/Locale.js'; export default class ReactionUpdater extends Factory { /** @@ -88,10 +89,7 @@ export default class ReactionUpdater extends Factory { ReactionUpdater.updateReactions(messageInDb.channelAndMessageIds, dbReactions); } - - /** - * Listens for a reaction button or select menu interaction and updates the reactions accordingly. - * */ + /** Listens for a reaction button or select menu interaction and updates the reactions accordingly. */ @RegisterInteractionHandler('reaction_') async listenForReactionButton(interaction: ButtonInteraction | AnySelectMenuInteraction) { await interaction.deferUpdate(); @@ -192,14 +190,14 @@ export default class ReactionUpdater extends Factory { else { if (userBlacklisted) { await interaction.followUp({ - content: 'You are blacklisted from this hub.', + content: t({ phrase: 'errors.userBlacklisted', locale: interaction.user.locale }), ephemeral: true, }); return; } else if (serverBlacklisted) { await interaction.followUp({ - content: 'This server is blacklisted from this hub.', + content: t({ phrase: 'errors.userBlacklisted', locale: interaction.user.locale }), ephemeral: true, }); return; From f0e3d419ed53c020348c76766fb6c1fb2947dfe8 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sat, 16 Dec 2023 17:05:18 +0530 Subject: [PATCH 14/15] fix: fix translations stuff --- locales | 2 +- src/commands/context-menu/editMsg.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales b/locales index f875ca216..80e806374 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit f875ca2160b642403030c9dfe16918426a3b1c83 +Subproject commit 80e806374754ac870138da34515939b639ccf19d diff --git a/src/commands/context-menu/editMsg.ts b/src/commands/context-menu/editMsg.ts index ad6bef008..23faf9644 100644 --- a/src/commands/context-menu/editMsg.ts +++ b/src/commands/context-menu/editMsg.ts @@ -208,7 +208,7 @@ export default class EditMessage extends BaseCommand { const edited = resultsArray.reduce((acc, cur) => acc + (cur ? 1 : 0), 0); await interaction.editReply( t( - { phrase: 'editMsg.editSuccess', locale: interaction.user.locale }, + { phrase: 'network.editSuccess', locale: interaction.user.locale }, { edited: `${edited}`, total: `${resultsArray.length}` }, ), ); From ba20b62c6f1beb09540ced16eaf04edf5e6ea145 Mon Sep 17 00:00:00 2001 From: dev-737 <73829355+dev-737@users.noreply.github.com> Date: Sat, 16 Dec 2023 20:40:45 +0530 Subject: [PATCH 15/15] fix: fix some i18n stuff --- locales | 2 +- src/commands/slash/Main/blacklist/user.ts | 13 ++++++++----- src/commands/slash/Main/hub/delete.ts | 16 ++++++++++------ src/commands/slash/Main/hub/join.ts | 5 ++++- src/commands/slash/Main/hub/servers.ts | 4 +++- src/commands/slash/Support/support/report.ts | 14 +++++++++----- src/managers/CommandManager.ts | 3 ++- src/services/CooldownService.ts | 2 +- 8 files changed, 38 insertions(+), 21 deletions(-) diff --git a/locales b/locales index 80e806374..5deb7249a 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 80e806374754ac870138da34515939b639ccf19d +Subproject commit 5deb7249a6c5108e9495e8bae321c667776422ee diff --git a/src/commands/slash/Main/blacklist/user.ts b/src/commands/slash/Main/blacklist/user.ts index aacac8ddd..629b51388 100644 --- a/src/commands/slash/Main/blacklist/user.ts +++ b/src/commands/slash/Main/blacklist/user.ts @@ -26,9 +26,7 @@ export default class Server extends BlacklistCommand { if (!hubInDb) { return await interaction.editReply({ - embeds: [ - simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale })), - ], + embeds: [simpleEmbed(t({ phrase: 'hub.notFound_mod', locale: interaction.user.locale }))], }); } @@ -87,7 +85,12 @@ export default class Server extends BlacklistCommand { blacklistManager.notifyBlacklist('user', user.id, hubInDb.id, expires, reason); const successEmbed = new EmbedBuilder() - .setDescription(t({ phrase: 'blacklist.user.success', locale: interaction.user.locale })) + .setDescription( + t( + { phrase: 'blacklist.user.success', locale: interaction.user.locale }, + { username: user.username }, + ), + ) .setColor('Green') .addFields( { @@ -119,7 +122,7 @@ export default class Server extends BlacklistCommand { await interaction.followUp( t( { phrase: 'blacklist.user.removed', locale: interaction.user.locale }, - { emoji: emojis.delete, server: result.username }, + { emoji: emojis.delete, username: result.username }, ), ); if (user) { diff --git a/src/commands/slash/Main/hub/delete.ts b/src/commands/slash/Main/hub/delete.ts index d934b3d34..04891147e 100644 --- a/src/commands/slash/Main/hub/delete.ts +++ b/src/commands/slash/Main/hub/delete.ts @@ -29,10 +29,7 @@ export default class Delete extends Hub { const confirmEmbed = new EmbedBuilder() .setDescription( - t( - { phrase: 'hub.delete.confirm', locale: interaction.user.locale }, - { hub: hubInDb.name }, - ), + t({ phrase: 'hub.delete.confirm', locale: interaction.user.locale }, { hub: hubInDb.name }), ) .setColor('Red'); const confirmButtons = new ActionRowBuilder().addComponents( @@ -81,7 +78,14 @@ export default class Delete extends Hub { if (customId.postfix === 'cancel') { await interaction.update({ - embeds: [simpleEmbed(t({ phrase: 'hub.delete.cancel', locale: interaction.user.locale }))], + embeds: [ + simpleEmbed( + t( + { phrase: 'hub.delete.cancelled', locale: interaction.user.locale }, + { emoji: emojis.no }, + ), + ), + ], components: [], }); return; @@ -111,7 +115,7 @@ export default class Delete extends Hub { simpleEmbed( t( { phrase: 'hub.delete.success', locale: interaction.user.locale }, - { emoji: emojis.tick }, + { emoji: emojis.tick, hub: hubInDb.name }, ), ), ], diff --git a/src/commands/slash/Main/hub/join.ts b/src/commands/slash/Main/hub/join.ts index facf99a69..9849fd3e9 100644 --- a/src/commands/slash/Main/hub/join.ts +++ b/src/commands/slash/Main/hub/join.ts @@ -72,7 +72,10 @@ export default class JoinSubCommand extends Hub { return await interaction.reply({ embeds: [ simpleEmbed( - t({ phrase: 'hub.alreadyJoined', locale }, { channel: `<#${alreadyInHub.channelId}>` }), + t( + { phrase: 'hub.alreadyJoined', locale }, + { hub: hub.name, channel: `<#${alreadyInHub.channelId}>` }, + ), ), ], ephemeral: true, diff --git a/src/commands/slash/Main/hub/servers.ts b/src/commands/slash/Main/hub/servers.ts index 85b52afba..436c9518a 100644 --- a/src/commands/slash/Main/hub/servers.ts +++ b/src/commands/slash/Main/hub/servers.ts @@ -47,7 +47,9 @@ export default class Servers extends Hub { const connection = hub.connections.find((con) => con.serverId === serverOpt); if (!connection) { return await interaction.reply({ - embeds: [simpleEmbed(t({ phrase: 'hub.servers.notConnected', locale }))], + embeds: [ + simpleEmbed(t({ phrase: 'hub.servers.notConnected', locale }, { hub: hub.name })), + ], ephemeral: true, }); } diff --git a/src/commands/slash/Support/support/report.ts b/src/commands/slash/Support/support/report.ts index 22481be82..864595340 100644 --- a/src/commands/slash/Support/support/report.ts +++ b/src/commands/slash/Support/support/report.ts @@ -59,11 +59,10 @@ export default class Report extends Support { ephemeral: true, }); } - else if (reportType === 'server' || reportType === 'user' || reportType === 'other') { + else { const modal = new ModalBuilder() .setTitle(t({ phrase: 'report.modal.title', locale: interaction.user.locale })) .setCustomId(new CustomID().setIdentifier('report_modal', reportType).toString()) - .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() @@ -76,6 +75,10 @@ export default class Report extends Support { .setMinLength(10) .setMaxLength(950), ), + ); + + if (reportType !== 'other') { + modal.addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('id') @@ -93,6 +96,7 @@ export default class Report extends Support { .setMaxLength(20), ), ); + } await interaction.showModal(modal); } @@ -208,7 +212,7 @@ export default class Report extends Support { if (!reportedUser) { await interaction.reply({ content: t( - { phrase: 'report.serverOrUser.invalidUser', locale: interaction.user.locale }, + { phrase: 'report.invalidUser', locale: interaction.user.locale }, { dot: emojis.dotYellow }, ), ephemeral: true, @@ -236,7 +240,7 @@ export default class Report extends Support { if (!reportedServer) { await interaction.reply({ content: t( - { phrase: 'report.serverOrUser.invalidUser', locale: interaction.user.locale }, + { phrase: 'report.invalidServer', locale: interaction.user.locale }, { dot: emojis.dotYellow }, ), ephemeral: true, @@ -282,7 +286,7 @@ export default class Report extends Support { await interaction.reply({ content: t( { phrase: 'report.submitted', locale: interaction.user.locale }, - { support_invite: LINKS.SUPPORT_INVITE }, + { support_invite: `${LINKS.SUPPORT_INVITE} ` }, ), ephemeral: true, }); diff --git a/src/managers/CommandManager.ts b/src/managers/CommandManager.ts index 868dbf508..b4757681a 100644 --- a/src/managers/CommandManager.ts +++ b/src/managers/CommandManager.ts @@ -73,10 +73,11 @@ export default class CommandManager extends Factory { // check if command is in cooldown for the user if (remainingCooldown) { + const waitUntil = Math.round((Date.now() + (remainingCooldown)) / 1000); await interaction.reply({ content: t( { phrase: 'errors.cooldown', locale: interaction.user.locale }, - { time: `` }, + { time: `until ()` }, ), ephemeral: true, }); diff --git a/src/services/CooldownService.ts b/src/services/CooldownService.ts index ea7e96e2e..2dedc5cd7 100644 --- a/src/services/CooldownService.ts +++ b/src/services/CooldownService.ts @@ -11,7 +11,7 @@ export default class CooldownService { this.cooldowns.forEach((expires, key) => { if (expires < Date.now()) this.cooldowns.delete(key); }); - }, 60 * 1000); + }, 1000); } /**