diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0709477 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +MONGOURL= +DISCORD_TOKEN= +OFFTOPIC_WEBHOOK= \ No newline at end of file diff --git a/.gitignore b/.gitignore index c2e453b..ec6cce0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Taken from https://github.com/github/gitignore/blob/main/Node.gitignore +# and added custom ignores + # Logs logs *.log @@ -5,6 +8,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* +.pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json @@ -41,8 +45,8 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ # TypeScript cache *.tsbuildinfo @@ -53,6 +57,9 @@ typings/ # Optional eslint cache .eslintcache +# Optional stylelint cache +.stylelintcache + # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ @@ -68,15 +75,20 @@ typings/ # Yarn Integrity file .yarn-integrity -# dotenv environment variables file +# dotenv environment variable files .env -.env.test +.env.development.local +.env.test.local +.env.production.local +.env.local # parcel-bundler cache (https://parceljs.org/) .cache +.parcel-cache # Next.js build output .next +out # Nuxt.js build / generate output .nuxt @@ -84,13 +96,20 @@ dist # Gatsby files .cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js +# Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + # Serverless directories .serverless/ @@ -102,4 +121,20 @@ dist # TernJS port file .tern-port -.vscode \ No newline at end of file + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# custom added ignores below +bundle +dist +node_modules +TODO +.envrc diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..4a4d77e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,141 @@ +# Taken from https://github.com/github/gitignore/blob/main/Node.gitignore +# and added custom ignores + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# custom added ignores below +bundle +dist +node_modules +TODO +# prettierignore specific +.yarn diff --git a/README.md b/README.md index 5563e8b..541f129 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # Agent > Agent é um bot privado, desenvolvido com Nodejs e Discord.js, com o propósito de automatizar tarefas dentro de um servidor de Discord chamado ["Elite Penguin Force"](https://discord.gg/epf). O servidor reúne administradores, moderadores e desenvolvedores de grandes servidores brasileiros, juntos, formamos uma comunidade onde compartilhamos experiências de administração, ferramentas, metodologias, serviços e muito mais. @@ -35,32 +34,49 @@ MONGOURL='' OFFTOPIC_WEBHOOK='' ``` -É importante também configurar o arquivo que está localizado no caminho `src/config.js`, caso contrário sua aplicação irá crashar instantâneamente. +É importante também configurar o arquivo que está localizado no caminho `src/config.ts`, caso contrário sua aplicação irá crashar instantâneamente. ```js -module.exports = { - devID: "", // ID para cargo de desenvolvedor - master: "", // ID do cargo de Penguin Master - guard: "", // ID do cargo de Penguin Guard - agent: "", // ID do Bot - guild: "", // ID do servidor - aproveChannel: '', // ID do canal onde será enviado os formulários que serão aprovados - formChannel: '', // ID do canal onde será enviado o botão para fazer os formulários - levels: ['', '', ''], // IDs dos cargos de Moderador, Administrador e Dono, respectivamente - logs: '', // ID do canal de log - membersForRole: 5, // Quantidade mínima de membros registrados em uma staff para ser criado o cargo do servidor - serversChannel: '', // ID do canal onde serão enviados as embeds contendo todos os servidores registrados - formChannelData: { - developerFormEmoji: '', // Emoji do botão de formulário de desenvolvedor - guildFormEmoji: '', // Emoji do botão de formulário de novo servidor - developerFormBanner: '', // Url do banner da embed do formulário de devenvolvedor - guildFormBanner: '', // Url do banner da embed do formulário de servidor - developerFormColor: 0xE18002, // Cor do embed de formulário de desenvolvedor - guildFormColor: 0x66f392, // Cor do embed de formulário de novo servidor +export default { + ids: { + devs: ["...", "..."], // Id dos desenvolvedores do bot + roles: { + dev: "...", // Id do cargo de desenvolvedor + master: "...", // Id do cargo de master + guard: "...", // Id do cargo de guard + mod: "...", // Id do cargo de mod + admin: "...", // Id do cargo de admin + owner: "...", // Id do cargo de owner + guildsDiv: "...", // Id do cargo divisor entre cargos de servidor e os demais cargos + guest: "...", // Id do cargo de guest + }, + channels: { + approve: "...", // Id do canal de aprovação + serversList: "...", // Id do canal de lista de servidores + suggestions: "...", // Id do canal de sugestões + changeLog: "...", // Id do canal de logs + }, + agent: "...", // Id do agent + guild: "...", // Id do servidor + tags: { + pending: "...", // Id da tag de sugestões pendentes + }, + }, + minMembersToCreateGuildRole: 5, // Quantidade mínima de membros minimos para criar o cargo de servidor + forms: { + dev: { + channelId: "...", // Id do canal de formulário de desenvolvedor + emoji: "...", // Emoji do formulário de desenvolvedor + bannerURL: "...", // URL do banner do formulário de desenvolvedor + color: 0xe18002, // Cor do formulário de desenvolvedor + }, + guild: { + channelId: "...", // Id do canal de formulário de servidor + emoji: "...", // Emoji do formulário de servidor + bannerURL: "...", // URL do banner do formulário de servidor + color: 0x66f392, // Cor do formulário de servidor }, - serversDivRole: '', // Id do cargo imediatamente abaixo dos cargos de servidores - suggestionsChannel: '', // Id do canal de sugestões - pendingTag: '', // Id da tag de sugestão pendente + }, }; ``` diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..960c7f5 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,49 @@ +import tsPlugin from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; + +export default [ + { + files: ["**/*.ts"], + plugins: { + "@typescript-eslint": tsPlugin, + }, + languageOptions: { + parser: tsParser, + ecmaVersion: 5, + sourceType: "script", + parserOptions: { + project: true, + tsconfigRootDir: "./", + }, + }, + ignores: [ + "node_modules", + "dist", + "logs", + "*.log", + "*.tsbuildinfo", + ".env", + ".env.*", + ], + rules: { + ...tsPlugin.configs.recommended.rules, + "no-cond-assign": ["error", "always"], + eqeqeq: ["error"], + "no-constant-binary-expression": "error", + curly: "error", + "default-case": "error", + "default-case-last": "error", + "no-constant-condition": "error", + "no-duplicate-imports": "error", + "no-fallthrough": "error", + "use-isnan": "error", + "no-loss-of-precision": "error", + "no-promise-executor-return": "error", + "no-useless-escape": "error", + "prefer-object-spread": "error", + "prefer-spread": "error", + "no-empty": "error", + "no-useless-catch": "error", + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index 7179681..bd3b6ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1794 +1,1767 @@ { "name": "agent", - "version": "1.13.21", - "lockfileVersion": 2, + "version": "2.0.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "agent", - "version": "1.13.21", + "version": "2.0.0", "license": "GPL-3.0", "dependencies": { - "@discordjs/rest": "^1.0.1", - "chalk": "^4.1.2", - "discord-api-types": "^0.37.1", - "discord.js": "^14.14.1", - "mongoose": "^6.7.0" + "discord.js": "^14.16.3", + "mongoose": "^8.9.2" + }, + "devDependencies": { + "@types/node": "20.17.9", + "@typescript-eslint/eslint-plugin": "^8.18.1", + "@typescript-eslint/parser": "^8.18.1", + "eslint": "^9.17.0", + "eslint-config-prettier": "9.1.0", + "nodemon": "3.1.7", + "prettier": "3.4.1", + "typescript": "5.7.2" + }, + "engines": { + "node": ">=20.0.6" } }, - "node_modules/@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "optional": true, - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" + "node_modules/@discordjs/builders": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.9.0.tgz", + "integrity": "sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/formatters": "^0.5.0", + "@discordjs/util": "^1.1.1", + "@sapphire/shapeshift": "^4.0.0", + "discord-api-types": "0.37.97", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.4", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@aws-crypto/crc32/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "node_modules/@discordjs/builders/node_modules/discord-api-types": { + "version": "0.37.97", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", + "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", + "license": "MIT" }, - "node_modules/@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "optional": true, - "dependencies": { - "tslib": "^1.11.1" + "node_modules/@discordjs/collection": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.11.0" } }, - "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "optional": true, + "node_modules/@discordjs/formatters": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.5.0.tgz", + "integrity": "sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==", + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "discord-api-types": "0.37.97" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "node_modules/@discordjs/formatters/node_modules/discord-api-types": { + "version": "0.37.97", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", + "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", + "license": "MIT" }, - "node_modules/@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "optional": true, - "dependencies": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" + "node_modules/@discordjs/rest": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.0.tgz", + "integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/collection": "^2.1.1", + "@discordjs/util": "^1.1.1", + "@sapphire/async-queue": "^1.5.3", + "@sapphire/snowflake": "^3.5.3", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "0.37.97", + "magic-bytes.js": "^1.10.0", + "tslib": "^2.6.3", + "undici": "6.19.8" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "optional": true, - "dependencies": { - "tslib": "^1.11.1" + "node_modules/@discordjs/rest/node_modules/discord-api-types": { + "version": "0.37.97", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz", + "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==", + "license": "MIT" + }, + "node_modules/@discordjs/util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", + "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "node_modules/@discordjs/ws": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz", + "integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/collection": "^2.1.0", + "@discordjs/rest": "^2.3.0", + "@discordjs/util": "^1.1.0", + "@sapphire/async-queue": "^1.5.2", + "@types/ws": "^8.5.10", + "@vladfrangu/async_event_emitter": "^2.2.4", + "discord-api-types": "0.37.83", + "tslib": "^2.6.2", + "ws": "^8.16.0" + }, + "engines": { + "node": ">=16.11.0" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } }, - "node_modules/@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "optional": true, - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@aws-crypto/util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true + "node_modules/@discordjs/ws/node_modules/discord-api-types": { + "version": "0.37.83", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz", + "integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==", + "license": "MIT" }, - "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.474.0.tgz", - "integrity": "sha512-vdO5eiJI8VnIKku/RNkj49MR4PnRFtSkIFGFRRuMIx64TiduHaA7cJfj43Kpw68/Gq1HS4fuoj/Y41xm0QZKdg==", - "optional": true, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.474.0", - "@aws-sdk/core": "3.474.0", - "@aws-sdk/credential-provider-node": "3.474.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-signing": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=14.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.474.0.tgz", - "integrity": "sha512-6toUmQUIHkDM/P2/nyLEO/mcWOIPByTlegqX9VCHhYh9Fs5MDT2nit7I6fZzBjZjB5oVTwKjbzgxae9cE3bhqw==", - "optional": true, - "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.474.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.474.0.tgz", - "integrity": "sha512-qPPMbrDVAUJgYiFWVewFG7dg0VyMfuGNNK4IC1nZr0eXejUTbdm8cio6IZ8OkWtK+A+L+wx1vX5686WYVgQ0dQ==", - "optional": true, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.474.0", - "@aws-sdk/credential-provider-node": "3.474.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/core": "^1.1.0", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/core": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.474.0.tgz", - "integrity": "sha512-eVRdeB+AoTNSzfc4viHfr0jfkHujSlf4ToExJtTuxS1wlgmIyyxRNrVKxbf0K78YK/TXRsRlJPoS5QCD5h1S2w==", - "optional": true, + "node_modules/@eslint/config-array/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, + "license": "MIT", "dependencies": { - "@smithy/core": "^1.1.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/signature-v4": "^2.0.0", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.474.0.tgz", - "integrity": "sha512-YD0AHBSXlGXv5cU7BZ0oXWUNCnhW4pyun/M9XsDlU9ptdJDyYa860bsfM6JCs3TwMqMOc+OXOzJMJdsibmR9Pg==", - "optional": true, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "@aws-sdk/client-cognito-identity": "3.474.0", - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=14.0.0" + "node": "*" } }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.468.0.tgz", - "integrity": "sha512-k/1WHd3KZn0EQYjadooj53FC0z24/e4dUZhbSKTULgmxyO62pwh9v3Brvw4WRa/8o2wTffU/jo54tf4vGuP/ZA==", - "optional": true, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.468.0.tgz", - "integrity": "sha512-pUF+gmeCr4F1De69qEsWgnNeF7xzlLcjiGcbpO6u9k6NQdRR7Xr3wTQnQt1+3MgoIdbgoXpCfQYNZ4LfX6B/sA==", - "optional": true, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/util-stream": "^2.0.23", - "tslib": "^2.5.0" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.474.0.tgz", - "integrity": "sha512-3Y2fHI4ZCNjdOO47Vh/xBgLXOrKm3KwBkYkBKKT2g02FUGNT8NLjJg8WBo3D4RQX2h34qx4mtW5nTY6YcGP80Q==", - "optional": true, + "node_modules/@eslint/eslintrc/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, + "license": "MIT", "dependencies": { - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.474.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.474.0.tgz", - "integrity": "sha512-3OVVVGnb8Ru5hWeeHkg76YZT5mrufweIiWr6ge5zn7FYxc7WkyqIJ0XehqUqG5VQfaYhqh7uq/zmk8OE2B04lQ==", - "optional": true, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-ini": "3.474.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.474.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=14.0.0" + "node": "*" } }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.468.0.tgz", - "integrity": "sha512-OYSn1A/UsyPJ7Z8Q2cNhTf55O36shPmSsvOfND04nSfu1nPaR+VUvvsP7v+brhGpwC/GAKTIdGAo4blH31BS6A==", - "optional": true, - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.474.0.tgz", - "integrity": "sha512-ik4rzhQtcRLSHB/MLQfi/dSpILxPd3zITb79DIEnqT3gpZRNjoARkZ3Hi68pujkU2530NYf8NcFwLCWoV1hS7Q==", - "optional": true, - "dependencies": { - "@aws-sdk/client-sso": "3.474.0", - "@aws-sdk/token-providers": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.468.0.tgz", - "integrity": "sha512-rexymPmXjtkwCPfhnUq3EjO1rSkf39R4Jz9CqiM7OsqK2qlT5Y/V3gnMKn0ZMXsYaQOMfM3cT5xly5R+OKDHlw==", - "optional": true, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "levn": "^0.4.1" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@aws-sdk/credential-providers": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.474.0.tgz", - "integrity": "sha512-n4NG2Y1kgt6cTT7QZhGgTAbzw83tTDoKSNAcHDzLNKRmtEAf3aSX/lBMrBAovsrDWr6FF8saKEHfEv1XhP8ewA==", - "optional": true, - "dependencies": { - "@aws-sdk/client-cognito-identity": "3.474.0", - "@aws-sdk/client-sso": "3.474.0", - "@aws-sdk/client-sts": "3.474.0", - "@aws-sdk/credential-provider-cognito-identity": "3.474.0", - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-http": "3.468.0", - "@aws-sdk/credential-provider-ini": "3.474.0", - "@aws-sdk/credential-provider-node": "3.474.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.474.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": ">=18.18.0" } }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.468.0.tgz", - "integrity": "sha512-gwQ+/QhX+lhof304r6zbZ/V5l5cjhGRxLL3CjH1uJPMcOAbw9wUlMdl+ibr8UwBZ5elfKFGiB1cdW/0uMchw0w==", - "optional": true, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.18.0" } }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.468.0.tgz", - "integrity": "sha512-X5XHKV7DHRXI3f29SAhJPe/OxWRFgDWDMMCALfzhmJfCi6Jfh0M14cJKoC+nl+dk9lB+36+jKjhjETZaL2bPlA==", - "optional": true, - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.468.0.tgz", - "integrity": "sha512-vch9IQib2Ng9ucSyRW2eKNQXHUPb5jUPCLA5otTW/8nGjcOU37LxQG4WrxO7uaJ9Oe8hjHO+hViE3P0KISUhtA==", - "optional": true, - "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@aws-sdk/middleware-signing": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.468.0.tgz", - "integrity": "sha512-s+7fSB1gdnnTj5O0aCCarX3z5Vppop8kazbNSZADdkfHIDWCN80IH4ZNjY3OWqaAz0HmR4LNNrovdR304ojb4Q==", - "optional": true, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "sparse-bitfield": "^3.0.3" } }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.470.0.tgz", - "integrity": "sha512-s0YRGgf4fT5KwwTefpoNUQfB5JghzXyvmPfY1QuFEMeVQNxv0OPuydzo3rY2oXPkZjkulKDtpm5jzIHwut75hA==", - "optional": true, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.470.0.tgz", - "integrity": "sha512-C1o1J06iIw8cyAAOvHqT4Bbqf+PgQ/RDlSyjt2gFfP2OovDpc2o2S90dE8f8iZdSGpg70N5MikT1DBhW9NbhtQ==", - "optional": true, - "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.470.0.tgz", - "integrity": "sha512-rzxnJxEUJiV69Cxsf0AHXTqJqTACITwcSH/PL4lWP4uvtzdrzSi3KA3u2aWHWpOcdE6+JFvdICscsbBSo3/TOg==", - "optional": true, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@aws-sdk/types": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.468.0.tgz", - "integrity": "sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=v14.0.0", + "npm": ">=7.0.0" } }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.470.0.tgz", - "integrity": "sha512-6N6VvPCmu+89p5Ez/+gLf+X620iQ9JpIs8p8ECZiCodirzFOe8NC1O2S7eov7YiG9IHSuodqn/0qNq+v+oLe0A==", - "optional": true, + "node_modules/@sapphire/shapeshift": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz", + "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/util-endpoints": "^1.0.7", - "tslib": "^2.5.0" + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" }, "engines": { - "node": ">=14.0.0" + "node": ">=v16" } }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.465.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz", - "integrity": "sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz", + "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==", + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=v14.0.0", + "npm": ">=7.0.0" } }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.468.0.tgz", - "integrity": "sha512-OJyhWWsDEizR3L+dCgMXSUmaCywkiZ7HSbnQytbeKGwokIhD69HTiJcibF/sgcM5gk4k3Mq3puUhGnEZ46GIig==", - "optional": true, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.17.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", + "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" + "undici-types": "~6.19.2" } }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.470.0.tgz", - "integrity": "sha512-QxsZ9iVHcBB/XRdYvwfM5AMvNp58HfqkIrH88mY0cmxuvtlIGDfWjczdDrZMJk9y0vIq+cuoCHsGXHu7PyiEAQ==", - "optional": true, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", "dependencies": { - "@aws-sdk/types": "3.468.0", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "@types/webidl-conversions": "*" } }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "optional": true, + "node_modules/@types/ws": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "license": "MIT", "dependencies": { - "tslib": "^2.3.1" + "@types/node": "*" } }, - "node_modules/@discordjs/builders": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz", - "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==", - "dependencies": { - "@discordjs/formatters": "^0.3.3", - "@discordjs/util": "^1.0.2", - "@sapphire/shapeshift": "^3.9.3", - "discord-api-types": "0.37.61", - "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.3", - "tslib": "^2.6.2" + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz", + "integrity": "sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/type-utils": "8.18.1", + "@typescript-eslint/utils": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=16.11.0" - } - }, - "node_modules/@discordjs/builders/node_modules/@discordjs/util": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", - "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", - "engines": { - "node": ">=16.11.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@discordjs/collection": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", - "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==", + "node_modules/@typescript-eslint/parser": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.1.tgz", + "integrity": "sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/typescript-estree": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", + "debug": "^4.3.4" + }, "engines": { - "node": ">=16.11.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@discordjs/formatters": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz", - "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz", + "integrity": "sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==", + "dev": true, + "license": "MIT", "dependencies": { - "discord-api-types": "0.37.61" + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1" }, "engines": { - "node": ">=16.11.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@discordjs/rest": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.7.0.tgz", - "integrity": "sha512-r2HzmznRIo8IDGYBWqQfkEaGN1LrFfWQd3dSyC4tOpMU8nuVvFUEw6V/lwnG44jyOq+vgyDny2fxeUDMt9I4aQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz", + "integrity": "sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@discordjs/collection": "^1.5.0", - "@discordjs/util": "^0.2.0", - "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.4.0", - "discord-api-types": "^0.37.37", - "file-type": "^18.2.1", - "tslib": "^2.5.0", - "undici": "^5.21.0" + "@typescript-eslint/typescript-estree": "8.18.1", + "@typescript-eslint/utils": "8.18.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=16.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@discordjs/util": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz", - "integrity": "sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg==", + "node_modules/@typescript-eslint/types": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.1.tgz", + "integrity": "sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=16.9.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@discordjs/ws": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz", - "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==", - "dependencies": { - "@discordjs/collection": "^2.0.0", - "@discordjs/rest": "^2.1.0", - "@discordjs/util": "^1.0.2", - "@sapphire/async-queue": "^1.5.0", - "@types/ws": "^8.5.9", - "@vladfrangu/async_event_emitter": "^2.2.2", - "discord-api-types": "0.37.61", - "tslib": "^2.6.2", - "ws": "^8.14.2" + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz", + "integrity": "sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=16.11.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@discordjs/ws/node_modules/@discordjs/collection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", - "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "node_modules/@typescript-eslint/utils": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.1.tgz", + "integrity": "sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/typescript-estree": "8.18.1" + }, "engines": { - "node": ">=18" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/@discordjs/ws/node_modules/@discordjs/rest": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", - "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz", + "integrity": "sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@discordjs/collection": "^2.0.0", - "@discordjs/util": "^1.0.2", - "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.5.1", - "@vladfrangu/async_event_emitter": "^2.2.2", - "discord-api-types": "0.37.61", - "magic-bytes.js": "^1.5.0", - "tslib": "^2.6.2", - "undici": "5.27.2" + "@typescript-eslint/types": "8.18.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">=16.11.0" - } - }, - "node_modules/@discordjs/ws/node_modules/@discordjs/util": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", - "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", - "engines": { - "node": ">=16.11.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", - "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14" - } - }, - "node_modules/@mongodb-js/saslprep": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", - "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", - "optional": true, - "dependencies": { - "sparse-bitfield": "^3.0.3" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@sapphire/async-queue": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", - "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", + "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==", + "license": "MIT", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" } }, - "node_modules/@sapphire/shapeshift": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.5.tgz", - "integrity": "sha512-AGdHe+51gF7D3W8hBfuSFLBocURDCXVQczScTHXDS3RpNjNgrktIx/amlz5y8nHhm8SAdFt/X8EF8ZSfjJ0tnA==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21" + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=v18" + "node": ">=0.4.0" } }, - "node_modules/@sapphire/snowflake": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", - "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==", - "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@smithy/abort-controller": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.15.tgz", - "integrity": "sha512-JkS36PIS3/UCbq/MaozzV7jECeL+BTt4R75bwY8i+4RASys4xOyUS1HsRyUNSqUXFP4QyCz5aNnh3ltuaxv+pw==", - "optional": true, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=14.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@smithy/config-resolver": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.21.tgz", - "integrity": "sha512-rlLIGT+BeqjnA6C2FWumPRJS1UW07iU5ZxDHtFuyam4W65gIaOFMjkB90ofKCIh+0mLVQrQFrl/VLtQT/6FWTA==", - "optional": true, + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@smithy/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.1.0.tgz", - "integrity": "sha512-k1zaT5S4K0bG67Q5TmPZ6PdWNQBTMQErChuDvTi+NTx21kKDt+/4YRidsK6nDbHizN6fn1bafUxrougZdKrpxA==", - "optional": true, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@smithy/credential-provider-imds": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.4.tgz", - "integrity": "sha512-cwPJN1fa1YOQzhBlTXRavABEYRRchci1X79QRwzaNLySnIMJfztyv1Zkst0iZPLMnpn8+CnHu3wOHS11J5Dr3A==", - "optional": true, - "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/property-provider": "^2.0.16", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "tslib": "^2.5.0" - }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "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, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@smithy/eventstream-codec": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.15.tgz", - "integrity": "sha512-crjvz3j1gGPwA0us6cwS7+5gAn35CTmqu/oIxVbYJo2Qm/sGAye6zGJnMDk3BKhWZw5kcU1G4MxciTkuBpOZPg==", - "optional": true, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" + "balanced-match": "^1.0.0" } }, - "node_modules/@smithy/fetch-http-handler": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.3.1.tgz", - "integrity": "sha512-6MNk16fqb8EwcYY8O8WxB3ArFkLZ2XppsSNo1h7SQcFdDDwIumiJeO6wRzm7iB68xvsOQzsdQKbdtTieS3hfSQ==", - "optional": true, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/protocol-http": "^3.0.11", - "@smithy/querystring-builder": "^2.0.15", - "@smithy/types": "^2.7.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@smithy/hash-node": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.17.tgz", - "integrity": "sha512-Il6WuBcI1nD+e2DM7tTADMf01wEPGK8PAhz4D+YmDUVaoBqlA+CaH2uDJhiySifmuKBZj748IfygXty81znKhw==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.7.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, + "node_modules/bson": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": ">=16.20.1" } }, - "node_modules/@smithy/invalid-dependency": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.15.tgz", - "integrity": "sha512-dlEKBFFwVfzA5QroHlBS94NpgYjXhwN/bFfun+7w3rgxNvVy79SK0w05iGc7UAeC5t+D7gBxrzdnD6hreZnDVQ==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.5.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@smithy/middleware-content-length": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.17.tgz", - "integrity": "sha512-OyadvMcKC7lFXTNBa8/foEv7jOaqshQZkjWS9coEXPRZnNnihU/Ls+8ZuJwGNCOrN2WxXZFmDWhegbnM4vak8w==", - "optional": true, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/@smithy/middleware-endpoint": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.3.tgz", - "integrity": "sha512-nYfxuq0S/xoAjdLbyn1ixeVB6cyH9wYCMtbbOCpcCRYR5u2mMtqUtVjjPAZ/DIdlK3qe0tpB0Q76szFGNuz+kQ==", - "optional": true, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/middleware-serde": "^2.0.15", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/shared-ini-file-loader": "^2.2.7", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">= 6" } }, - "node_modules/@smithy/middleware-retry": { - "version": "2.0.24", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.24.tgz", - "integrity": "sha512-q2SvHTYu96N7lYrn3VSuX3vRpxXHR/Cig6MJpGWxd0BWodUQUWlKvXpWQZA+lTaFJU7tUvpKhRd4p4MU3PbeJg==", - "optional": true, + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/protocol-http": "^3.0.11", - "@smithy/service-error-classification": "^2.0.8", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-retry": "^2.0.8", - "tslib": "^2.5.0", - "uuid": "^8.3.2" + "color-name": "~1.1.4" }, "engines": { - "node": ">=14.0.0" + "node": ">=7.0.0" } }, - "node_modules/@smithy/middleware-serde": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.15.tgz", - "integrity": "sha512-FOZRFk/zN4AT4wzGuBY+39XWe+ZnCFd0gZtyw3f9Okn2CJPixl9GyWe98TIaljeZdqWkgrzGyPre20AcW2UMHQ==", - "optional": true, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">= 8" } }, - "node_modules/@smithy/middleware-stack": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.9.tgz", - "integrity": "sha512-bCB5dUtGQ5wh7QNL2ELxmDc6g7ih7jWU3Kx6MYH1h4mZbv9xL3WyhKHojRltThCB1arLPyTUFDi+x6fB/oabtA==", - "optional": true, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@smithy/node-config-provider": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.8.tgz", - "integrity": "sha512-+w26OKakaBUGp+UG+dxYZtFb5fs3tgHg3/QrRrmUZj+rl3cIuw840vFUXX35cVPTUCQIiTqmz7CpVF7+hdINdQ==", - "optional": true, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/discord-api-types": { + "version": "0.37.100", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.100.tgz", + "integrity": "sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==", + "license": "MIT" + }, + "node_modules/discord.js": { + "version": "14.16.3", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.16.3.tgz", + "integrity": "sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==", + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^2.0.16", - "@smithy/shared-ini-file-loader": "^2.2.7", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "@discordjs/builders": "^1.9.0", + "@discordjs/collection": "1.5.3", + "@discordjs/formatters": "^0.5.0", + "@discordjs/rest": "^2.4.0", + "@discordjs/util": "^1.1.1", + "@discordjs/ws": "1.1.1", + "@sapphire/snowflake": "3.5.3", + "discord-api-types": "0.37.100", + "fast-deep-equal": "3.1.3", + "lodash.snakecase": "4.1.1", + "tslib": "^2.6.3", + "undici": "6.19.8" }, "engines": { - "node": ">=14.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" } }, - "node_modules/@smithy/node-http-handler": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.2.1.tgz", - "integrity": "sha512-8iAKQrC8+VFHPAT8pg4/j6hlsTQh+NKOWlctJBrYtQa4ExcxX7aSg3vdQ2XLoYwJotFUurg/NLqFCmZaPRrogw==", - "optional": true, - "dependencies": { - "@smithy/abort-controller": "^2.0.15", - "@smithy/protocol-http": "^3.0.11", - "@smithy/querystring-builder": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/@smithy/property-provider": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.16.tgz", - "integrity": "sha512-28Ky0LlOqtEjwg5CdHmwwaDRHcTWfPRzkT6HrhwOSRS2RryAvuDfJrZpM+BMcrdeCyEg1mbcgIMoqTla+rdL8Q==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "engines": { - "node": ">=14.0.0" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/@smithy/protocol-http": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.11.tgz", - "integrity": "sha512-3ziB8fHuXIRamV/akp/sqiWmNPR6X+9SB8Xxnozzj+Nq7hSpyKdFHd1FLpBkgfGFUTzzcBJQlDZPSyxzmdcx5A==", - "optional": true, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/querystring-builder": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.15.tgz", - "integrity": "sha512-e1q85aT6HutvouOdN+dMsN0jcdshp50PSCvxDvo6aIM57LqeXimjfONUEgfqQ4IFpYWAtVixptyIRE5frMp/2A==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.7.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/querystring-parser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.15.tgz", - "integrity": "sha512-jbBvoK3cc81Cj1c1TH1qMYxNQKHrYQ2DoTntN9FBbtUWcGhc+T4FP6kCKYwRLXyU4AajwGIZstvNAmIEgUUNTQ==", - "optional": true, + "node_modules/eslint/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, + "license": "MIT", "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/service-error-classification": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.8.tgz", - "integrity": "sha512-jCw9+005im8tsfYvwwSc4TTvd29kXRFkH9peQBg5R/4DD03ieGm6v6Hpv9nIAh98GwgYg1KrztcINC1s4o7/hg==", - "optional": true, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/types": "^2.7.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=14.0.0" + "node": "*" } }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.7.tgz", - "integrity": "sha512-0Qt5CuiogIuvQIfK+be7oVHcPsayLgfLJGkPlbgdbl0lD28nUKu4p11L+UG3SAEsqc9UsazO+nErPXw7+IgDpQ==", - "optional": true, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/signature-v4": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.18.tgz", - "integrity": "sha512-SJRAj9jT/l9ocm8D0GojMbnA1sp7I4JeStOQ4lEXI8A5eHE73vbjlzlqIFB7cLvIgau0oUl4cGVpF9IGCrvjlw==", - "optional": true, - "dependencies": { - "@smithy/eventstream-codec": "^2.0.15", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/smithy-client": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.18.tgz", - "integrity": "sha512-7FqdbaJiVaHJDD9IfDhmzhSDbpjyx+ZsfdYuOpDJF09rl8qlIAIlZNoSaflKrQ3cEXZN2YxGPaNWGhbYimyIRQ==", - "optional": true, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@smithy/middleware-stack": "^2.0.9", - "@smithy/types": "^2.7.0", - "@smithy/util-stream": "^2.0.23", - "tslib": "^2.5.0" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=0.10" } }, - "node_modules/@smithy/types": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.7.0.tgz", - "integrity": "sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw==", - "optional": true, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "tslib": "^2.5.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=4.0" } }, - "node_modules/@smithy/url-parser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.15.tgz", - "integrity": "sha512-sADUncUj9rNbOTrdDGm4EXlUs0eQ9dyEo+V74PJoULY4jSQxS+9gwEgsPYyiu8PUOv16JC/MpHonOgqP/IEDZA==", - "optional": true, - "dependencies": { - "@smithy/querystring-parser": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "optional": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=14.0.0" + "node": ">=0.10.0" } }, - "node_modules/@smithy/util-body-length-browser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.1.tgz", - "integrity": "sha512-NXYp3ttgUlwkaug4bjBzJ5+yIbUbUx8VsSLuHZROQpoik+gRkIBeEG9MPVYfvPNpuXb/puqodeeUXcKFe7BLOQ==", - "optional": true, - "dependencies": { - "tslib": "^2.5.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==", + "license": "MIT" }, - "node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "optional": true, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.5.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=14.0.0" + "node": ">=8.6.0" } }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=14.0.0" + "node": ">= 6" } }, - "node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, + "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, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" + "reusify": "^1.0.4" } }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.22.tgz", - "integrity": "sha512-qcF20IHHH96FlktvBRICDXDhLPtpVmtksHmqNGtotb9B0DYWXsC6jWXrkhrrwF7tH26nj+npVTqh9isiFV1gdA==", - "optional": true, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/property-provider": "^2.0.16", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">= 10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.29.tgz", - "integrity": "sha512-+uG/15VoUh6JV2fdY9CM++vnSuMQ1VKZ6BdnkUM7R++C/vLjnlg+ToiSR1FqKZbMmKBXmsr8c/TsDWMAYvxbxQ==", - "optional": true, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/config-resolver": "^2.0.21", - "@smithy/credential-provider-imds": "^2.1.4", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/property-provider": "^2.0.16", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 10.0.0" + "node": ">=8" } }, - "node_modules/@smithy/util-endpoints": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.7.tgz", - "integrity": "sha512-Q2gEind3jxoLk6hdKWyESMU7LnXz8aamVwM+VeVjOYzYT1PalGlY/ETa48hv2YpV4+YV604y93YngyzzzQ4IIA==", - "optional": true, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 14.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.5.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=14.0.0" + "node": ">=16" } }, - "node_modules/@smithy/util-middleware": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.8.tgz", - "integrity": "sha512-qkvqQjM8fRGGA8P2ydWylMhenCDP8VlkPn8kiNuFEaFz9xnUKC2irfqsBSJrfrOB9Qt6pQsI58r3zvvumhFMkw==", + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", "optional": true, - "dependencies": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - }, + "os": [ + "darwin" + ], "engines": { - "node": ">=14.0.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/@smithy/util-retry": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.8.tgz", - "integrity": "sha512-cQTPnVaVFMjjS6cb44WV2yXtHVyXDC5icKyIbejMarJEApYeJWpBU3LINTxHqp/tyLI+MZOUdosr2mZ3sdziNg==", - "optional": true, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/service-error-classification": "^2.0.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 14.0.0" + "node": ">=10.13.0" } }, - "node_modules/@smithy/util-stream": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.23.tgz", - "integrity": "sha512-OJMWq99LAZJUzUwTk+00plyxX3ESktBaGPhqNIEVab+53gLULiWN9B/8bRABLg0K6R6Xg4t80uRdhk3B/LZqMQ==", - "optional": true, - "dependencies": { - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/types": "^2.7.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" }, - "node_modules/@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", - "optional": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.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==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, - "node_modules/@types/node": { - "version": "18.6.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz", - "integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==" - }, - "node_modules/@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" - }, - "node_modules/@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "dependencies": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", - "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", - "dependencies": { - "@types/node": "*" + "node": ">=8" } }, - "node_modules/@vladfrangu/async_event_emitter": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz", - "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=v14.0.0", - "npm": ">=7.0.0" + "node": ">= 4" } }, - "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==", + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "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/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "optional": true - }, - "node_modules/bson": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", - "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", - "dependencies": { - "buffer": "^5.6.0" - }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "node": ">=0.8.19" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "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" - }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=7.0.0" + "node": ">=0.10.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/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/debug/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/discord-api-types": { - "version": "0.37.61", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", - "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" - }, - "node_modules/discord.js": { - "version": "14.14.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz", - "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==", - "dependencies": { - "@discordjs/builders": "^1.7.0", - "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.3.3", - "@discordjs/rest": "^2.1.0", - "@discordjs/util": "^1.0.2", - "@discordjs/ws": "^1.0.2", - "@sapphire/snowflake": "3.5.1", - "@types/ws": "8.5.9", - "discord-api-types": "0.37.61", - "fast-deep-equal": "3.1.3", - "lodash.snakecase": "4.1.1", - "tslib": "2.6.2", - "undici": "5.27.2", - "ws": "8.14.2" - }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=16.11.0" + "node": ">=0.12.0" } }, - "node_modules/discord.js/node_modules/@discordjs/rest": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", - "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", "dependencies": { - "@discordjs/collection": "^2.0.0", - "@discordjs/util": "^1.0.2", - "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.5.1", - "@vladfrangu/async_event_emitter": "^2.2.2", - "discord-api-types": "0.37.61", - "magic-bytes.js": "^1.5.0", - "tslib": "^2.6.2", - "undici": "5.27.2" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=16.11.0" - } + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" }, - "node_modules/discord.js/node_modules/@discordjs/rest/node_modules/@discordjs/collection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", - "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==", + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=12.0.0" } }, - "node_modules/discord.js/node_modules/@discordjs/util": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", - "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==", - "engines": { - "node": ">=16.11.0" + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" } }, - "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-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "optional": true, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, - "bin": { - "fxparser": "src/cli/cli.js" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/file-type": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.1.tgz", - "integrity": "sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", "dependencies": { - "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0", - "token-types": "^5.0.1" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=14.16" + "node": ">=10" }, "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "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/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" - }, - "node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", - "engines": { - "node": ">=12.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "license": "MIT" }, "node_modules/magic-bytes.js": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.7.0.tgz", - "integrity": "sha512-YzVU2+/hrjwx8xcgAw+ffNq3jkactpj+f1iSL4LonrFKhvnwDzHSqtFdk/MMRP53y9ScouJ7cKEnqYsJwsHoYA==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==", + "license": "MIT" }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/mongodb": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.1.tgz", - "integrity": "sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", + "license": "Apache-2.0", "dependencies": { - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", + "mongodb-connection-string-url": "^3.0.0" }, "engines": { - "node": ">=12.9.0" + "node": ">=16.20.1" }, - "optionalDependencies": { - "@aws-sdk/credential-providers": "^3.186.0", - "@mongodb-js/saslprep": "^1.1.0" + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } } }, "node_modules/mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "license": "Apache-2.0", "dependencies": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" } }, "node_modules/mongoose": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.12.3.tgz", - "integrity": "sha512-MNJymaaXali7w7rHBxVUoQ3HzHHMk/7I/+yeeoSa4rUzdjZwIWQznBNvVgc0A8ghuJwsuIkb5LyLV6gSjGjWyQ==", - "dependencies": { - "bson": "^4.7.2", - "kareem": "2.5.1", - "mongodb": "4.17.1", + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.2.tgz", + "integrity": "sha512-mLWynmZS1v8HTeMxyLhskQncS1SkrjW1eLNuFDYGQMQ/5QrFrxTLNwWXeCRZeKT2lXyaxW8bnJC9AKPT9jYMkw==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.1", + "kareem": "2.6.3", + "mongodb": "~6.12.0", "mpath": "0.9.0", - "mquery": "4.0.3", + "mquery": "5.0.0", "ms": "2.1.3", - "sift": "16.0.1" + "sift": "17.1.3" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.20.1" }, "funding": { "type": "opencollective", @@ -1799,81 +1772,268 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/mquery": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", - "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", "dependencies": { "debug": "4.x" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "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/peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, "engines": { - "node": ">=14.16" + "node": ">=10" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "type": "opencollective", + "url": "https://opencollective.com/nodemon" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/nodemon/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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=4" } }, - "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==", + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 6" + "node": "*" } }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", "dependencies": { - "readable-stream": "^3.6.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "engines": { + "node": ">= 0.8.0" } }, - "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", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", + "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", "url": "https://github.com/sponsors/feross" }, { @@ -1884,78 +2044,150 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, - "node_modules/sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" + "node": ">=10" } }, - "node_modules/sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "optional": true, + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "memory-pager": "^1.0.2" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "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/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "optional": true + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" }, - "node_modules/strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=14.16" + "node": ">=10" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "url": "https://github.com/sponsors/sindresorhus" } }, "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==", + "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1963,92 +2195,178 @@ "node": ">=8" } }, - "node_modules/token-types": { + "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "node": ">=8.0" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" } }, "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "license": "MIT", "dependencies": { - "punycode": "^2.1.1" + "punycode": "^2.3.0" }, "engines": { - "node": ">=12" + "node": ">=14" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, "node_modules/ts-mixer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", - "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", + "license": "MIT" }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, - "node_modules/undici": { - "version": "5.27.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", - "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", "dependencies": { - "@fastify/busboy": "^2.0.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=14.0" + "node": ">= 0.8.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/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "optional": true, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", "bin": { - "uuid": "dist/bin/uuid" + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" } }, "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "license": "MIT", "dependencies": { - "tr46": "^3.0.0", + "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=16" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, "node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -2064,1635 +2382,19 @@ "optional": true } } - } - }, - "dependencies": { - "@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "optional": true, - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "optional": true, - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "optional": true, - "requires": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "optional": true, - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "optional": true, - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } }, - "@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "optional": true, - "requires": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true - } - } - }, - "@aws-sdk/client-cognito-identity": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.474.0.tgz", - "integrity": "sha512-vdO5eiJI8VnIKku/RNkj49MR4PnRFtSkIFGFRRuMIx64TiduHaA7cJfj43Kpw68/Gq1HS4fuoj/Y41xm0QZKdg==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.474.0", - "@aws-sdk/core": "3.474.0", - "@aws-sdk/credential-provider-node": "3.474.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-signing": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sso": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.474.0.tgz", - "integrity": "sha512-6toUmQUIHkDM/P2/nyLEO/mcWOIPByTlegqX9VCHhYh9Fs5MDT2nit7I6fZzBjZjB5oVTwKjbzgxae9cE3bhqw==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.474.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/client-sts": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.474.0.tgz", - "integrity": "sha512-qPPMbrDVAUJgYiFWVewFG7dg0VyMfuGNNK4IC1nZr0eXejUTbdm8cio6IZ8OkWtK+A+L+wx1vX5686WYVgQ0dQ==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/core": "3.474.0", - "@aws-sdk/credential-provider-node": "3.474.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/core": "^1.1.0", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/core": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.474.0.tgz", - "integrity": "sha512-eVRdeB+AoTNSzfc4viHfr0jfkHujSlf4ToExJtTuxS1wlgmIyyxRNrVKxbf0K78YK/TXRsRlJPoS5QCD5h1S2w==", - "optional": true, - "requires": { - "@smithy/core": "^1.1.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/signature-v4": "^2.0.0", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.474.0.tgz", - "integrity": "sha512-YD0AHBSXlGXv5cU7BZ0oXWUNCnhW4pyun/M9XsDlU9ptdJDyYa860bsfM6JCs3TwMqMOc+OXOzJMJdsibmR9Pg==", - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.474.0", - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.468.0.tgz", - "integrity": "sha512-k/1WHd3KZn0EQYjadooj53FC0z24/e4dUZhbSKTULgmxyO62pwh9v3Brvw4WRa/8o2wTffU/jo54tf4vGuP/ZA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-http": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.468.0.tgz", - "integrity": "sha512-pUF+gmeCr4F1De69qEsWgnNeF7xzlLcjiGcbpO6u9k6NQdRR7Xr3wTQnQt1+3MgoIdbgoXpCfQYNZ4LfX6B/sA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/util-stream": "^2.0.23", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.474.0.tgz", - "integrity": "sha512-3Y2fHI4ZCNjdOO47Vh/xBgLXOrKm3KwBkYkBKKT2g02FUGNT8NLjJg8WBo3D4RQX2h34qx4mtW5nTY6YcGP80Q==", - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.474.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.474.0.tgz", - "integrity": "sha512-3OVVVGnb8Ru5hWeeHkg76YZT5mrufweIiWr6ge5zn7FYxc7WkyqIJ0XehqUqG5VQfaYhqh7uq/zmk8OE2B04lQ==", - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-ini": "3.474.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.474.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.468.0.tgz", - "integrity": "sha512-OYSn1A/UsyPJ7Z8Q2cNhTf55O36shPmSsvOfND04nSfu1nPaR+VUvvsP7v+brhGpwC/GAKTIdGAo4blH31BS6A==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.474.0.tgz", - "integrity": "sha512-ik4rzhQtcRLSHB/MLQfi/dSpILxPd3zITb79DIEnqT3gpZRNjoARkZ3Hi68pujkU2530NYf8NcFwLCWoV1hS7Q==", - "optional": true, - "requires": { - "@aws-sdk/client-sso": "3.474.0", - "@aws-sdk/token-providers": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.468.0.tgz", - "integrity": "sha512-rexymPmXjtkwCPfhnUq3EjO1rSkf39R4Jz9CqiM7OsqK2qlT5Y/V3gnMKn0ZMXsYaQOMfM3cT5xly5R+OKDHlw==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/credential-providers": { - "version": "3.474.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.474.0.tgz", - "integrity": "sha512-n4NG2Y1kgt6cTT7QZhGgTAbzw83tTDoKSNAcHDzLNKRmtEAf3aSX/lBMrBAovsrDWr6FF8saKEHfEv1XhP8ewA==", - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.474.0", - "@aws-sdk/client-sso": "3.474.0", - "@aws-sdk/client-sts": "3.474.0", - "@aws-sdk/credential-provider-cognito-identity": "3.474.0", - "@aws-sdk/credential-provider-env": "3.468.0", - "@aws-sdk/credential-provider-http": "3.468.0", - "@aws-sdk/credential-provider-ini": "3.474.0", - "@aws-sdk/credential-provider-node": "3.474.0", - "@aws-sdk/credential-provider-process": "3.468.0", - "@aws-sdk/credential-provider-sso": "3.474.0", - "@aws-sdk/credential-provider-web-identity": "3.468.0", - "@aws-sdk/types": "3.468.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.468.0.tgz", - "integrity": "sha512-gwQ+/QhX+lhof304r6zbZ/V5l5cjhGRxLL3CjH1uJPMcOAbw9wUlMdl+ibr8UwBZ5elfKFGiB1cdW/0uMchw0w==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.468.0.tgz", - "integrity": "sha512-X5XHKV7DHRXI3f29SAhJPe/OxWRFgDWDMMCALfzhmJfCi6Jfh0M14cJKoC+nl+dk9lB+36+jKjhjETZaL2bPlA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.468.0.tgz", - "integrity": "sha512-vch9IQib2Ng9ucSyRW2eKNQXHUPb5jUPCLA5otTW/8nGjcOU37LxQG4WrxO7uaJ9Oe8hjHO+hViE3P0KISUhtA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.468.0.tgz", - "integrity": "sha512-s+7fSB1gdnnTj5O0aCCarX3z5Vppop8kazbNSZADdkfHIDWCN80IH4ZNjY3OWqaAz0HmR4LNNrovdR304ojb4Q==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.470.0.tgz", - "integrity": "sha512-s0YRGgf4fT5KwwTefpoNUQfB5JghzXyvmPfY1QuFEMeVQNxv0OPuydzo3rY2oXPkZjkulKDtpm5jzIHwut75hA==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/region-config-resolver": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.470.0.tgz", - "integrity": "sha512-C1o1J06iIw8cyAAOvHqT4Bbqf+PgQ/RDlSyjt2gFfP2OovDpc2o2S90dE8f8iZdSGpg70N5MikT1DBhW9NbhtQ==", - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/token-providers": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.470.0.tgz", - "integrity": "sha512-rzxnJxEUJiV69Cxsf0AHXTqJqTACITwcSH/PL4lWP4uvtzdrzSi3KA3u2aWHWpOcdE6+JFvdICscsbBSo3/TOg==", - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.468.0", - "@aws-sdk/middleware-logger": "3.468.0", - "@aws-sdk/middleware-recursion-detection": "3.468.0", - "@aws-sdk/middleware-user-agent": "3.470.0", - "@aws-sdk/region-config-resolver": "3.470.0", - "@aws-sdk/types": "3.468.0", - "@aws-sdk/util-endpoints": "3.470.0", - "@aws-sdk/util-user-agent-browser": "3.468.0", - "@aws-sdk/util-user-agent-node": "3.470.0", - "@smithy/config-resolver": "^2.0.21", - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/hash-node": "^2.0.17", - "@smithy/invalid-dependency": "^2.0.15", - "@smithy/middleware-content-length": "^2.0.17", - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/middleware-stack": "^2.0.9", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^3.0.11", - "@smithy/shared-ini-file-loader": "^2.0.6", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-body-length-browser": "^2.0.1", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.22", - "@smithy/util-defaults-mode-node": "^2.0.29", - "@smithy/util-endpoints": "^1.0.7", - "@smithy/util-retry": "^2.0.8", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/types": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.468.0.tgz", - "integrity": "sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.470.0.tgz", - "integrity": "sha512-6N6VvPCmu+89p5Ez/+gLf+X620iQ9JpIs8p8ECZiCodirzFOe8NC1O2S7eov7YiG9IHSuodqn/0qNq+v+oLe0A==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/util-endpoints": "^1.0.7", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-locate-window": { - "version": "3.465.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz", - "integrity": "sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.468.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.468.0.tgz", - "integrity": "sha512-OJyhWWsDEizR3L+dCgMXSUmaCywkiZ7HSbnQytbeKGwokIhD69HTiJcibF/sgcM5gk4k3Mq3puUhGnEZ46GIig==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/types": "^2.7.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.470.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.470.0.tgz", - "integrity": "sha512-QxsZ9iVHcBB/XRdYvwfM5AMvNp58HfqkIrH88mY0cmxuvtlIGDfWjczdDrZMJk9y0vIq+cuoCHsGXHu7PyiEAQ==", - "optional": true, - "requires": { - "@aws-sdk/types": "3.468.0", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } - }, - "@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "optional": true, - "requires": { - "tslib": "^2.3.1" - } - }, - "@discordjs/builders": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.7.0.tgz", - "integrity": "sha512-GDtbKMkg433cOZur8Dv6c25EHxduNIBsxeHrsRoIM8+AwmEZ8r0tEpckx/sHwTLwQPOF3e2JWloZh9ofCaMfAw==", - "requires": { - "@discordjs/formatters": "^0.3.3", - "@discordjs/util": "^1.0.2", - "@sapphire/shapeshift": "^3.9.3", - "discord-api-types": "0.37.61", - "fast-deep-equal": "^3.1.3", - "ts-mixer": "^6.0.3", - "tslib": "^2.6.2" - }, - "dependencies": { - "@discordjs/util": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", - "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==" - } - } - }, - "@discordjs/collection": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz", - "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==" - }, - "@discordjs/formatters": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.3.tgz", - "integrity": "sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==", - "requires": { - "discord-api-types": "0.37.61" - } - }, - "@discordjs/rest": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.7.0.tgz", - "integrity": "sha512-r2HzmznRIo8IDGYBWqQfkEaGN1LrFfWQd3dSyC4tOpMU8nuVvFUEw6V/lwnG44jyOq+vgyDny2fxeUDMt9I4aQ==", - "requires": { - "@discordjs/collection": "^1.5.0", - "@discordjs/util": "^0.2.0", - "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.4.0", - "discord-api-types": "^0.37.37", - "file-type": "^18.2.1", - "tslib": "^2.5.0", - "undici": "^5.21.0" - } - }, - "@discordjs/util": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz", - "integrity": "sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg==" - }, - "@discordjs/ws": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.0.2.tgz", - "integrity": "sha512-+XI82Rm2hKnFwAySXEep4A7Kfoowt6weO6381jgW+wVdTpMS/56qCvoXyFRY0slcv7c/U8My2PwIB2/wEaAh7Q==", - "requires": { - "@discordjs/collection": "^2.0.0", - "@discordjs/rest": "^2.1.0", - "@discordjs/util": "^1.0.2", - "@sapphire/async-queue": "^1.5.0", - "@types/ws": "^8.5.9", - "@vladfrangu/async_event_emitter": "^2.2.2", - "discord-api-types": "0.37.61", - "tslib": "^2.6.2", - "ws": "^8.14.2" - }, - "dependencies": { - "@discordjs/collection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", - "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==" - }, - "@discordjs/rest": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", - "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", - "requires": { - "@discordjs/collection": "^2.0.0", - "@discordjs/util": "^1.0.2", - "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.5.1", - "@vladfrangu/async_event_emitter": "^2.2.2", - "discord-api-types": "0.37.61", - "magic-bytes.js": "^1.5.0", - "tslib": "^2.6.2", - "undici": "5.27.2" - } - }, - "@discordjs/util": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", - "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==" - } - } - }, - "@fastify/busboy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", - "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==" - }, - "@mongodb-js/saslprep": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", - "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, - "@sapphire/async-queue": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", - "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==" - }, - "@sapphire/shapeshift": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.9.5.tgz", - "integrity": "sha512-AGdHe+51gF7D3W8hBfuSFLBocURDCXVQczScTHXDS3RpNjNgrktIx/amlz5y8nHhm8SAdFt/X8EF8ZSfjJ0tnA==", - "requires": { - "fast-deep-equal": "^3.1.3", - "lodash": "^4.17.21" - } - }, - "@sapphire/snowflake": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.1.tgz", - "integrity": "sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==" - }, - "@smithy/abort-controller": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.15.tgz", - "integrity": "sha512-JkS36PIS3/UCbq/MaozzV7jECeL+BTt4R75bwY8i+4RASys4xOyUS1HsRyUNSqUXFP4QyCz5aNnh3ltuaxv+pw==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/config-resolver": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.21.tgz", - "integrity": "sha512-rlLIGT+BeqjnA6C2FWumPRJS1UW07iU5ZxDHtFuyam4W65gIaOFMjkB90ofKCIh+0mLVQrQFrl/VLtQT/6FWTA==", - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - } - }, - "@smithy/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.1.0.tgz", - "integrity": "sha512-k1zaT5S4K0bG67Q5TmPZ6PdWNQBTMQErChuDvTi+NTx21kKDt+/4YRidsK6nDbHizN6fn1bafUxrougZdKrpxA==", - "optional": true, - "requires": { - "@smithy/middleware-endpoint": "^2.2.3", - "@smithy/middleware-retry": "^2.0.24", - "@smithy/middleware-serde": "^2.0.15", - "@smithy/protocol-http": "^3.0.11", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/credential-provider-imds": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.4.tgz", - "integrity": "sha512-cwPJN1fa1YOQzhBlTXRavABEYRRchci1X79QRwzaNLySnIMJfztyv1Zkst0iZPLMnpn8+CnHu3wOHS11J5Dr3A==", - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/property-provider": "^2.0.16", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "tslib": "^2.5.0" - } - }, - "@smithy/eventstream-codec": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.15.tgz", - "integrity": "sha512-crjvz3j1gGPwA0us6cwS7+5gAn35CTmqu/oIxVbYJo2Qm/sGAye6zGJnMDk3BKhWZw5kcU1G4MxciTkuBpOZPg==", - "optional": true, - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/fetch-http-handler": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.3.1.tgz", - "integrity": "sha512-6MNk16fqb8EwcYY8O8WxB3ArFkLZ2XppsSNo1h7SQcFdDDwIumiJeO6wRzm7iB68xvsOQzsdQKbdtTieS3hfSQ==", - "optional": true, - "requires": { - "@smithy/protocol-http": "^3.0.11", - "@smithy/querystring-builder": "^2.0.15", - "@smithy/types": "^2.7.0", - "@smithy/util-base64": "^2.0.1", - "tslib": "^2.5.0" - } - }, - "@smithy/hash-node": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.17.tgz", - "integrity": "sha512-Il6WuBcI1nD+e2DM7tTADMf01wEPGK8PAhz4D+YmDUVaoBqlA+CaH2uDJhiySifmuKBZj748IfygXty81znKhw==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/invalid-dependency": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.15.tgz", - "integrity": "sha512-dlEKBFFwVfzA5QroHlBS94NpgYjXhwN/bFfun+7w3rgxNvVy79SK0w05iGc7UAeC5t+D7gBxrzdnD6hreZnDVQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-content-length": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.17.tgz", - "integrity": "sha512-OyadvMcKC7lFXTNBa8/foEv7jOaqshQZkjWS9coEXPRZnNnihU/Ls+8ZuJwGNCOrN2WxXZFmDWhegbnM4vak8w==", - "optional": true, - "requires": { - "@smithy/protocol-http": "^3.0.11", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-endpoint": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.3.tgz", - "integrity": "sha512-nYfxuq0S/xoAjdLbyn1ixeVB6cyH9wYCMtbbOCpcCRYR5u2mMtqUtVjjPAZ/DIdlK3qe0tpB0Q76szFGNuz+kQ==", - "optional": true, - "requires": { - "@smithy/middleware-serde": "^2.0.15", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/shared-ini-file-loader": "^2.2.7", - "@smithy/types": "^2.7.0", - "@smithy/url-parser": "^2.0.15", - "@smithy/util-middleware": "^2.0.8", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-retry": { - "version": "2.0.24", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.24.tgz", - "integrity": "sha512-q2SvHTYu96N7lYrn3VSuX3vRpxXHR/Cig6MJpGWxd0BWodUQUWlKvXpWQZA+lTaFJU7tUvpKhRd4p4MU3PbeJg==", - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/protocol-http": "^3.0.11", - "@smithy/service-error-classification": "^2.0.8", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-retry": "^2.0.8", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - } - }, - "@smithy/middleware-serde": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.15.tgz", - "integrity": "sha512-FOZRFk/zN4AT4wzGuBY+39XWe+ZnCFd0gZtyw3f9Okn2CJPixl9GyWe98TIaljeZdqWkgrzGyPre20AcW2UMHQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/middleware-stack": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.9.tgz", - "integrity": "sha512-bCB5dUtGQ5wh7QNL2ELxmDc6g7ih7jWU3Kx6MYH1h4mZbv9xL3WyhKHojRltThCB1arLPyTUFDi+x6fB/oabtA==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-config-provider": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.8.tgz", - "integrity": "sha512-+w26OKakaBUGp+UG+dxYZtFb5fs3tgHg3/QrRrmUZj+rl3cIuw840vFUXX35cVPTUCQIiTqmz7CpVF7+hdINdQ==", - "optional": true, - "requires": { - "@smithy/property-provider": "^2.0.16", - "@smithy/shared-ini-file-loader": "^2.2.7", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/node-http-handler": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.2.1.tgz", - "integrity": "sha512-8iAKQrC8+VFHPAT8pg4/j6hlsTQh+NKOWlctJBrYtQa4ExcxX7aSg3vdQ2XLoYwJotFUurg/NLqFCmZaPRrogw==", - "optional": true, - "requires": { - "@smithy/abort-controller": "^2.0.15", - "@smithy/protocol-http": "^3.0.11", - "@smithy/querystring-builder": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/property-provider": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.16.tgz", - "integrity": "sha512-28Ky0LlOqtEjwg5CdHmwwaDRHcTWfPRzkT6HrhwOSRS2RryAvuDfJrZpM+BMcrdeCyEg1mbcgIMoqTla+rdL8Q==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/protocol-http": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.11.tgz", - "integrity": "sha512-3ziB8fHuXIRamV/akp/sqiWmNPR6X+9SB8Xxnozzj+Nq7hSpyKdFHd1FLpBkgfGFUTzzcBJQlDZPSyxzmdcx5A==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-builder": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.15.tgz", - "integrity": "sha512-e1q85aT6HutvouOdN+dMsN0jcdshp50PSCvxDvo6aIM57LqeXimjfONUEgfqQ4IFpYWAtVixptyIRE5frMp/2A==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/querystring-parser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.15.tgz", - "integrity": "sha512-jbBvoK3cc81Cj1c1TH1qMYxNQKHrYQ2DoTntN9FBbtUWcGhc+T4FP6kCKYwRLXyU4AajwGIZstvNAmIEgUUNTQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/service-error-classification": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.8.tgz", - "integrity": "sha512-jCw9+005im8tsfYvwwSc4TTvd29kXRFkH9peQBg5R/4DD03ieGm6v6Hpv9nIAh98GwgYg1KrztcINC1s4o7/hg==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0" - } - }, - "@smithy/shared-ini-file-loader": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.7.tgz", - "integrity": "sha512-0Qt5CuiogIuvQIfK+be7oVHcPsayLgfLJGkPlbgdbl0lD28nUKu4p11L+UG3SAEsqc9UsazO+nErPXw7+IgDpQ==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/signature-v4": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.18.tgz", - "integrity": "sha512-SJRAj9jT/l9ocm8D0GojMbnA1sp7I4JeStOQ4lEXI8A5eHE73vbjlzlqIFB7cLvIgau0oUl4cGVpF9IGCrvjlw==", - "optional": true, - "requires": { - "@smithy/eventstream-codec": "^2.0.15", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.7.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.8", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/smithy-client": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.18.tgz", - "integrity": "sha512-7FqdbaJiVaHJDD9IfDhmzhSDbpjyx+ZsfdYuOpDJF09rl8qlIAIlZNoSaflKrQ3cEXZN2YxGPaNWGhbYimyIRQ==", - "optional": true, - "requires": { - "@smithy/middleware-stack": "^2.0.9", - "@smithy/types": "^2.7.0", - "@smithy/util-stream": "^2.0.23", - "tslib": "^2.5.0" - } - }, - "@smithy/types": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.7.0.tgz", - "integrity": "sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/url-parser": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.15.tgz", - "integrity": "sha512-sADUncUj9rNbOTrdDGm4EXlUs0eQ9dyEo+V74PJoULY4jSQxS+9gwEgsPYyiu8PUOv16JC/MpHonOgqP/IEDZA==", - "optional": true, - "requires": { - "@smithy/querystring-parser": "^2.0.15", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-base64": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", - "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", - "optional": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-browser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.1.tgz", - "integrity": "sha512-NXYp3ttgUlwkaug4bjBzJ5+yIbUbUx8VsSLuHZROQpoik+gRkIBeEG9MPVYfvPNpuXb/puqodeeUXcKFe7BLOQ==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.22.tgz", - "integrity": "sha512-qcF20IHHH96FlktvBRICDXDhLPtpVmtksHmqNGtotb9B0DYWXsC6jWXrkhrrwF7tH26nj+npVTqh9isiFV1gdA==", - "optional": true, - "requires": { - "@smithy/property-provider": "^2.0.16", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-defaults-mode-node": { - "version": "2.0.29", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.29.tgz", - "integrity": "sha512-+uG/15VoUh6JV2fdY9CM++vnSuMQ1VKZ6BdnkUM7R++C/vLjnlg+ToiSR1FqKZbMmKBXmsr8c/TsDWMAYvxbxQ==", - "optional": true, - "requires": { - "@smithy/config-resolver": "^2.0.21", - "@smithy/credential-provider-imds": "^2.1.4", - "@smithy/node-config-provider": "^2.1.8", - "@smithy/property-provider": "^2.0.16", - "@smithy/smithy-client": "^2.1.18", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-endpoints": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.7.tgz", - "integrity": "sha512-Q2gEind3jxoLk6hdKWyESMU7LnXz8aamVwM+VeVjOYzYT1PalGlY/ETa48hv2YpV4+YV604y93YngyzzzQ4IIA==", - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.1.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-middleware": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.8.tgz", - "integrity": "sha512-qkvqQjM8fRGGA8P2ydWylMhenCDP8VlkPn8kiNuFEaFz9xnUKC2irfqsBSJrfrOB9Qt6pQsI58r3zvvumhFMkw==", - "optional": true, - "requires": { - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-retry": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.8.tgz", - "integrity": "sha512-cQTPnVaVFMjjS6cb44WV2yXtHVyXDC5icKyIbejMarJEApYeJWpBU3LINTxHqp/tyLI+MZOUdosr2mZ3sdziNg==", - "optional": true, - "requires": { - "@smithy/service-error-classification": "^2.0.8", - "@smithy/types": "^2.7.0", - "tslib": "^2.5.0" - } - }, - "@smithy/util-stream": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.23.tgz", - "integrity": "sha512-OJMWq99LAZJUzUwTk+00plyxX3ESktBaGPhqNIEVab+53gLULiWN9B/8bRABLg0K6R6Xg4t80uRdhk3B/LZqMQ==", - "optional": true, - "requires": { - "@smithy/fetch-http-handler": "^2.3.1", - "@smithy/node-http-handler": "^2.2.1", - "@smithy/types": "^2.7.0", - "@smithy/util-base64": "^2.0.1", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.2", - "tslib": "^2.5.0" - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "requires": { - "tslib": "^2.5.0" - } - }, - "@smithy/util-utf8": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", - "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", - "optional": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, - "@types/node": { - "version": "18.6.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz", - "integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==" - }, - "@types/webidl-conversions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" - }, - "@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "requires": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "@types/ws": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", - "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", - "requires": { - "@types/node": "*" - } - }, - "@vladfrangu/async_event_emitter": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.4.tgz", - "integrity": "sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "optional": true - }, - "bson": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", - "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", - "requires": { - "buffer": "^5.6.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "discord-api-types": { - "version": "0.37.61", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.61.tgz", - "integrity": "sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==" - }, - "discord.js": { - "version": "14.14.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.14.1.tgz", - "integrity": "sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==", - "requires": { - "@discordjs/builders": "^1.7.0", - "@discordjs/collection": "1.5.3", - "@discordjs/formatters": "^0.3.3", - "@discordjs/rest": "^2.1.0", - "@discordjs/util": "^1.0.2", - "@discordjs/ws": "^1.0.2", - "@sapphire/snowflake": "3.5.1", - "@types/ws": "8.5.9", - "discord-api-types": "0.37.61", - "fast-deep-equal": "3.1.3", - "lodash.snakecase": "4.1.1", - "tslib": "2.6.2", - "undici": "5.27.2", - "ws": "8.14.2" - }, - "dependencies": { - "@discordjs/rest": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.2.0.tgz", - "integrity": "sha512-nXm9wT8oqrYFRMEqTXQx9DUTeEtXUDMmnUKIhZn6O2EeDY9VCdwj23XCPq7fkqMPKdF7ldAfeVKyxxFdbZl59A==", - "requires": { - "@discordjs/collection": "^2.0.0", - "@discordjs/util": "^1.0.2", - "@sapphire/async-queue": "^1.5.0", - "@sapphire/snowflake": "^3.5.1", - "@vladfrangu/async_event_emitter": "^2.2.2", - "discord-api-types": "0.37.61", - "magic-bytes.js": "^1.5.0", - "tslib": "^2.6.2", - "undici": "5.27.2" - }, - "dependencies": { - "@discordjs/collection": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.0.0.tgz", - "integrity": "sha512-YTWIXLrf5FsrLMycpMM9Q6vnZoR/lN2AWX23/Cuo8uOOtS8eHB2dyQaaGnaF8aZPYnttf2bkLMcXn/j6JUOi3w==" - } - } - }, - "@discordjs/util": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.0.2.tgz", - "integrity": "sha512-IRNbimrmfb75GMNEjyznqM1tkI7HrZOf14njX7tCAAUetyZM1Pr8hX/EK2lxBCOgWDRmigbp24fD1hdMfQK5lw==" - } - } - }, - "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==" - }, - "fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "optional": true, - "requires": { - "strnum": "^1.0.5" - } - }, - "file-type": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.2.1.tgz", - "integrity": "sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==", - "requires": { - "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0", - "token-types": "^5.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" - }, - "kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==" - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" - }, - "magic-bytes.js": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.7.0.tgz", - "integrity": "sha512-YzVU2+/hrjwx8xcgAw+ffNq3jkactpj+f1iSL4LonrFKhvnwDzHSqtFdk/MMRP53y9ScouJ7cKEnqYsJwsHoYA==" - }, - "memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "mongodb": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.1.tgz", - "integrity": "sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==", - "requires": { - "@aws-sdk/credential-providers": "^3.186.0", - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.6.0", - "socks": "^2.7.1" - } - }, - "mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", - "requires": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" - } - }, - "mongoose": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.12.3.tgz", - "integrity": "sha512-MNJymaaXali7w7rHBxVUoQ3HzHHMk/7I/+yeeoSa4rUzdjZwIWQznBNvVgc0A8ghuJwsuIkb5LyLV6gSjGjWyQ==", - "requires": { - "bson": "^4.7.2", - "kareem": "2.5.1", - "mongodb": "4.17.1", - "mpath": "0.9.0", - "mquery": "4.0.3", - "ms": "2.1.3", - "sift": "16.0.1" - } - }, - "mpath": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", - "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" - }, - "mquery": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", - "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", - "requires": { - "debug": "4.x" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "peek-readable": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", - "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==" - }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "requires": { - "readable-stream": "^3.6.0" - } - }, - "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==" - }, - "sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" - }, - "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "requires": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - } - }, - "sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "optional": true, - "requires": { - "memory-pager": "^1.0.2" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "optional": true - }, - "strtok3": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", - "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", - "requires": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "token-types": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", - "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", - "requires": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - } - }, - "tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "requires": { - "punycode": "^2.1.1" - } - }, - "ts-mixer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", - "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "undici": { - "version": "5.27.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", - "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", - "requires": { - "@fastify/busboy": "^2.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "optional": true - }, - "webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" - }, - "whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "requires": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - } - }, - "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", - "requires": {} } } } diff --git a/package.json b/package.json index 767ef50..2e5628e 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,36 @@ { "name": "agent", - "version": "1.13.24", + "version": "2.0.0", "description": "", - "main": "./src/index.js", + "main": "./dist/src/index.js", + "type": "module", "scripts": { - "test": "node .", - "dev": "node --trace-deprecation --env-file .env src/index.js" + "compile": "tsc", + "dev": "nodemon --watch src --watch --ext ts,json --exec 'npm run compile && npm start'", + "format": "prettier . --write", + "lint": "eslint . --fix", + "check": "npm run format && npm run lint && tsc --noEmit", + "start": "node --enable-source-maps --env-file .env ." }, "engines": { - "node": ">=20.0.0" + "node": ">=20.0.6" }, "keywords": [], "author": "ElitePenguinForce", "license": "GPL-3.0", + "devDependencies": { + "@types/node": "20.17.9", + "@typescript-eslint/eslint-plugin": "^8.18.1", + "@typescript-eslint/parser": "^8.18.1", + "eslint": "^9.17.0", + "eslint-config-prettier": "9.1.0", + "nodemon": "3.1.7", + "prettier": "3.4.1", + "typescript": "5.7.2" + }, + "packageManager": "npm@10.1.0", "dependencies": { - "@discordjs/rest": "^1.0.1", - "chalk": "^4.1.2", - "discord-api-types": "^0.37.1", - "discord.js": "^14.14.1", - "mongoose": "^6.7.0" + "discord.js": "^14.16.3", + "mongoose": "^8.9.2" } } diff --git a/src/app/commands/context/list-staffs.user.ts b/src/app/commands/context/list-staffs.user.ts new file mode 100644 index 0000000..6831957 --- /dev/null +++ b/src/app/commands/context/list-staffs.user.ts @@ -0,0 +1,103 @@ +import { EmbedBuilder } from "discord.js"; +import Member, { + type GuildPopulatedMemberSchemaType, +} from "../../../core/db/models/member.js"; +import createUserContext from "../../../shared/factories/user-context.js"; + +export default createUserContext({ + data: { + name: "liststaffs", + nameLocalizations: { + "pt-BR": "Listar Staffs", + "en-US": "List Staffs", + "en-GB": "List Staffs", + }, + dmPermission: false, + defaultMemberPermissions: ["ViewChannel"], + }, + async execute(interaction) { + const user = interaction.targetUser; + const memberDocs = await Member.find({ + user: user.id, + }).populate("guild"); + + if (!memberDocs.length) { + return interaction.reply({ + content: "O usuário não faz parte de nenhuma staff!", + ephemeral: true, + }); + } + + const embed = new EmbedBuilder().setColor(0x2f3136).setAuthor({ + name: `Staffs que ${user.displayName} faz parte`, + iconURL: user.avatarURL({ extension: "gif" }) || "", + }); + + const ownedGuilds = memberDocs.filter( + (doc) => doc.user === doc.guild.owner && doc.guild.pending !== true, + ); + + if (ownedGuilds.length) { + embed.addFields({ + name: "Dono de", + value: ownedGuilds + .map( + (doc) => + `[\`${doc.guild.name}\`](https://discord.gg/${doc.guild.invite})` + + `${ + doc.guild.representative === interaction.targetUser.id + ? " **[REPRESENTANTE]**" + : "" + }`, + ) + .join("\n"), + }); + } + + const adminGuilds = memberDocs.filter( + (doc) => + doc.admin && doc.user !== doc.guild.owner && doc.guild.pending !== true, + ); + if (adminGuilds.length) { + embed.addFields({ + name: "Administra", + value: adminGuilds + .map( + (doc) => + `[\`${doc.guild.name}\`](https://discord.gg/${doc.guild.invite})` + + `${ + doc.guild.representative === interaction.targetUser.id + ? " **[REPRESENTANTE]**" + : "" + }`, + ) + .join("\n"), + }); + } + + const modGuilds = memberDocs.filter( + (doc) => !doc.admin && doc.guild.pending !== true, + ); + if (modGuilds.length) { + embed.addFields({ + name: "Modera", + value: modGuilds + .map( + (doc) => + `[\`${doc.guild.name}\`](https://discord.gg/${doc.guild.invite})` + + `${ + doc.guild.representative === interaction.targetUser.id + ? " **[REPRESENTANTE]**" + : "" + }`, + ) + .join("\n"), + }); + } + + return interaction.reply({ + embeds: [embed], + ephemeral: true, + }); + }, +}); diff --git a/src/app/commands/context/throw-snowball.user.ts b/src/app/commands/context/throw-snowball.user.ts new file mode 100644 index 0000000..22040f0 --- /dev/null +++ b/src/app/commands/context/throw-snowball.user.ts @@ -0,0 +1,30 @@ +import createColorlessEmbed from "../../../shared/factories/embeds/colorless.js"; +import createUserContext from "../../../shared/factories/user-context.js"; + +export default createUserContext({ + data: { + name: "Throw a snowball", + nameLocalizations: { + "pt-BR": "Atire uma bola de neve", + }, + dmPermission: true, + }, + async execute(interaction) { + const target = interaction.targetUser; + + return interaction.reply({ + content: `<@${target.id}>`, + embeds: [ + createColorlessEmbed({ + description: `**${interaction.user}** jogou uma bola de neve em **${target}**`, + image: { + url: "https://i.imgur.com/cPibpID.gif", + }, + }), + ], + allowedMentions: { + users: [target.id], + }, + }); + }, +}); diff --git a/src/app/commands/slash/change-invite.ts b/src/app/commands/slash/change-invite.ts new file mode 100644 index 0000000..a8fbea1 --- /dev/null +++ b/src/app/commands/slash/change-invite.ts @@ -0,0 +1,128 @@ +import { ApplicationCommandOptionType } from "discord.js"; +import Guild from "../../../core/db/models/guild.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import handleServerAutocomplete from "../../../shared/helpers/handleServerAutocomplete.js"; +import isGuard from "../../../shared/helpers/isGuard.js"; + +export default createCommand({ + data: { + name: "changeinvite", + nameLocalizations: { + "pt-BR": "alterarconvite", + }, + description: "Changes the invite link of a server", + descriptionLocalizations: { + "pt-BR": "Altera o link de convite de um servidor", + }, + options: [ + { + type: ApplicationCommandOptionType.String, + name: "server", + nameLocalizations: { + "pt-BR": "servidor", + }, + description: "The server you want to change the invite link of", + descriptionLocalizations: { + "pt-BR": "O servidor que você deseja alterar o link de convite", + }, + required: true, + autocomplete: handleServerAutocomplete, + }, + { + type: ApplicationCommandOptionType.String, + name: "invite", + nameLocalizations: { + "pt-BR": "convite", + }, + description: "The new invite link", + descriptionLocalizations: { + "pt-BR": "O novo link de convite", + }, + required: true, + }, + ], + }, + async execute(interaction) { + const guildId = interaction.options.getString("server", true); + const guildDoc = await Guild.findById(guildId); + + if (!guildDoc) { + return interaction.reply({ + content: "Servidor não encontrado no sistema", + ephemeral: true, + }); + } + + if (guildDoc.pending) { + return interaction.reply({ + content: "O servidor ainda não foi aprovado na EPF", + ephemeral: true, + }); + } + + if ( + guildDoc.representative !== interaction.user.id && + !isGuard(interaction.member) + ) { + return interaction.reply({ + content: "Você não é o representante desse servidor", + ephemeral: true, + }); + } + + const inviteString = interaction.options.getString("invite", true); + const invite = await interaction.client + .fetchInvite(inviteString) + .catch(() => null); + + if (!invite?.guild) { + return interaction.reply({ + content: "Link de convite inválido", + ephemeral: true, + }); + } + + if (invite.guild.id !== guildDoc._id) { + return interaction.reply({ + content: "O convite não pertence ao servidor selecionado", + ephemeral: true, + }); + } + + if (invite.maxUses) { + return interaction.reply({ + content: + "O convite tem um limite de uso, crie um novo convite sem limite", + ephemeral: true, + }); + } + + if (invite.maxAge) { + return interaction.reply({ + content: + "O convite tem um tempo de expiração, crie um novo convite sem tempo de expiração", + ephemeral: true, + }); + } + + guildDoc.invite = invite.code; + guildDoc.name = invite.guild.name; + await guildDoc.save(); + + if (guildDoc.role) { + await interaction.guild.roles.cache + .get(guildDoc.role) + ?.setName(invite.guild.name); + } + + interaction.client.updateServersData([ + `<:icon_guild:1037801942149242926> **|** Novo convite para o servidor **${guildDoc.name}** foi gerado`, + ]); + + return interaction.reply({ + content: + `Convite do servidor \`${guildDoc.name}\` alterado com sucesso!\n` + + `Novo convite: ${invite.url}`, + }); + }, +}); diff --git a/src/app/commands/slash/check-members.ts b/src/app/commands/slash/check-members.ts new file mode 100644 index 0000000..61d533e --- /dev/null +++ b/src/app/commands/slash/check-members.ts @@ -0,0 +1,53 @@ +import config from "../../../core/config/index.js"; +import Member from "../../../core/db/models/member.js"; +import { MAX_MESSAGE_CONTENT_LENGTH } from "../../../shared/constants.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import { ChunkedString } from "../../../shared/helpers/ChunkedString.js"; + +export default createCommand({ + data: { + name: "checkmembers", + description: "Faça um checkup de todos os membros do servidor.", + dmPermission: false, + defaultMemberPermissions: ["Administrator"], + }, + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + + const memberDocs = await Member.find({}); + const members = await interaction.guild.members.fetch({ + limit: 1000, + }); + + const warnings = new ChunkedString(MAX_MESSAGE_CONTENT_LENGTH); + + for (const member of members.values()) { + const serverRoles = [ + config.ids.roles.mod, + config.ids.roles.admin, + config.ids.roles.owner, + ]; + + if (member.roles.cache.hasAny(...serverRoles)) { + const userDocs = memberDocs.filter((doc) => doc.user === member.id); + if (!userDocs.length) { + warnings.addLine( + `**<@${member.id}> (${member.id})** have a moderator/admin/owner role but is not in any server!`, + ); + } + } + + if (member.roles.cache.has(config.ids.roles.guest)) { + warnings.addLine(`**<@${member.id}> (${member.id})** is a guest!`); + } + } + + for (const [index, warning] of warnings.get().entries()) { + const reply = index === 0 ? interaction.editReply : interaction.followUp; + + await reply({ + content: warning || "Nenhum membro com problemas encontrado!", + }); + } + }, +}); diff --git a/src/app/commands/slash/delete-server.ts b/src/app/commands/slash/delete-server.ts new file mode 100644 index 0000000..7c7b243 --- /dev/null +++ b/src/app/commands/slash/delete-server.ts @@ -0,0 +1,169 @@ +import { ApplicationCommandOptionType } from "discord.js"; +import config from "../../../core/config/index.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member, { + type GuildPopulatedMemberSchemaType, +} from "../../../core/db/models/member.js"; +import createConfirmationButtons from "../../../shared/factories/buttons/createConfirmationButtons.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import handleServerAutocomplete from "../../../shared/helpers/handleServerAutocomplete.js"; +import isGuard from "../../../shared/helpers/isGuard.js"; + +export default createCommand({ + data: { + name: "deleteserver", + description: "Delete um servidor do banco de dados", + dmPermission: false, + defaultMemberPermissions: ["Administrator"], + options: [ + { + type: ApplicationCommandOptionType.String, + name: "server", + description: "Selecione o servidor que será deletado", + required: true, + autocomplete: handleServerAutocomplete, + }, + ], + }, + async execute(interaction) { + if (!isGuard(interaction.member)) { + return interaction.reply({ + content: "Você não tem permissão para executar esse comando", + ephemeral: true, + }); + } + + const guildId = interaction.options.getString("server", true); + const guildDoc = await Guild.findById(guildId); + if (!guildDoc) { + return interaction.reply({ + content: "Servidor não encontrado", + ephemeral: true, + }); + } + + const membersCount = await Member.countDocuments({ guild: guildId }); + const row = createConfirmationButtons(); + + const reply = await interaction.reply({ + content: + `Tem certeza de que deseja deletar o servidor **${guildDoc.name}** do bando de dados?` + + `${ + membersCount === 0 + ? "" + : `\n${membersCount} membros ainda são staffs desse servidor.` + }`, + components: [row], + ephemeral: true, + }); + const collector = reply.createMessageComponentCollector({ + filter: (interaction) => interaction.user.id === interaction.user.id, + time: 60000, + max: 1, + }); + + collector.on("collect", async (interaction) => { + if (interaction.customId === "collector:cancel") { + return await interaction.update({ + content: "Operação cancelada", + components: [], + }); + } + + await interaction.update({ + content: "Deletando servidor...", + components: [], + }); + + if (guildDoc.role) { + await interaction.guild.roles + .delete(guildDoc.role) + .catch(async (err) => { + console.error(err); + await interaction.followUp({ + content: "Não foi possível deletar o cargo do servidor", + ephemeral: true, + }); + }); + } + const memberDocs = await Member.find({ guild: guildId }); + for (const memberDoc of memberDocs) { + const member = await interaction.guild.members + .fetch(memberDoc.user) + .catch(() => null); + if (!member) { + continue; + } + + if (guildDoc.owner === member.id) { + const ownedGuildExists = await Guild.exists({ + _id: { + $ne: guildId, + }, + owner: member.id, + pending: { + $ne: true, + }, + }); + if (!ownedGuildExists) { + await member.roles.remove(config.ids.roles.owner); + } + } else if (memberDoc.admin) { + const adminStaffs = await Member.find( + { + guild: { + $ne: guildId, + }, + user: member.id, + admin: true, + }, + ).populate("guild"); + + const isStillAdmin = adminStaffs.some( + (doc) => doc.guild.pending !== true, + ); + + if (!isStillAdmin) { + await member.roles.remove(config.ids.roles.admin); + } + } else { + const modStaffs = await Member.find({ + user: member.id, + admin: { + $ne: true, + }, + guild: { + $ne: guildId, + }, + }).populate("guild"); + + const isStillMod = modStaffs.some( + (doc) => doc.guild.pending !== true, + ); + + if (!isStillMod) { + await member.roles.remove(config.ids.roles.mod); + } + } + } + + await guildDoc.deleteOne(); + await Member.deleteMany({ guild: guildId }); + await interaction.editReply({ content: "Servidor deletado" }); + interaction.client.updateServersData( + [ + `<:icon_guild:1037801942149242926> **|** O servidor **${guildDoc.name}** saiu da EPF`, + ], + false, + ); + }); + collector.on("end", async (_, reason) => { + if (reason === "time") { + await interaction.editReply({ + content: "Operação cancelada", + components: [], + }); + } + }); + }, +}); diff --git a/src/app/commands/slash/list-staff.ts b/src/app/commands/slash/list-staff.ts new file mode 100644 index 0000000..92c33c7 --- /dev/null +++ b/src/app/commands/slash/list-staff.ts @@ -0,0 +1,82 @@ +import { ApplicationCommandOptionType, EmbedBuilder } from "discord.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member from "../../../core/db/models/member.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import handleServerAutocomplete from "../../../shared/helpers/handleServerAutocomplete.js"; + +export default createCommand({ + data: { + name: "staff", + description: "Mostra todos os membros da staff desse servidor presentes", + dmPermission: false, + defaultMemberPermissions: ["ViewChannel"], + options: [ + { + type: ApplicationCommandOptionType.String, + name: "server", + description: "O servidor que você quer listar", + required: true, + autocomplete: handleServerAutocomplete, + }, + ], + }, + execute: async (interaction) => { + const guildId = interaction.options.getString("server", true); + + const members = await Member.find({ guild: guildId }); + if (members.length === 0) { + return interaction.reply({ + content: "Não há membros da staff nesse servidor", + ephemeral: true, + }); + } + + const guild = await Guild.findOne({ _id: guildId, pending: { $ne: true } }); + if (!guild) { + return interaction.reply({ + content: "Servidor não encontrado", + ephemeral: true, + }); + } + + let description = `Representante: <@${guild.representative}>\n`; + + if (guild.owner) { + description += `Dono: <@${guild.owner}>\n`; + } + + if (guild.role) { + description += `Cargo: <@&${guild.role}>\n`; + } + + const invite = await interaction.client + .fetchInvite(guild.invite) + .catch(() => null); + const embed = new EmbedBuilder() + .setColor(0x2f3136) + .setAuthor({ + name: `Staff de ${guild.name}`, + iconURL: invite?.guild?.iconURL({ extension: "gif" }) ?? undefined, + url: invite?.url ?? undefined, + }) + .setDescription(description); + + const admins = members.filter((member) => member.admin); + if (admins.length) { + embed.addFields({ + name: "Administradores", + value: admins.map((admin) => `<@${admin.user}>`).join("\n"), + }); + } + + const moderators = members.filter((member) => !member.admin); + if (moderators.length) { + embed.addFields({ + name: "Moderadores", + value: moderators.map((moderator) => `<@${moderator.user}>`).join("\n"), + }); + } + + return interaction.reply({ embeds: [embed], ephemeral: true }); + }, +}); diff --git a/src/app/commands/slash/ping.ts b/src/app/commands/slash/ping.ts new file mode 100644 index 0000000..11078fc --- /dev/null +++ b/src/app/commands/slash/ping.ts @@ -0,0 +1,12 @@ +import createCommand from "../../../shared/factories/commands/index.js"; + +export default createCommand({ + data: { + name: "ping", + description: "Verifica o latência atual do bot", + dmPermission: false, + }, + execute(interaction) { + return interaction.reply("Pong!"); + }, +}); diff --git a/src/app/commands/slash/register.ts b/src/app/commands/slash/register.ts new file mode 100644 index 0000000..11da9e1 --- /dev/null +++ b/src/app/commands/slash/register.ts @@ -0,0 +1,222 @@ +import { ApplicationCommandOptionType, Role } from "discord.js"; +import config from "../../../core/config/index.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member from "../../../core/db/models/member.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import handleServerAutocomplete from "../../../shared/helpers/handleServerAutocomplete.js"; +import isGuard from "../../../shared/helpers/isGuard.js"; +import type { StaffRole } from "../../../shared/types/index.js"; + +export default createCommand({ + data: { + name: "register", + description: "Adicione um novo membro a uma das suas equipes", + dmPermission: false, + defaultMemberPermissions: ["ViewChannel"], + options: [ + { + type: ApplicationCommandOptionType.User, + name: "membro", + description: "O membro que deve ser registrado nessa equipe", + required: true, + }, + { + type: ApplicationCommandOptionType.String, + name: "server", + description: "Em qual das suas equipes esse membro deve ser registrado", + required: true, + autocomplete: handleServerAutocomplete, + }, + { + type: ApplicationCommandOptionType.String, + name: "cargo", + description: "O cargo que esse membro tem nessa equipe", + required: true, + choices: [ + { + name: "Moderador", + value: "mod", + }, + { + name: "Administrador", + value: "admin", + }, + { + name: "Dono", + value: "owner", + }, + ], + }, + ], + }, + async execute(interaction) { + const member = interaction.options.getMember("membro"); + if (!member) { + return interaction.reply({ + content: "Membro não encontrado no servidor", + ephemeral: true, + }); + } + + if (member.user.bot) { + return interaction.reply({ + content: "Não é possível adicionar bots a uma equipe", + ephemeral: true, + }); + } + + const guildId = interaction.options.getString("server", true); + const guildDoc = await Guild.findById(guildId); + if (!guildDoc) { + return interaction.reply({ + content: "Servidor não encontrado", + ephemeral: true, + }); + } + + if (guildDoc.pending) { + return interaction.reply({ + content: "O servidor ainda não foi aprovado na EPF", + ephemeral: true, + }); + } + + if ( + guildDoc.representative !== interaction.user.id && + !isGuard(interaction.member) + ) { + return interaction.reply({ + content: + "Apenas o representante desse servicor pode adicionar novos membros na EPF", + }); + } + + const isOldOwner = guildDoc.representative === member.id; + const role = interaction.options.getString("cargo", true) as StaffRole; + + if (role === "owner") { + if (guildDoc.owner) { + return interaction.reply({ + content: `O dono desse servidor já está cadastrado como <@${guildDoc.owner}>`, + ephemeral: true, + }); + } + + guildDoc.owner = member.id; + await guildDoc.save(); + } else if (guildDoc.owner === member.id) { + guildDoc.owner = null; + await guildDoc.save(); + } + + const oldMemberDoc = await Member.findOneAndUpdate( + { + guild: guildId, + user: member.id, + }, + { $set: { admin: role !== "mod" } }, + { upsert: true, setDefaultsOnInsert: true }, + ); + + if (oldMemberDoc) { + if (isOldOwner) { + const ownSomeGuild = await Guild.exists({ + owner: member.id, + }); + if (!ownSomeGuild) { + member.roles.remove(config.ids.roles.owner); + } + } else if (oldMemberDoc.admin) { + const isAdmin = await Member.exists({ + admin: true, + user: member.id, + }); + if (!isAdmin) { + member.roles.remove(config.ids.roles.admin); + } + } else { + const isMod = await Member.exists({ + admin: { $ne: true }, + user: member.id, + }); + if (!isMod) { + member.roles.remove(config.ids.roles.mod); + } + } + } + + await member.roles.add(config.ids.roles[role]); + await interaction.reply({ + content: `${member} registrado em [${guildDoc.name}](https://discord.gg/${guildDoc.invite}) com sucesso`, + }); + + let updates: string[] = []; + const invite = await interaction.client + .fetchInvite(guildDoc.invite) + .catch(() => null); + if (invite) { + if (invite.guild && guildDoc.name !== invite.guild.name) { + updates.push( + `<:icon_guild:1037801942149242926> **|** Nome de servidor alterado: **${guildDoc.name}** -> **${invite.guild.name}**`, + ); + guildDoc.name = invite.guild.name; + await guildDoc.save(); + if (guildDoc.role) { + await interaction.guild.roles.cache + .get(guildDoc.role) + ?.setName(invite.guild.name); + } + } + } else { + await interaction.followUp({ + content: + `Por favor adicione um convite válido para o seu servidor utilizando ` + + ` cmd.name === "changeinvite", + )?.id + }>`, + ephemeral: true, + }); + } + + const memberDocs = await Member.find({ guild: guildId }); + if (memberDocs.length > config.minMembersToCreateGuildRole) { + let role: Role | undefined; + if (guildDoc.role) { + role = interaction.guild.roles.cache.get(guildDoc.role); + } else { + role = await interaction.guild.roles.create({ + name: guildDoc.name, + mentionable: true, + color: 0x607d8b, + position: + interaction.guild.roles.cache.get(config.ids.roles.guildsDiv) + ?.position ?? 0 + 1, + permissions: 0n, + }); + guildDoc.role = role.id; + await guildDoc.save(); + for (const otherMemberDoc of memberDocs) { + const otherMember = await interaction.guild.members + .fetch(otherMemberDoc.user) + .catch(() => null); + if (otherMember) { + await otherMember.roles.add(role); + } + } + updates.push( + `<:mod:1040429385066491946> **|** Cargo para o servidor **${guildDoc.name}** criado`, + ); + } + + if (role) { + await member.roles.add(role); + } + } + + if (updates.length) { + interaction.client.updateServersData(updates); + } + }, +}); diff --git a/src/app/commands/slash/remove.ts b/src/app/commands/slash/remove.ts new file mode 100644 index 0000000..2b23d9e --- /dev/null +++ b/src/app/commands/slash/remove.ts @@ -0,0 +1,158 @@ +import { ApplicationCommandOptionType } from "discord.js"; +import config from "../../../core/config/index.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member, { + type GuildPopulatedMemberSchemaType, +} from "../../../core/db/models/member.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import handleServerAutocomplete from "../../../shared/helpers/handleServerAutocomplete.js"; +import isGuard from "../../../shared/helpers/isGuard.js"; + +export default createCommand({ + data: { + name: "remove", + description: "Remove um membro de uma staff", + dmPermission: false, + defaultMemberPermissions: ["ViewChannel"], + options: [ + { + type: ApplicationCommandOptionType.User, + name: "member", + description: "O membro que deve ser removido dessa equipe", + required: true, + }, + { + type: ApplicationCommandOptionType.String, + name: "server", + description: "De qual das suas equipes esse membro deve ser removido", + required: true, + autocomplete: handleServerAutocomplete, + }, + ], + }, + async execute(interaction) { + const member = interaction.options.getMember("member"); + if (!member) { + return await interaction.reply({ + content: "Membro não encontrado", + ephemeral: true, + }); + } + const guildId = interaction.options.getString("server"); + const guildDoc = await Guild.findById(guildId); + if (!guildDoc) { + return interaction.reply({ + content: "Servidor não cadastrado no banco de dados", + ephemeral: true, + }); + } + if (guildDoc.representative === interaction.user.id) { + if (member.id === interaction.user.id) { + return interaction.reply({ + content: + "Você não pode se remover de um servidor que você representa", + ephemeral: true, + }); + } + } else if (!isGuard(interaction.member)) { + return await interaction.reply({ + content: + "Apenas o representante desse servidor pode remover membros da staff", + ephemeral: true, + }); + } + const memberDoc = await Member.findOneAndDelete({ + user: member.id, + guild: guildId, + }); + if (!memberDoc) { + return await interaction.reply({ + content: "Esse membro não pertence a staff desse servidor", + ephemeral: true, + }); + } + if (guildDoc.owner === member.id) { + guildDoc.owner = null; + await guildDoc.save(); + const ownedGuildExists = await Guild.exists({ + owner: member.id, + pending: { + $ne: true, + }, + }); + if (!ownedGuildExists) { + await member.roles.remove(config.ids.roles.owner); + } + } else if (memberDoc.admin) { + const adminStaffs = await Member.find({ + user: member.id, + admin: true, + }).populate("guild"); + const isStillAdmin = adminStaffs.some( + (doc) => doc.guild.pending !== true, + ); + if (!isStillAdmin) { + await member.roles.remove(config.ids.roles.admin); + } + } else { + const modStaffs = await Member.find({ + user: member.id, + admin: { + $ne: true, + }, + }).populate("guild"); + const isStillMod = modStaffs.some((doc) => doc.guild.pending !== true); + if (!isStillMod) { + await member.roles.remove(config.ids.roles.mod); + } + } + await interaction.reply( + `${member} removido de [${guildDoc.name}](https://discord.gg/${guildDoc.invite}) com sucesso`, + ); + const updates: string[] = []; + const invite = await interaction.client + .fetchInvite(guildDoc.invite) + .catch(() => null); + if (invite) { + if (invite.guild && guildDoc.name !== invite.guild.name) { + updates.push( + `<:icon_guild:1037801942149242926> **|** Nome de servidor alterado: **${guildDoc.name}** -> **${invite.guild.name}**`, + ); + guildDoc.name = invite.guild.name; + await guildDoc.save(); + if (guildDoc.role) { + await interaction.guild.roles.cache + .get(guildDoc.role) + ?.setName(invite.guild.name); + } + } + } else { + await interaction.followUp({ + content: + `Por favor adicione um convite válido para o seu servidor utilizando ` + + ` cmd.name === "changeinvite", + )?.id + }>`, + ephemeral: true, + }); + } + if (guildDoc.role) { + await member.roles.remove(guildDoc.role); + const memberCount = await Member.countDocuments({ guild: guildId }); + if (memberCount < config.minMembersToCreateGuildRole) { + await interaction.guild.roles.delete(guildDoc.role); + guildDoc.role = null; + await guildDoc.save(); + updates.push( + `<:mod:1040429385066491946> **|** O servidor **${guildDoc.name}** perdeu o seu cargo.`, + ); + } + } + + if (updates.length) { + interaction.client.updateServersData(updates); + } + }, +}); diff --git a/src/app/commands/slash/representative.ts b/src/app/commands/slash/representative.ts new file mode 100644 index 0000000..47f8bcf --- /dev/null +++ b/src/app/commands/slash/representative.ts @@ -0,0 +1,97 @@ +import { ApplicationCommandOptionType } from "discord.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member from "../../../core/db/models/member.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import handleServerAutocomplete from "../../../shared/helpers/handleServerAutocomplete.js"; +import isGuard from "../../../shared/helpers/isGuard.js"; + +export default createCommand({ + data: { + name: "representative", + description: "Define manualmente o representante de um servidor", + dmPermission: false, + defaultMemberPermissions: ["ViewChannel"], + options: [ + { + type: ApplicationCommandOptionType.String, + name: "server", + description: "O servidor para definir um representante", + required: true, + autocomplete: handleServerAutocomplete, + }, + { + type: ApplicationCommandOptionType.User, + name: "representative", + description: "O usuário que será o representante do servidor", + required: true, + }, + ], + }, + async execute(interaction) { + const member = interaction.options.getMember("representative"); + if (!member) { + return interaction.reply({ + content: "Membro não encontrado", + ephemeral: true, + }); + } + + const guildId = interaction.options.getString("server"); + const guildDoc = await Guild.findById(guildId); + if (!guildDoc) { + return interaction.reply({ + content: "Servidor não encontrado no banco de dados", + ephemeral: true, + }); + } + + if (guildDoc.pending) { + return interaction.reply({ + content: "O servidor ainda não foi aprovado na EPF", + ephemeral: true, + }); + } + + if ( + guildDoc.representative !== interaction.user.id && + !isGuard(interaction.member) + ) { + return interaction.reply({ + content: + "Apenas o representante desse servidor pode definir um novo representante", + ephemeral: true, + }); + } + const memberExists = await Member.exists({ + user: member.id, + guild: guildId, + }); + if (!memberExists) { + return interaction.reply({ + content: "Esse membro não faz parte da staff desse servidor", + ephemeral: true, + }); + } + guildDoc.representative = member.id; + await guildDoc.save(); + return interaction.reply( + `${member} definido como representante de [\`${guildDoc.name}\`](https://discord.gg/${guildDoc.invite})`, + ); + }, + // async autocomplete$server(interaction, value) { + // const guildModel = require("../models/guild.js"); + // const name = { + // $regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i"), + // }; + // const representing = interaction.member.roles.cache.has(config.guard) + // ? await guildModel.find({ name }).sort({ name: 1 }).limit(25) + // : await guildModel.find({ + // representative: interaction.user.id, + // name, + // }).sort({ name: 1 }).limit(25); + // return representing.map((guildDoc) => ({ + // name: guildDoc.name, + // value: guildDoc._id, + // })); + // } +}); diff --git a/src/app/commands/slash/throw-snowball.ts b/src/app/commands/slash/throw-snowball.ts new file mode 100644 index 0000000..fd9f88c --- /dev/null +++ b/src/app/commands/slash/throw-snowball.ts @@ -0,0 +1,43 @@ +import { ApplicationCommandOptionType } from "discord.js"; +import createCommand from "../../../shared/factories/commands/index.js"; +import createColorlessEmbed from "../../../shared/factories/embeds/colorless.js"; + +export default createCommand({ + data: { + name: "throw-snowball", + description: "Throw a snowball at someone", + descriptionLocalizations: { + "pt-BR": "Jogue uma bola de neve em alguém", + }, + dmPermission: false, + options: [ + { + type: ApplicationCommandOptionType.User, + name: "user", + description: "The user to throw the snowball at", + descriptionLocalizations: { + "pt-BR": "O usuário para quem você quer jogar a bola de neve", + }, + required: true, + }, + ], + }, + async execute(interaction) { + const target = interaction.options.getUser("user", true); + + return interaction.reply({ + content: `<@${target.id}>`, + embeds: [ + createColorlessEmbed({ + description: `**${interaction.user}** jogou uma bola de neve em **${target}**`, + image: { + url: "https://i.imgur.com/cPibpID.gif", + }, + }), + ], + allowedMentions: { + users: [target.id], + }, + }); + }, +}); diff --git a/src/app/commands/slash/update-servers.ts b/src/app/commands/slash/update-servers.ts new file mode 100644 index 0000000..1826d22 --- /dev/null +++ b/src/app/commands/slash/update-servers.ts @@ -0,0 +1,84 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; +import Constants from "../../../core/db/models/constants.js"; +import createCommand from "../../../shared/factories/commands/index.js"; + +export default createCommand({ + data: { + name: "updateservers", + description: "Atualiza a lista de servidores que fazem parte da EPF", + dmPermission: false, + defaultMemberPermissions: ["Administrator"], + }, + async execute(interaction) { + const constants = await Constants.getConstants(); + if (constants.updatingGuildsChannel) { + return await interaction.reply({ + content: + "Uma atualização já foi solicitada, aguarde o término da atualização para solicitar novamente.", + ephemeral: true, + }); + } + await interaction.deferReply({ ephemeral: true }); + const now = Date.now(); + if (now - constants.lastGuildsChannelUpdate.getTime() < 600000) { + const confirmButton = new ButtonBuilder() + .setCustomId("collector:confirm") + .setLabel("Confirmar") + .setStyle(ButtonStyle.Success); + const cancelButton = new ButtonBuilder() + .setCustomId("collector:cancel") + .setLabel("Cancelar") + .setStyle(ButtonStyle.Danger); + + const reply = await interaction.editReply({ + content: + "A última atualização foi a menos de 10 minutos, deseja atualizar novamente?", + components: [ + new ActionRowBuilder().setComponents( + confirmButton, + cancelButton, + ), + ], + }); + + const collector = reply.createMessageComponentCollector({ + time: 60000, + filter: (interaction) => interaction.user.id === interaction.user.id, + max: 1, + }); + + collector.on("collect", async (interaction) => { + if (interaction.customId === "collector:cancel") { + return interaction.update({ + content: "Operação cancelada", + components: [], + }); + } + await interaction.update({ + content: "Lista de servidores atualizada", + components: [], + }); + interaction.client.updateServersData([ + `<:e_repeat:1049017561175568404> **|** A lista de servidores foi atualizada pois o ${interaction.user} pediu!`, + ]); + }); + + collector.on("end", async (_, reason) => { + if (reason === "time") { + return interaction.editReply({ + content: "Operação cancelada", + components: [], + }); + } + }); + + return; + } + + interaction.client.updateServersData([ + `<:e_repeat:1049017561175568404> **|** A lista de servidores foi atualizada pois o ${interaction.user} pediu!`, + ]); + + return interaction.editReply("Lista de servidores atualizada"); + }, +}); diff --git a/src/app/components/buttons/apply-dev-form.ts b/src/app/components/buttons/apply-dev-form.ts new file mode 100644 index 0000000..8882f02 --- /dev/null +++ b/src/app/components/buttons/apply-dev-form.ts @@ -0,0 +1,16 @@ +import { ButtonStyle } from "discord.js"; +import config from "../../../core/config/index.js"; +import createButton from "../../../shared/factories/buttons/index.js"; +import devRoleRequestModal from "../modals/dev-form.js"; + +export default createButton({ + id: "request-dev-role-form", + data: { + style: ButtonStyle.Secondary, + label: "Formulário de Desenvolvedor", + emoji: config.forms.dev.emoji, + }, + async execute(interaction) { + return interaction.showModal(devRoleRequestModal.create()); + }, +}); diff --git a/src/app/components/buttons/apply-server-form.ts b/src/app/components/buttons/apply-server-form.ts new file mode 100644 index 0000000..ac2fcd7 --- /dev/null +++ b/src/app/components/buttons/apply-server-form.ts @@ -0,0 +1,94 @@ +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + EmbedBuilder, +} from "discord.js"; +import config from "../../../core/config/index.js"; +import createButton from "../../../shared/factories/buttons/index.js"; +import serverRequestModal from "../modals/server-form.js"; + +export default createButton({ + id: "register-server-form", + data: { + label: "Formulário de Servidor", + style: ButtonStyle.Secondary, + emoji: config.forms.guild.emoji, + }, + async execute(interaction) { + const confirmationEmbed = new EmbedBuilder() + .setTitle("Leia antes de continuar") + .setDescription( + "Esse formulário deve ser preenchido para associar um **novo servidor** à EPF, se você deseja apenas " + + "ser registrado em um servidor que já faz parte da comunidade, por favor peça para o representante do" + + " servidor em questão te registrar.\n\n" + + "Ao confirmar, você concorda que você será o representante do servidor dentro da EPF (Elite Penguin " + + "Force). E que a responsabilidade da sua staff dentro do nosso servidor será inteiramente sua.\n\n" + + "Tenha em mente de que a resposta do formulário será enviada diretamente em sua DM, então mantenha " + + "ela aberta para receber a resposta.", + ) + .setColor(0x2f3136); + + const buttonConfirm = new ButtonBuilder() + .setCustomId("collector:confirm") + .setLabel("Confirmar") + .setStyle(ButtonStyle.Success); + + const buttonCancel = new ButtonBuilder() + .setCustomId("collector:cancel") + .setLabel("Cancelar") + .setStyle(ButtonStyle.Danger); + + const row = new ActionRowBuilder().setComponents( + buttonConfirm, + buttonCancel, + ); + + const reply = await interaction.reply({ + embeds: [confirmationEmbed], + components: [row], + ephemeral: true, + fetchReply: true, + }); + + const collector = reply.createMessageComponentCollector({ + filter: (interaction) => interaction.user.id === interaction.user.id, + time: 60000, + max: 1, + }); + + collector.on("collect", async (interaction) => { + if (interaction.customId === "collector:confirm") { + return await interaction.showModal(serverRequestModal.create()); + } + + return interaction.reply({ + content: "Operação cancelada.", + ephemeral: true, + }); + }); + + collector.on("end", async (collected) => { + if (!reply.editable) { + return; + } + + if (collected.size) { + buttonConfirm.setDisabled(true); + buttonCancel.setDisabled(true); + row.setComponents(buttonConfirm, buttonCancel); + + return interaction.editReply({ + embeds: [confirmationEmbed], + components: [row], + }); + } + + return interaction.editReply({ + content: "Tempo esgotado.", + embeds: [], + components: [], + }); + }); + }, +}); diff --git a/src/app/components/buttons/approve-dev.ts b/src/app/components/buttons/approve-dev.ts new file mode 100644 index 0000000..63b551b --- /dev/null +++ b/src/app/components/buttons/approve-dev.ts @@ -0,0 +1,108 @@ +import { ButtonStyle, EmbedBuilder, parseWebhookURL } from "discord.js"; +import env from "../../../core/config/env.js"; +import config from "../../../core/config/index.js"; +import createButton from "../../../shared/factories/buttons/index.js"; + +export default createButton({ + id: "approve-dev-request", + data: { + label: "Aprovar", + style: ButtonStyle.Success, + }, + execute: async (interaction, userId) => { + if (!userId) { + console.error( + `Button ${interaction.customId} is missing userId`, + interaction, + ); + return interaction.reply({ + content: "Ocorreu um erro ao processar o botão clicado", + ephemeral: true, + }); + } + const member = await interaction.guild.members + .fetch(userId) + .catch(() => null); + + if (member) { + await member.roles.add(config.ids.roles.dev); + } else { + return interaction[ + interaction.replied || interaction.deferred ? "followUp" : "reply" + ]({ + content: "Membro não encontrado... Não consegui dar o cargo à ele", + ephemeral: true, + }); + } + + await interaction.deferReply({ ephemeral: true }); + + const messageEmbed = interaction.message.embeds[0]; + if (!messageEmbed) { + console.error( + `Button ${interaction.customId} is missing message embed`, + interaction, + ); + return interaction.followUp({ + content: "Ocorreu um erro ao processar o botão clicado", + ephemeral: true, + }); + } + + const embed = EmbedBuilder.from(messageEmbed); + + const message = await member + .send({ + content: `Parabéns, você foi aprovado e está apto para receber o cargo de Developer no EPF!`, + }) + .catch(() => null); + if (!message) { + console.error( + `Button ${interaction.customId} is missing message`, + interaction, + ); + return interaction.followUp({ + content: + "Não foi possível entrar em contato com o requisitante do cargo", + ephemeral: true, + }); + } + + embed.setColor("#58e600").setTitle("Formulário Aprovado - Developer"); + await interaction.message.edit({ embeds: [embed], components: [] }); + + if (interaction.message.thread) { + await interaction.message.thread.setArchived(true); + } + + await interaction.followUp({ + content: "Desenvolvedor Aprovado", + ephemeral: true, + }); + + const webhook = parseWebhookURL(env.OFFTOPIC_WEBHOOK); + if (!webhook) { + console.error( + `Button ${interaction.customId} is missing webhook`, + interaction, + ); + return interaction.followUp({ + content: "Não foi possível enviar a mensagem para o canal de off-topic", + ephemeral: true, + }); + } + + await interaction.client + .fetchWebhook(webhook.id, webhook.token) + .then(async (webhook) => { + await webhook.send({ + content: `<:icons_djoin:875754472834469948> O membro ${member} foi aprovado como desenvolvedor na EPF`, + username: interaction.guild.name, + avatarURL: interaction.guild.iconURL() || undefined, + allowedMentions: { + users: [], + }, + }); + }); + }, +}); diff --git a/src/app/components/buttons/approve-server.ts b/src/app/components/buttons/approve-server.ts new file mode 100644 index 0000000..a5ee362 --- /dev/null +++ b/src/app/components/buttons/approve-server.ts @@ -0,0 +1,108 @@ +import { ButtonStyle, EmbedBuilder, parseWebhookURL } from "discord.js"; +import env from "../../../core/config/env.js"; +import config from "../../../core/config/index.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member from "../../../core/db/models/member.js"; +import createButton from "../../../shared/factories/buttons/index.js"; + +export default createButton({ + id: "approve-server-form", + data: { + label: "Aprovar", + style: ButtonStyle.Success, + }, + async execute(interaction, guildId) { + const messageEmbed = interaction.message.embeds[0]; + if (!messageEmbed) { + return interaction.reply({ + content: "Não foi possível encontrar o embed da mensagem", + ephemeral: true, + }); + } + + await interaction.deferReply({ ephemeral: true }); + + const guildDoc = await Guild.findById(guildId); + + if (!guildDoc) { + return interaction.editReply("Servidor não encontrado"); + } + + const member = await interaction.guild.members + .fetch(guildDoc.representative) + .catch(() => null); + if (!member) { + return interaction.editReply( + `O representante não está mais no servidor (${guildDoc.representative})`, + ); + } + + guildDoc.pending = false; + await guildDoc.save(); + + if (guildDoc.owner === member.id) { + await member.roles.add(config.ids.roles.owner); + } else { + const memberDoc = await Member.findOne({ + user: member.id, + guild: guildDoc._id, + }); + await member.roles.add( + memberDoc?.admin ? config.ids.roles.admin : config.ids.roles.mod, + ); + } + + const hasSentMessage = await member + .send({ + content: `Parabéns, o seu servidor \`${guildDoc.name}\` foi aprovado na EPF!`, + }) + .then(() => true) + .catch(() => false); + if (!hasSentMessage) { + await interaction.editReply( + "Não foi possível entrar em contato com o representante do servidor", + ); + } + const embed = EmbedBuilder.from(messageEmbed) + .setColor("#58e600") + .setTitle("Formulário Aprovado") + .addFields({ + name: "Aprovado por", + value: `<@${interaction.user.id}>`, + }); + await interaction.message.edit({ embeds: [embed], components: [] }); + + if (interaction.message.thread) { + await interaction.message.thread.setArchived(true); + } + + if (interaction.replied) { + await interaction.followUp({ + content: "Servidor Aprovado", + ephemeral: true, + }); + } else { + await interaction.editReply("Servidor Aprovado"); + } + + interaction.client.updateServersData([ + `<:icon_guild:1037801942149242926> **|** Novo servidor aprovado: **${guildDoc.name}**`, + ]); + + const webhookData = parseWebhookURL(env.OFFTOPIC_WEBHOOK); + if (!webhookData) { + return; + } + + const webhook = await interaction.client.fetchWebhook( + webhookData.id, + webhookData.token, + ); + + return webhook.send({ + content: `<:icons_djoin:875754472834469948> O servidor **${guildDoc.name}** entrou para a EPF`, + username: interaction.guild.name, + avatarURL: interaction.guild.iconURL() || undefined, + }); + }, +}); diff --git a/src/app/components/buttons/refuse-dev.ts b/src/app/components/buttons/refuse-dev.ts new file mode 100644 index 0000000..9916f32 --- /dev/null +++ b/src/app/components/buttons/refuse-dev.ts @@ -0,0 +1,14 @@ +import { ButtonStyle } from "discord.js"; +import createButton from "../../../shared/factories/buttons/index.js"; +import refuseRequestModal from "../modals/refuse-dev-reason.js"; + +export default createButton({ + id: "refuse-dev-request", + data: { + label: "Recusar", + style: ButtonStyle.Danger, + }, + async execute(interaction) { + return interaction.showModal(refuseRequestModal.create()); + }, +}); diff --git a/src/app/components/buttons/refuse-server.ts b/src/app/components/buttons/refuse-server.ts new file mode 100644 index 0000000..741c204 --- /dev/null +++ b/src/app/components/buttons/refuse-server.ts @@ -0,0 +1,14 @@ +import { ButtonStyle } from "discord.js"; +import createButton from "../../../shared/factories/buttons/index.js"; +import refuseServerRequest from "../modals/refuse-server-reason.js"; + +export default createButton({ + id: "refuse-server-form", + data: { + label: "Recusar", + style: ButtonStyle.Danger, + }, + async execute(interaction) { + return interaction.showModal(refuseServerRequest.create()); + }, +}); diff --git a/src/app/components/modals/dev-form.ts b/src/app/components/modals/dev-form.ts new file mode 100644 index 0000000..d1583e6 --- /dev/null +++ b/src/app/components/modals/dev-form.ts @@ -0,0 +1,142 @@ +import { + ComponentType, + EmbedBuilder, + TextInputStyle, + type User, +} from "discord.js"; +import config from "../../../core/config/index.js"; +import { DISCORD_INVITE_REGEX } from "../../../shared/constants.js"; +import createModal from "../../../shared/factories/modal.js"; +import devApproveButton from "../buttons/approve-dev.js"; +import devRefuseButton from "../buttons/refuse-dev.js"; + +export default createModal({ + data: { + customId: "devRequestModal", + title: "Elite Penguin Force", + components: [ + { + type: ComponentType.TextInput, + customId: "githubLink", + label: "O link do seu perfil no Github, Gitlab ou Bitbucket", + required: true, + style: TextInputStyle.Short, + }, + { + type: ComponentType.TextInput, + customId: "experienceInfo", + label: "Qual sua atuação na área?", + required: true, + style: TextInputStyle.Paragraph, + placeholder: "Criação de sites/bots, etc...", + }, + { + type: ComponentType.TextInput, + customId: "example", + label: "Nos envie um exemplo de um projeto seu", + required: true, + style: TextInputStyle.Paragraph, + }, + { + type: ComponentType.TextInput, + customId: "botInvite", + label: "Convite do seu bot (se houver)", + required: false, + style: TextInputStyle.Short, + }, + ], + }, + execute: async (interaction) => { + let bot: User | null = null; + let botInvite: string | null = null; + + const invite = interaction.fields.getTextInputValue("botInvite"); + + if (DISCORD_INVITE_REGEX.test(invite)) { + const clientId = invite.split("client_id=")[1]?.split("&")?.[0]; + if (!clientId) { + return interaction.reply({ + content: "O convite do bot não é válido", + ephemeral: true, + }); + } + + bot = await interaction.client.users.fetch(clientId).catch(() => null); + botInvite = invite.replace(/permissions=[0-9]*/gi, "permissions=0"); + } + + const embed = new EmbedBuilder() + .setTitle("Novo formulário - Desenvolvedor") + .setColor("#e3ba03") + .setFields([ + { + name: "Enviado por", + value: `${interaction.user.username} (${interaction.user.id})`, + inline: true, + }, + { + name: "GitHub", + value: `${interaction.fields.getTextInputValue("githubLink")}`, + inline: true, + }, + { + name: "Atuação", + value: `${interaction.fields.getTextInputValue("experienceInfo")}`, + inline: true, + }, + { + name: "Exemplo", + value: `${interaction.fields.getTextInputValue("example")}`, + inline: true, + }, + { + name: "Bot", + value: bot ? `${bot.tag} (${bot.id})` : "Não tem", + inline: true, + }, + { + name: "Convite", + value: `${bot ? botInvite || "Inválido" : "Não tem"}`, + inline: true, + }, + ]); + + const aproveChannel = interaction.client.channels.cache.get( + config.ids.channels.approve, + ); + + if (!aproveChannel) { + return interaction.reply({ + content: "O canal de aprovação não foi encontrado", + ephemeral: true, + }); + } + + if (!aproveChannel.isSendable()) { + return interaction.reply({ + content: "O canal de aprovação não é um canal de texto", + ephemeral: true, + }); + } + + const message = await aproveChannel.send({ + embeds: [embed], + components: [ + { + type: ComponentType.ActionRow, + components: [ + devApproveButton.create(interaction.user.id), + devRefuseButton.create(interaction.user.id), + ], + }, + ], + }); + + await message.startThread({ name: `Dev ${interaction.user.username}` }); + + await interaction.reply({ + content: "Seu pedido foi enviado para avaliação.", + ephemeral: true, + }); + }, +}); diff --git a/src/app/components/modals/refuse-dev-reason.ts b/src/app/components/modals/refuse-dev-reason.ts new file mode 100644 index 0000000..a587906 --- /dev/null +++ b/src/app/components/modals/refuse-dev-reason.ts @@ -0,0 +1,92 @@ +import { ComponentType, EmbedBuilder, TextInputStyle } from "discord.js"; +import createModal from "../../../shared/factories/modal.js"; + +export default createModal({ + data: { + customId: "devRefuseReasonModal", + title: "Elite Penguin Force", + components: [ + { + type: ComponentType.TextInput, + customId: "reason", + label: "Defina um motivo", + required: false, + style: TextInputStyle.Paragraph, + }, + ], + }, + execute: async (interaction) => { + const message = interaction.message; + if (!message) { + console.error("Não foi possível encontrar a mensagem", interaction); + return interaction.reply({ + content: "Não foi possível encontrar a mensagem", + ephemeral: true, + }); + } + + const messageEmbed = message.embeds[0]; + if (!messageEmbed) { + console.error("Não foi possível encontrar a mensagem", interaction); + return interaction.reply({ + content: "Não foi possível encontrar a mensagem", + ephemeral: true, + }); + } + + const userId = interaction.customId.split(":")[1]; + if (!userId) { + console.error("Não foi possível encontrar o usuário", interaction); + return interaction.reply({ + content: "Não foi possível encontrar o usuário", + ephemeral: true, + }); + } + + const reason = interaction.fields.getTextInputValue("reason"); + const embed = EmbedBuilder.from(messageEmbed) + .setColor("#d12c2c") + .setTitle("Pedido Recusado") + .addFields([ + { + name: "Recusado pelo motivo", + value: `${reason || "Sem Motivo"}`, + inline: true, + }, + { + name: "Recusado por", + value: `${interaction.user} (${interaction.user.id})`, + inline: true, + }, + ]); + + await message.edit({ embeds: [embed], components: [] }); + + if (message.thread) { + await message.thread.setArchived(true); + } + + const member = await interaction.guild.members + .fetch(userId) + .catch(() => null); + if (member) { + const content = + "Lamentamos informar que a sua requisição de cargo de desenvolvedor na EPF foi recusada."; + await member + .send(reason ? `${content}\nMotivo: ${reason}` : content) + .catch(() => { + interaction.reply({ + content: "Não foi possível entrar em contato com o membro", + ephemeral: true, + }); + }); + } + + return interaction[ + interaction.replied || interaction.deferred ? "followUp" : "reply" + ]({ + content: "Desenvolvedor Recusado", + ephemeral: true, + }); + }, +}); diff --git a/src/app/components/modals/refuse-server-reason.ts b/src/app/components/modals/refuse-server-reason.ts new file mode 100644 index 0000000..52af689 --- /dev/null +++ b/src/app/components/modals/refuse-server-reason.ts @@ -0,0 +1,95 @@ +import { ComponentType, EmbedBuilder, TextInputStyle } from "discord.js"; +import Guild from "../../../core/db/models/guild.js"; +import createModal from "../../../shared/factories/modal.js"; + +export default createModal({ + data: { + customId: "refuse-server-form", + title: "Elite Penguin Force", + components: [ + { + type: ComponentType.TextInput, + customId: "reason", + label: "Defina um motivo", + required: false, + style: TextInputStyle.Paragraph, + }, + ], + }, + async execute(interaction) { + await interaction.deferReply({ ephemeral: true }); + + const message = interaction.message; + if (!message) { + return interaction.editReply({ + content: "Não foi possível encontrar a mensagem", + }); + } + + const messageEmbed = message.embeds[0]; + if (!messageEmbed) { + return interaction.editReply({ + content: "Não foi possível encontrar a embed da mensagem", + }); + } + + const embed = EmbedBuilder.from(messageEmbed); + const reason = interaction.fields.getTextInputValue("reason"); + + embed + .setColor("#d12c2c") + .setTitle("Formulário Recusado") + .addFields([ + { + name: "Recusado pelo motivo", + value: `${reason || "Sem Motivo"}`, + inline: true, + }, + { + name: "Recusado por", + value: `${interaction.user} (${interaction.user.id})`, + inline: true, + }, + ]); + + await message.edit({ embeds: [embed], components: [] }); + + if (message.thread) { + await message.thread.setArchived(true); + } + + const guildId = interaction.customId.split(":")[1]; + if (!guildId) { + return interaction.editReply({ + content: "ID do servidor não foi encontrado", + }); + } + + const guildDoc = await Guild.findByIdAndDelete(guildId); + if (!guildDoc) { + return interaction.editReply({ + content: `ID do servidor é inválido e não foi encontrado: ${guildId}`, + }); + } + + const member = await interaction.guild.members + .fetch(guildDoc.representative) + .catch(() => null); + if (member) { + const content = `O servidor \`${guildDoc.name}\` foi recusado da EPF.`; + await member + .send(reason ? `${content}\nMotivo: ${reason}` : content) + .catch(() => { + interaction.reply({ + content: + "Não foi possível entrar em contato com o representante do servidor", + ephemeral: true, + }); + }); + } + + return interaction.editReply({ + content: "Servidor Recusado", + }); + }, +}); diff --git a/src/app/components/modals/server-form.ts b/src/app/components/modals/server-form.ts new file mode 100644 index 0000000..76ec37a --- /dev/null +++ b/src/app/components/modals/server-form.ts @@ -0,0 +1,108 @@ +import { ComponentType, TextInputStyle } from "discord.js"; +import Guild from "../../../core/db/models/guild.js"; +import createModal from "../../../shared/factories/modal.js"; +import { + default as createServerRejection, + default as reject, +} from "../../../shared/helpers/forms/createServerRejection.js"; +import createServerRequest from "../../../shared/helpers/forms/createServerRequest.js"; +import parseMemberRole from "../../../shared/helpers/forms/parseMemberRole.js"; + +export default createModal({ + data: { + customId: "serverRequestModal", + title: "Elite Penguin Force", + components: [ + { + customId: "serverLink", + type: ComponentType.TextInput, + style: TextInputStyle.Short, + minLength: 10, + maxLength: 30, + label: "Convite permanente do servidor", + required: true, + }, + { + type: ComponentType.TextInput, + customId: "serverRole", + required: true, + label: "Qual o seu cargo nesse servidor?", + placeholder: "Dono(a), Adm ou mod", + style: TextInputStyle.Short, + minLength: 3, + maxLength: 4, + }, + { + type: ComponentType.TextInput, + customId: "serverAbout", + label: "Conte-nos mais sobre esse servidor", + required: true, + style: TextInputStyle.Paragraph, + minLength: 20, + maxLength: 150, + }, + { + type: ComponentType.TextInput, + customId: "epfAbout", + label: "Por onde você conheceu a EPF?", + required: true, + style: TextInputStyle.Paragraph, + minLength: 10, + maxLength: 150, + }, + ], + }, + async execute(interaction) { + const invite = interaction.fields.getTextInputValue("serverLink"); + const fetchedInvite = await interaction.client + .fetchInvite(invite) + .catch(() => null); + + const role = interaction.fields.getTextInputValue("serverRole"); + const parsedRole = parseMemberRole(role); + + const errors: string[] = []; + + if (!parsedRole) { + errors.push(`Cargo Inválido (${role})`); + } + + if (!fetchedInvite?.guild) { + errors.push(`Convite Inválido: ${invite}`); + return reject(interaction, errors); + } + + const guildDoc = await Guild.findById(fetchedInvite.guild.id); + if (guildDoc) { + if (guildDoc.pending) { + return interaction.reply({ + content: "Esse servidor já está em processo de avaliação", + ephemeral: true, + }); + } else { + errors.push("Esse servidor já faz parte da EPF"); + } + } + + if (fetchedInvite.expiresTimestamp) { + errors.push("Convite Expirável"); + } + + if (fetchedInvite.maxUses) { + errors.push("Convite com limite de uso"); + } + + if ( + fetchedInvite.memberCount < 5000 && + !fetchedInvite.guild.features.includes("PARTNERED") + ) { + errors.push("O servidor não atingiu os requisitos mínimos"); + } + + if (errors.length) { + return createServerRejection(interaction, errors); + } + + return createServerRequest(interaction, parsedRole, fetchedInvite); + }, +}); diff --git a/src/app/events/guild-member-remove.ts b/src/app/events/guild-member-remove.ts new file mode 100644 index 0000000..06c5490 --- /dev/null +++ b/src/app/events/guild-member-remove.ts @@ -0,0 +1,47 @@ +import config from "../../core/config/index.js"; +import Guild from "../../core/db/models/guild.js"; +import Member, { + type GuildPopulatedMemberSchemaType, +} from "../../core/db/models/member.js"; +import createEvent from "../../shared/factories/event.js"; +import deleteGuildRole from "../../shared/helpers/deleteGuildRole.js"; +import logRepresentativeExit from "../../shared/helpers/logRepresentativeExit.js"; + +export default createEvent({ + name: "guildMemberRemove", + execute: async (member) => { + await Guild.updateMany({ owner: member.id }, { $set: { owner: null } }); + + const memberDocs = await Member.find({ + user: member.id, + }).populate("guild"); + + for (const memberDoc of memberDocs.filter((doc) => doc.guild.role)) { + const memberCount = await Member.countDocuments({ + guild: memberDoc.guild._id, + }); + + if ( + memberCount <= config.minMembersToCreateGuildRole && + memberDoc.guild.role + ) { + await deleteGuildRole({ + guild: member.guild, + roleId: memberDoc.guild.role, + }); + } + } + + await Member.deleteMany({ user: member.id }); + + const representingDocs = await Guild.find({ representative: member.id }); + await Guild.updateMany( + { representative: member.id }, + { $set: { representative: config.ids.agent } }, + ); + + if (representingDocs.length) { + await logRepresentativeExit({ member, representingDocs }); + } + }, +}); diff --git a/src/app/events/interaction-create.ts b/src/app/events/interaction-create.ts new file mode 100644 index 0000000..180d5fd --- /dev/null +++ b/src/app/events/interaction-create.ts @@ -0,0 +1,9 @@ +import InteractionService from "../../services/InteractionService.js"; +import createEvent from "../../shared/factories/event.js"; + +export default createEvent({ + name: "interactionCreate", + async execute(interaction) { + await InteractionService.handleInteraction(interaction); + }, +}); diff --git a/src/app/events/ready.ts b/src/app/events/ready.ts new file mode 100644 index 0000000..3793078 --- /dev/null +++ b/src/app/events/ready.ts @@ -0,0 +1,31 @@ +import Constants from "../../core/db/models/constants.js"; +import BackgroundTasksService from "../../services/BackgroundTasksService.js"; +import createEvent from "../../shared/factories/event.js"; +import checkFormMessages from "../../shared/helpers/checkFormMessages.js"; + +export default createEvent({ + name: "ready", + execute: async (client) => { + console.log(`Logado em ${client.user.username} (${client.user.id})`); + + // se crashar ou reiniciar no meio de uma atualização, ele vai refazer essa atualização já + // que a ela não foi finalizada devidamente + const constants = await Constants.getConstants(); + if (constants.updatingGuildsChannel || constants.scheduledUpdate) { + await Constants.updateConstants({ + updatingGuildsChannel: false, + scheduledUpdate: false, + }); + client.updateServersData( + [ + "<:e_repeat:1049017561175568404> **|** A última atualização não foi bem sucedida, por isso será refeita", + ], + true, + ); + } + + await checkFormMessages(client); + + BackgroundTasksService.initTasks(client); + }, +}); diff --git a/src/app/events/thread-create.ts b/src/app/events/thread-create.ts new file mode 100644 index 0000000..cedf120 --- /dev/null +++ b/src/app/events/thread-create.ts @@ -0,0 +1,15 @@ +import config from "../../core/config/index.js"; +import createEvent from "../../shared/factories/event.js"; + +export default createEvent({ + name: "threadCreate", + execute: async (thread, newlyCreated) => { + if (!newlyCreated) { + return; + } + + if (thread.parentId === config.ids.channels.suggestions) { + await thread.setAppliedTags([config.ids.tags.pending]); + } + }, +}); diff --git a/src/app/tasks/kick-inactive-members.ts b/src/app/tasks/kick-inactive-members.ts new file mode 100644 index 0000000..b6565af --- /dev/null +++ b/src/app/tasks/kick-inactive-members.ts @@ -0,0 +1,38 @@ +import config from "../../core/config/index.js"; +import Guild from "../../core/db/models/guild.js"; +import createTask from "../../shared/factories/task.js"; + +export default createTask({ + data: { + name: "kickInactiveMembers", + interval: 60 * 60 * 1000, + }, + execute: async (client) => { + const now = Date.now(); + + const guild = client.guilds.cache.get(config.ids.guild); + if (!guild) { + throw new Error("Guild not found"); + } + + const divRole = guild.roles.cache.get(config.ids.roles.guildsDiv); + if (!divRole) { + throw new Error("Guilds division role not found"); + } + + await guild.members.fetch(); + const pendingGuilds = await Guild.find({ pending: true }); + for (const member of guild.members.cache + .filter( + (member) => + !member.user.bot && + member.roles.highest.position < divRole.position && + member.joinedTimestamp && + member.joinedTimestamp < now - 24 * 60 * 60 * 1000 && + !pendingGuilds.some((g) => g.representative === member.id), + ) + .values()) { + await member.kick(); + } + }, +}); diff --git a/src/commands/changeinvite.js b/src/commands/changeinvite.js deleted file mode 100644 index b797dfd..0000000 --- a/src/commands/changeinvite.js +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) 2022 HordLawk & vitoUwu & PeterStark000 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -const { SlashCommandBuilder, PermissionsBitField, SlashCommandStringOption } = require("discord.js"); -const config = require("../config.js"); -const Command = require('../structures/command.js'); - -class ChangeInviteCommand extends Command { - constructor() { - super({ - active: true, - data: new SlashCommandBuilder() - .setName('changeinvite') - .setNameLocalization('pt-BR', 'alterarconvite') - .setDescription('Altere o convite de um servidor') - .setDMPermission(false) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ViewChannel) - .addStringOption( - new SlashCommandStringOption() - .setName('server') - .setNameLocalization('pt-BR', 'servidor') - .setDescription('O servidor que você deseja alterar o convite') - .setAutocomplete(true) - .setRequired(true), - ) - .addStringOption( - new SlashCommandStringOption() - .setName('new_invite') - .setNameLocalization('pt-BR', 'novo_convite') - .setDescription('O novo convite para o servidor') - .setRequired(true), - ), - }); - } - - async execute(interaction, client) { - const guildModel = require('../models/guild.js'); - const guildId = interaction.options.getString('server'); - require('../models/member.js'); - const guildDoc = await guildModel.findById(guildId); - - if (!guildDoc) - return interaction.reply({ - content: 'Servidor não encontrado ou removido... ID não está cadastrado no sistema', - ephemeral: true, - }); - - if (guildDoc.pending) return await interaction.reply({ - content: 'O servidor ainda não foi aprovado na EPF', - ephemeral: true - }); - - //Uma verificação simples para ver se o usuário é representante da equipe, isto serve para quando - //o comando falhar no passado e o usuário tentar chamá-lo novamente não sendo o representante no presente - if ((guildDoc.representative !== interaction.user.id) && !interaction.member.roles.cache.has(config.guard)) - return await interaction.reply({ - content: 'Você não é o representante dessa equipe.', - ephemeral: true, - }) - - const newInvite = interaction.options.getString('new_invite'); - await client.fetchInvite(newInvite) - .then(async (invite) => { - if (invite.guild.id !== guildId) { - return await interaction.reply({ - content: 'O convite não é válido para esse servidor', - ephemeral: true, - }); - } - - const guildDoc = await guildModel.findOneAndUpdate( - { - _id: guildId - }, - { - invite: invite.code, - name: invite?.guild.name ?? guildDoc.name, - } - ); - - if (guildDoc.role) { - await interaction.guild.roles.cache - .get(guildDoc.role) - .setName(invite.guild.name); - } - - client.emit( - 'updateGuilds', - false, - `<:icon_guild:1037801942149242926> **|** Novo convite para o servidor **${guildDoc.name}** foi gerado` - ); - - return await interaction.reply({ - content: ( - `Convite do servidor \`${guildDoc.name}\` alterado com sucesso!\n` + - `Novo convite: ${invite.url}` - ), - }); - }, async () => { - //Aqui é feita a verificação se o convite é válido, se não for, retornaremos uma mensagem de erro - await interaction.reply({ content: 'Convite inválido... Tente novamente', ephemeral: true }) - }); - } - - async autocomplete$server(interaction, value){ - const guildModel = require('../models/guild.js'); - const name = {$regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')}; - const representing = ( - interaction.member.roles.cache.has(config.guard) - ? await guildModel.find({name}).sort({name: 1}).limit(25) - : await guildModel.find({ - representative: interaction.user.id, - name, - }).sort({name: 1}).limit(25) - ); - - return representing.map(guildDoc => ({ - name: guildDoc.name, - value: guildDoc._id, - })); - } -} - -module.exports = new ChangeInviteCommand(); diff --git a/src/commands/deleteserver.js b/src/commands/deleteserver.js deleted file mode 100644 index e52ea58..0000000 --- a/src/commands/deleteserver.js +++ /dev/null @@ -1,182 +0,0 @@ -const { - SlashCommandBuilder, - PermissionsBitField, - SlashCommandStringOption, - ActionRowBuilder, - ButtonBuilder, - ButtonStyle, -} = require("discord.js"); -const config = require("../config"); -const Command = require("../structures/command"); - -class DeleteServerCommand extends Command { - constructor() { - super({ - active: true, - data: new SlashCommandBuilder() - .setName("deleteserver") - .setDescription("Delete um servidor da database do agent.") - .setDMPermission(false) - .setDefaultMemberPermissions( - PermissionsBitField.Flags.ManageRoles - ) - .addStringOption( - new SlashCommandStringOption() - .setName("server") - .setNameLocalization("pt-BR", "servidor") - .setDescription( - "Selecione o servidor que será deletado" - ) - .setRequired(true) - .setAutocomplete(true) - ), - }); - } - - async execute(interaction, client) { - if (!interaction.member.roles.cache.has(config.guard)) { - return await interaction.reply({ - content: "Apenas um guard pode deletar servidores", - ephemeral: true, - }); - } - const guildId = interaction.options.getString("server"); - const guildModel = require("../models/guild"); - const guildDoc = await guildModel.findById(guildId); - if (!guildDoc) { - return await interaction.reply({ - content: "Servidor não cadastrado no banco de dados", - ephemeral: true, - }); - } - const memberModel = require("../models/member"); - const membersCount = await memberModel.find({ guild: guildId }).count(); - const actionRow = new ActionRowBuilder().setComponents( - new ButtonBuilder() - .setCustomId("collector:confirm") - .setLabel("Confirmar") - .setStyle(ButtonStyle.Success), - new ButtonBuilder() - .setCustomId("collector:cancel") - .setLabel("Cancelar") - .setStyle(ButtonStyle.Danger) - ); - const reply = await interaction.reply({ - content: - `Tem certeza de que deseja deletar o servidor **${guildDoc.name}** do bando de dados?` + - `${ - membersCount === 0 - ? "" - : `\n${membersCount} membros ainda são staffs desse servidor.` - }`, - components: [actionRow], - ephemeral: true, - }); - const collector = reply.createMessageComponentCollector({ - filter: (i) => i.user.id === interaction.user.id, - time: 60000, - max: 1, - }); - collector.on("collect", async (i) => { - if (i.customId === "collector:cancel") { - return await i.update({ - content: "Operação cancelada", - components: [], - }); - } - await i.update({ - content: "Deletando servidor...", - components: [], - }); - if (guildDoc.role) { - await interaction.guild.roles - .delete(guildDoc.role) - .catch(async (err) => { - console.log(err); - await interaction.followUp({ - content: - "Não foi possível deletar o cargo do servidor", - ephemeral: true, - }); - }); - } - const memberDocs = await memberModel.find({guild: guildId}); - for(const memberDoc of memberDocs){ - const member = await interaction.guild.members.fetch(memberDoc.user).catch(() => null); - if(!member) continue; - if(guildDoc.owner === member.id){ - const ownedGuildExists = await guildModel.exists({ - _id: {$ne: guildId}, - owner: member.id, - pending: { - $ne: true - } - }); - if(!ownedGuildExists) await member.roles.remove(config.levels[2]); - } - else if(memberDoc.admin){ - const adminStaffs = await memberModel.find({ - user: member.id, - admin: true, - guild: { - $ne: guildId - } - }).populate('guild'); - const isStillAdmin = adminStaffs.some((doc) => doc.guild.pending !== true); - if(!isStillAdmin) await member.roles.remove(config.levels[1]); - } - else{ - const modStaffs = await memberModel.find({ - user: member.id, - admin: { - $ne: true - }, - guild: { - $ne: guildId - } - }).populate('guild'); - const isStillMod = modStaffs.some((doc) => doc.guild.pending !== true); - if(!isStillMod) await member.roles.remove(config.levels[0]); - } - } - await guildDoc.delete(); - await memberModel.deleteMany({ guild: guildId }); - await i.editReply({ content: 'Servidor deletado' }); - client.emit( - 'updateGuilds', - false, - `<:icon_guild:1037801942149242926> **|** O servidor **${guildDoc.name}** saiu da EPF` - ); - }); - collector.on("end", async (_, reason) => { - if (reason === "time") { - await interaction.editReply({ - content: "Operação cancelada", - components: [], - }); - } - }); - } - - async autocomplete$server(interaction, value) { - const guildModel = require("../models/guild.js"); - const guildDocs = await guildModel - .find({ - name: { - $regex: new RegExp( - value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), - "i" - ), - }, - pending: { $ne: true }, - }) - .sort({ name: 1 }) - .limit(25); - return guildDocs.map((doc) => ({ - name: doc.name, - value: doc._id, - })); - } -} - -module.exports = new DeleteServerCommand(); diff --git a/src/commands/liststaffs.js b/src/commands/liststaffs.js deleted file mode 100644 index ac503cc..0000000 --- a/src/commands/liststaffs.js +++ /dev/null @@ -1,68 +0,0 @@ -const { ContextMenuCommandBuilder, PermissionsBitField, ApplicationCommandType, EmbedBuilder } = require('discord.js'); -const Command = require('../structures/command.js'); - -class ListstaffsCommand extends Command{ - constructor(){ - super({ - active: true, - data: new ContextMenuCommandBuilder() - .setName('liststaffs') - .setNameLocalizations({ - "en-GB": 'List staffs', - "en-US": 'List staffs', - "pt-BR": 'Listar staffs', - }) - .setDMPermission(true) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ViewChannel) - .setType(ApplicationCommandType.User), - }); - } - - async execute(interaction){ - const memberModel = require('../models/member.js'); - require('../models/guild.js'); - const memberDocs = await memberModel.find({user: interaction.targetUser.id}).populate('guild'); - const embed = new EmbedBuilder() - .setColor(0x2f3136) - .setAuthor({ - name: `Staffs que ${interaction.targetUser.username} faz parte`, - iconURL: interaction.targetUser.avatarURL({dynamic: true}), - }); - const ownedGuilds = memberDocs.filter(doc => (doc.user === doc.guild.owner && doc.guild.pending !== true)); - if(ownedGuilds.length) embed.addFields({ - name: 'Dono de', - value: ownedGuilds - .map(doc => ( - `[\`${doc.guild.name}\`](https://discord.gg/${doc.guild.invite})` + - `${(doc.guild.representative === interaction.targetUser.id) ? ' **[REPRESENTANTE]**' : ''}` - )) - .join('\n'), - }); - const adminGuilds = memberDocs.filter(doc => (doc.admin && (doc.user !== doc.guild.owner) && doc.guild.pending !== true)); - if(adminGuilds.length) embed.addFields({ - name: 'Administra', - value: adminGuilds - .map(doc => ( - `[\`${doc.guild.name}\`](https://discord.gg/${doc.guild.invite})` + - `${(doc.guild.representative === interaction.targetUser.id) ? ' **[REPRESENTANTE]**' : ''}` - )) - .join('\n'), - }); - const modGuilds = memberDocs.filter(doc => !doc.admin && doc.guild.pending !== true); - if(modGuilds.length) embed.addFields({ - name: 'Modera', - value: modGuilds - .map(doc => ( - `[\`${doc.guild.name}\`](https://discord.gg/${doc.guild.invite})` + - `${(doc.guild.representative === interaction.targetUser.id) ? ' **[REPRESENTANTE]**' : ''}` - )) - .join('\n'), - }); - await interaction.reply({ - embeds: [embed], - ephemeral: true, - }); - } -} - -module.exports = new ListstaffsCommand(); \ No newline at end of file diff --git a/src/commands/register.js b/src/commands/register.js deleted file mode 100644 index 37d3885..0000000 --- a/src/commands/register.js +++ /dev/null @@ -1,206 +0,0 @@ -const { - SlashCommandBuilder, - PermissionsBitField, - SlashCommandStringOption, - SlashCommandUserOption, - SlashCommandIntegerOption, -} = require("discord.js"); -const Command = require('../structures/command.js'); -const config = require('../config.js'); - -class RegisterCommand extends Command{ - constructor(){ - super({ - active: true, - data: new SlashCommandBuilder() - .setName('register') - .setNameLocalization('pt-BR', 'registrar') - .setDescription('Adicione um novo membro a uma das suas equipes') - .setDMPermission(false) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ViewChannel) - .addUserOption( - new SlashCommandUserOption() - .setName('member') - .setNameLocalization('pt-BR', 'membro') - .setDescription('O membro que deve ser registrado nessa equipe') - .setRequired(true), - ) - .addStringOption( - new SlashCommandStringOption() - .setName('server') - .setNameLocalization('pt-BR', 'servidor') - .setDescription('Em qual das suas equipes esse membro deve ser registrado') - .setAutocomplete(true) - .setRequired(true), - ) - .addIntegerOption( - new SlashCommandIntegerOption() - .setName('role') - .setNameLocalization('pt-BR', 'cargo') - .setDescription('O cargo que esse membro tem nessa equipe') - .setRequired(true) - .addChoices( - { - name: 'Moderador', - value: 0, - }, - { - name: 'Administrador', - value: 1, - }, - { - name: 'Dono (único)', - value: 2, - }, - ), - ), - }); - } - - async execute(interaction, client){ - const guildModel = require('../models/guild.js'); - const memberModel = require('../models/member.js'); - const member = interaction.options.getMember('member'); - if(!member) return await interaction.reply({ - content: 'Membro não encontrado no servidor', - ephemeral: true, - }); - const guildId = interaction.options.getString('server'); - const guildDoc = await guildModel.findById(guildId); - if(!guildDoc) return await interaction.reply({ - content: 'Servidor não cadastrado no banco de dados', - ephemeral: true, - }); - if (guildDoc.pending) return await interaction.reply({ - content: 'O servidor ainda não foi aprovado na EPF', - ephemeral: true - }); - if((guildDoc.representative !== interaction.user.id) && !interaction.member.roles.cache.has(config.guard)){ - return await interaction.reply({ - content: 'Apenas o representante desse servidor pode adicionar novos membros a staff', - ephemeral: true, - }); - } - const isOldOwner = (guildDoc.owner === member.id); - const level = interaction.options.getInteger('role'); - if(level === 2){ - if(guildDoc.owner) return await interaction.reply({ - content: `O dono desse servidor já está cadastrado como <@${guildDoc.owner}>`, - ephemeral: true, - }); - guildDoc.owner = member.id; - await guildDoc.save(); - } - else if(guildDoc.owner === member.id){ - guildDoc.owner = null; - await guildDoc.save(); - } - const oldMemberDoc = await memberModel.findOneAndUpdate({ - user: member.id, - guild: guildId, - }, {$set: {admin: !!level}}, { - upsert: true, - setDefaultsOnInsert: true, - }); - if(oldMemberDoc){ - if(isOldOwner){ - const ownedGuildExists = await guildModel.exists({owner: member.id}); - if(!ownedGuildExists) await member.roles.remove(config.levels[2]); - } - else if(oldMemberDoc.admin){ - const adminGuildExists = await memberModel.exists({ - user: member.id, - admin: true, - }); - if(!adminGuildExists) await member.roles.remove(config.levels[1]); - } - else{ - const modGuildExists = await memberModel.exists({ - user: member.id, - admin: {$ne: true}, - }); - if(!modGuildExists) await member.roles.remove(config.levels[0]); - } - } - await member.roles.add(config.levels[level]); - await interaction.reply( - `${member} registrado em [${guildDoc.name}](https://discord.gg/${guildDoc.invite}) com sucesso` - ); - let updates = []; - const invite = await client - .fetchInvite(guildDoc.invite) - .catch(() => null); - if (invite) { - if (invite.guild && guildDoc.name !== invite.guild.name) { - updates.push( - `<:icon_guild:1037801942149242926> **|** Nome de servidor alterado: **${guildDoc.name}** -> **${invite.guild.name}**` - ); - guildDoc.name = invite.guild.name; - await guildDoc.save(); - if (guildDoc.role) { - await interaction.guild.roles.cache - .get(guildDoc.role) - .setName(invite.guild.name); - } - } - } else { - await interaction.followUp({ - content: ( - `Por favor adicione um convite válido para o seu servidor utilizando ` + - ` (cmd.name === 'changeinvite')).id}>` - ), - ephemeral: true, - }); - } - const memberDocs = await memberModel.find({guild: guildId}); - if(memberDocs.length >= config.membersForRole){ - let role; - if(guildDoc.role){ - role = interaction.guild.roles.cache.get(guildDoc.role); - } - else{ - role = await interaction.guild.roles.create({ - name: guildDoc.name, - mentionable: true, - color: 0x607D8B, - position: interaction.guild.roles.cache.get(config.serversDivRole).position + 1, - permissions: 0n, - }); - guildDoc.role = role.id; - await guildDoc.save(); - for(const otherMemberDoc of memberDocs){ - const otherMember = await interaction.guild.members.fetch(otherMemberDoc.user).catch(() => null); - if(otherMember) await otherMember.roles.add(role); - } - updates.push( - `<:mod:1040429385066491946> **|** Cargo para o servidor **${guildDoc.name}** criado` - ); - } - await member.roles.add(role); - } - - if (updates.length) { - client.emit('updateGuilds', false, updates.join('\n')); - updates = []; - } - } - - async autocomplete$server(interaction, value){ - const guildModel = require('../models/guild.js'); - const name = {$regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')}; - const representing = ( - interaction.member.roles.cache.has(config.guard) - ? await guildModel.find({name}).sort({name: 1}).limit(25) - : await guildModel.find({ - representative: interaction.user.id, - name, - }).sort({name: 1}).limit(25) - ); - return representing.map(guildDoc => ({ - name: guildDoc.name, - value: guildDoc._id, - })); - } -} - -module.exports = new RegisterCommand(); diff --git a/src/commands/remove.js b/src/commands/remove.js deleted file mode 100644 index ebaa894..0000000 --- a/src/commands/remove.js +++ /dev/null @@ -1,168 +0,0 @@ -const { - SlashCommandBuilder, - PermissionsBitField, - SlashCommandUserOption, - SlashCommandStringOption, -} = require('discord.js'); -const Command = require('../structures/command.js'); -const config = require('../config.js'); - -class RemoveCommand extends Command{ - constructor(){ - super({ - active: true, - data: new SlashCommandBuilder() - .setName('remove') - .setNameLocalization('pt-BR', 'remover') - .setDescription('Remove um membro de uma staff') - .setDMPermission(false) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ViewChannel) - .addUserOption( - new SlashCommandUserOption() - .setName('member') - .setNameLocalization('pt-BR', 'membro') - .setDescription('O membro que deve ser removido dessa equipe') - .setRequired(true), - ) - .addStringOption( - new SlashCommandStringOption() - .setName('server') - .setNameLocalization('pt-BR', 'servidor') - .setDescription('De qual das suas equipes esse membro deve ser removido') - .setAutocomplete(true) - .setRequired(true), - ) - }); - } - - async execute(interaction, client){ - const guildModel = require('../models/guild.js'); - const memberModel = require('../models/member.js'); - const member = interaction.options.getMember('member'); - if(!member) return await interaction.reply({ - content: 'Membro não encontrado no servidor', - ephemeral: true, - }); - const guildId = interaction.options.getString('server'); - const guildDoc = await guildModel.findById(guildId); - if(!guildDoc) return await interaction.reply({ - content: 'Servidor não cadastrado no banco de dados', - ephemeral: true, - }); - if(guildDoc.representative === interaction.user.id){ - if(member.id === interaction.user.id) return await interaction.reply({ - content: 'Você não pode se remover de um servidor que você representa', - ephemeral: true, - }); - } - else if(!interaction.member.roles.cache.has(config.guard)){ - return await interaction.reply({ - content: 'Apenas o representante desse servidor pode remover membros da staff', - ephemeral: true, - }); - } - const memberDoc = await memberModel.findOneAndDelete({ - user: member.id, - guild: guildId, - }); - if (!memberDoc) return await interaction.reply({ - content: 'Esse membro não pertence a staff desse servidor', - ephemeral: true - }); - if(guildDoc.owner === member.id){ - guildDoc.owner = null; - await guildDoc.save(); - const ownedGuildExists = await guildModel.exists({ - owner: member.id, - pending: { - $ne: true - } - }); - if(!ownedGuildExists) await member.roles.remove(config.levels[2]); - } - else if(memberDoc.admin){ - const adminStaffs = await memberModel.find({ - user: member.id, - admin: true - }).populate('guild'); - const isStillAdmin = adminStaffs.some((doc) => doc.guild.pending !== true); - if(!isStillAdmin) await member.roles.remove(config.levels[1]); - } - else{ - const modStaffs = await memberModel.find({ - user: member.id, - admin: { - $ne: true - } - }).populate('guild'); - const isStillMod = modStaffs.some((doc) => doc.guild.pending !== true); - if(!isStillMod) await member.roles.remove(config.levels[0]); - } - await interaction.reply( - `${member} removido de [${guildDoc.name}](https://discord.gg/${guildDoc.invite}) com sucesso` - ); - let updates = []; - const invite = await client - .fetchInvite(guildDoc.invite) - .catch(() => null); - if (invite) { - if (invite.guild && guildDoc.name !== invite.guild.name) { - updates.push( - `<:icon_guild:1037801942149242926> **|** Nome de servidor alterado: **${guildDoc.name}** -> **${invite.guild.name}**` - ); - guildDoc.name = invite.guild.name; - await guildDoc.save(); - if (guildDoc.role) { - await interaction.guild.roles.cache - .get(guildDoc.role) - .setName(invite.guild.name); - } - } - } - else{ - await interaction.followUp({ - content: ( - `Por favor adicione um convite válido para o seu servidor utilizando ` + - ` (cmd.name === 'changeinvite')).id}>` - ), - ephemeral: true, - }); - } - if(guildDoc.role){ - await member.roles.remove(guildDoc.role); - const memberCount = await memberModel.countDocuments({guild: guildId}); - if(memberCount < config.membersForRole){ - await interaction.guild.roles.delete(guildDoc.role); - guildDoc.role = null; - await guildDoc.save(); - updates.push( - `<:mod:1040429385066491946> **|** O servidor **${guildDoc.name}** perdeu o seu cargo.` - ); - } - } - - if (updates.length) { - client.emit('updateGuilds', false, updates.join('\n')); - updates = []; - } - } - - async autocomplete$server(interaction, value){ - const guildModel = require('../models/guild.js'); - const name = {$regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')}; - const representing = ( - interaction.member.roles.cache.has(config.guard) - ? await guildModel.find({name}).sort({name: 1}).limit(25) - : await guildModel.find({ - representative: interaction.user.id, - name, - }).sort({name: 1}).limit(25) - ); - return representing.map(guildDoc => ({ - name: guildDoc.name, - value: guildDoc._id, - })); - } -} - -module.exports = new RemoveCommand(); diff --git a/src/commands/representative.js b/src/commands/representative.js deleted file mode 100644 index 9559996..0000000 --- a/src/commands/representative.js +++ /dev/null @@ -1,95 +0,0 @@ -const { - SlashCommandBuilder, - PermissionsBitField, - SlashCommandStringOption, - SlashCommandUserOption, -} = require('discord.js'); -const Command = require('../structures/command.js'); -const config = require('../config.js'); - -class RepresentativeCommand extends Command{ - constructor(){ - super({ - active: true, - data: new SlashCommandBuilder() - .setName('representative') - .setNameLocalization('pt-BR', 'representante') - .setDescription('Define manualmente o representante de um servidor') - .setDMPermission(false) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ViewChannel) - .addStringOption( - new SlashCommandStringOption() - .setName('server') - .setNameLocalization('pt-BR', 'servidor') - .setDescription('O servidor para definir um representante') - .setAutocomplete(true) - .setRequired(true), - ) - .addUserOption( - new SlashCommandUserOption() - .setName('representative') - .setNameLocalization('pt-BR', 'representante') - .setDescription('O usuário que será o representante do servidor') - .setRequired(true), - ), - }); - } - - async execute(interaction){ - const member = interaction.options.getMember('representative'); - if(!member) return await interaction.reply({ - content: 'Membro não encontrado', - ephemeral: true, - }); - const guildModel = require('../models/guild.js'); - const guildId = interaction.options.getString('server'); - const guildDoc = await guildModel.findById(guildId); - if(!guildDoc) return await interaction.reply({ - content: 'Servidor não encontrado no banco de dados', - ephemeral: true, - }); - if (guildDoc.pending) return await interaction.reply({ - content: 'O servidor ainda não foi aprovado na EPF', - ephemeral: true - }); - if((guildDoc.representative !== interaction.user.id) && !interaction.member.roles.cache.has(config.guard)){ - return await interaction.reply({ - content: 'Apenas o representante desse servidor pode definir um novo representante', - ephemeral: true, - }); - } - const memberModel = require('../models/member.js'); - const memberExists = await memberModel.exists({ - user: member.id, - guild: guildId, - }); - if(!memberExists) return await interaction.reply({ - content: 'Esse membro não faz parte da staff desse servidor', - ephemeral: true, - }); - guildDoc.representative = member.id; - await guildDoc.save(); - await interaction.reply( - `${member} definido como representante de [\`${guildDoc.name}\`](https://discord.gg/${guildDoc.invite})` - ); - } - - async autocomplete$server(interaction, value){ - const guildModel = require('../models/guild.js'); - const name = {$regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')}; - const representing = ( - interaction.member.roles.cache.has(config.guard) - ? await guildModel.find({name}).sort({name: 1}).limit(25) - : await guildModel.find({ - representative: interaction.user.id, - name, - }).sort({name: 1}).limit(25) - ); - return representing.map(guildDoc => ({ - name: guildDoc.name, - value: guildDoc._id, - })); - } -} - -module.exports = new RepresentativeCommand(); \ No newline at end of file diff --git a/src/commands/staff.js b/src/commands/staff.js deleted file mode 100644 index 80381c1..0000000 --- a/src/commands/staff.js +++ /dev/null @@ -1,84 +0,0 @@ -const { SlashCommandBuilder, PermissionsBitField, SlashCommandStringOption, EmbedBuilder } = require('discord.js'); -const Command = require('../structures/command.js'); - -class StaffCommand extends Command{ - constructor(){ - super({ - active: true, - data: new SlashCommandBuilder() - .setName('staff') - .setDescription('Mostra todos os membros da staff desse servidor presentes') - .setDMPermission(true) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ViewChannel) - .addStringOption( - new SlashCommandStringOption() - .setName('server') - .setNameLocalization('pt-BR', 'servidor') - .setDescription('O servidor que deseja consultar a staff') - .setAutocomplete(true) - .setRequired(true), - ), - }); - } - - async execute(interaction, client){ - const memberModel = require('../models/member.js'); - const guildId = interaction.options.getString('server'); - const memberDocs = await memberModel.find({guild: guildId}); - if(!memberDocs.length) return await interaction.reply({ - content: 'Nenhum membro desse servidor cadastrado', - ephemeral: true, - }); - const guildModel = require('../models/guild.js'); - const guildDoc = await guildModel.findOne({ - _id: guildId, - pending: {$ne: true}, - }); - if(!guildDoc) return await interaction.reply({ - content: 'Servidor não encontrado', - ephemeral: true, - }); - const invite = await client.fetchInvite(guildDoc.invite).catch(() => null); - let description = `Representante: <@${guildDoc.representative}>`; - if(guildDoc.owner) description += `\nDono: <@${guildDoc.owner}>`; - const embed = new EmbedBuilder() - .setColor(0x2f3136) - .setAuthor({ - name: `Staff de ${guildDoc.name}`, - iconURL: invite?.guild.iconURL({dynamic: true}), - url: `https://discord.gg/${guildDoc.invite}`, - }) - .setDescription(guildDoc.role ? `${description}\nCargo: <@&${guildDoc.role}>` : description); - const adminDocs = memberDocs.filter(doc => doc.admin); - if(adminDocs.length) embed.addFields({ - name: `Administradores (${adminDocs.length})`, - value: adminDocs.map(doc => `<@${doc.user}>`).join('\n'), - }); - const modDocs = memberDocs.filter(doc => !doc.admin); - if(modDocs.length) embed.addFields({ - name: `Moderadores (${modDocs.length})`, - value: modDocs.map(doc => `<@${doc.user}>`).join('\n'), - }); - await interaction.reply({ - embeds: [embed], - ephemeral: true, - }); - } - - async autocomplete$server(_, value){ - const guildModel = require('../models/guild.js'); - const guildDocs = await guildModel - .find({ - name: {$regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')}, - pending: {$ne: true}, - }) - .sort({name: 1}) - .limit(25); - return guildDocs.map(doc => ({ - name: doc.name, - value: doc._id, - })); - } -} - -module.exports = new StaffCommand(); diff --git a/src/commands/updateservers.js b/src/commands/updateservers.js deleted file mode 100644 index 3347c94..0000000 --- a/src/commands/updateservers.js +++ /dev/null @@ -1,78 +0,0 @@ -const { SlashCommandBuilder, PermissionsBitField, ButtonBuilder, ButtonStyle, ActionRowBuilder } = require('discord.js'); -const Command = require('../structures/command.js'); - -class UpdateserversCommand extends Command{ - constructor(){ - super({ - active: true, - data: new SlashCommandBuilder() - .setName('updateservers') - .setNameLocalization('pt-BR', 'atualizarservidores') - .setDescription('Atualiza a lista de servidores que fazem parte da EPF') - .setDMPermission(true) - .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles), - }); - } - - async execute(interaction, client){ - const constantsModel = require('../models/constants'); - const constants = await constantsModel.getConstants(); - if (constants.updatingGuildsChannel) return await interaction.reply({ - content: 'Uma atualização já foi solicitada, aguarde o término da atualização para solicitar novamente.', - ephemeral: true - }); - await interaction.deferReply({ephemeral: true}); - if ((new Date() - constants.lastGuildsChannelUpdate) < 600000) { - const confirmButton = new ButtonBuilder() - .setCustomId('collector:confirm') - .setLabel('Confirmar') - .setStyle(ButtonStyle.Success); - const cancelButton = new ButtonBuilder() - .setCustomId('collector:cancel') - .setLabel('Cancelar') - .setStyle(ButtonStyle.Danger); - - const reply = await interaction.editReply({ - content: 'A última atualização foi a menos de 10 minutos, deseja atualizar novamente?', - components: [new ActionRowBuilder().setComponents(confirmButton, cancelButton)] - }) - - const collector = reply.createMessageComponentCollector({ - time: 60000, - filter: (i) => i.user.id === interaction.user.id, - max: 1 - }); - - collector.on('collect', async (i) => { - if (i.customId === 'collector:cancel') { - await i.update({ content: 'Operação cancelada', components: [] }); - return; - } - await i.update({ content: 'Lista de servidores atualizada', components: [] }); - client.emit( - 'updateGuilds', - true, - `<:e_repeat:1049017561175568404> **|** A lista de servidores foi atualizada pois o ${interaction.user} pediu!` - ); - }); - - collector.on('end', async (_, reason) => { - if (reason === 'time') { - await interaction.editReply({ content: 'Operação cancelada', components: [] }); - return; - } - }) - - return; - } - - client.emit( - 'updateGuilds', - false, - `<:e_repeat:1049017561175568404> **|** A lista de servidores foi atualizada pois o ${interaction.user} pediu!` - ); - await interaction.editReply('Lista de servidores atualizada'); - } -} - -module.exports = new UpdateserversCommand() diff --git a/src/components/buttons/aprove-dev-request.js b/src/components/buttons/aprove-dev-request.js deleted file mode 100644 index 18a0cfd..0000000 --- a/src/components/buttons/aprove-dev-request.js +++ /dev/null @@ -1,42 +0,0 @@ -const { EmbedBuilder, parseWebhookURL } = require("discord.js"); -const config = require('../../config'); - -module.exports = { - data: { - name: "aprove-dev-request" - }, - - async execute(interaction, client) { - const member = await interaction.guild.members.fetch(interaction.customId.split(":")[1]).catch(() => null); - - if (member) await member.roles.add(config.devID); - else return interaction[interaction.replied || interaction.deferred ? 'followUp' : 'reply']({ content: "Membro não encontrado... Não consegui dar o cargo à ele", ephemeral: true }); - - await interaction.deferReply({ ephemeral: true }); - - const embed = EmbedBuilder.from(interaction.message.embeds[0]); - - const message = await member.send({ content: `Parabéns, você foi aprovado e está apto para receber o cargo de Developer no EPF!` }).catch(() => null); - if (!message) await interaction.followUp({ content: "Não foi possível entrar em contato com o requisitante do cargo", ephemeral: true }); - - embed.setColor('#58e600').setTitle("Formulário Aprovado - Developer"); - await interaction.message.edit({ embeds: [embed], components: [] }); - - await interaction.message.thread.setArchived(true); - - await interaction.followUp({ content: "Desenvolvedor Aprovado", ephemeral: true }); - - const webhook = parseWebhookURL(process.env.OFFTOPIC_WEBHOOK); - await interaction.client.fetchWebhook(webhook.id, webhook.token) - .then(async (webhook) => { - await webhook.send({ - content: `<:icons_djoin:875754472834469948> O membro ${member} foi aprovado como desenvolvedor na EPF`, - username: interaction.guild.name, - avatarURL: interaction.guild.iconURL(), - allowedMentions: { - users: [] - } - }) - }); - } -} diff --git a/src/components/buttons/aprove-server-form.js b/src/components/buttons/aprove-server-form.js deleted file mode 100644 index 07420a5..0000000 --- a/src/components/buttons/aprove-server-form.js +++ /dev/null @@ -1,68 +0,0 @@ -const { EmbedBuilder, parseWebhookURL } = require("discord.js"); -const config = require("../../config"); -const guildModel = require("../../models/guild"); - -module.exports = { - data: { - name: "aprove-server-form" - }, - async execute(interaction, client) { - const embed = EmbedBuilder.from(interaction.message.embeds[0]); - - await interaction.deferReply({ephemeral: true}); - - const guildDoc = await guildModel.findById(interaction.customId.split(':')[1]); - - const guildMember = await interaction.guild.members.fetch(guildDoc.representative).catch(() => null); - if (!guildMember) return await interaction.editReply(`O representante não está mais no servidor (${guildDoc.representative})`); - - guildDoc.pending = false; - await guildDoc.save(); - - if(guildDoc.owner === guildMember.id){ - await guildMember.roles.add(config.levels[2]); - } - else{ - const memberModel = require('../../models/member.js'); - const memberDoc = await memberModel.findOne({ - user: guildMember.id, - guild: guildDoc._id, - }); - await guildMember.roles.add(config.levels[+memberDoc.admin]); - } - - const message = await guildMember.send({ content: `Parabéns, o seu servidor \`${guildDoc.name}\` foi aprovado na EPF!` }).catch(() => null); - if (!message) await interaction.editReply("Não foi possível entrar em contato com o representante do servidor"); - - embed.setColor('#58e600').setTitle("Formulário Aprovado"); - await interaction.message.edit({ embeds: [embed], components: [] }); - - await interaction.message.thread.setArchived(true); - - if(interaction.replied){ - await interaction.followUp({ - content: "Servidor Aprovado", - ephemeral: true, - }); - } - else{ - await interaction.editReply("Servidor Aprovado"); - } - - client.emit( - 'updateGuilds', - false, - `<:icon_guild:1037801942149242926> **|** Novo servidor aprovado: **${guildDoc.name}**` - ); - - const webhook = parseWebhookURL(process.env.OFFTOPIC_WEBHOOK); - await interaction.client.fetchWebhook(webhook.id, webhook.token) - .then(async (webhook) => { - await webhook.send({ - content: `<:icons_djoin:875754472834469948> O servidor **${guildDoc.name}** entrou para a EPF`, - username: interaction.guild.name, - avatarURL: interaction.guild.iconURL(), - }); - }); - }, -}; diff --git a/src/components/buttons/refuse-dev-request.js b/src/components/buttons/refuse-dev-request.js deleted file mode 100644 index fd16e58..0000000 --- a/src/components/buttons/refuse-dev-request.js +++ /dev/null @@ -1,23 +0,0 @@ -const { ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } = require("discord.js"); - -module.exports = { - data: { - name: "refuse-dev-request" - }, - - async execute(interaction, client) { - const modal = new ModalBuilder() - .setTitle("Elite Penguin Force") - .setCustomId(`devRefuseReasonModal:${interaction.customId.split(':')[1]}`); - - const reasonInput = new TextInputBuilder() - .setCustomId("reason") - .setLabel("Defina um motivo") - .setRequired(false) - .setStyle(TextInputStyle.Paragraph); - - modal.addComponents(new ActionRowBuilder().setComponents(reasonInput)); - - await interaction.showModal(modal); - } -} \ No newline at end of file diff --git a/src/components/buttons/refuse-server-form.js b/src/components/buttons/refuse-server-form.js deleted file mode 100644 index 37e4ad5..0000000 --- a/src/components/buttons/refuse-server-form.js +++ /dev/null @@ -1,22 +0,0 @@ -const { ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } = require("discord.js"); - -module.exports = { - data: { - name: "refuse-server-form" - }, - async execute(interaction, client) { - const modal = new ModalBuilder() - .setTitle("Elite Penguin Force") - .setCustomId(`serverRefuseReasonModal:${interaction.customId.split(':')[1]}`); - - const reasonInput = new TextInputBuilder() - .setCustomId("reason") - .setLabel("Defina um motivo") - .setRequired(false) - .setStyle(TextInputStyle.Paragraph); - - modal.addComponents(new ActionRowBuilder().setComponents(reasonInput)); - - await interaction.showModal(modal); - } -} \ No newline at end of file diff --git a/src/components/buttons/register-server-form.js b/src/components/buttons/register-server-form.js deleted file mode 100644 index a9f90b5..0000000 --- a/src/components/buttons/register-server-form.js +++ /dev/null @@ -1,112 +0,0 @@ -const { ButtonBuilder, ModalBuilder, EmbedBuilder, ActionRowBuilder, TextInputBuilder, TextInputStyle, ButtonStyle } = require ('discord.js'); - -module.exports = { - data: { - name: `register-server-form` - }, - async execute(interaction, client) { - const confirmationEmbed = new EmbedBuilder() - .setTitle("Leia antes de continuar") - .setDescription( - "Esse formulário deve ser preenchido para associar um **novo servidor** à EPF, se você deseja apenas " + - "ser registrado em um servidor que já faz parte da comunidade, por favor peça para o representante do" + - " servidor em questão te registrar.\n\n" + - "Ao confirmar, você concorda que você será o representante do servidor dentro da EPF (Elite Penguin " + - "Force). E que a responsabilidade da sua staff dentro do nosso servidor será inteiramente sua.\n\n" + - "Tenha em mente de que a resposta do formulário será enviada diretamente em sua DM, então mantenha " + - "ela aberta para receber a resposta." - ) - .setColor(0x2f3136); - - const buttonConfirm = new ButtonBuilder() - .setCustomId("collector:confirm") - .setLabel("Confirmar") - .setStyle(ButtonStyle.Success); - - const buttonCancel = new ButtonBuilder() - .setCustomId("collector:cancel") - .setLabel("Cancelar") - .setStyle(ButtonStyle.Danger); - - const row = new ActionRowBuilder() - .setComponents(buttonConfirm, buttonCancel); - - const reply = await interaction.reply({ embeds: [confirmationEmbed], components: [row], ephemeral: true, fetchReply: true }); - - const filter = (i) => i.user.id === interaction.user.id; - const collector = reply.createMessageComponentCollector({ - filter, - time: 60000, - max: 1 - }); - - collector.on("collect", async (i) => { - if (i.customId === "collector:confirm") { - const modal = new ModalBuilder() - .setCustomId(`serverRequestModal`) - .setTitle(`Elite Penguin Force`) - - const serverLink = new TextInputBuilder() - .setCustomId(`serverLink`) - .setLabel(`Link permanente do servidor`) - .setRequired(true) - .setStyle(TextInputStyle.Short) - .setMinLength(10) - .setMaxLength(30); - - const serverRole = new TextInputBuilder() - .setCustomId(`serverRole`) - .setLabel(`Qual o seu cargo nesse servidor?`) - .setRequired(true) - .setPlaceholder("\"mod\", \"adm\", ou \"dono\" (posse)") - .setStyle(TextInputStyle.Paragraph) - .setMinLength(3) - .setMaxLength(4); - - const serverAbout = new TextInputBuilder() - .setCustomId(`serverAbout`) - .setLabel(`Conte-nos mais sobre esse servidor`) - .setRequired(true) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(20) - .setMaxLength(150); - - const epfAbout = new TextInputBuilder() - .setCustomId(`epfAbout`) - .setLabel(`Por onde você conheceu a EPF?`) - .setRequired(true) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(10) - .setMaxLength(150); - - modal.addComponents(new ActionRowBuilder().addComponents(serverLink)); - modal.addComponents(new ActionRowBuilder().addComponents(serverRole)); - modal.addComponents(new ActionRowBuilder().addComponents(serverAbout)); - modal.addComponents(new ActionRowBuilder().addComponents(epfAbout)); - - return await i.showModal(modal); - } - await i.reply({ - content: 'Operação cancelada.', - ephemeral: true, - }); - }); - collector.on('end', async collected => { - if(!reply.editable) return; - if(collected.size){ - buttonConfirm.setDisabled(true); - buttonCancel.setDisabled(true); - row.setComponents(buttonConfirm, buttonCancel); - return await interaction.editReply({ - embeds: [confirmationEmbed], - components: [row], - }); - } - await interaction.editReply({ - content: 'Tempo esgotado.', - embeds: [], - components: [], - }); - }); - } -} \ No newline at end of file diff --git a/src/components/buttons/request-dev-role-form.js b/src/components/buttons/request-dev-role-form.js deleted file mode 100644 index 8f79ca9..0000000 --- a/src/components/buttons/request-dev-role-form.js +++ /dev/null @@ -1,45 +0,0 @@ -const { ModalBuilder, ActionRowBuilder, TextInputBuilder, TextInputStyle } = require('discord.js'); - -module.exports = { - data: { - name: "request-dev-role-form" - }, - - async execute(interaction, client) { - const modal = new ModalBuilder() - .setCustomId(`devRequestModal`) - .setTitle(`Elite Penguin Force`) - - const githubLink = new TextInputBuilder() - .setCustomId(`githubLink`) - .setLabel(`O link do seu perfil no GitHub`) - .setRequired(true) - .setStyle(TextInputStyle.Short) - - const experienceInfo = new TextInputBuilder() - .setCustomId(`experienceInfo`) - .setLabel(`Qual sua atuação na área?`) - .setRequired(true) - .setPlaceholder("Criação de sites/bots, etc...") - .setStyle(TextInputStyle.Paragraph) - - const example = new TextInputBuilder() - .setCustomId(`example`) - .setLabel(`Nos envie um exemplo de um projeto seu`) - .setRequired(true) - .setStyle(TextInputStyle.Paragraph) - - const botInvite = new TextInputBuilder() - .setCustomId(`botInvite`) - .setLabel(`Convite do seu bot (se houver)`) - .setStyle(TextInputStyle.Short) - .setRequired(false) - - modal.addComponents(new ActionRowBuilder().addComponents(githubLink)); - modal.addComponents(new ActionRowBuilder().addComponents(experienceInfo)); - modal.addComponents(new ActionRowBuilder().addComponents(example)); - modal.addComponents(new ActionRowBuilder().addComponents(botInvite)); - - return await interaction.showModal(modal); - } -} \ No newline at end of file diff --git a/src/components/modals/devRefuseReasonModal.js b/src/components/modals/devRefuseReasonModal.js deleted file mode 100644 index 32fbb4b..0000000 --- a/src/components/modals/devRefuseReasonModal.js +++ /dev/null @@ -1,42 +0,0 @@ -const { EmbedBuilder } = require("discord.js"); - -module.exports = { - data: { - name: 'devRefuseReasonModal' - }, - async execute(interaction, client) { - const embed = EmbedBuilder.from(interaction.message.embeds[0]); - - const reasonInput = interaction.fields.getTextInputValue("reason"); - - embed.setColor('#d12c2c') - .setTitle("Pedido Recusado") - .addFields([ - { name: "Recusado pelo motivo", value: `${reasonInput || "Sem Motivo"}`, inline: true }, - { name: "Recusado por", value: `${interaction.user} (${interaction.user.id})`, inline: true }, - ]) - - await interaction.message.edit({ embeds: [embed], components: [] }); - - await interaction.message.thread.setArchived(true); - - const member = await interaction.guild.members.fetch(interaction.customId.split(':')[1]).catch(() => null); - const content = 'Lamentamos informar que a sua requisição de cargo de desenvolvedor na EPF foi recusada.'; - if (member) { - await member - .send(reasonInput ? `${content}\nMotivo: ${reasonInput}` : content) - .catch(async () => { - await interaction.reply({ - content: "Não foi possível entrar em contato com o membro", - ephemeral: true, - }); - }); - } - - await interaction[interaction.replied || interaction.deferred ? 'followUp' : 'reply']({ - content: "Desenvolvedor Recusado", - ephemeral: true, - }); - } -} - diff --git a/src/components/modals/devRequestModal.js b/src/components/modals/devRequestModal.js deleted file mode 100644 index aa84181..0000000 --- a/src/components/modals/devRequestModal.js +++ /dev/null @@ -1,64 +0,0 @@ -const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js') -const config = require('../../config'); - -module.exports = { - data: { - name: 'devRequestModal' - }, - - async execute(interaction, client) { - const invite = interaction.fields.getTextInputValue("botInvite"); - const aproveChannel = client.channels.cache.get(config.aproveChannel); - - var regex = /discord\.com\/(api\/)?oauth2\/authorize/gi; - var bot = null, botInvite = null; - - if (invite !== null) { - if (regex.test(invite)) { - const clientId = invite.split("client_id=")[1].split("&")[0]; - - bot = await client.users.fetch(clientId).catch(() => null); - - if (clientId !== null) { - botInvite = invite.replace(/permissions=[0-9]*/ig, "permissions=0"); - } - } - } - - const embed = new EmbedBuilder() - .setTitle(`Novo formulário - Desenvolvedor`) - .setColor('#e3ba03') - .setFields([ - { name: "Enviado por", value: `${interaction.user.username} (${interaction.user.id})`, inline: true }, - { name: "GitHub", value: `${interaction.fields.getTextInputValue("githubLink")}`, inline: true }, - { name: "Atuação", value: `${interaction.fields.getTextInputValue("experienceInfo")}`, inline: true }, - { name: "Exemplo", value: `${interaction.fields.getTextInputValue("example")}`, inline: true }, - { name: "Bot", value: bot ? `${bot?.tag} (${bot?.id})` : "Não tem", inline: true }, - { name: "Convite", value: `${bot ? (botInvite || "Inválido") : "Não tem"}`, inline: true }, - ]); - - const row = new ActionRowBuilder() - .setComponents( - new ButtonBuilder() - .setCustomId(`aprove-dev-request:${interaction.user.id}`) - .setLabel("Aprovar") - .setStyle(ButtonStyle.Success), - new ButtonBuilder() - .setCustomId(`refuse-dev-request:${interaction.user.id}`) - .setLabel("Recusar") - .setStyle(ButtonStyle.Danger) - ); - - const message = await aproveChannel.send({ - embeds: [embed], components: [row] - }); - - await message.startThread({ name: `Dev ${interaction.user.username}` }); - - await interaction.reply({ - content: 'Seu pedido foi enviado para avaliação.', - ephemeral: true, - }); - } -} - diff --git a/src/components/modals/serverRefuseReasonModal.js b/src/components/modals/serverRefuseReasonModal.js deleted file mode 100644 index aba76ee..0000000 --- a/src/components/modals/serverRefuseReasonModal.js +++ /dev/null @@ -1,46 +0,0 @@ -const { EmbedBuilder } = require("discord.js"); - -module.exports = { - data: { - name: 'serverRefuseReasonModal' - }, - async execute(interaction, client) { - await interaction.deferReply({ ephemeral: true }); - - const embed = EmbedBuilder.from(interaction.message.embeds[0]); - - const reasonInput = interaction.fields.getTextInputValue("reason"); - - embed.setColor('#d12c2c') - .setTitle("Formulário Recusado") - .addFields([ - { name: "Recusado pelo motivo", value: `${reasonInput || "Sem Motivo"}`, inline: true }, - { name: "Recusado por", value: `${interaction.user} (${interaction.user.id})`, inline: true }, - ]) - - await interaction.message.edit({ embeds: [embed], components: [] }); - - await interaction.message.thread.setArchived(true); - - const guildModel = require('../../models/guild.js'); - const guildDoc = await guildModel.findByIdAndDelete(interaction.customId.split(':')[1]); - - const member = await interaction.guild.members.fetch(guildDoc.representative).catch(() => null); - const content = `O servidor \`${guildDoc.name}\` foi recusado da EPF.`; - if(member) { - await member - .send(reasonInput ? `${content}\nMotivo: ${reasonInput}` : content) - .catch(async () => { - await interaction.reply({ - content: "Não foi possível entrar em contato com o representante do servidor", - ephemeral: true, - }); - }); - } - - await interaction.editReply({ - content: "Servidor Recusado", - ephemeral: true, - }); - } -} \ No newline at end of file diff --git a/src/components/modals/serverRequestModal.js b/src/components/modals/serverRequestModal.js deleted file mode 100644 index 986f607..0000000 --- a/src/components/modals/serverRequestModal.js +++ /dev/null @@ -1,118 +0,0 @@ - -const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require ('discord.js') -const config = require('../../config'); - -module.exports = { - data: { - name: `serverRequestModal` - }, - - async execute(interaction, client) { - - const inviteInput = interaction.fields.getTextInputValue("serverLink"); - const fetchedInvite = await client.fetchInvite(inviteInput) - .then((invite) => invite) - .catch(() => null); - - const roleInput = interaction.fields.getTextInputValue("serverRole").toLowerCase(); - const parsedRole = roleInput === 'mod' ? 'Moderador' : roleInput === 'adm' ? 'Administrador' : roleInput === 'dono' ? 'Dono' : null; - - const serverAbout = interaction.fields.getTextInputValue("serverAbout"); - const epfAbout = interaction.fields.getTextInputValue("epfAbout"); - - const errors = []; - - const guildModel = require('../../models/guild.js'); - if (!fetchedInvite) errors.push("Convite Inválido"); - else { - if (fetchedInvite._expiresTimestamp) errors.push("Convite Expirável"); - if (fetchedInvite.maxUses) errors.push("Convite com limite de uso"); - const guildDoc = await guildModel.findById(fetchedInvite.guild.id); - if(guildDoc){ - if(guildDoc.pending){ - return await interaction.reply({ - content: 'Esse servidor já está em processo de avaliação', - ephemeral: true, - }); - } - else{ - errors.push('Esse servidor já faz parte da EPF'); - } - } - if (fetchedInvite.memberCount < 5000) errors.push("O servidor não atingiu os requisitos mínimos"); - } - if (!parsedRole) errors.push(`Cargo Inválido (${roleInput})`); - - const userEmbed = new EmbedBuilder() - .setTitle(`Formulário enviado`) - .setDescription(`Sua solicitação foi enviada com sucesso\n\nFazer esse formulário **não** garante a entrada da sua comunidade na EPF. \nSua requisição passará por uma analise e a equipe determinará se seu servidor está apto ou não.`) - .setColor('#fccf03') - - await interaction.reply({ - embeds: [userEmbed], - ephemeral: true - }); - - const aproveChannel = client.channels.cache.get(config.aproveChannel); - - if (errors.length) { - const embed = new EmbedBuilder() - .setTitle("Formulário Recusado") - .setColor("#d12c2c") - .setFields([ - { name: "Enviado por", value: `${interaction.user.username} (${interaction.user.id})`, inline: true }, - { name: "Link permanente do servidor", value: `${fetchedInvite?.url || "Inválido"}`, inline: true }, - { name: "Cargo Principal no Servidor", value: `${parsedRole || "Inválido"}`, inline: true }, - { name: "Sobre o servidor", value: `${serverAbout}`, inline: false }, - { name: "Por onde conheceu a EPF", value: `${epfAbout}`, inline: false }, - { name: "Recusado pelo Motivo", value: `${errors.join("\n")}`, inline: true }, - { name: "Recusado por", value: `${client.user} (${client.user.id})`, inline: true } - ]); - - await interaction.member.send({ content: `O seu servidor foi recusado automaticamente pelo seguinte motivo:\n${errors.join("\n")}`, ephemeral: true }).catch(() => null); - - return await aproveChannel.send({ embeds: [embed] }); - } - - const embed = new EmbedBuilder() - .setTitle(`Novo formulário`) - .setColor('#fccf03') - .setFields([ - { name: "Enviado por", value: `${interaction.user.username} (${interaction.user.id})`, inline: true }, - { name: "Link permanente do servidor", value: `${fetchedInvite?.url || "Inválido"}`, inline: true }, - { name: "Cargo Principal no Servidor", value: `${parsedRole || "Inválido"}`, inline: true }, - { name: "Sobre o servidor", value: `${serverAbout}`, inline: false }, - { name: "Por onde conheceu a EPF", value: `${epfAbout}`, inline: false } - ]); - - const row = new ActionRowBuilder() - .setComponents( - new ButtonBuilder() - .setCustomId(`aprove-server-form:${fetchedInvite.guild.id}`) - .setLabel("Aprovar") - .setStyle(ButtonStyle.Success), - new ButtonBuilder() - .setCustomId(`refuse-server-form:${fetchedInvite.guild.id}`) - .setLabel("Recusar") - .setStyle(ButtonStyle.Danger), - ); - - const newGuildDoc = new guildModel({ - _id: fetchedInvite.guild.id, - representative: interaction.user.id, - invite: fetchedInvite.code, - name: fetchedInvite.guild.name, - owner: (roleInput === 'dono') ? interaction.user.id : null, - pending: true, - }); - await newGuildDoc.save(); - const memberModel = require('../../models/member.js'); - await memberModel.create({ - user: interaction.user.id, - guild: newGuildDoc._id, - admin: ['adm', 'dono'].includes(roleInput), - }); - const message = await aproveChannel.send({embeds: [embed], components: [row]}); - await message.startThread({name: `Server ${fetchedInvite.guild.name}`}); - } -} \ No newline at end of file diff --git a/src/components/selectMenus/form-menu.js b/src/components/selectMenus/form-menu.js deleted file mode 100644 index cdb3280..0000000 --- a/src/components/selectMenus/form-menu.js +++ /dev/null @@ -1,61 +0,0 @@ -const { SlashCommandBuilder, ModalBuilder, ActionRowBuilder, TextInputBuilder, TextInputStyle } = require ('discord.js') - -module.exports = { - data: { - name: `form-menu` - }, - async execute(interaction, client) { - const modal = new ModalBuilder() - .setCustomId(`serverRequestModal`) - .setTitle(`Elite Penguin Force`) - - const serverLink = new TextInputBuilder() - .setCustomId(`serverLink`) - .setLabel(`Link permanente do servidor`) - .setRequired(true) - .setStyle(TextInputStyle.Short) - .setMinLength(10) - .setMaxLength(30); - - const question1 = new TextInputBuilder() - .setCustomId(`question1`) - .setLabel(`Você representa esse servidor?`) - .setRequired(true) - .setStyle(TextInputStyle.Short) - .setMinLength(3) - .setMaxLength(3); - - - const question2 = new TextInputBuilder() - .setCustomId(`question2`) - .setLabel(`Conte-nos mais sobre esse servidor`) - .setRequired(true) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(20) - .setMaxLength(150); - - const question3 = new TextInputBuilder() - .setCustomId(`question3`) - .setLabel(`Por que seu servidor deveria ser aceito?`) - .setRequired(true) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(20) - .setMaxLength(250); - - const question4 = new TextInputBuilder() - .setCustomId(`question4`) - .setLabel(`Por onde você conheceu a EPF?`) - .setRequired(true) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(20) - .setMaxLength(150); - - modal.addComponents(new ActionRowBuilder().addComponents(serverLink)); - modal.addComponents(new ActionRowBuilder().addComponents(question1)); - modal.addComponents(new ActionRowBuilder().addComponents(question2)); - modal.addComponents(new ActionRowBuilder().addComponents(question3)); - modal.addComponents(new ActionRowBuilder().addComponents(question4)); - - await interaction.showModal(modal); - } -} \ No newline at end of file diff --git a/src/config.js b/src/config.js deleted file mode 100644 index cc3de3f..0000000 --- a/src/config.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2022 HordLawk & vitoUwu & PeterStark000 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -module.exports = { - devID: "822202388780941313", - master: "848351616084344883", - guard: "819692196378443800", - agent: "1005619303078428693", - guild: "801560356795056168", - aproveChannel: '1037163654900105266', - serverFormChannel: '1094520535901286450', - devFormChannel: '1094520773030445136', - levels: ['803652717024378950', '802335037349560350', '802332925562847272'], - logs: '946097024804212777', - membersForRole: 5, - serversChannel: '1037551167749771294', - formChannelData: { - developerFormEmoji: '<:icon_developer:1037801876185432134>', - guildFormEmoji: '<:icon_guild:1037801942149242926>', - developerFormBanner: 'https://cdn.discordapp.com/attachments/946097024804212777/1037798853287620668/Aplicar_Developer.png', - guildFormBanner: 'https://cdn.discordapp.com/attachments/946097024804212777/1037798730142843011/Aplicar_Servidor.png', - developerFormColor: 0xE18002, - guildFormColor: 0x66f392, - }, - serversDivRole: '822191650381365289', - suggestionsChannel: '1025927402800558090', - pendingTag: '1040402827064922155', - guildsChangeLog: "1049034683968663612", -}; diff --git a/src/core/client/Client.ts b/src/core/client/Client.ts new file mode 100644 index 0000000..790c1ee --- /dev/null +++ b/src/core/client/Client.ts @@ -0,0 +1,90 @@ +import { Client, type ClientOptions } from "discord.js"; +import CommandService from "../../services/CommandService.js"; +import EventService from "../../services/EventService.js"; +import safeCall from "../../shared/helpers/safeCall.js"; +import sendChangeLogs from "../../shared/helpers/sendChangeLogs.js"; +import updateServerList from "../../shared/helpers/updateServersList.js"; +import env from "../config/env.js"; +import Constants from "../db/models/constants.js"; + +declare module "discord.js" { + interface Client { + updateServersData(updates: string[], force?: boolean): Promise; + } +} + +export default class Agent extends Client { + private serversUpdateTimeout: NodeJS.Timeout | undefined; + + constructor(options: ClientOptions) { + super(options); + } + + private listen() { + const events = EventService.getEvents(); + + for (const event of events) { + this.on(event.name, (...args) => { + safeCall(event.execute, ...args); + }); + } + } + + public override async login(token?: string) { + token ??= env.DISCORD_TOKEN; + + this.listen(); + + return super.login(token); + } + + public override async updateServersData(updates: string[], force?: boolean) { + const constants = await Constants.getConstants(); + constants.updateLogs.push(...updates); + await constants.save(); + + if (constants.scheduledUpdate && !force) { + return; + } + + const timeDifference = + Date.now() - constants.lastGuildsChannelUpdate.getTime(); + + if (timeDifference > 600000 || force) { + try { + if (force) { + await Constants.updateConstants({ + scheduledUpdate: false, + }); + clearTimeout(this.serversUpdateTimeout); + } + await sendChangeLogs(this); + await updateServerList(this); + } catch (err) { + console.error(err); + } + return; + } + + await Constants.updateConstants({ scheduledUpdate: true }); + + this.serversUpdateTimeout = setTimeout(async () => { + try { + await sendChangeLogs(this); + await updateServerList(this); + } catch (err) { + console.error(err); + } finally { + await Constants.updateConstants({ + scheduledUpdate: false, + }); + } + }, 600000 - timeDifference); + } + + public async updateCommands() { + await this.application?.commands.set([ + ...CommandService.getCommands().map((command) => command.data), + ]); + } +} diff --git a/src/core/client/index.ts b/src/core/client/index.ts new file mode 100644 index 0000000..9b15bfa --- /dev/null +++ b/src/core/client/index.ts @@ -0,0 +1,8 @@ +import { GatewayIntentBits, Partials } from "discord.js"; +import Agent from "./Client.js"; + +export default new Agent({ + intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers], + partials: [Partials.GuildMember, Partials.Channel], + allowedMentions: { repliedUser: false }, +}); diff --git a/src/core/config/env.ts b/src/core/config/env.ts new file mode 100644 index 0000000..89e69aa --- /dev/null +++ b/src/core/config/env.ts @@ -0,0 +1,19 @@ +function createEnv>( + vars: T, +): Record { + for (const key in vars) { + if (typeof vars[key] === "undefined") { + throw new Error(`${key} is not defined`); + } + } + + return vars as Record; +} + +const env = createEnv({ + MONGOURL: process.env.MONGOURL, + DISCORD_TOKEN: process.env.DISCORD_TOKEN, + OFFTOPIC_WEBHOOK: process.env.OFFTOPIC_WEBHOOK, +}); + +export default env; diff --git a/src/core/config/index.ts b/src/core/config/index.ts new file mode 100644 index 0000000..a455579 --- /dev/null +++ b/src/core/config/index.ts @@ -0,0 +1,58 @@ +// Copyright (C) 2022 HordLawk & vitoUwu & PeterStark000 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +export default { + ids: { + devs: ["844881358017789965", "504717946124369937"], + roles: { + dev: "822202388780941313", + master: "848351616084344883", + guard: "819692196378443800", + mod: "803652717024378950", + admin: "802335037349560350", + owner: "802332925562847272", + guildsDiv: "822191650381365289", + guest: "921091439876780052", + }, + channels: { + approve: "1037163654900105266", + serversList: "1037551167749771294", + suggestions: "1025927402800558090", + changeLog: "1049034683968663612", + }, + agent: "1005619303078428693", + guild: "801560356795056168", + tags: { + pending: "1040402827064922155", + }, + }, + minMembersToCreateGuildRole: 5, + forms: { + dev: { + channelId: "1094520773030445136", + emoji: "<:icon_developer:1037801876185432134>", + bannerURL: + "https://cdn.discordapp.com/attachments/946097024804212777/1037798853287620668/Aplicar_Developer.png", + color: 0xe18002, + }, + guild: { + channelId: "1094520535901286450", + emoji: "<:icon_guild:1037801942149242926>", + bannerURL: + "https://cdn.discordapp.com/attachments/946097024804212777/1037798730142843011/Aplicar_Servidor.png", + color: 0x66f392, + }, + }, +}; diff --git a/src/core/db/index.ts b/src/core/db/index.ts new file mode 100644 index 0000000..dbb564b --- /dev/null +++ b/src/core/db/index.ts @@ -0,0 +1,17 @@ +import mongoose from "mongoose"; +import env from "../config/env.js"; + +const app = mongoose.connection; + +export const connect = async () => { + return mongoose.connect(env.MONGOURL).catch(console.error); +}; + +app.on("error", async (err) => { + console.error(err); + await mongoose.disconnect(); +}); + +app.on("disconnected", () => { + return process.exit(); +}); diff --git a/src/core/db/models/constants.ts b/src/core/db/models/constants.ts new file mode 100644 index 0000000..407a47a --- /dev/null +++ b/src/core/db/models/constants.ts @@ -0,0 +1,64 @@ +import { type InferSchemaType, model, Schema } from "mongoose"; +import config from "../../config/index.js"; + +const ConstantsSchema = new Schema( + { + _id: { + type: String, + match: /^\d{17,19}$/, + }, + lastGuildsChannelUpdate: { + type: Date, + default: new Date(), + }, + updatingGuildsChannel: { + type: Boolean, + default: false, + }, + scheduledUpdate: { + type: Boolean, + }, + updateLogs: { + type: [String], + default: () => { + return []; + }, + }, + }, + { + statics: { + async getConstants() { + return ( + (await this.findById(config.ids.agent)) || + (await this.create({ _id: config.ids.agent })) + ); + }, + async updateConstants(data) { + return await this.updateOne({ _id: config.ids.agent }, data, { + new: true, + upsert: true, + setDefaultOnInsert: true, + }); + }, + }, + }, +); + +export type ConstantsSchemaType = InferSchemaType; + +ConstantsSchema.statics.getConstants = async function () { + return ( + (await this.findById(config.ids.agent)) || + (await this.create({ _id: config.ids.agent })) + ); +}; + +ConstantsSchema.statics.updateConstants = async function (data) { + return await this.updateOne({ _id: config.ids.agent }, data, { + new: true, + upsert: true, + setDefaultOnInsert: true, + }); +}; + +export default model("constants", ConstantsSchema); diff --git a/src/core/db/models/guild.ts b/src/core/db/models/guild.ts new file mode 100644 index 0000000..2a43dc0 --- /dev/null +++ b/src/core/db/models/guild.ts @@ -0,0 +1,49 @@ +import { type InferSchemaType, type Model, model, Schema } from "mongoose"; +import type { MemberSchemaType } from "./member.js"; + +const GuildSchema = new Schema({ + _id: { + type: String, + match: /^\d{17,19}$/, + }, + role: { + type: String, + match: /^\d{17,19}$/, + }, + representative: { + type: String, + match: /^\d{17,19}$/, + required: true, + index: true, + }, + invite: { + type: String, + match: /^[\w-]+$/, + required: true, + }, + name: { + type: String, + minLength: 2, + maxLength: 100, + required: true, + index: true, + }, + owner: { + type: String, + match: /^\d{17,19}$/, + index: true, + }, + pending: Boolean, +}); + +export type GuildSchemaType = InferSchemaType; +export type GuildModelType = Model; + +GuildSchema.pre("findOneAndDelete", async function () { + const memberModel = (await import( + "./member.js" + )) as unknown as Model; // shitty type checking + await memberModel.deleteMany({ guild: this.getQuery()._id }); +}); + +export default model("guild", GuildSchema); diff --git a/src/core/db/models/member.ts b/src/core/db/models/member.ts new file mode 100644 index 0000000..9169aff --- /dev/null +++ b/src/core/db/models/member.ts @@ -0,0 +1,27 @@ +import { type InferSchemaType, type Model, model, Schema } from "mongoose"; +import type { GuildSchemaType } from "./guild.js"; + +const MemberSchema = new Schema({ + user: { + type: String, + match: /^\d{17,19}$/, + required: true, + index: true, + }, + guild: { + type: String, + match: /^\d{17,19}$/, + ref: "guild", + required: true, + index: true, + }, + admin: Boolean, +}); + +export type MemberSchemaType = InferSchemaType; +export type MemberModelType = Model; +export type GuildPopulatedMemberSchemaType = Omit & { + guild: GuildSchemaType; +}; + +export default model("member", MemberSchema); diff --git a/src/database.js b/src/database.js deleted file mode 100644 index a2fda14..0000000 --- a/src/database.js +++ /dev/null @@ -1,13 +0,0 @@ -const mongoose = require("mongoose"); -const app = mongoose.connection; - -const connect = async () => await mongoose.connect(process.env.MONGOURL).catch(console.error); - -app.on("error", async err => { - console.error(err); - await mongoose.disconnect(); -}); - -app.on("disconnected", () => process.exit()); - -(async () => await connect())(); \ No newline at end of file diff --git a/src/events/guildMemberRemove.js b/src/events/guildMemberRemove.js deleted file mode 100644 index a4f2997..0000000 --- a/src/events/guildMemberRemove.js +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2022 HordLawk & vitoUwu & PeterStark000 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -const { EmbedBuilder } = require('discord.js'); -const config = require('../config.js'); - -module.exports = { - name: 'guildMemberRemove', - execute: async (member, client) => { - const user = member.user ?? await client.users.fetch(member.id); - const guildModel = require('../models/guild.js'); - await guildModel.updateMany({owner: user.id}, {$set: {owner: null}}); - const memberModel = require('../models/member.js'); - const memberDocs = await memberModel.find({user: user.id}).populate('guild'); - for(const memberDoc of memberDocs.filter(doc => doc.guild.role)){ - const memberCount = await memberModel.countDocuments({guild: memberDoc.guild._id}); - if(memberCount <= config.membersForRole){ - await client.guilds.cache.get(config.guild).roles.delete(memberDoc.guild.role); - await guildModel.updateOne({_id: memberDoc.guild._id}, {$set: {role: null}}); - client.emit( - 'updateGuilds', - false, - `<:mod:1040429385066491946> **|** O servidor **${memberDoc.guild.name}** perdeu o seu cargo.` - ); - } - } - await memberModel.deleteMany({user: user.id}); - const representingDocs = await guildModel.find({representative: user.id}); - await guildModel.updateMany({representative: user.id}, {$set: {representative: config.agent}}); - if(representingDocs.length){ - const embed = new EmbedBuilder() - .setColor(0x2f3136) - .setDescription( - `${user} \`${user.username}\` saiu do servidor enquanto representava os servidores: ` + - representingDocs.map(doc => `[\`${doc.name}\`](https://discord.gg/${doc.invite})`).join(' ') - ); - await client.channels.cache.get(config.logs).send({embeds: [embed]}); - } - }, -}; \ No newline at end of file diff --git a/src/events/interactionCreate.js b/src/events/interactionCreate.js deleted file mode 100644 index c664503..0000000 --- a/src/events/interactionCreate.js +++ /dev/null @@ -1,87 +0,0 @@ -const { InteractionType } = require('discord.js') - -module.exports = { - name: 'interactionCreate', - async execute(interaction, client) { - if (interaction.isChatInputCommand()) { - const { commands } = client; - const { commandName } = interaction; - const command = commands.get(commandName); - if (!command) return; - - try { - await command.execute(interaction, client); - } catch (error) { - console.error(error) - await interaction[interaction.replied || interaction.deferred ? 'followUp' : 'reply']({ - content: `Não foi possível executar esse comando no momento`, - ephemeral: true - }); - } - } else if (interaction.isButton()) { - const { buttons } = client; - const { customId } = interaction; - const button = buttons.get(customId.split(':')[0]); - if (customId.startsWith("collector")) return; - if (!button) return new Error('Não é um botão'); - - try { - await button.execute(interaction, client); - - } catch (err) { - console.error(err) - } - } else if (interaction.isStringSelectMenu()) { - const { selectMenus } = client; - const { customId } = interaction; - const menu = selectMenus.get(customId); - if (!menu) return new Error('Não é um menu'); - - try { - await menu.execute(interaction, client); - } catch (error) { - console.error(error) - } - } else if (interaction.type == InteractionType.ModalSubmit) { - const { modals } = client; - const { customId} = interaction; - const modal = modals.get(customId.split(':')[0]) - if (!modal) return new Error('Não é um modal'); - - try { - await modal.execute(interaction, client); - } catch (error) { - console.error(error) - } - } else if (interaction.isContextMenuCommand()) { - const { commands } = client; - const { commandName } = interaction; - const contextCommand = commands.get(commandName); - if (!contextCommand) return; - - try { - await contextCommand.execute(interaction, client); - } catch (error) { - console.error(error) - } - } - else if(interaction.isAutocomplete()){ - const command = client.commands.get(interaction.commandName); - if(!command) return; - const subCommandName = interaction.options.getSubcommand(false); - const subCommandGroupName = interaction.options.getSubcommandGroup(false); - const option = interaction.options.getFocused(true); - const response = await command - [ - 'autocomplete$' + - ( - subCommandName - ? `${subCommandGroupName ? `${subCommandGroupName}_` : ''}${subCommandName}$` - : '' - ) + - option.name - ](interaction, option.value); - await interaction.respond(response); - } - } -} \ No newline at end of file diff --git a/src/events/ready.js b/src/events/ready.js deleted file mode 100644 index b5b1819..0000000 --- a/src/events/ready.js +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (C) 2022 HordLawk & vitoUwu & PeterStark000 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); -const config = require("../config"); - -module.exports = { - name: 'ready', - once: true, - - async execute(client) { - console.log(`Logado em ${client.user.username} (${client.user.id})`); - const guild = client.guilds.cache.get(config.guild); - await guild.members.fetch(); - await guild.commands.fetch(); - - const serverFormChannel = guild.channels.cache.get(config.serverFormChannel); - const serverFormMessage = (await serverFormChannel?.messages?.fetch({ limit: 1 }).catch(() => null))?.first(); - - if (!serverFormMessage || serverFormMessage.author.id !== client.user.id) { - const serverRequirementsEmbed = new EmbedBuilder() - .setTitle('<:info:1095440963620573394> Requisitos') - .setImage( - 'https://cdn.discordapp.com/attachments/1034845518452502658/1034845554796150815/Requisitos.png', - ) - .setColor(config.formChannelData.guildFormColor) - .setDescription( - '<:globe:1095440970516021298> Para adicionar algum servidor a EPF é necessário que tenha no' + - ' mínimo **5.000 membros** ou que sejam **verificados** ou **parceiros** do Discord e atenda a' + - ' todos os [termos de serviço](https://discord.com/terms) e as' + - ' [diretrizes](https://discord.com/guidelines) do Discord.', - ) - - const registerServerEmbed = new EmbedBuilder() - .setTitle(`${config.formChannelData.guildFormEmoji} Aplicar Servidor`) - .setImage(config.formChannelData.guildFormBanner) - .setColor(config.formChannelData.guildFormColor) - .setDescription( - `<:green_dot:1037803471077908553> **|** Se você representa um servidor que cumpre os requisitos listados acima e acha que ele merece fazer parte da **Elite Penguin Force**, clique no botão abaixo e preencha o formulário para enviá-lo para uma avaliação.\n\n`+ - `<:icon_idle_green:1037806417438068766> **| Lembrando:** Você será avisado assim que a avaliação tenha terminado. Por isso lembre de deixar suas "Mensagens Diretas" abertas.` - ) - - const row = new ActionRowBuilder() - .setComponents( - new ButtonBuilder() - .setCustomId("register-server-form") - .setLabel("Formulário de Servidor") - .setStyle(ButtonStyle.Secondary) - .setEmoji(config.formChannelData.guildFormEmoji), - ); - - serverFormChannel.send({ embeds: [serverRequirementsEmbed, registerServerEmbed], components: [row] }); - } - - const devFormChannel = guild.channels.cache.get(config.devFormChannel); - const devFormMessage = (await devFormChannel?.messages?.fetch({ limit: 1 }).catch(() => null))?.first(); - - if (!devFormMessage || devFormMessage.author.id !== client.user.id) { - const devRequirementsEmbed = new EmbedBuilder() - .setTitle('<:info:1095440963620573394> Requisitos') - .setImage( - 'https://cdn.discordapp.com/attachments/1034845518452502658/1034845554796150815/Requisitos.png', - ) - .setColor(config.formChannelData.developerFormColor) - .setDescription( - '<:monitor:1095441115936739450> Para receber o cargo <@&822202388780941313>, é necessário' + - ' que cumpra com algum dos seguintes requisitos:\n' + - '<:point_epf:1037183758761205841> Possuir um bot verificado (online);\n' + - '<:point_epf:1037183758761205841> Ter um bot privado de algum dos servidores associados à EPF;\n' + - '<:point_epf:1037183758761205841> Trabalhar com desenvolvimento de software;\n' + - '<:point_epf:1037183758761205841> Ter contribuições/projetos documentados no' + - ' [GitHub](https://github.com/).', - ) - - const requestDevRoleEmbed = new EmbedBuilder() - .setTitle(`${config.formChannelData.developerFormEmoji} Aplicar para Desenvolvedor`) - .setImage(config.formChannelData.developerFormBanner) - .setColor(config.formChannelData.developerFormColor) - .setDescription( - `<:point_epf:1037183758761205841> **|** Se você atualmente atua na área da programação que cumpre os requisitos listados acima e quer fazer parte da **Elite Penguin Force** como um Developer, clique no botão abaixo e preencha o formulário para enviá-lo para uma avaliação.\n\n`+ - `<:icon_idle:1037801013358379048> **| Lembrando:** Você será avisado assim que a avaliação tenha terminado. Por isso lembre de deixar suas "Mensagens Diretas" abertas.` - ) - - const row = new ActionRowBuilder() - .setComponents( - new ButtonBuilder() - .setCustomId("request-dev-role-form") - .setLabel("Formulário de Desenvolvedor") - .setStyle(ButtonStyle.Secondary) - .setEmoji(config.formChannelData.developerFormEmoji), - ); - - devFormChannel.send({ embeds: [devRequirementsEmbed, requestDevRoleEmbed], components: [row] }); - } - - // se crashar ou reiniciar no meio de uma atualização, ele vai refazer essa atualização já - // que a ela não foi finalizada devidamente - const constantsModel = require('../models/constants'); - const constants = await constantsModel.getConstants(); - if (constants.updatingGuildsChannel || constants.scheduledUpdate) { - await constantsModel.updateConstants({ - updatingGuildsChannel: false, - scheduledUpdate: false, - }); - client.emit( - 'updateGuilds', - true, - '<:e_repeat:1049017561175568404> **|** A última atualização não foi bem sucedida, por isso será refeita' - ); - } - - const guildModel = require('../models/guild.js'); - const clock = () => setTimeout(async () => { - const now = Date.now() - await guild.members.fetch(); - const pendingGuilds = await guildModel.find({pending: true}); - for( - const member - of guild.members.cache - .filter(m => ( - !m.user.bot - && - (m.roles.highest.position < guild.roles.cache.get(config.serversDivRole).position) - && - (m.joinedTimestamp < (now - (24 * 60 * 60 * 1000))) - && - !pendingGuilds.some(g => (g.representative === m.id)) - )) - .values() - ) await member.kick(); - clock(); - }, 60 * 60 * 1000); - clock(); - } -} diff --git a/src/events/updateGuilds.js b/src/events/updateGuilds.js deleted file mode 100644 index 98e1912..0000000 --- a/src/events/updateGuilds.js +++ /dev/null @@ -1,56 +0,0 @@ -let timeout; - -module.exports = { - name: 'updateGuilds', - execute: async (forceUpdate, reason, client) => { - const constantsModel = require('../models/constants'); - let constants = await constantsModel.findOneAndUpdate( - {}, - { - $push: { updateLogs: reason }, - }, - { - new: true, - setDefaultsOnInsert: true, - } - ); - - if (constants.scheduledUpdate && !forceUpdate) return; - - const timeDifference = Date.now() - constants.lastGuildsChannelUpdate; - - let updateGuildsChannel = require('../utils/updateGuildsChannel'); - let sendGuildChangelogs = require('../utils/sendGuildChangelogs'); - - if (timeDifference > 600000 || forceUpdate) { - try { - if (forceUpdate) { - await constantsModel.updateConstants({ - scheduledUpdate: false, - }); - clearTimeout(timeout); - } - await sendGuildChangelogs(client); - await updateGuildsChannel(client); - } catch (err) { - console.error(err); - } - return; - } - - await constantsModel.updateConstants({ scheduledUpdate: true }); - - timeout = setTimeout(async () => { - try { - await sendGuildChangelogs(client); - await updateGuildsChannel(client); - } catch (err) { - console.error(err); - } finally { - await constantsModel.updateConstants({ - scheduledUpdate: false, - }); - } - }, 600000 - timeDifference); - }, -}; diff --git a/src/handlers/handleCommands.js b/src/handlers/handleCommands.js deleted file mode 100644 index 703e2d4..0000000 --- a/src/handlers/handleCommands.js +++ /dev/null @@ -1,28 +0,0 @@ -const { REST } = require('@discordjs/rest') -const { Routes } = require('discord-api-types/v9') -const fs = require('fs'); -const path = require('path'); -const config = require('../config.js'); - -module.exports = async client => { - commandArray = []; - fs.readdirSync(path.join(__dirname, '..', 'commands')).filter(file => file.endsWith('.js')).forEach(file => { - const command = require(`../commands/${file}`); - if(!command.active) return; - client.commands.set(command.data.name, command); - commandArray.push(command.data.toJSON()); - console.log(`Command: ${command.data.name} has been passed through the handler`); - }); - const rest = new REST({ version: '9' }).setToken(process.env.DISCORD_TOKEN); - try { - console.log('Reiniciando comandos') - - await rest.put( - Routes.applicationGuildCommands(config.agent, config.guild), { - body: commandArray, - }); - console.log('Comandos reiniciados'); - } catch (error) { - console.error(error); - } -} \ No newline at end of file diff --git a/src/handlers/handleComponents.js b/src/handlers/handleComponents.js deleted file mode 100644 index 35b9918..0000000 --- a/src/handlers/handleComponents.js +++ /dev/null @@ -1,15 +0,0 @@ -const { ModalSubmitFields, Collection } = require('discord.js'); -const fs = require('fs'); -const path = require('path'); - -module.exports = async client => { - fs - .readdirSync(path.join(__dirname, '..', 'components')) - .forEach(folder => { - client[folder] = fs - .readdirSync(path.join(__dirname, '..', 'components', folder)) - .filter(file => file.endsWith('.js')) - .map(file => require(`../components/${folder}/${file}`)) - .reduce((col, component) => col.set(component.data.name, component), new Collection()); - }); -} \ No newline at end of file diff --git a/src/handlers/handleEvents.js b/src/handlers/handleEvents.js deleted file mode 100644 index a1a11f1..0000000 --- a/src/handlers/handleEvents.js +++ /dev/null @@ -1,9 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -module.exports = async client => { - fs.readdirSync(path.join(__dirname, '..', 'events')).filter(file => file.endsWith(".js")).forEach(file => { - const event = require(`../events/${file}`); - client[event.once ? 'once' : 'on'](event.name, async (...args) => await event.execute(...args, client).catch(console.error)); - }); -} \ No newline at end of file diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 50b2dc7..0000000 --- a/src/index.js +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2022 HordLawk & vitoUwu & PeterStark000 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -require('./database.js'); - -const { Client, Collection, GatewayIntentBits, Partials } = require('discord.js') -const fs = require('fs'); -const path = require('path'); - -const client = new Client({ - intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers], - partials: [Partials.GuildMember, Partials.Channel], - allowedMentions: {repliedUser: false}, -}); -client.commands = new Collection(); - -const handlerFiles = fs.readdirSync(path.join(__dirname, 'handlers')).filter(file => file.endsWith('.js')); - -(async () => { - for(const file of handlerFiles) await require(`./handlers/${file}`)(client); - - await client.login(); -})(); \ No newline at end of file diff --git a/src/events/threadCreate.js b/src/index.ts similarity index 54% rename from src/events/threadCreate.js rename to src/index.ts index e7a96ae..d9660c4 100644 --- a/src/events/threadCreate.js +++ b/src/index.ts @@ -13,13 +13,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -const config = require("../config"); - -module.exports = { - name: 'threadCreate', - execute: async (thread, newlyCreated) => { - if(!newlyCreated) return; - if(thread.partial) await thread.fetch(); - if(thread.parentId === config.suggestionsChannel) await thread.setAppliedTags([config.pendingTag]); - }, -}; \ No newline at end of file +import client from "./core/client/index.js"; +import * as db from "./core/db/index.js"; +import BackgroundTasksService from "./services/BackgroundTasksService.js"; +import EventService from "./services/EventService.js"; +import InteractionService from "./services/InteractionService.js"; + +async function main() { + console.time("Bot startup"); + + await Promise.all([ + db.connect(), + BackgroundTasksService.load(), + InteractionService.load(), + EventService.load(), + ]); + + await client.login(); + await client.updateCommands(); + + console.timeEnd("Bot startup"); +} + +main(); diff --git a/src/models/constants.js b/src/models/constants.js deleted file mode 100644 index 0813270..0000000 --- a/src/models/constants.js +++ /dev/null @@ -1,38 +0,0 @@ -const { model, Schema } = require('mongoose'); -const config = require('../config'); - -const constantsSchema = new Schema({ - _id: { - type: String, - match: /^\d{17,19}$/ - }, - lastGuildsChannelUpdate: { - type: Date, - default: new Date() - }, - updatingGuildsChannel: { - type: Boolean, - default: false - }, - scheduledUpdate: { - type: Boolean, - }, - updateLogs: { - type: [String], - default: () => [], - }, -}); - -constantsSchema.statics.getConstants = async function() { - return await this.findById(config.agent) || await this.create({ _id: config.agent }) -} - -constantsSchema.statics.updateConstants = async function (data) { - return await this.updateOne({ _id: config.agent }, data, { - new: true, - upsert: true, - setDefaultOnInsert: true, - }); -}; - -module.exports = model('constants', constantsSchema); \ No newline at end of file diff --git a/src/models/guild.js b/src/models/guild.js deleted file mode 100644 index ef1628c..0000000 --- a/src/models/guild.js +++ /dev/null @@ -1,43 +0,0 @@ -const {Schema, model} = require('mongoose'); - -const guildSchema = new Schema({ - _id: { - type: String, - match: /^\d{17,19}$/, - }, - role: { - type: String, - match: /^\d{17,19}$/, - }, - representative: { - type: String, - match: /^\d{17,19}$/, - required: true, - index: true, - }, - invite: { - type: String, - match: /^[\w\-]+$/, - required: true, - }, - name: { - type: String, - minLength: 2, - maxLength: 100, - required: true, - index: true, - }, - owner: { - type: String, - match: /^\d{17,19}$/, - index: true, - }, - pending: Boolean, -}); - -guildSchema.pre('findOneAndDelete', async function(){ - const memberModel = require('./member.js'); - await memberModel.deleteMany({guild: this.getQuery()._id}); -}); - -module.exports = model('guild', guildSchema); \ No newline at end of file diff --git a/src/models/member.js b/src/models/member.js deleted file mode 100644 index 34d8a55..0000000 --- a/src/models/member.js +++ /dev/null @@ -1,20 +0,0 @@ -const {Schema, model} = require('mongoose'); - -const memberSchema = new Schema({ - user: { - type: String, - match: /^\d{17,19}$/, - required: true, - index: true, - }, - guild: { - type: String, - match: /^\d{17,19}$/, - ref: 'guild', - required: true, - index: true, - }, - admin: Boolean, -}); - -module.exports = model('member', memberSchema); \ No newline at end of file diff --git a/src/services/BackgroundTasksService.ts b/src/services/BackgroundTasksService.ts new file mode 100644 index 0000000..ef8b6f4 --- /dev/null +++ b/src/services/BackgroundTasksService.ts @@ -0,0 +1,109 @@ +import type { Client } from "discord.js"; +import { getJavascriptPaths, importUsingRoot } from "../shared/helpers/path.js"; +import type { Task } from "../shared/types/task.js"; + +/** + * @description The service that handles the background tasks + */ +class BackgroundTasksService { + private tasks: Map = new Map(); + + /** + * @description Validates the task import + * + * @param task The task to validate + * @param path The path of the task + */ + private validateTaskImport( + task: unknown, + path: string, + ): asserts task is Task { + if ( + typeof task !== "object" || + task === null || + !("data" in task) || + !("execute" in task) + ) { + throw new Error(`Invalid command import: ${path}`); + } + } + + /** + * @description Loads the tasks + */ + public async load() { + const paths = getJavascriptPaths("./dist/src/app/tasks/"); + + for (const path of paths) { + const task = (await importUsingRoot(path)).default; + this.validateTaskImport(task, path); + this.tasks.set(task.data.name, task); + } + } + + /** + * @description Gets a task by its name + * + * @param name The name of the task + * @returns The task + */ + public getTask(name: string) { + if (this.tasks.size === 0) { + throw new Error("Tasks not loaded"); + } + + return this.tasks.get(name); + } + + /** + * @description Gets all the tasks + * + * @returns The tasks + */ + public getTasks() { + if (this.tasks.size === 0) { + throw new Error("Tasks not loaded"); + } + + return [...this.tasks.values()]; + } + + /** + * @description Handles the task execution + * + * @param name The name of the task + * @param client The client that is running the bot + */ + public async handleTaskExecution(name: string, client: Client) { + const task = this.tasks.get(name); + if (!task) { + return; + } + + try { + await task.execute(client); + } catch (error) { + console.error(error); + } + } + + /** + * @description Initializes the tasks + * + * @param client The client that is running the bot + */ + public initTasks(client: Client) { + const tasks = this.getTasks(); + for (const task of tasks) { + this.handleTaskExecution(task.data.name, client); + + console.log(`Task ${task.data.name} started`); + + setInterval(() => { + this.handleTaskExecution(task.data.name, client); + }, task.data.interval); + } + } +} + +export default new BackgroundTasksService(); diff --git a/src/services/CommandService.ts b/src/services/CommandService.ts new file mode 100644 index 0000000..2ffe330 --- /dev/null +++ b/src/services/CommandService.ts @@ -0,0 +1,154 @@ +import { + ApplicationCommandType, + type AutocompleteInteraction, + type Interaction, +} from "discord.js"; +import { getJavascriptPaths, importUsingRoot } from "../shared/helpers/path.js"; +import type { Command, CommandType } from "../shared/types/command.js"; + +/** + * @description The service that handles the commands + */ +class CommandService { + private commands: Map< + ApplicationCommandType, + Map> + > = new Map(); + + private validateCommandImport( + command: unknown, + path: string, + ): asserts command is Command { + if ( + typeof command !== "object" || + command === null || + !("data" in command) || + !("execute" in command) + ) { + throw new Error(`Invalid command import: ${path}`); + } + } + + public async load() { + const paths = getJavascriptPaths("./dist/src/app/").filter((path) => + path.includes("/commands/"), + ); + + for (const path of paths) { + const command = (await importUsingRoot(path)).default; + this.validateCommandImport(command, path); + + if (!command.data.type) { + console.warn(`Command ${command.data.name} has no type`); + continue; + } + + if (this.commands.has(command.data.type)) { + this.commands.get(command.data.type)!.set(command.data.name, command); + continue; + } + + this.commands.set( + command.data.type, + new Map([[command.data.name, command]]), + ); + } + } + + public getCommand(type: ApplicationCommandType, name: string) { + if (this.commands.size === 0) { + throw new Error("Commands not loaded"); + } + + return this.commands.get(type)?.get(name); + } + + public getCommands() { + if (this.commands.size === 0) { + throw new Error("Commands not loaded"); + } + + return [...this.commands.values()].flatMap((commands) => [ + ...commands.values(), + ]); + } + + /** + * Get the autocomplete for a given key + * @param key The key to get the autocomplete for, composed by subcommand, subcommand group and option name, separated by dots + */ + public getAutocomplete(commandName: string, key: string) { + const command = this.commands + .get(ApplicationCommandType.ChatInput) + ?.get(commandName); + if (!command) { + return null; + } + + const autocomplete = command.autocomplete?.get(key); + if (!autocomplete) { + return null; + } + + return autocomplete; + } + + public async handleCommandInteraction(interaction: Interaction) { + if (!interaction.isCommand()) { + return; + } + + const command = this.commands + .get(interaction.commandType) + ?.get(interaction.commandName); + if (!command) { + return; + } + + if (interaction.inRawGuild()) { + await interaction.client.guilds + .fetch(interaction.guildId) + .catch(() => null); + } + + try { + // @ts-expect-error just ignore this error + await command.execute(interaction); + } catch (error) { + console.error(error); + + const reply = interaction.replied + ? interaction.followUp + : interaction.reply; + await reply({ + content: "Ocorreu um erro ao executar este comando.", + ephemeral: true, + }); + } + } + + public async handleAutocompleteInteraction( + interaction: AutocompleteInteraction, + ) { + const key = [ + interaction.options.getSubcommandGroup(false), + interaction.options.getSubcommand(false), + interaction.options.getFocused(true).name, + ] + .filter(Boolean) + .join("."); + + const autocomplete = this.getAutocomplete(interaction.commandName, key); + if (!autocomplete) { + return; + } + + try { + await autocomplete(interaction as AutocompleteInteraction<"cached">); + } catch (error) { + console.error(error); + } + } +} + +export default new CommandService(); diff --git a/src/services/ComponentsService.ts b/src/services/ComponentsService.ts new file mode 100644 index 0000000..d4ebcff --- /dev/null +++ b/src/services/ComponentsService.ts @@ -0,0 +1,150 @@ +import { + ComponentType as DiscordComponentType, + type Interaction, + type MessageComponentInteraction, +} from "discord.js"; +import { getJavascriptPaths, importUsingRoot } from "../shared/helpers/path.js"; +import type { + AnyComponent, + ComponentType, +} from "../shared/types/components.js"; + +/** + * @description The service that handles the components + */ +class ComponentsService { + private components: Map> = new Map(); + private validTypes: ComponentType[] = ["button", "modal"]; + + /** + * @description Validates the component import + * + * @param component The component to validate + * @param path The path of the component + */ + private validateComponentImport( + component: unknown, + path: string, + ): asserts component is AnyComponent { + if ( + typeof component !== "object" || + component === null || + !("id" in component) || + !("execute" in component) || + !("create" in component) || + !("type" in component) || + typeof component.type !== "string" || + !this.validTypes.includes(component.type as ComponentType) + ) { + throw new Error(`Invalid component import: ${path}`); + } + } + + /** + * @description Gets the component type from the API + * + * @param type The type of the component + * @returns The component type + */ + private messageComponentTypeFromAPI( + type: MessageComponentInteraction["component"]["type"], + ): ComponentType | null { + return type === DiscordComponentType.Button ? "button" : null; + } + + /** + * @description Parses the arguments from the customId + * + * @param customId The customId of the component + */ + private parseArguments(customId: string) { + const [id, ...args] = customId.split(":"); + return { id: id || customId, args }; + } + + /** + * @description Loads the components + */ + public async load() { + const paths = getJavascriptPaths("./dist/src/app/").filter((path) => + path.includes("/components/"), + ); + + for (const path of paths) { + const component = (await importUsingRoot(path)).default; + this.validateComponentImport(component, path); + + if (!this.components.has(component.type)) { + this.components.set( + component.type, + new Map([[component.id, component]]), + ); + } + + this.components.get(component.type)?.set(component.id, component); + } + } + + /** + * @description Gets a component by its type and id + * + * @param type The type of the component + * @param id The id of the component + * @returns The component + */ + public getComponent(type: ComponentType, id: string) { + return this.components.get(type)?.get(id); + } + + /** + * @description Gets all the components of a given type + * + * @param type The type of the components + * @returns The components + */ + public getComponents(type: ComponentType) { + return Array.from(this.components.get(type)?.values() || []); + } + + /** + * @description Handles the component interaction + * + * @param interaction The interaction + */ + public async handleComponentInteraction(interaction: Interaction) { + if (!interaction.isMessageComponent() && !interaction.isModalSubmit()) { + return; + } + + const componentType = interaction.isModalSubmit() + ? "modal" + : this.messageComponentTypeFromAPI(interaction.component.type); + + if (!componentType) { + return; + } + + const { id, args } = this.parseArguments(interaction.customId); + const component = this.getComponent(componentType, id); + + if (!component) { + return; + } + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await component.execute(interaction as any, ...args); + } catch (error) { + console.error(error); + const reply = interaction.replied + ? interaction.followUp + : interaction.reply; + await reply({ + content: "Ocorreu um erro ao processar o componente", + ephemeral: true, + }); + } + } +} + +export default new ComponentsService(); diff --git a/src/services/EventService.ts b/src/services/EventService.ts new file mode 100644 index 0000000..08aef33 --- /dev/null +++ b/src/services/EventService.ts @@ -0,0 +1,73 @@ +import { getJavascriptPaths, importUsingRoot } from "../shared/helpers/path.js"; +import type { DiscordEvent } from "../shared/types/event.js"; + +/** + * @description The service that handles the events + */ +class EventService { + private events: Map = new Map(); + + /** + * @description Validates the event import + * + * @param event The event to validate + * @param path The path of the event + */ + private validateEventImport( + event: unknown, + path: string, + ): asserts event is DiscordEvent { + if ( + typeof event !== "object" || + event === null || + !("name" in event) || + !("execute" in event) + ) { + throw new Error(`Invalid event import: ${path}`); + } + } + + /** + * @description Loads the events + */ + public async load() { + const paths = getJavascriptPaths("./dist/src/app/").filter((path) => + path.includes("/events/"), + ); + + for (const path of paths) { + const event = (await importUsingRoot(path)).default; + this.validateEventImport(event, path); + this.events.set(event.name, event); + } + } + + /** + * @description Gets an event by its name + * + * @param name The name of the event + * @returns The event + */ + public getEvent(name: string) { + if (this.events.size === 0) { + throw new Error("Events not loaded"); + } + + return this.events.get(name); + } + + /** + * @description Gets all the events + * + * @returns The events + */ + public getEvents() { + if (this.events.size === 0) { + throw new Error("Events not loaded"); + } + + return [...this.events.values()]; + } +} + +export default new EventService(); diff --git a/src/services/InteractionService.ts b/src/services/InteractionService.ts new file mode 100644 index 0000000..8bdb8b6 --- /dev/null +++ b/src/services/InteractionService.ts @@ -0,0 +1,43 @@ +import type { Interaction } from "discord.js"; +import CommandService from "./CommandService.js"; +import ComponentsService from "./ComponentsService.js"; + +/** + * @description The service that handles the interactions + */ +class InteractionService { + /** + * @description Handles the incoming interaction + * + * @param interaction The interaction to handle + */ + public async handleInteraction(interaction: Interaction) { + try { + if (interaction.isCommand()) { + await CommandService.handleCommandInteraction(interaction); + return; + } + + if (interaction.isAutocomplete()) { + await CommandService.handleAutocompleteInteraction(interaction); + return; + } + + if (interaction.isMessageComponent() || interaction.isModalSubmit()) { + await ComponentsService.handleComponentInteraction(interaction); + return; + } + } catch (error) { + console.error(error); + } + } + + /** + * @description Loads the interaction service, with the command and component services + */ + public load() { + return Promise.all([CommandService.load(), ComponentsService.load()]); + } +} + +export default new InteractionService(); diff --git a/src/shared/constants.ts b/src/shared/constants.ts new file mode 100644 index 0000000..780d1ac --- /dev/null +++ b/src/shared/constants.ts @@ -0,0 +1,34 @@ +import { pathToFileURL } from "url"; + +export const DISCORD_INVITE_REGEX = /discord\.com\/(api\/)?oauth2\/authorize/gi; +export const MAX_MESSAGE_CONTENT_LENGTH = 2000; +export const ALMOST_TWO_WEEKS = 1209540000; +export const ALPHABET = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", +]; +export const ROOT = pathToFileURL(process.cwd()).href; diff --git a/src/shared/factories/buttons/createConfirmationButtons.ts b/src/shared/factories/buttons/createConfirmationButtons.ts new file mode 100644 index 0000000..39c9228 --- /dev/null +++ b/src/shared/factories/buttons/createConfirmationButtons.ts @@ -0,0 +1,17 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; + +/** + * Creates a row with cancel and confirm buttons + */ +export default function createConfirmationButtons() { + return new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId("collector:confirm") + .setLabel("Confirmar") + .setStyle(ButtonStyle.Success), + new ButtonBuilder() + .setCustomId("collector:cancel") + .setLabel("Cancelar") + .setStyle(ButtonStyle.Danger), + ); +} diff --git a/src/shared/factories/buttons/index.ts b/src/shared/factories/buttons/index.ts new file mode 100644 index 0000000..7b2b3a2 --- /dev/null +++ b/src/shared/factories/buttons/index.ts @@ -0,0 +1,90 @@ +import { + ButtonStyle, + type CacheType, + type ComponentEmojiResolvable, + ComponentType, + parseEmoji, +} from "discord.js"; +import type { Component, ComponentExecute } from "../../types/components.js"; + +type BaseButtonComponentData = { + style: ButtonStyle; + disabled?: boolean; + emoji?: ComponentEmojiResolvable; + label?: string; +}; + +type LinkButtonComponentData = { + style: ButtonStyle.Link; + url: string; +} & BaseButtonComponentData; + +type InteractionButtonComponentData = { + style: Exclude; + customId: string; +} & BaseButtonComponentData; + +type ButtonComponentData = + | LinkButtonComponentData + | InteractionButtonComponentData; + +type Props = { + /** + * @description The id of the button + */ + id: string; + /** + * @description The data of the button containing the style, disabled, emoji, and label + */ + data: Omit; + /** + * @description The function to execute the button + */ + execute: ComponentExecute<"button", C>; +}; + +function isButtonWithURL( + data: Omit, +): data is LinkButtonComponentData { + return "url" in data && typeof data.url === "string"; +} + +export default function createButton( + props: Props, +): Component<"button", C> { + return { + ...props, + type: "button", + // @ts-expect-error - skill issue + create: (...args: T) => { + const data = props.data; + if (isButtonWithURL(data)) { + return { + url: data.url, + style: ButtonStyle.Link, + disabled: data.disabled, + emoji: + typeof data.emoji === "string" + ? parseEmoji(data.emoji) || undefined + : data.emoji, + label: data.label, + type: ComponentType.Button, + }; + } + + return { + custom_id: args.length ? `${props.id}:${args.join(":")}` : props.id, + style: + data.style === ButtonStyle.Link ? ButtonStyle.Secondary : data.style, + disabled: data.disabled, + emoji: + typeof data.emoji === "string" + ? parseEmoji(data.emoji) || undefined + : data.emoji, + label: data.label, + type: ComponentType.Button, + }; + }, + execute: props.execute, + }; +} diff --git a/src/shared/factories/commands/index.ts b/src/shared/factories/commands/index.ts new file mode 100644 index 0000000..36f4cb6 --- /dev/null +++ b/src/shared/factories/commands/index.ts @@ -0,0 +1,96 @@ +import { + ApplicationCommandOptionType, + ApplicationCommandType, + type CacheType, + type ChatInputCommandInteraction, +} from "discord.js"; +import type { + AutocompleteExecute, + Command, + CommandData, + CommandOption, +} from "../../types/command.js"; + +type Options = { + /** + * @description The command data used to create the command + * @example + * ```ts + * { + * name: "ping", + * description: "Pings the bot", + * } + * ``` + */ + data: Omit, "options"> & { options?: CommandOption[] }; + /** + * @description Whether the command should be active or not + * @default true + */ + active?: boolean; + /** + * @description The function that will be executed when the command is used + */ + execute: ( + interaction: ChatInputCommandInteraction, + ) => Promise | unknown; +}; + +function mapAutocompleteOptions( + options: CommandOption[], + parentName?: string, +): Map { + const autocompleteOptions: Map = new Map(); + + for (const option of options) { + if ("options" in option) { + const map = mapAutocompleteOptions( + option.options, + parentName ? `${parentName}.${option.name}` : option.name, + ); + + for (const [key, value] of map) { + autocompleteOptions.set(key, value); + } + + continue; + } + + if ( + (option.type !== ApplicationCommandOptionType.String && + option.type !== ApplicationCommandOptionType.Number) || + !("autocomplete" in option) || + typeof option.autocomplete !== "function" + ) { + continue; + } + + autocompleteOptions.set( + parentName ? `${parentName}.${option.name}` : option.name, + option.autocomplete, + ); + } + + return autocompleteOptions; +} + +export default function createCommand( + command: Options, +): Command<"chat", C> { + return { + data: { + dmPermission: false, + defaultMemberPermissions: ["ViewChannel"], + ...command.data, + // @ts-expect-error just ignore this errors + options: command.data.options?.map((option) => ({ + ...option, + autocomplete: "autocomplete" in option || undefined, + })), + type: ApplicationCommandType.ChatInput, + }, + autocomplete: mapAutocompleteOptions(command.data.options || []), + active: command.active ?? true, + execute: command.execute, + }; +} diff --git a/src/shared/factories/embeds/autoRejectGuild.ts b/src/shared/factories/embeds/autoRejectGuild.ts new file mode 100644 index 0000000..9954e67 --- /dev/null +++ b/src/shared/factories/embeds/autoRejectGuild.ts @@ -0,0 +1,60 @@ +import { EmbedBuilder } from "discord.js"; +import config from "../../../core/config/index.js"; + +type Props = { + sender: { + id: string; + username: string; + }; + epfAbout: string; + serverAbout: string; + role: string | null; + errors: string[]; + inviteUrl?: string; +}; + +export function autoRejectEmbed({ + errors, + sender, + epfAbout, + serverAbout, + role, + inviteUrl, +}: Props): EmbedBuilder { + return new EmbedBuilder() + .setTitle("Formulário Recusado") + .setColor("#d12c2c") + .setFields([ + { + name: "Enviado por", + value: `${sender.username} (${sender.id})`, + inline: true, + }, + { + name: "Link permanente do servidor", + value: `${inviteUrl || "Inválido"}`, + inline: true, + }, + { + name: "Cargo Principal no Servidor", + value: `${role || "Inválido"}`, + inline: true, + }, + { name: "Sobre o servidor", value: `${serverAbout}`, inline: false }, + { + name: "Por onde conheceu a EPF", + value: `${epfAbout}`, + inline: false, + }, + { + name: "Recusado pelo Motivo", + value: `${errors.join("\n")}`, + inline: true, + }, + { + name: "Recusado por", + value: `<@${config.ids.agent}> (${config.ids.agent})`, + inline: true, + }, + ]); +} diff --git a/src/shared/factories/embeds/colorless.ts b/src/shared/factories/embeds/colorless.ts new file mode 100644 index 0000000..99a526e --- /dev/null +++ b/src/shared/factories/embeds/colorless.ts @@ -0,0 +1,5 @@ +import { type APIEmbed, EmbedBuilder } from "discord.js"; + +export default function createColorlessEmbed(data: Omit) { + return new EmbedBuilder({ ...data, color: 0x2b2d31 }); +} diff --git a/src/shared/factories/event.ts b/src/shared/factories/event.ts new file mode 100644 index 0000000..40eaeac --- /dev/null +++ b/src/shared/factories/event.ts @@ -0,0 +1,15 @@ +import type { ClientEvents } from "discord.js"; +import type { DiscordEvent } from "../../shared/types/event.js"; + +export default function createEvent(event: { + /** + * @description The name of the event + */ + name: T; + /** + * @description The function to execute the event + */ + execute: (...args: ClientEvents[T]) => Promise | unknown; +}): DiscordEvent { + return event; +} diff --git a/src/shared/factories/modal.ts b/src/shared/factories/modal.ts new file mode 100644 index 0000000..93feaa4 --- /dev/null +++ b/src/shared/factories/modal.ts @@ -0,0 +1,43 @@ +import { + type CacheType, + ComponentType, + type ModalActionRowComponentData, +} from "discord.js"; +import type { Component, ComponentExecute } from "../types/components.js"; + +type Props = { + /** + * @description The data of the modal containing the customId, title, and components + */ + data: { + customId: string; + title: string; + components: ModalActionRowComponentData[]; + }; + /** + * @description The function that will be executed when the modal is submitted + */ + execute: ComponentExecute<"modal", T>; +}; + +export default function createModal( + props: Props, +): Component<"modal", T> { + return { + id: props.data.customId, + type: "modal", + create(...args) { + return { + customId: args.length + ? `${props.data.customId}:${args.join(":")}` + : props.data.customId, + title: props.data.title, + components: props.data.components.map((component) => ({ + type: ComponentType.ActionRow, + components: [component], + })), + }; + }, + execute: props.execute, + }; +} diff --git a/src/shared/factories/task.ts b/src/shared/factories/task.ts new file mode 100644 index 0000000..766b0a7 --- /dev/null +++ b/src/shared/factories/task.ts @@ -0,0 +1,7 @@ +import type { Task } from "../types/task.js"; + +type Options = Task; + +export default function createTask(task: Options): Task { + return task; +} diff --git a/src/shared/factories/user-context.ts b/src/shared/factories/user-context.ts new file mode 100644 index 0000000..5ab6f61 --- /dev/null +++ b/src/shared/factories/user-context.ts @@ -0,0 +1,26 @@ +import { + ApplicationCommandType, + type UserApplicationCommandData, +} from "discord.js"; +import type { Command, CommandExecute } from "../types/command.js"; + +type Props = { + /** + * @description The data of the user context command + */ + data: Omit; + /** + * @description The function that will be executed when the user context is executed + */ + execute: CommandExecute<"user", "cached">; +}; + +export default function createUserContext(props: Props): Command<"user"> { + return { + data: { + ...props.data, + type: ApplicationCommandType.User, + }, + execute: props.execute, + }; +} diff --git a/src/shared/helpers/ChunkedString.ts b/src/shared/helpers/ChunkedString.ts new file mode 100644 index 0000000..f916f17 --- /dev/null +++ b/src/shared/helpers/ChunkedString.ts @@ -0,0 +1,36 @@ +/** + * A class that helps to split a string into chunks of a maximum length + * + * @example + * ```ts + * const chunked = new ChunkedString(15); // max length of 15 characters + * const text = "Hello, world!"; // 12 characters + * chunked.addLine(text); + * chunked.addLine(text); + * console.log(chunked.get()); // ["Hello, world!", "Hello, world!"] + * ``` + */ +export class ChunkedString { + private chunks: string[] = []; + + constructor(private readonly maxLength: number) {} + + public addLine(str: string) { + if ( + !this.lastChunk() || + this.lastChunk()!.length + str.length >= this.maxLength + ) { + this.chunks.push(str); + } else { + this.chunks[this.chunks.length - 1] += `\n${str}`; + } + } + + public lastChunk() { + return this.chunks[this.chunks.length - 1]; + } + + public get() { + return this.chunks; + } +} diff --git a/src/shared/helpers/checkFormMessages.ts b/src/shared/helpers/checkFormMessages.ts new file mode 100644 index 0000000..57f2613 --- /dev/null +++ b/src/shared/helpers/checkFormMessages.ts @@ -0,0 +1,122 @@ +import { ComponentType, EmbedBuilder, type Client } from "discord.js"; +import applyDevButton from "../../app/components/buttons/apply-dev-form.js"; +import applyServerButton from "../../app/components/buttons/apply-server-form.js"; +import config from "../../core/config/index.js"; + +/** + * Checks if the form messages are already sent + * + * If not, it sends them + * + * @param client The client that is running the bot + */ +export default async function checkFormMessages(client: Client) { + const guild = client.guilds.cache.get(config.ids.guild); + + if (!guild) { + throw new Error("Guild not found"); + } + + await guild.members.fetch(); + await guild.commands.fetch(); + + const serverFormChannel = guild.channels.cache.get( + config.forms.guild.channelId, + ); + + if (!serverFormChannel) { + throw new Error("Server form channel not found"); + } + + if (!serverFormChannel.isTextBased()) { + throw new Error("Server form channel is not text based"); + } + + const serverFormMessage = ( + await serverFormChannel.messages.fetch({ limit: 1 }).catch(() => null) + )?.first(); + + if (!serverFormMessage || serverFormMessage.author.id !== client.user.id) { + const serverRequirementsEmbed = new EmbedBuilder() + .setTitle("<:info:1095440963620573394> Requisitos") + .setImage( + "https://cdn.discordapp.com/attachments/1034845518452502658/1034845554796150815/Requisitos.png", + ) + .setColor(config.forms.guild.color) + .setDescription( + "<:globe:1095440970516021298> Para adicionar algum servidor a EPF é necessário que tenha no" + + " mínimo **5.000 membros** ou que sejam **verificados** ou **parceiros** do Discord e atenda a" + + " todos os [termos de serviço](https://discord.com/terms) e as" + + " [diretrizes](https://discord.com/guidelines) do Discord.", + ); + + const registerServerEmbed = new EmbedBuilder() + .setTitle(`${config.forms.guild.emoji} Aplicar Servidor`) + .setImage(config.forms.guild.bannerURL) + .setColor(config.forms.guild.color) + .setDescription( + `<:green_dot:1037803471077908553> **|** Se você representa um servidor que cumpre os requisitos listados acima e acha que ele merece fazer parte da **Elite Penguin Force**, clique no botão abaixo e preencha o formulário para enviá-lo para uma avaliação.\n\n` + + `<:icon_idle_green:1037806417438068766> **| Lembrando:** Você será avisado assim que a avaliação tenha terminado. Por isso lembre de deixar suas "Mensagens Diretas" abertas.`, + ); + serverFormChannel.send({ + embeds: [serverRequirementsEmbed, registerServerEmbed], + components: [ + { + type: ComponentType.ActionRow, + components: [applyServerButton.create()], + }, + ], + }); + } + + const devFormChannel = guild.channels.cache.get(config.forms.dev.channelId); + + if (!devFormChannel) { + throw new Error("Developer form channel not found"); + } + + if (!devFormChannel.isTextBased()) { + throw new Error("Developer form channel is not text based"); + } + + const devFormMessage = ( + await devFormChannel.messages.fetch({ limit: 1 }).catch(() => null) + )?.first(); + + if (!devFormMessage || devFormMessage.author.id !== client.user.id) { + const devRequirementsEmbed = new EmbedBuilder() + .setTitle("<:info:1095440963620573394> Requisitos") + .setImage( + "https://cdn.discordapp.com/attachments/1034845518452502658/1034845554796150815/Requisitos.png", + ) + .setColor(config.forms.dev.color) + .setDescription( + "<:monitor:1095441115936739450> Para receber o cargo <@&822202388780941313>, é necessário" + + " que cumpra com algum dos seguintes requisitos:\n" + + "<:point_epf:1037183758761205841> Possuir um bot verificado (online);\n" + + "<:point_epf:1037183758761205841> Ter um bot privado de algum dos servidores associados à EPF;\n" + + "<:point_epf:1037183758761205841> Trabalhar com desenvolvimento de software;\n" + + "<:point_epf:1037183758761205841> Ter contribuições/projetos documentados no" + + " [GitHub](https://github.com/).", + ); + + const requestDevRoleEmbed = new EmbedBuilder() + .setTitle(`${config.forms.dev.emoji} Aplicar para Desenvolvedor`) + .setImage(config.forms.dev.bannerURL) + .setColor(config.forms.dev.color) + .setDescription( + `<:point_epf:1037183758761205841> **|** Se você atualmente atua na área da programação que cumpre os requisitos listados acima e quer fazer parte da **Elite Penguin Force** como um Developer, clique no botão abaixo e preencha o formulário para enviá-lo para uma avaliação.\n\n` + + `<:icon_idle:1037801013358379048> **| Lembrando:** Você será avisado assim que a avaliação tenha terminado. Por isso lembre de deixar suas "Mensagens Diretas" abertas.`, + ); + + devFormChannel.send({ + embeds: [devRequirementsEmbed, requestDevRoleEmbed], + components: [ + { + type: ComponentType.ActionRow, + components: [applyDevButton.create()], + }, + ], + }); + } +} diff --git a/src/shared/helpers/deleteGuildRole.ts b/src/shared/helpers/deleteGuildRole.ts new file mode 100644 index 0000000..2124d5f --- /dev/null +++ b/src/shared/helpers/deleteGuildRole.ts @@ -0,0 +1,23 @@ +import type { Guild as DiscordGuild } from "discord.js"; +import Guild from "../../core/db/models/guild.js"; + +type Options = { + guild: DiscordGuild; + roleId: string; +}; + +/** + * Deletes a guild role and updates the guild's role in the database + * + * @param options The options to delete the guild role + */ +export default async function deleteGuildRole(options: Options) { + const { guild, roleId } = options; + + await guild.roles.delete(roleId); + await Guild.updateOne({ _id: guild.id }, { $set: { role: null } }); + + guild.client.updateServersData([ + `<:mod:1040429385066491946> **|** O servidor **${guild.name}** perdeu o seu cargo.`, + ]); +} diff --git a/src/shared/helpers/forms/createServerRejection.ts b/src/shared/helpers/forms/createServerRejection.ts new file mode 100644 index 0000000..9a2f9e2 --- /dev/null +++ b/src/shared/helpers/forms/createServerRejection.ts @@ -0,0 +1,54 @@ +import type { ModalSubmitInteraction } from "discord.js"; +import config from "../../../core/config/index.js"; +import { autoRejectEmbed } from "../../factories/embeds/autoRejectGuild.js"; +import parseMemberRole from "./parseMemberRole.js"; + +/** + * Creates a server rejection + * + * Sends a message to the user and sends an embed to the approve channel + * + * @param interaction The interaction that triggered the rejection + * @param errors The errors that caused the rejection + */ +export default async function createServerRejection( + interaction: ModalSubmitInteraction<"cached">, + errors: string[], +) { + const role = interaction.fields.getTextInputValue("serverRole"); + const parsedRole = parseMemberRole(role); + + const serverAbout = interaction.fields.getTextInputValue("serverAbout"); + const epfAbout = interaction.fields.getTextInputValue("epfAbout"); + + await interaction.member + .send({ + content: `O seu servidor foi recusado automaticamente pelo seguinte motivo:\n${errors.join( + "\n", + )}`, + }) + .catch(() => null); + await interaction.reply({ + ephemeral: true, + content: + "Seu servidor foi rejeitado devido a um erro no formulário, consulte sua DM para mais informações, caso sua DM seja privada sugerimos que deixe ela pública para receber novas informaçòes vindas do nosso bot Agent", + }); + + const approveChannel = interaction.client.channels.cache.get( + config.ids.channels.approve, + ); + + if (approveChannel?.isSendable()) { + const embed = autoRejectEmbed({ + sender: interaction.user, + epfAbout, + serverAbout, + errors, + role: parsedRole, + }); + + await approveChannel.send({ embeds: [embed] }); + } + + return; +} diff --git a/src/shared/helpers/forms/createServerRequest.ts b/src/shared/helpers/forms/createServerRequest.ts new file mode 100644 index 0000000..ef463f3 --- /dev/null +++ b/src/shared/helpers/forms/createServerRequest.ts @@ -0,0 +1,107 @@ +import { + ComponentType, + EmbedBuilder, + type Guild as _Guild, + type Invite, + type ModalSubmitInteraction, +} from "discord.js"; +import approveFormButton from "../../../app/components/buttons/approve-server.js"; +import refuseFormButton from "../../../app/components/buttons/refuse-server.js"; +import config from "../../../core/config/index.js"; +import Guild from "../../../core/db/models/guild.js"; +import Member from "../../../core/db/models/member.js"; +import type { ReadableStaffRole } from "../../types/index.js"; + +/** + * Creates a server request + * + * Sends a message to the user and sends an embed to the approve channel + * + * @param interaction The interaction that triggered the request + * @param role The role of the user + * @param invite The invite of the guild + */ +export default async function createRequest( + interaction: ModalSubmitInteraction<"cached">, + role: ReadableStaffRole | null, + invite: Invite, +) { + const serverAbout = interaction.fields.getTextInputValue("serverAbout"); + const epfAbout = interaction.fields.getTextInputValue("epfAbout"); + const userEmbed = new EmbedBuilder() + .setTitle(`Formulário enviado`) + .setDescription( + "Sua solicitação foi enviada com sucesso\n\nFazer esse formulário **não** garante a entrada da sua comunidade na EPF." + + "\nSua requisição passará por uma analise e a equipe determinará se seu servidor está apto ou não.", + ) + .setColor("#fccf03"); + + await interaction.reply({ + embeds: [userEmbed], + ephemeral: true, + }); + + const embed = new EmbedBuilder() + .setTitle(`Novo formulário`) + .setColor("#fccf03") + .setFields([ + { + name: "Enviado por", + value: `${interaction.user.username} (${interaction.user.id})`, + inline: true, + }, + { + name: "Link permanente do servidor", + value: `${invite.url || "Inválido"}`, + inline: true, + }, + { + name: "Cargo Principal no Servidor", + value: `${role || "Inválido"}`, + inline: true, + }, + { name: "Sobre o servidor", value: `${serverAbout}`, inline: false }, + { + name: "Por onde conheceu a EPF", + value: `${epfAbout}`, + inline: false, + }, + ]); + + const guild = invite.guild as _Guild; + + const newGuildDoc = new Guild({ + _id: guild.id, + representative: interaction.user.id, + invite: invite.code, + name: guild.name, + owner: role === "Dono" ? interaction.user.id : null, + pending: true, + }); + await newGuildDoc.save(); + await Member.create({ + user: interaction.user.id, + guild: newGuildDoc._id, + admin: role === "Administrador" || role === "Dono", + }); + + const approveChannel = interaction.client.channels.cache.get( + config.ids.channels.approve, + ); + + if (approveChannel?.isSendable()) { + const message = await approveChannel.send({ + embeds: [embed], + components: [ + { + type: ComponentType.ActionRow, + components: [ + approveFormButton.create(guild.id), + refuseFormButton.create(guild.id), + ], + }, + ], + }); + await message.startThread({ name: `Server ${guild.name}` }); + } +} diff --git a/src/shared/helpers/forms/parseMemberRole.ts b/src/shared/helpers/forms/parseMemberRole.ts new file mode 100644 index 0000000..389faca --- /dev/null +++ b/src/shared/helpers/forms/parseMemberRole.ts @@ -0,0 +1,25 @@ +import type { ReadableStaffRole } from "../../types/index.js"; + +/** + * Parses the member role + * + * @param role The role to parse + * @returns {ReadableStaffRole | null} The parsed role or null if the role is not valid + */ +export default function parseMemberRole( + role: string, +): ReadableStaffRole | null { + switch (role.toLowerCase()) { + case "mod": + return "Moderador"; + case "adm": + case "admin": + return "Administrador"; + case "dono": + case "dona": + case "owner": + return "Dono"; + default: + return null; + } +} diff --git a/src/shared/helpers/handleServerAutocomplete.ts b/src/shared/helpers/handleServerAutocomplete.ts new file mode 100644 index 0000000..2ca590f --- /dev/null +++ b/src/shared/helpers/handleServerAutocomplete.ts @@ -0,0 +1,41 @@ +import type { AutocompleteInteraction } from "discord.js"; +import Guild from "../../core/db/models/guild.js"; +import isGuard from "./isGuard.js"; + +/** + * Handles the "server" option autocomplete + * + * Gets the guilds that match the value and responds with them + * + * If the user is a guard, it gets all guilds, otherwise it gets only the guilds that the user is a representative of + * + * @param interaction The interaction that triggered the autocomplete + */ +export default async function handleServerAutocomplete( + interaction: AutocompleteInteraction, +) { + if (!interaction.member) { + return; + } + + const value = interaction.options.getString("server", true); + const name = { + $regex: new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i"), + }; + + const query = isGuard(interaction.member) + ? { name } + : { + representative: interaction.user.id, + name, + }; + + const representing = await Guild.find(query).sort({ name: 1 }).limit(25); + + return interaction.respond( + representing.map((doc) => ({ + name: doc.name, + value: doc._id as string, + })), + ); +} diff --git a/src/shared/helpers/isGuard.ts b/src/shared/helpers/isGuard.ts new file mode 100644 index 0000000..0f05e03 --- /dev/null +++ b/src/shared/helpers/isGuard.ts @@ -0,0 +1,16 @@ +import { type APIGuildMember, GuildMember } from "discord.js"; +import config from "../../core/config/index.js"; + +/** + * Checks if a member is a guard + * + * @param member The member to check + * @returns Whether the member is a guard + */ +export default function isGuard(member: GuildMember | APIGuildMember) { + if (member instanceof GuildMember) { + return member.roles.cache.has(config.ids.roles.guard); + } + + return member.roles.includes(config.ids.roles.guard); +} diff --git a/src/shared/helpers/logRepresentativeExit.ts b/src/shared/helpers/logRepresentativeExit.ts new file mode 100644 index 0000000..a600c57 --- /dev/null +++ b/src/shared/helpers/logRepresentativeExit.ts @@ -0,0 +1,39 @@ +import { type GuildMember, type PartialGuildMember } from "discord.js"; +import config from "../../core/config/index.js"; +import type { GuildSchemaType } from "../../core/db/models/guild.js"; +import createColorlessEmbed from "../factories/embeds/colorless.js"; + +type Options = { + member: GuildMember | PartialGuildMember; + representingDocs: GuildSchemaType[]; +}; + +/** + * Logs when a member leaves a guild while representing another guild + * + * @param options The options to log the representative exit + */ +export default function logRepresentativeExit(options: Options) { + const { member, representingDocs } = options; + + const embed = createColorlessEmbed({ + description: + `\`${member.user.username}\` saiu do servidor enquanto representava os servidores: ` + + representingDocs + .map((doc) => `[\`${doc.name}\`](https://discord.gg/${doc.invite})`) + .join(" "), + }); + + const channel = member.client.channels.cache.get( + config.ids.channels.changeLog, + ); + + if (!channel?.isSendable()) { + console.error( + `[logRepresentativeExit] Channel ${config.ids.channels.changeLog} is not sendable`, + ); + return; + } + + return channel.send({ embeds: [embed] }); +} diff --git a/src/shared/helpers/path.ts b/src/shared/helpers/path.ts new file mode 100644 index 0000000..7b7c241 --- /dev/null +++ b/src/shared/helpers/path.ts @@ -0,0 +1,37 @@ +import { readdirSync } from "fs"; +import { sep } from "path"; +import { ROOT } from "../constants.js"; + +/** + * Reads a directory recursively and returns the paths of all files + * + * @param dir The directory to read + * @returns The paths of all files in the directory + */ +export function deepReadDirectory(dir: string) { + return readdirSync(dir, { + recursive: true, + encoding: "utf-8", + withFileTypes: true, + }).map((path) => `${path.parentPath}/${path.name}`.replaceAll(sep, "/")); +} + +/** + * Gets the paths of all javascript files in a directory + * + * @param dir The directory to read + * @returns The paths of all javascript files in the directory + */ +export function getJavascriptPaths(dir: string) { + return deepReadDirectory(dir).filter((path) => path.endsWith(".js")); +} + +/** + * Imports a file using the root path + * + * @param path The path of the file to import + * @returns The imported file + */ +export function importUsingRoot(path: string) { + return import(ROOT + "/" + path); +} diff --git a/src/shared/helpers/safeCall.ts b/src/shared/helpers/safeCall.ts new file mode 100644 index 0000000..e05fddb --- /dev/null +++ b/src/shared/helpers/safeCall.ts @@ -0,0 +1,18 @@ +/** + * Safely calls a function and logs any errors that occur + * + * @param fn The function to call + * @param args The arguments to pass to the function + * @returns The result of the function call + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export default function safeCall any>( + fn: T, + ...args: Parameters +) { + try { + return fn(...args); + } catch (error) { + console.error(error); + } +} diff --git a/src/shared/helpers/sendChangeLogs.ts b/src/shared/helpers/sendChangeLogs.ts new file mode 100644 index 0000000..6d13217 --- /dev/null +++ b/src/shared/helpers/sendChangeLogs.ts @@ -0,0 +1,28 @@ +import { ChannelType, type Client } from "discord.js"; +import config from "../../core/config/index.js"; +import Constants from "../../core/db/models/constants.js"; + +/** + * Sends the change logs to the change log channel + * + * @param client The client that is running the bot + */ +export default async function sendChangeLogs(client: Client) { + const constants = await Constants.getConstants(); + if (!constants.updateLogs.length) { + return; + } + + const channel = client.channels.cache.get(config.ids.channels.changeLog); + if (channel?.type !== ChannelType.GuildText) { + throw new Error("Change log channel is not a text channel"); + } + + await channel.send({ + content: constants.updateLogs.join("\n"), + allowedMentions: { roles: [], users: [] }, + }); + + constants.updateLogs = []; + await constants.save(); +} diff --git a/src/shared/helpers/updateServersList.ts b/src/shared/helpers/updateServersList.ts new file mode 100644 index 0000000..32a83b1 --- /dev/null +++ b/src/shared/helpers/updateServersList.ts @@ -0,0 +1,72 @@ +import { ChannelType, type Client, EmbedBuilder } from "discord.js"; +import config from "../../core/config/index.js"; +import Constants from "../../core/db/models/constants.js"; +import Guild from "../../core/db/models/guild.js"; +import { ALMOST_TWO_WEEKS, ALPHABET } from "../constants.js"; + +/** + * Updates the server list channel + * + * @param client The client that is running the bot + */ +export default async function updateServerList(client: Client) { + await Constants.updateConstants({ + updatingGuildsChannel: true, + lastGuildsChannelUpdate: new Date(), + }); + + const channel = client.channels.cache.get(config.ids.channels.serversList); + if (channel?.type !== ChannelType.GuildText) { + throw new Error("Server list channel is not guild text"); + } + + const messages = await channel.messages.fetch(); + const lastMessageTimestamp = messages.last()?.createdTimestamp; + + if ( + lastMessageTimestamp && + lastMessageTimestamp > Date.now() - ALMOST_TWO_WEEKS + ) { + await channel.bulkDelete(100); + } else { + for (const message of messages.values()) { + await message.delete(); + } + } + + let guildCount = 0; + for (const letter of ALPHABET) { + const guildDocs = await Guild.find({ + name: { $regex: new RegExp(`^[^a-z]*${letter}`, "i") }, + pending: { $ne: true }, + }); + + if (!guildDocs.length) { + continue; + } + + guildCount += guildDocs.length; + + const embed = new EmbedBuilder() + .setTitle(`Comunidades (${letter})`) + .setImage( + `https://cdn.discordapp.com/attachments/946097024804212777/1037824425191542845/Divisoria.png`, + ) + .setColor(0x5765f0) + .setDescription( + guildDocs + .map((doc) => { + let str = `[\`${doc.name.replaceAll( + "`", + "ˋ", + )}\`](https://discord.gg/${doc.invite})`; + return doc.role ? `${str} | <@&${doc.role}>` : str; + }) + .join("\n"), + ); + await channel.send({ embeds: [embed] }); + } + + await channel.setTopic(`🧭 | **${guildCount} servidores associados.**`); + await Constants.updateConstants({ updatingGuildsChannel: false }); +} diff --git a/src/shared/types/command.d.ts b/src/shared/types/command.d.ts new file mode 100644 index 0000000..5598214 --- /dev/null +++ b/src/shared/types/command.d.ts @@ -0,0 +1,150 @@ +import type { + ApplicationCommandOptionType, + AutocompleteInteraction, + CacheType, + ChannelType, + ChatInputApplicationCommandData, + ChatInputCommandInteraction, + LocalizationMap, + MessageApplicationCommandData, + MessageContextMenuCommandInteraction, + UserApplicationCommandData, + UserContextMenuCommandInteraction, +} from "discord.js"; +import type { AutocompleteExecute } from "./autocomplete.js"; + +type BaseOption = { + name: string; + nameLocalizations?: LocalizationMap; + description: string; + descriptionLocalizations?: LocalizationMap; + required?: boolean; +}; + +type Choise = { + name: string; + value: T; +}; + +type AutocompletableOption = { + /** + * @description The function that will be executed when then option's autocomplete is triggered + */ + autocomplete: AutocompleteExecute; + /** + * @description Choices are not allowed for autocomplete options + */ + choices?: never; +}; + +type ChoosableOption = { + /** + * @description The choices for the option + */ + choices: Choise[]; + /** + * @description Autocomplete is not allowed for choosable options + */ + autocomplete?: never; +}; + +type BaseStringOption = BaseOption & { + minLength?: number; + maxLength?: number; + type: ApplicationCommandOptionType.String; +}; + +type AutocompletableStringOption = BaseStringOption & AutocompletableOption; +type ChoosableStringOption = BaseStringOption & ChoosableOption; + +type BaseNumberOption = BaseOption & { + minValue?: number; + maxValue?: number; + type: ApplicationCommandOptionType.Number; +}; + +type AutocompletableNumberOption = BaseNumberOption & AutocompletableOption; +type ChoosableNumberOption = BaseNumberOption & ChoosableOption; + +type BooleanOption = BaseOption & { + type: ApplicationCommandOptionType.Boolean; +}; + +type UserOption = BaseOption & { + type: ApplicationCommandOptionType.User; +}; + +type RoleOption = BaseOption & { + type: ApplicationCommandOptionType.Role; +}; + +type ChannelOption = BaseOption & { + type: ApplicationCommandOptionType.Channel; + channelTypes: ChannelType[]; +}; + +type MentionableOption = BaseOption & { + type: ApplicationCommandOptionType.Mentionable; +}; + +type SubcommandOption = BaseOption & { + type: ApplicationCommandOptionType.Subcommand; + options: Exclude[]; +}; + +type SubcommandGroupOption = BaseOption & { + type: ApplicationCommandOptionType.SubcommandGroup; + options: Exclude[]; +}; + +export type CommandOption = + | BaseStringOption + | AutocompletableStringOption + | ChoosableStringOption + | AutocompletableNumberOption + | ChoosableNumberOption + | BooleanOption + | UserOption + | RoleOption + | ChannelOption + | MentionableOption + | SubcommandOption + | SubcommandGroupOption; + +export type AutocompleteExecute = ( + interaction: AutocompleteInteraction, +) => Promise | unknown; + +type CommandType = "chat" | "user" | "message"; +type CommandData = T extends "chat" + ? ChatInputApplicationCommandData + : T extends "user" + ? UserApplicationCommandData + : T extends "message" + ? MessageApplicationCommandData + : never; +type CommandExecute< + T extends CommandType, + C extends CacheType = "cached", +> = T extends "chat" + ? (interaction: ChatInputCommandInteraction) => unknown | Promise + : T extends "user" + ? ( + interaction: UserContextMenuCommandInteraction, + ) => unknown | Promise + : T extends "message" + ? ( + interaction: MessageContextMenuCommandInteraction, + ) => unknown | Promise + : never; + +export type Command = { + data: CommandData; + /** + * Whether the command should be active or not + * @default true + */ + active?: boolean; + autocomplete?: T extends "chat" ? Map : never; + execute: CommandExecute; +}; diff --git a/src/shared/types/components.d.ts b/src/shared/types/components.d.ts new file mode 100644 index 0000000..baba331 --- /dev/null +++ b/src/shared/types/components.d.ts @@ -0,0 +1,42 @@ +import type { + APIButtonComponent, + ButtonInteraction, + CacheType, + ModalComponentData, + ModalSubmitInteraction, +} from "discord.js"; + +type ComponentType = "button" | "modal"; +type ComponentData = T extends "button" + ? APIButtonComponent + : T extends "modal" + ? ModalComponentData + : never; +type ComponentInteraction< + T extends ComponentType, + C extends CacheType, +> = T extends "button" + ? ButtonInteraction + : T extends "modal" + ? ModalSubmitInteraction + : never; +export type ComponentExecute< + T extends ComponentType, + C extends CacheType = "cached", +> = ( + interaction: ComponentInteraction, + ...args: string[] +) => Promise | unknown; + +export type Component< + T extends ComponentType, + C extends CacheType = "cached", +> = { + id: string; + type: T; + create: (...args: (string | number | boolean)[]) => ComponentData; + execute: ComponentExecute; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type AnyComponent = Component; diff --git a/src/shared/types/event.d.ts b/src/shared/types/event.d.ts new file mode 100644 index 0000000..d85913c --- /dev/null +++ b/src/shared/types/event.d.ts @@ -0,0 +1,6 @@ +import type { Awaitable, ClientEvents } from "discord.js"; + +export type DiscordEvent = { + name: T; + execute: (...args: ClientEvents[T]) => Awaitable; +}; diff --git a/src/shared/types/index.d.ts b/src/shared/types/index.d.ts new file mode 100644 index 0000000..3714000 --- /dev/null +++ b/src/shared/types/index.d.ts @@ -0,0 +1,2 @@ +export type StaffRole = "mod" | "admin" | "owner"; +export type ReadableStaffRole = "Moderador" | "Administrador" | "Dono"; diff --git a/src/shared/types/task.d.ts b/src/shared/types/task.d.ts new file mode 100644 index 0000000..5905f6b --- /dev/null +++ b/src/shared/types/task.d.ts @@ -0,0 +1,9 @@ +import type { Client } from "discord.js"; + +export type Task = { + data: { + name: string; + interval: number; + }; + execute: (client: Client) => unknown | Promise; +}; diff --git a/src/structures/command.js b/src/structures/command.js deleted file mode 100644 index 813f5c3..0000000 --- a/src/structures/command.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = class { - constructor({active, data}){ - this.active = active; - this.data = data; - } -}; \ No newline at end of file diff --git a/src/utils/sendGuildChangelogs.js b/src/utils/sendGuildChangelogs.js deleted file mode 100644 index 559a969..0000000 --- a/src/utils/sendGuildChangelogs.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = async (client) => { - const constantsModel = require('../models/constants'); - let constants = await constantsModel.findOne({}); - if (!constants.updateLogs?.length) return; - const config = require('../config'); - await client.channels.cache.get(config.guildsChangeLog)?.send({ - content: constants.updateLogs.join('\n'), - allowedMentions: { roles: [], users: [] }, - }); - constants.updateLogs = []; - await constants.save(); -}; diff --git a/src/utils/updateGuildsChannel.js b/src/utils/updateGuildsChannel.js deleted file mode 100644 index 51efb0c..0000000 --- a/src/utils/updateGuildsChannel.js +++ /dev/null @@ -1,42 +0,0 @@ -const { EmbedBuilder } = require('discord.js') - -module.exports = async (client) => { - const constantsModel = require('../models/constants') - await constantsModel.updateConstants({ updatingGuildsChannel: true, lastGuildsChannelUpdate: new Date() }); - const config = require('../config') - const channel = client.channels.cache.get(config.serversChannel); - const messages = await channel.messages.fetch(); - const minute = 60 * 1000; - if(messages.last()?.createdTimestamp > (Date.now() - ((2 * 7 * 24 * 60 * minute) - minute))){ - await channel.bulkDelete(100); - } - else{ - for(const message of messages.values()) await message.delete(); - } - const guildModel = require('../models/guild.js'); - let guildCount = 0; - for(let i = 65; i < 91; i++){ - const letter = String.fromCharCode(i); - const guildDocs = await guildModel.find({ - name: {$regex: new RegExp(`^[^a-z]*${letter}`, 'i')}, - pending: {$ne: true}, - }); - if(!guildDocs.length) continue; - guildCount += guildDocs.length; - const embed = new EmbedBuilder() - .setTitle(`Comunidades (${letter})`) - .setImage(`https://cdn.discordapp.com/attachments/946097024804212777/1037824425191542845/Divisoria.png`) - .setColor(0x5765f0) - .setDescription( - guildDocs - .map(doc => { - let str = `[\`${doc.name.replaceAll('`', 'ˋ')}\`](https://discord.gg/${doc.invite})`; - return doc.role ? `${str} | <@&${doc.role}>` : str; - }) - .join('\n'), - ); - await channel.send({embeds: [embed]}); - } - await channel.setTopic(`🧭 | **${guildCount} servidores associados.**`); - await constantsModel.updateConstants({ updatingGuildsChannel: false }); -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7b80654 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + /* Base Options: */ + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + /* Strictness */ + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "module": "NodeNext", + "outDir": "dist", + "sourceMap": true, + "lib": ["es2022"], + "removeComments": true + }, + "exclude": ["node_modules", "dist"] +}