From 39f89a9f63fb857c9834c28a070d9ca27672d1d6 Mon Sep 17 00:00:00 2001 From: WatDuhHekBro <44940783+WatDuhHekBro@users.noreply.github.com> Date: Mon, 14 Dec 2020 19:44:28 -0600 Subject: [PATCH] Formatting Preview Alpha --- .github/codeql/codeql-config.yml | 6 +- .github/workflows/codeql-analysis.yml | 62 +- .github/workflows/image.yml | 42 +- .prettierignore | 7 + README.md | 6 +- docs/GettingStarted.md | 1 + package-lock.json | 2948 ++++++++--------- package.json | 84 +- prettier.config.js | 24 +- src/commands/admin.ts | 465 +-- src/commands/fun/8ball.ts | 67 +- src/commands/fun/cookie.ts | 104 +- src/commands/fun/eco.ts | 61 +- src/commands/fun/neko.ts | 57 +- src/commands/fun/ok.ts | 137 +- src/commands/fun/owoify.ts | 26 +- src/commands/fun/poll.ts | 50 +- src/commands/fun/subcommands/eco-core.ts | 312 +- .../fun/subcommands/eco-shop-items.ts | 122 +- src/commands/fun/subcommands/eco-shop.ts | 161 +- src/commands/fun/subcommands/eco-utils.ts | 144 +- src/commands/help.ts | 276 +- src/commands/info.ts | 531 +-- src/commands/scanemotes.ts | 367 +- src/commands/utilities/desc.ts | 38 +- src/commands/utilities/emote.ts | 36 +- src/commands/utilities/lsemotes.ts | 60 +- src/commands/utilities/react.ts | 140 +- src/commands/utilities/say.ts | 28 +- src/commands/utilities/shorten.ts | 50 +- src/core/command.ts | 423 +-- src/core/event.ts | 58 +- src/core/lib.ts | 1021 +++--- src/core/permissions.ts | 101 +- src/core/storage.ts | 148 +- src/core/structures.ts | 162 +- src/core/wrappers.ts | 139 +- src/defs/info.ts | 78 +- src/events/channelCreate.ts | 26 +- src/events/channelDelete.ts | 26 +- src/events/message.ts | 258 +- src/events/messageReactionRemove.ts | 30 +- src/events/ready.ts | 30 +- src/index.ts | 52 +- src/setup.ts | 111 +- test/wrappers.ts | 190 +- tsconfig.json | 30 +- 47 files changed, 4714 insertions(+), 4581 deletions(-) diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 08ced75..ef82e9f 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -1,6 +1,6 @@ -name: 'CodeQL Config' +name: "CodeQL Config" queries: - - uses: security-and-quality + - uses: security-and-quality paths: - - dist + - dist diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f28a185..200682a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,41 +1,41 @@ -name: 'CodeQL' +name: "CodeQL" on: - push: - branches: [typescript] - pull_request: - branches: [typescript] - schedule: - - cron: '0 5 * * 1' + push: + branches: [typescript] + pull_request: + branches: [typescript] + schedule: + - cron: "0 5 * * 1" jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest + analyze: + name: Analyze + runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - fetch-depth: 2 + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 2 - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} - - name: Setup Node.JS - uses: actions/setup-node@v2-beta - with: - node-version: '12' - - run: npm ci + - name: Setup Node.JS + uses: actions/setup-node@v2-beta + with: + node-version: "12" + - run: npm ci - - name: Build codebase - run: npm run build + - name: Build codebase + run: npm run build - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - config-file: ./.github/codeql/codeql-config.yml - languages: javascript + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + config-file: ./.github/codeql/codeql-config.yml + languages: javascript - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index d6650c9..dac2e7a 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -1,25 +1,25 @@ name: Build Docker Image + Push on: - push: - branches: - - typescript - - docker + push: + branches: + - typescript + - docker jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - name: Install Docker BuildX - id: buildx - uses: crazy-max/ghaction-docker-buildx@v1 - with: - buildx-version: latest - - name: Login to Docker Hub - run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin - - name: Build the image - run: | - docker buildx build \ - --tag keanucode/travbot-v3:latest \ - --platform linux/amd64,linux/arm/v7,linux/arm64 --push . + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Install Docker BuildX + id: buildx + uses: crazy-max/ghaction-docker-buildx@v1 + with: + buildx-version: latest + - name: Login to Docker Hub + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + - name: Build the image + run: | + docker buildx build \ + --tag keanucode/travbot-v3:latest \ + --platform linux/amd64,linux/arm/v7,linux/arm64 --push . diff --git a/.prettierignore b/.prettierignore index 3b0f1ed..960cb07 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,10 @@ +# Specific to prettier (so it doesn't throw a bunch of errors when running "npm run format") +.dockerignore +.gitignore +.prettierignore +Dockerfile +LICENSE + # Specific to this repository dist/ data/ diff --git a/README.md b/README.md index 292e3f9..b099c29 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ Thank you for coming on this journey with me, but it is time to put the big chan Special thanks to: -- Lexi Sother (TravBot v2, structural overhaul. Reviewing PRs.) -- WatDuhHekBro (a _lot_ of contributions to TravBot v2) -- Zeehondie (Ideas for various commands.) +- Lexi Sother (TravBot v2, structural overhaul. Reviewing PRs.) +- WatDuhHekBro (a _lot_ of contributions to TravBot v2) +- Zeehondie (Ideas for various commands.) ### License diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 398cc33..a003a81 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -17,3 +17,4 @@ - ...update the [changelog](CHANGELOG.md) and any other necessary docs. - ...update the version numbers in `package.json` and `package-lock.json`. - ...make sure the test suite passes by running `npm test`. +- ...format the code by running `npm run format`. diff --git a/package-lock.json b/package-lock.json index 78eba10..c7e8e8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1491 +1,1491 @@ { - "name": "d.js-v12-bot", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@discordjs/collection": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", - "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" - }, - "@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "@lavacord/discord.js": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@lavacord/discord.js/-/discord.js-0.0.5.tgz", - "integrity": "sha512-qc2lw0zB48fq4SrSlpMJOmogUXIeM5YvufQfPg0ubjp7jqm20JnOXF9fliy1MPdcKPnZ8LwIZ222H0+ghGzP/Q==", - "requires": { - "lavacord": "^1.1.9" - }, - "dependencies": { - "lavacord": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/lavacord/-/lavacord-1.1.9.tgz", - "integrity": "sha512-haZghbblO1w3Hodc9q63ZWgV5zA/jB6xFKS17fImK5aIdn0PkKuZ6AsJBxMFpR275v8GNYOxg6cTQBYBQ+batQ==", - "requires": { - "node-fetch": "^2.6.0", - "ws": "^7.3.0" - } - } - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, - "@types/inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==", - "dev": true, - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "@types/mocha": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz", - "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", - "dev": true - }, - "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", - "dev": true - }, - "@types/node": { - "version": "14.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.2.tgz", - "integrity": "sha512-jeYJU2kl7hL9U5xuI/BhKPZ4vqGM/OmK6whiFAXVhlstzZhVamWhDSmHyGLIp+RVyuF9/d0dqr2P85aFj4BvJg==", - "dev": true - }, - "@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/ws": { - "version": "7.2.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.7.tgz", - "integrity": "sha512-UUFC/xxqFLP17hTva8/lVT0SybLUrfSD9c+iapKb0fEiC8uoDbA+xuZ3pAN603eW+bY8ebSMLm9jXdIPnD0ZgA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { + "name": "d.js-v12-bot", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@discordjs/collection": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", + "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" + }, + "@discordjs/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "@lavacord/discord.js": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@lavacord/discord.js/-/discord.js-0.0.5.tgz", + "integrity": "sha512-qc2lw0zB48fq4SrSlpMJOmogUXIeM5YvufQfPg0ubjp7jqm20JnOXF9fliy1MPdcKPnZ8LwIZ222H0+ghGzP/Q==", + "requires": { + "lavacord": "^1.1.9" + }, + "dependencies": { + "lavacord": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/lavacord/-/lavacord-1.1.9.tgz", + "integrity": "sha512-haZghbblO1w3Hodc9q63ZWgV5zA/jB6xFKS17fImK5aIdn0PkKuZ6AsJBxMFpR275v8GNYOxg6cTQBYBQ+batQ==", + "requires": { + "node-fetch": "^2.6.0", + "ws": "^7.3.0" + } + } + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "@types/inquirer": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", + "integrity": "sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==", + "dev": true, + "requires": { + "@types/through": "*", + "rxjs": "^6.4.0" + } + }, + "@types/mocha": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz", + "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", + "dev": true + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "@types/node": { + "version": "14.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.2.tgz", + "integrity": "sha512-jeYJU2kl7hL9U5xuI/BhKPZ4vqGM/OmK6whiFAXVhlstzZhVamWhDSmHyGLIp+RVyuF9/d0dqr2P85aFj4BvJg==", + "dev": true + }, + "@types/through": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", + "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.7.tgz", + "integrity": "sha512-UUFC/xxqFLP17hTva8/lVT0SybLUrfSD9c+iapKb0fEiC8uoDbA+xuZ3pAN603eW+bY8ebSMLm9jXdIPnD0ZgA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "requires": { + "type-fest": "^0.11.0" + } + }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.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==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "discord.js": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.4.0.tgz", - "integrity": "sha512-Lc+/vKzih1DPEya/0MO0BAp4Ru/4+MfDsOJkfbyzGoyIlFqiWSQ78RdyRk4tqAtabl6d5QR4Ygo0Ub0TGEsXBg==", - "requires": { - "@discordjs/collection": "^0.1.6", - "@discordjs/form-data": "^3.0.1", - "abort-controller": "^3.0.0", - "node-fetch": "^2.6.1", - "prism-media": "^1.2.2", - "setimmediate": "^1.0.5", - "tweetnacl": "^1.0.3", - "ws": "^7.3.1" - } - }, - "discord.js-lavalink-lib": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/discord.js-lavalink-lib/-/discord.js-lavalink-lib-0.1.7.tgz", - "integrity": "sha512-czjsdRcbB+sdfTXsko76JYAAPJzoRhTiaphZJXqFpsAYdv0hjkNVLHxuuG2d4hVuXjt/UU7HDYyWEb2e/3g2Ew==", - "requires": { - "@lavacord/discord.js": "0.0.5", - "axios": "^0.19.2", - "discord.js": "^12.2.0", - "lavacord": "^1.1.7" - } - }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "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, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - } - }, - "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, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "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 - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "lavacord": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/lavacord/-/lavacord-1.1.9.tgz", - "integrity": "sha512-haZghbblO1w3Hodc9q63ZWgV5zA/jB6xFKS17fImK5aIdn0PkKuZ6AsJBxMFpR275v8GNYOxg6cTQBYBQ+batQ==", - "requires": { - "node-fetch": "^2.6.0", - "ws": "^7.3.0" - } - }, - "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, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mocha": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.0.tgz", - "integrity": "sha512-lEWEMq2LMfNJMKeuEwb5UELi+OgFDollXaytR5ggQcHpzG3NP/R7rvixAvF+9/lLsTWhWG+4yD2M70GsM06nxw==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", - "dev": true - }, - "node-cleanup": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", - "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=", - "dev": true - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "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 - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "os": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", - "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "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, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "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 - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "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 - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "prettier": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", - "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", - "dev": true - }, - "prism-media": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz", - "integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw==" - }, - "ps-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", - "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, - "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", - "requires": { - "tslib": "^1.9.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==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "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, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "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 - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, - "string-argv": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz", - "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "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 - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-node": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", - "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.17", - "yn": "3.1.1" - } - }, - "tsc-watch": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.9.tgz", - "integrity": "sha512-DlTaoDs74+KUpyWr7dCGhuscAUKCz6CiFduBN7R9RbLJSSN1moWdwoCLASE7+zLgGvV5AwXfYDiEMAsPGaO+Vw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "node-cleanup": "^2.1.2", - "ps-tree": "^1.2.0", - "string-argv": "^0.1.1", - "strip-ansi": "^6.0.0" - } - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" - }, - "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } + "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.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.1.0.tgz", - "integrity": "sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==", - "dev": true + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } }, "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "discord.js": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-12.4.0.tgz", + "integrity": "sha512-Lc+/vKzih1DPEya/0MO0BAp4Ru/4+MfDsOJkfbyzGoyIlFqiWSQ78RdyRk4tqAtabl6d5QR4Ygo0Ub0TGEsXBg==", + "requires": { + "@discordjs/collection": "^0.1.6", + "@discordjs/form-data": "^3.0.1", + "abort-controller": "^3.0.0", + "node-fetch": "^2.6.1", + "prism-media": "^1.2.2", + "setimmediate": "^1.0.5", + "tweetnacl": "^1.0.3", + "ws": "^7.3.1" + } + }, + "discord.js-lavalink-lib": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/discord.js-lavalink-lib/-/discord.js-lavalink-lib-0.1.7.tgz", + "integrity": "sha512-czjsdRcbB+sdfTXsko76JYAAPJzoRhTiaphZJXqFpsAYdv0hjkNVLHxuuG2d4hVuXjt/UU7HDYyWEb2e/3g2Ew==", + "requires": { + "@lavacord/discord.js": "0.0.5", + "axios": "^0.19.2", + "discord.js": "^12.2.0", + "lavacord": "^1.1.7" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + } + }, + "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, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "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 + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "lavacord": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/lavacord/-/lavacord-1.1.9.tgz", + "integrity": "sha512-haZghbblO1w3Hodc9q63ZWgV5zA/jB6xFKS17fImK5aIdn0PkKuZ6AsJBxMFpR275v8GNYOxg6cTQBYBQ+batQ==", + "requires": { + "node-fetch": "^2.6.0", + "ws": "^7.3.0" + } + }, + "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, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mocha": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-lEWEMq2LMfNJMKeuEwb5UELi+OgFDollXaytR5ggQcHpzG3NP/R7rvixAvF+9/lLsTWhWG+4yD2M70GsM06nxw==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "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 + }, + "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, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true + }, + "node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=", + "dev": true + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "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 + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "os": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.1.tgz", + "integrity": "sha1-IIhF6J4ZOtTZcUdLk5R3NqVtE/M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "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, + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "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 + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "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 + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "prettier": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", + "dev": true + }, + "prism-media": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.2.tgz", + "integrity": "sha512-I+nkWY212lJ500jLe4tN9tWO7nRiBAVdMv76P9kffZjYhw20raMlW1HSSvS+MLXC9MmbNZCazMrAr+5jEEgTuw==" + }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "requires": { + "tslib": "^1.9.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==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, + "string-argv": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz", + "integrity": "sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "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 + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tsc-watch": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.9.tgz", + "integrity": "sha512-DlTaoDs74+KUpyWr7dCGhuscAUKCz6CiFduBN7R9RbLJSSN1moWdwoCLASE7+zLgGvV5AwXfYDiEMAsPGaO+Vw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.1.1", + "strip-ansi": "^6.0.0" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + }, + "typescript": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.1.0.tgz", + "integrity": "sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true } - } } diff --git a/package.json b/package.json index 208ddfc..27612ab 100644 --- a/package.json +++ b/package.json @@ -1,42 +1,42 @@ -{ - "name": "d.js-v12-bot", - "version": "0.0.1", - "description": "A Discord bot built on Discord.JS v12", - "main": "dist/index.js", - "private": true, - "dependencies": { - "chalk": "^4.1.0", - "discord.js": "^12.4.0", - "discord.js-lavalink-lib": "^0.1.7", - "inquirer": "^7.3.3", - "moment": "^2.29.1", - "ms": "^2.1.2", - "os": "^0.1.1" - }, - "devDependencies": { - "@types/inquirer": "^6.5.0", - "@types/mocha": "^8.0.3", - "@types/ms": "^0.7.31", - "@types/node": "^14.14.2", - "@types/ws": "^7.2.7", - "mocha": "^8.2.0", - "prettier": "2.1.2", - "ts-node": "^9.0.0", - "tsc-watch": "^4.2.9", - "typescript": "^3.9.7" - }, - "scripts": { - "build": "tsc && npm prune --production", - "start": "node dist/index.js", - "once": "tsc && npm start", - "dev": "tsc-watch --onSuccess \"node dist/index.js dev\"", - "test": "mocha --require ts-node/register --extension ts --recursive", - "format": "prettier --write **/*" - }, - "keywords": [ - "discord.js", - "bot" - ], - "author": "Keanu Timmermans", - "license": "MIT" -} \ No newline at end of file +{ + "name": "d.js-v12-bot", + "version": "0.0.1", + "description": "A Discord bot built on Discord.JS v12", + "main": "dist/index.js", + "private": true, + "dependencies": { + "chalk": "^4.1.0", + "discord.js": "^12.4.0", + "discord.js-lavalink-lib": "^0.1.7", + "inquirer": "^7.3.3", + "moment": "^2.29.1", + "ms": "^2.1.2", + "os": "^0.1.1" + }, + "devDependencies": { + "@types/inquirer": "^6.5.0", + "@types/mocha": "^8.0.3", + "@types/ms": "^0.7.31", + "@types/node": "^14.14.2", + "@types/ws": "^7.2.7", + "mocha": "^8.2.0", + "prettier": "2.1.2", + "ts-node": "^9.0.0", + "tsc-watch": "^4.2.9", + "typescript": "^3.9.7" + }, + "scripts": { + "build": "tsc && npm prune --production", + "start": "node dist/index.js", + "once": "tsc && npm start", + "dev": "tsc-watch --onSuccess \"node dist/index.js dev\"", + "test": "mocha --require ts-node/register --extension ts --recursive", + "format": "prettier --write **/*" + }, + "keywords": [ + "discord.js", + "bot" + ], + "author": "Keanu Timmermans", + "license": "MIT" +} diff --git a/prettier.config.js b/prettier.config.js index 4cbaf1d..f5d98bc 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,14 +1,14 @@ module.exports = { - printWidth: 80, - tabWidth: 2, - useTabs: false, - semi: true, - singleQuote: true, - quoteProps: 'as-needed', - jsxSingleQuote: false, - trailingComma: 'all', - bracketSpacing: true, - jsxBracketSameLine: true, - arrowParens: 'always', - endOfLine: 'auto', + printWidth: 80, + tabWidth: 4, + useTabs: false, + semi: true, + singleQuote: false, + quoteProps: "as-needed", + jsxSingleQuote: false, + trailingComma: "none", + bracketSpacing: false, + jsxBracketSameLine: false, + arrowParens: "always", + endOfLine: "lf" }; diff --git a/src/commands/admin.ts b/src/commands/admin.ts index ad34bc4..48320c0 100644 --- a/src/commands/admin.ts +++ b/src/commands/admin.ts @@ -1,228 +1,237 @@ -import Command from '../core/command'; -import { CommonLibrary, logs, botHasPermission, clean } from '../core/lib'; -import { Config, Storage } from '../core/structures'; -import { PermissionNames, getPermissionLevel } from '../core/permissions'; -import { Permissions } from 'discord.js'; -import * as discord from 'discord.js'; - -function getLogBuffer(type: string) { - return { - files: [ - { - attachment: Buffer.alloc(logs[type].length, logs[type]), - name: `${Date.now()}.${type}.log`, - }, - ], - }; -} - -const activities = ['playing', 'listening', 'streaming', 'watching']; -const statuses = ['online', 'idle', 'dnd', 'invisible']; - -export default new Command({ - description: - "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", - async run($: CommonLibrary): Promise { - if (!$.member) - return $.channel.send( - "Couldn't find a member object for you! Did you make sure you used this in a server?", - ); - const permLevel = getPermissionLevel($.member); - $.channel.send( - `${$.author.toString()}, your permission level is \`${ - PermissionNames[permLevel] - }\` (${permLevel}).`, - ); - }, - subcommands: { - set: new Command({ - description: 'Set different per-guild settings for the bot.', - run: 'You have to specify the option you want to set.', - permission: Command.PERMISSIONS.ADMIN, - subcommands: { - prefix: new Command({ - description: - 'Set a custom prefix for your guild. Removes your custom prefix if none is provided.', - usage: '()', - async run($: CommonLibrary): Promise { - Storage.getGuild($.guild?.id || 'N/A').prefix = null; - Storage.save(); - $.channel.send( - `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.`, - ); - }, - any: new Command({ - async run($: CommonLibrary): Promise { - Storage.getGuild($.guild?.id || 'N/A').prefix = $.args[0]; - Storage.save(); - $.channel.send( - `The custom prefix for this guild is now \`${$.args[0]}\`.`, - ); - }, - }), - }), - }, - }), - diag: new Command({ - description: 'Requests a debug log with the "info" verbosity level.', - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - $.channel.send(getLogBuffer('info')); - }, - any: new Command({ - description: `Select a verbosity to listen to. Available levels: \`[${Object.keys( - logs, - ).join(', ')}]\``, - async run($: CommonLibrary): Promise { - const type = $.args[0]; - - if (type in logs) $.channel.send(getLogBuffer(type)); - else - $.channel.send( - `Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys( - logs, - ).join(', ')}]\`.`, - ); - }, - }), - }), - status: new Command({ - description: "Changes the bot's status.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - $.channel.send('Setting status to `online`...'); - }, - any: new Command({ - description: `Select a status to set to. Available statuses: \`[${statuses.join( - ', ', - )}]\`.`, - async run($: CommonLibrary): Promise { - if (!statuses.includes($.args[0])) - return $.channel.send("That status doesn't exist!"); - else { - $.client.user?.setStatus($.args[0]); - $.channel.send(`Setting status to \`${$.args[0]}\`...`); - } - }, - }), - }), - purge: new Command({ - description: 'Purges bot messages.', - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - if ($.message.channel instanceof discord.DMChannel) { - return; - } - $.message.delete(); - const msgs = await $.channel.messages.fetch({ - limit: 100, - }); - const travMessages = msgs.filter( - (m) => m.author.id === $.client.user?.id, - ); - - await $.message.channel - .send(`Found ${travMessages.size} messages to delete.`) - .then((m) => - m.delete({ - timeout: 5000, - }), - ); - await $.message.channel.bulkDelete(travMessages); - }, - }), - clear: new Command({ - description: "Clears a given amount of messages.", - usage: "", - run: "A number was not provided.", - number: new Command({ - description: "Amount of messages to delete.", - async run($: CommonLibrary): Promise { - $.message.delete(); - const fetched = await $.channel.messages.fetch({ - limit: $.args[0], - }); - /// @ts-ignore - $.channel.bulkDelete(fetched) - .catch((error: any) => $.channel.send(`Error: ${error}`)); - } - }) - }), - eval: new Command({ - description: 'Evaluate code.', - usage: '', - permission: Command.PERMISSIONS.BOT_OWNER, - async run($: CommonLibrary): Promise { - try { - const code = $.args.join(' '); - let evaled = eval(code); - - if (typeof evaled !== 'string') - evaled = require('util').inspect(evaled); - $.channel.send(clean(evaled), { code: 'x1' }); - } catch (err) { - $.channel.send(`\`ERROR\` \`\`\`x1\n${clean(err)}\n\`\`\``); - } - }, - }), - nick: new Command({ - description: "Change the bot's nickname.", - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - const nickName = $.args.join(' '); - const trav = $.guild?.members.cache.find( - (member) => member.id === $.client.user?.id, - ); - await trav?.setNickname(nickName); - if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES)) - $.message.delete({ timeout: 5000 }).catch($.handler.bind($)); - $.channel - .send(`Nickname set to \`${nickName}\``) - .then((m) => m.delete({ timeout: 5000 })); - }, - }), - guilds: new Command({ - description: 'Shows a list of all guilds the bot is a member of.', - permission: Command.PERMISSIONS.BOT_SUPPORT, - async run($: CommonLibrary): Promise { - const guildList = $.client.guilds.cache.array().map((e) => e.name); - $.channel.send(guildList); - }, - }), - activity: new Command({ - description: 'Set the activity of the bot.', - permission: Command.PERMISSIONS.BOT_SUPPORT, - usage: ' ', - async run($: CommonLibrary): Promise { - $.client.user?.setActivity('.help', { - type: 'LISTENING', - }); - $.channel.send('Activity set to default.'); - }, - any: new Command({ - description: `Select an activity type to set. Available levels: \`[${activities.join( - ', ', - )}]\``, - async run($: CommonLibrary): Promise { - const type = $.args[0]; - - if (activities.includes(type)) { - $.client.user?.setActivity($.args.slice(1).join(' '), { - type: $.args[0].toUpperCase(), - }); - $.channel.send( - `Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args - .slice(1) - .join(' ')}\`.`, - ); - } else - $.channel.send( - `Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join( - ', ', - )}]\`.`, - ); - }, - }), - }), - }, -}); +import Command from "../core/command"; +import {CommonLibrary, logs, botHasPermission, clean} from "../core/lib"; +import {Config, Storage} from "../core/structures"; +import {PermissionNames, getPermissionLevel} from "../core/permissions"; +import {Permissions} from "discord.js"; +import * as discord from "discord.js"; + +function getLogBuffer(type: string) { + return { + files: [ + { + attachment: Buffer.alloc(logs[type].length, logs[type]), + name: `${Date.now()}.${type}.log` + } + ] + }; +} + +const activities = ["playing", "listening", "streaming", "watching"]; +const statuses = ["online", "idle", "dnd", "invisible"]; + +export default new Command({ + description: + "An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.", + async run($: CommonLibrary): Promise { + if (!$.member) + return $.channel.send( + "Couldn't find a member object for you! Did you make sure you used this in a server?" + ); + const permLevel = getPermissionLevel($.member); + $.channel.send( + `${$.author.toString()}, your permission level is \`${ + PermissionNames[permLevel] + }\` (${permLevel}).` + ); + }, + subcommands: { + set: new Command({ + description: "Set different per-guild settings for the bot.", + run: "You have to specify the option you want to set.", + permission: Command.PERMISSIONS.ADMIN, + subcommands: { + prefix: new Command({ + description: + "Set a custom prefix for your guild. Removes your custom prefix if none is provided.", + usage: "()", + async run($: CommonLibrary): Promise { + Storage.getGuild($.guild?.id || "N/A").prefix = null; + Storage.save(); + $.channel.send( + `The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.` + ); + }, + any: new Command({ + async run($: CommonLibrary): Promise { + Storage.getGuild($.guild?.id || "N/A").prefix = + $.args[0]; + Storage.save(); + $.channel.send( + `The custom prefix for this guild is now \`${$.args[0]}\`.` + ); + } + }) + }) + } + }), + diag: new Command({ + description: + 'Requests a debug log with the "info" verbosity level.', + permission: Command.PERMISSIONS.BOT_SUPPORT, + async run($: CommonLibrary): Promise { + $.channel.send(getLogBuffer("info")); + }, + any: new Command({ + description: `Select a verbosity to listen to. Available levels: \`[${Object.keys( + logs + ).join(", ")}]\``, + async run($: CommonLibrary): Promise { + const type = $.args[0]; + + if (type in logs) $.channel.send(getLogBuffer(type)); + else + $.channel.send( + `Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys( + logs + ).join(", ")}]\`.` + ); + } + }) + }), + status: new Command({ + description: "Changes the bot's status.", + permission: Command.PERMISSIONS.BOT_SUPPORT, + async run($: CommonLibrary): Promise { + $.channel.send("Setting status to `online`..."); + }, + any: new Command({ + description: `Select a status to set to. Available statuses: \`[${statuses.join( + ", " + )}]\`.`, + async run($: CommonLibrary): Promise { + if (!statuses.includes($.args[0])) + return $.channel.send("That status doesn't exist!"); + else { + $.client.user?.setStatus($.args[0]); + $.channel.send(`Setting status to \`${$.args[0]}\`...`); + } + } + }) + }), + purge: new Command({ + description: "Purges bot messages.", + permission: Command.PERMISSIONS.BOT_SUPPORT, + async run($: CommonLibrary): Promise { + if ($.message.channel instanceof discord.DMChannel) { + return; + } + $.message.delete(); + const msgs = await $.channel.messages.fetch({ + limit: 100 + }); + const travMessages = msgs.filter( + (m) => m.author.id === $.client.user?.id + ); + + await $.message.channel + .send(`Found ${travMessages.size} messages to delete.`) + .then((m) => + m.delete({ + timeout: 5000 + }) + ); + await $.message.channel.bulkDelete(travMessages); + } + }), + clear: new Command({ + description: "Clears a given amount of messages.", + usage: "", + run: "A number was not provided.", + number: new Command({ + description: "Amount of messages to delete.", + async run($: CommonLibrary): Promise { + $.message.delete(); + const fetched = await $.channel.messages.fetch({ + limit: $.args[0] + }); + $.channel + /// @ts-ignore + .bulkDelete(fetched) + .catch((error: any) => + $.channel.send(`Error: ${error}`) + ); + } + }) + }), + eval: new Command({ + description: "Evaluate code.", + usage: "", + permission: Command.PERMISSIONS.BOT_OWNER, + async run($: CommonLibrary): Promise { + try { + const code = $.args.join(" "); + let evaled = eval(code); + + if (typeof evaled !== "string") + evaled = require("util").inspect(evaled); + $.channel.send(clean(evaled), {code: "x1"}); + } catch (err) { + $.channel.send(`\`ERROR\` \`\`\`x1\n${clean(err)}\n\`\`\``); + } + } + }), + nick: new Command({ + description: "Change the bot's nickname.", + permission: Command.PERMISSIONS.BOT_SUPPORT, + async run($: CommonLibrary): Promise { + const nickName = $.args.join(" "); + const trav = $.guild?.members.cache.find( + (member) => member.id === $.client.user?.id + ); + await trav?.setNickname(nickName); + if ( + botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES) + ) + $.message.delete({timeout: 5000}).catch($.handler.bind($)); + $.channel + .send(`Nickname set to \`${nickName}\``) + .then((m) => m.delete({timeout: 5000})); + } + }), + guilds: new Command({ + description: "Shows a list of all guilds the bot is a member of.", + permission: Command.PERMISSIONS.BOT_SUPPORT, + async run($: CommonLibrary): Promise { + const guildList = $.client.guilds.cache + .array() + .map((e) => e.name); + $.channel.send(guildList); + } + }), + activity: new Command({ + description: "Set the activity of the bot.", + permission: Command.PERMISSIONS.BOT_SUPPORT, + usage: " ", + async run($: CommonLibrary): Promise { + $.client.user?.setActivity(".help", { + type: "LISTENING" + }); + $.channel.send("Activity set to default."); + }, + any: new Command({ + description: `Select an activity type to set. Available levels: \`[${activities.join( + ", " + )}]\``, + async run($: CommonLibrary): Promise { + const type = $.args[0]; + + if (activities.includes(type)) { + $.client.user?.setActivity($.args.slice(1).join(" "), { + type: $.args[0].toUpperCase() + }); + $.channel.send( + `Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args + .slice(1) + .join(" ")}\`.` + ); + } else + $.channel.send( + `Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join( + ", " + )}]\`.` + ); + } + }) + }) + } +}); diff --git a/src/commands/fun/8ball.ts b/src/commands/fun/8ball.ts index ebd2a35..c43ac31 100644 --- a/src/commands/fun/8ball.ts +++ b/src/commands/fun/8ball.ts @@ -1,38 +1,39 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; + const responses = [ - 'Most likely,', - 'It is certain,', - 'It is decidedly so,', - 'Without a doubt,', - 'Definitely,', - 'You may rely on it,', - 'As I see it, yes,', - 'Outlook good,', - 'Yes,', - 'Signs point to yes,', - 'Reply hazy, try again,', - 'Ask again later,', - 'Better not tell you now,', - 'Cannot predict now,', - 'Concentrate and ask again,', - "Don't count on it,", - 'My reply is no,', - 'My sources say no,', - 'Outlook not so good,', - 'Very doubtful,', + "Most likely,", + "It is certain,", + "It is decidedly so,", + "Without a doubt,", + "Definitely,", + "You may rely on it,", + "As I see it, yes,", + "Outlook good,", + "Yes,", + "Signs point to yes,", + "Reply hazy, try again,", + "Ask again later,", + "Better not tell you now,", + "Cannot predict now,", + "Concentrate and ask again,", + "Don't count on it,", + "My reply is no,", + "My sources say no,", + "Outlook not so good,", + "Very doubtful," ]; export default new Command({ - description: 'Answers your question in an 8-ball manner.', - endpoint: false, - usage: '', - run: 'Please provide a question.', - any: new Command({ - description: 'Question to ask the 8-ball.', - async run($: CommonLibrary): Promise { - const sender = $.message.author; - $.channel.send($(responses).random() + ` <@${sender.id}>`); - }, - }), + description: "Answers your question in an 8-ball manner.", + endpoint: false, + usage: "", + run: "Please provide a question.", + any: new Command({ + description: "Question to ask the 8-ball.", + async run($: CommonLibrary): Promise { + const sender = $.message.author; + $.channel.send($(responses).random() + ` <@${sender.id}>`); + } + }) }); diff --git a/src/commands/fun/cookie.ts b/src/commands/fun/cookie.ts index 15f75e8..f9be54f 100644 --- a/src/commands/fun/cookie.ts +++ b/src/commands/fun/cookie.ts @@ -1,50 +1,54 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; - -export default new Command({ - description: "Gives specified user a cookie.", - usage: "['all'/@user]", - run: ":cookie: Here's a cookie!", - any: new Command({ - async run($: CommonLibrary): Promise { - if ($.args[0] == "all") return $.channel.send(`${$.author} gave everybody a cookie!`) - } - }), - user: new Command({ - description: "User to give cookie to.", - async run($: CommonLibrary): Promise { - const sender = $.author; - const mention = $.message.mentions.users.first(); - if (!mention) return; - const cookies = [ - `has given <@${mention.id}> a chocolate chip cookie!`, - `has given <@${mention.id}> a soft homemade oatmeal cookie!`, - `has given <@${mention.id}> a plain, dry, old cookie. It was the last one in the bag. Gross.`, - `gives <@${mention.id}> a sugar cookie. What, no frosting and sprinkles? 0/10 would not touch.`, - `gives <@${mention.id}> a chocolate chip cookie. Oh wait, those are raisins. Bleck!`, - `gives <@${mention.id}> an enormous cookie. Poking it gives you more cookies. Weird.`, - `gives <@${mention.id}> a fortune cookie. It reads "Why aren't you working on any projects?"`, - `gives <@${mention.id}> a fortune cookie. It reads "Give that special someone a compliment"`, - `gives <@${mention.id}> a fortune cookie. It reads "Take a risk!"`, - `gives <@${mention.id}> a fortune cookie. It reads "Go outside."`, - `gives <@${mention.id}> a fortune cookie. It reads "Don't forget to eat your veggies!"`, - `gives <@${mention.id}> a fortune cookie. It reads "Do you even lift?"`, - `gives <@${mention.id}> a fortune cookie. It reads "m808 pls"`, - `gives <@${mention.id}> a fortune cookie. It reads "If you move your hips, you'll get all the ladies."`, - `gives <@${mention.id}> a fortune cookie. It reads "I love you."`, - `gives <@${mention.id}> a Golden Cookie. You can't eat it because it is made of gold. Dammit.`, - `gives <@${mention.id}> an Oreo cookie with a glass of milk!`, - `gives <@${mention.id}> a rainbow cookie made with love :heart:`, - `gives <@${mention.id}> an old cookie that was left out in the rain, it's moldy.`, - `bakes <@${mention.id}> fresh cookies, it smells amazing.`, - ]; - if (mention.id == sender.id) - return $.channel.send("You can't give yourself cookies!"); - $.channel.send( - `:cookie: <@${sender.id}> ` + - cookies[Math.floor(Math.random() * cookies.length)], - ); - - } - }) -}) \ No newline at end of file +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; + +export default new Command({ + description: "Gives specified user a cookie.", + usage: "['all'/@user]", + run: ":cookie: Here's a cookie!", + any: new Command({ + async run($: CommonLibrary): Promise { + if ($.args[0] == "all") + return $.channel.send(`${$.author} gave everybody a cookie!`); + } + }), + user: new Command({ + description: "User to give cookie to.", + async run($: CommonLibrary): Promise { + const sender = $.author; + const mention = $.message.mentions.users.first(); + + if (!mention) return; + + const cookies = [ + `has given <@${mention.id}> a chocolate chip cookie!`, + `has given <@${mention.id}> a soft homemade oatmeal cookie!`, + `has given <@${mention.id}> a plain, dry, old cookie. It was the last one in the bag. Gross.`, + `gives <@${mention.id}> a sugar cookie. What, no frosting and sprinkles? 0/10 would not touch.`, + `gives <@${mention.id}> a chocolate chip cookie. Oh wait, those are raisins. Bleck!`, + `gives <@${mention.id}> an enormous cookie. Poking it gives you more cookies. Weird.`, + `gives <@${mention.id}> a fortune cookie. It reads "Why aren't you working on any projects?"`, + `gives <@${mention.id}> a fortune cookie. It reads "Give that special someone a compliment"`, + `gives <@${mention.id}> a fortune cookie. It reads "Take a risk!"`, + `gives <@${mention.id}> a fortune cookie. It reads "Go outside."`, + `gives <@${mention.id}> a fortune cookie. It reads "Don't forget to eat your veggies!"`, + `gives <@${mention.id}> a fortune cookie. It reads "Do you even lift?"`, + `gives <@${mention.id}> a fortune cookie. It reads "m808 pls"`, + `gives <@${mention.id}> a fortune cookie. It reads "If you move your hips, you'll get all the ladies."`, + `gives <@${mention.id}> a fortune cookie. It reads "I love you."`, + `gives <@${mention.id}> a Golden Cookie. You can't eat it because it is made of gold. Dammit.`, + `gives <@${mention.id}> an Oreo cookie with a glass of milk!`, + `gives <@${mention.id}> a rainbow cookie made with love :heart:`, + `gives <@${mention.id}> an old cookie that was left out in the rain, it's moldy.`, + `bakes <@${mention.id}> fresh cookies, it smells amazing.` + ]; + + if (mention.id == sender.id) + return $.channel.send("You can't give yourself cookies!"); + + $.channel.send( + `:cookie: <@${sender.id}> ` + + cookies[Math.floor(Math.random() * cookies.length)] + ); + } + }) +}); diff --git a/src/commands/fun/eco.ts b/src/commands/fun/eco.ts index ceab224..9e5978c 100644 --- a/src/commands/fun/eco.ts +++ b/src/commands/fun/eco.ts @@ -1,35 +1,36 @@ -import Command from '../../core/command'; -import { isAuthorized, getMoneyEmbed } from './subcommands/eco-utils'; -import { DailyCommand, PayCommand, GuildCommand } from './subcommands/eco-core'; -import { BuyCommand, ShopCommand } from './subcommands/eco-shop'; +import Command from "../../core/command"; +import {isAuthorized, getMoneyEmbed} from "./subcommands/eco-utils"; +import {DailyCommand, PayCommand, GuildCommand} from "./subcommands/eco-core"; +import {BuyCommand, ShopCommand} from "./subcommands/eco-shop"; export default new Command({ - description: 'Economy command for Monika.', - - async run({ guild, channel, author }) { - if (isAuthorized(guild, channel)) channel.send(getMoneyEmbed(author)); - }, - subcommands: { - daily: DailyCommand, - pay: PayCommand, - guild: GuildCommand, - buy: BuyCommand, - shop: ShopCommand, - }, - user: new Command({ - description: - 'See how much money someone else has by using their user ID or pinging them.', - async run({ guild, channel, args }) { - if (isAuthorized(guild, channel)) channel.send(getMoneyEmbed(args[0])); + description: "Economy command for Monika.", + async run({guild, channel, author}) { + if (isAuthorized(guild, channel)) channel.send(getMoneyEmbed(author)); }, - }), - any: new Command({ - description: 'See how much money someone else has by using their username.', - async run({ guild, channel, args, callMemberByUsername, message }) { - if (isAuthorized(guild, channel)) - callMemberByUsername(message, args.join(' '), (member) => { - channel.send(getMoneyEmbed(member.user)); - }); + subcommands: { + daily: DailyCommand, + pay: PayCommand, + guild: GuildCommand, + buy: BuyCommand, + shop: ShopCommand }, - }), + user: new Command({ + description: + "See how much money someone else has by using their user ID or pinging them.", + async run({guild, channel, args}) { + if (isAuthorized(guild, channel)) + channel.send(getMoneyEmbed(args[0])); + } + }), + any: new Command({ + description: + "See how much money someone else has by using their username.", + async run({guild, channel, args, callMemberByUsername, message}) { + if (isAuthorized(guild, channel)) + callMemberByUsername(message, args.join(" "), (member) => { + channel.send(getMoneyEmbed(member.user)); + }); + } + }) }); diff --git a/src/commands/fun/neko.ts b/src/commands/fun/neko.ts index e079581..73901fa 100644 --- a/src/commands/fun/neko.ts +++ b/src/commands/fun/neko.ts @@ -1,26 +1,31 @@ -/// @ts-nocheck -import { URL } from 'url' -import FileManager from '../../core/storage'; -import Command from '../../core/command'; -import { CommonLibrary, getContent } from '../../core/lib'; - -const endpoints = FileManager.read('endpoints'); - -export default new Command({ - description: 'Provides you with a random image with the selected argument.', - async run($: CommonLibrary): Promise { - console.log(endpoints.sfw) - $.channel.send(`Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(', ')}]\`.`) - }, - any: new Command({ - description: "Image type to send.", - async run($: CommonLibrary): Promise { - if (!($.args[0] in endpoints.sfw)) - return $.channel.send("Couldn't find that endpoint!"); - let baseURL = 'https://nekos.life/api/v2'; - let url = new URL(`${baseURL}${endpoints.sfw[$.args[0]]}`); - const content = await getContent(url.toString()) - $.channel.send(content.url) - }, - }) -}); +/// @ts-nocheck +import {URL} from "url"; +import FileManager from "../../core/storage"; +import Command from "../../core/command"; +import {CommonLibrary, getContent} from "../../core/lib"; + +const endpoints = FileManager.read("endpoints"); + +export default new Command({ + description: "Provides you with a random image with the selected argument.", + async run($: CommonLibrary): Promise { + console.log(endpoints.sfw); + $.channel.send( + `Please provide an image type. Available arguments:\n\`[${Object.keys( + endpoints.sfw + ).join(", ")}]\`.` + ); + }, + any: new Command({ + description: "Image type to send.", + async run($: CommonLibrary): Promise { + if (!($.args[0] in endpoints.sfw)) + return $.channel.send("Couldn't find that endpoint!"); + + let baseURL = "https://nekos.life/api/v2"; + let url = new URL(`${baseURL}${endpoints.sfw[$.args[0]]}`); + const content = await getContent(url.toString()); + $.channel.send(content.url); + } + }) +}); diff --git a/src/commands/fun/ok.ts b/src/commands/fun/ok.ts index 8fa0263..882021c 100644 --- a/src/commands/fun/ok.ts +++ b/src/commands/fun/ok.ts @@ -1,68 +1,69 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; - -export default new Command({ - description: "Sends random ok message.", - async run($: CommonLibrary): Promise { - const responses = [ - 'boomer', - 'zoomer', - 'the last generationer', - 'the last airbender', - 'fire nation', - 'fire lord', - 'guy fieri', - 'guy from final fight', - 'haggar', - 'Max Thunder from Streets of Rage 2', - 'police guy who fires bazookas', - 'Mr. X', - 'Leon Its Wrong If Its Not Ada Wong S. Kennedy.', - 'Jill', - 'JFK', - 'george bush', - 'obama', - 'the world', - 'copy of scott pilgrim vs the world', - 'ok', - 'ko', - 'Hot Daddy Venomous', - 'big daddy', - 'John Cena', - 'BubbleSpurJarJarBinks', - 'T-Series', - 'pewdiepie', - 'markiplier', - 'jacksepticeye', - 'vanossgaming', - 'miniladd', - 'Traves', - 'Wilbur Soot', - 'sootrhianna', - 'person with tiny ears', - 'anti-rabbit', - 'homo sapiens', - 'homo', - 'cute kitty', - 'ugly kitty', - 'sadness', - 'doomer', - 'gloomer', - 'bloomer', - 'edgelord', - 'weeb', - "m'lady", - 'Mr. Crabs', - 'hand', - 'lahoma', - 'big man', - 'fox', - 'pear', - 'cat', - 'large man', - ]; - $.channel.send( - 'ok ' + responses[Math.floor(Math.random() * responses.length)], - ); - } -}) \ No newline at end of file +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; + +export default new Command({ + description: "Sends random ok message.", + async run($: CommonLibrary): Promise { + const responses = [ + "boomer", + "zoomer", + "the last generationer", + "the last airbender", + "fire nation", + "fire lord", + "guy fieri", + "guy from final fight", + "haggar", + "Max Thunder from Streets of Rage 2", + "police guy who fires bazookas", + "Mr. X", + "Leon Its Wrong If Its Not Ada Wong S. Kennedy.", + "Jill", + "JFK", + "george bush", + "obama", + "the world", + "copy of scott pilgrim vs the world", + "ok", + "ko", + "Hot Daddy Venomous", + "big daddy", + "John Cena", + "BubbleSpurJarJarBinks", + "T-Series", + "pewdiepie", + "markiplier", + "jacksepticeye", + "vanossgaming", + "miniladd", + "Traves", + "Wilbur Soot", + "sootrhianna", + "person with tiny ears", + "anti-rabbit", + "homo sapiens", + "homo", + "cute kitty", + "ugly kitty", + "sadness", + "doomer", + "gloomer", + "bloomer", + "edgelord", + "weeb", + "m'lady", + "Mr. Crabs", + "hand", + "lahoma", + "big man", + "fox", + "pear", + "cat", + "large man" + ]; + + $.channel.send( + "ok " + responses[Math.floor(Math.random() * responses.length)] + ); + } +}); diff --git a/src/commands/fun/owoify.ts b/src/commands/fun/owoify.ts index 33be968..6c02084 100644 --- a/src/commands/fun/owoify.ts +++ b/src/commands/fun/owoify.ts @@ -1,12 +1,14 @@ -/// @ts-nocheck -import Command from '../../core/command'; -import { CommonLibrary, getContent } from '../../core/lib'; - -export default new Command({ - description: 'OwO-ifies the input.', - async run($: CommonLibrary): Promise { - let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(' ')}`); - const content = await getContent(url.toString()); - $.channel.send(content.owo); - }, -}); +/// @ts-nocheck +import Command from "../../core/command"; +import {CommonLibrary, getContent} from "../../core/lib"; + +export default new Command({ + description: "OwO-ifies the input.", + async run($: CommonLibrary): Promise { + let url = new URL( + `https://nekos.life/api/v2/owoify?text=${$.args.join(" ")}` + ); + const content = await getContent(url.toString()); + $.channel.send(content.owo); + } +}); diff --git a/src/commands/fun/poll.ts b/src/commands/fun/poll.ts index 0e46f90..e104798 100644 --- a/src/commands/fun/poll.ts +++ b/src/commands/fun/poll.ts @@ -1,28 +1,28 @@ -import { MessageEmbed } from 'discord.js'; -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; +import {MessageEmbed} from "discord.js"; +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; export default new Command({ - description: 'Create a poll.', - usage: '', - run: 'Please provide a question.', - any: new Command({ - description: 'Question for the poll.', - async run($: CommonLibrary): Promise { - const embed = new MessageEmbed() - .setAuthor( - `Poll created by ${$.message.author.username}`, - $.message.guild?.iconURL({ dynamic: true }) ?? undefined, - ) - .setColor(0xffffff) - .setFooter('React to vote.') - .setDescription($.args.join(' ')); - const msg = await $.channel.send(embed); - await msg.react('✅'); - await msg.react('⛔'); - $.message.delete({ - timeout: 1000, - }); - }, - }), + description: "Create a poll.", + usage: "", + run: "Please provide a question.", + any: new Command({ + description: "Question for the poll.", + async run($: CommonLibrary): Promise { + const embed = new MessageEmbed() + .setAuthor( + `Poll created by ${$.message.author.username}`, + $.message.guild?.iconURL({dynamic: true}) ?? undefined + ) + .setColor(0xffffff) + .setFooter("React to vote.") + .setDescription($.args.join(" ")); + const msg = await $.channel.send(embed); + await msg.react("✅"); + await msg.react("⛔"); + $.message.delete({ + timeout: 1000 + }); + } + }) }); diff --git a/src/commands/fun/subcommands/eco-core.ts b/src/commands/fun/subcommands/eco-core.ts index e2c8c5c..5bf937a 100644 --- a/src/commands/fun/subcommands/eco-core.ts +++ b/src/commands/fun/subcommands/eco-core.ts @@ -1,180 +1,182 @@ -import Command from '../../../core/command'; -import $ from '../../../core/lib'; -import { Storage } from '../../../core/structures'; -import { isAuthorized, getMoneyEmbed, getSendEmbed } from './eco-utils'; +import Command from "../../../core/command"; +import $ from "../../../core/lib"; +import {Storage} from "../../../core/structures"; +import {isAuthorized, getMoneyEmbed, getSendEmbed} from "./eco-utils"; export const DailyCommand = new Command({ - description: - 'Pick up your daily Mons. The cooldown is per user and every 22 hours to allow for some leeway.', - async run({ author, channel, guild }) { - if (isAuthorized(guild, channel)) { - const user = Storage.getUser(author.id); - const now = Date.now(); + description: + "Pick up your daily Mons. The cooldown is per user and every 22 hours to allow for some leeway.", + async run({author, channel, guild}) { + if (isAuthorized(guild, channel)) { + const user = Storage.getUser(author.id); + const now = Date.now(); - if (now - user.lastReceived >= 79200000) { - user.money++; - user.lastReceived = now; - Storage.save(); - channel.send({ - embed: { - title: 'Daily Reward', - description: 'You received 1 Mon!', - color: 0xf1c40f, - }, - }); - } else - channel.send({ - embed: { - title: 'Daily Reward', - description: `It's too soon to pick up your daily credits. You have about ${( - (user.lastReceived + 79200000 - now) / - 3600000 - ).toFixed(1)} hours to go.`, - color: 0xf1c40f, - }, - }); + if (now - user.lastReceived >= 79200000) { + user.money++; + user.lastReceived = now; + Storage.save(); + channel.send({ + embed: { + title: "Daily Reward", + description: "You received 1 Mon!", + color: 0xf1c40f + } + }); + } else + channel.send({ + embed: { + title: "Daily Reward", + description: `It's too soon to pick up your daily credits. You have about ${( + (user.lastReceived + 79200000 - now) / + 3600000 + ).toFixed(1)} hours to go.`, + color: 0xf1c40f + } + }); + } } - }, }); export const GuildCommand = new Command({ - description: 'See the richest players.', - async run({ guild, channel, client }) { - if (isAuthorized(guild, channel)) { - const users = Storage.users; - const ids = Object.keys(users); - ids.sort((a, b) => users[b].money - users[a].money); - const fields = []; + description: "See the richest players.", + async run({guild, channel, client}) { + if (isAuthorized(guild, channel)) { + const users = Storage.users; + const ids = Object.keys(users); + ids.sort((a, b) => users[b].money - users[a].money); + const fields = []; - for (let i = 0, limit = Math.min(10, ids.length); i < limit; i++) { - const id = ids[i]; - const user = await client.users.fetch(id); + for (let i = 0, limit = Math.min(10, ids.length); i < limit; i++) { + const id = ids[i]; + const user = await client.users.fetch(id); - fields.push({ - name: `#${i + 1}. ${user.username}#${user.discriminator}`, - value: $(users[id].money).pluralise('credit', 's'), - }); - } + fields.push({ + name: `#${i + 1}. ${user.username}#${user.discriminator}`, + value: $(users[id].money).pluralise("credit", "s") + }); + } - channel.send({ - embed: { - title: 'Top 10 Richest Players', - color: '#ffff00', - fields: fields, - }, - }); + channel.send({ + embed: { + title: "Top 10 Richest Players", + color: "#ffff00", + fields: fields + } + }); + } } - }, }); export const PayCommand = new Command({ - description: 'Send money to someone.', - usage: ' ', - run: 'Who are you sending this money to?', - user: new Command({ - run: "You need to enter an amount you're sending!", - number: new Command({ - async run({ args, author, channel, guild }): Promise { - if (isAuthorized(guild, channel)) { - const amount = Math.floor(args[1]); - const sender = Storage.getUser(author.id); - const target = args[0]; - const receiver = Storage.getUser(target.id); + description: "Send money to someone.", + usage: " ", + run: "Who are you sending this money to?", + user: new Command({ + run: "You need to enter an amount you're sending!", + number: new Command({ + async run({args, author, channel, guild}): Promise { + if (isAuthorized(guild, channel)) { + const amount = Math.floor(args[1]); + const sender = Storage.getUser(author.id); + const target = args[0]; + const receiver = Storage.getUser(target.id); - if (amount <= 0) - return channel.send('You must send at least one Mon!'); - else if (sender.money < amount) - return channel.send( - "You don't have enough Mons for that.", - getMoneyEmbed(author), - ); - else if (target.id === author.id) - return channel.send("You can't send Mons to yourself!"); - else if (target.bot && process.argv[2] !== 'dev') - return channel.send("You can't send Mons to a bot!"); + if (amount <= 0) + return channel.send("You must send at least one Mon!"); + else if (sender.money < amount) + return channel.send( + "You don't have enough Mons for that.", + getMoneyEmbed(author) + ); + else if (target.id === author.id) + return channel.send("You can't send Mons to yourself!"); + else if (target.bot && process.argv[2] !== "dev") + return channel.send("You can't send Mons to a bot!"); - sender.money -= amount; - receiver.money += amount; - Storage.save(); - return channel.send(getSendEmbed(author, target, amount)); - } - }, + sender.money -= amount; + receiver.money += amount; + Storage.save(); + return channel.send(getSendEmbed(author, target, amount)); + } + } + }) }), - }), - number: new Command({ - run: 'You must use the format `money send `!', - }), - any: new Command({ - async run({ args, author, channel, guild, prompt }) { - if (isAuthorized(guild, channel)) { - const last = args.pop(); + number: new Command({ + run: "You must use the format `money send `!" + }), + any: new Command({ + async run({args, author, channel, guild, prompt}) { + if (isAuthorized(guild, channel)) { + const last = args.pop(); - if (!/\d+/g.test(last) && args.length === 0) - return channel.send("You need to enter an amount you're sending!"); + if (!/\d+/g.test(last) && args.length === 0) + return channel.send( + "You need to enter an amount you're sending!" + ); - const amount = Math.floor(last); - const sender = Storage.getUser(author.id); + const amount = Math.floor(last); + const sender = Storage.getUser(author.id); - if (amount <= 0) - return channel.send('You must send at least one credit!'); - else if (sender.money < amount) - return channel.send( - "You don't have enough money to do that!", - getMoneyEmbed(author), - ); - else if (!guild) - return channel.send( - 'You have to use this in a server if you want to send money with a username!', - ); + if (amount <= 0) + return channel.send("You must send at least one credit!"); + else if (sender.money < amount) + return channel.send( + "You don't have enough money to do that!", + getMoneyEmbed(author) + ); + else if (!guild) + return channel.send( + "You have to use this in a server if you want to send money with a username!" + ); - const username = args.join(' '); - const member = ( - await guild.members.fetch({ - query: username, - limit: 1, - }) - ).first(); + const username = args.join(" "); + const member = ( + await guild.members.fetch({ + query: username, + limit: 1 + }) + ).first(); - if (!member) - return channel.send( - `Couldn't find a user by the name of \`${username}\`! If you want to send money to someone in a different server, you have to use their user ID!`, - ); - else if (member.user.id === author.id) - return channel.send("You can't send money to yourself!"); - else if (member.user.bot && process.argv[2] !== 'dev') - return channel.send("You can't send money to a bot!"); + if (!member) + return channel.send( + `Couldn't find a user by the name of \`${username}\`! If you want to send money to someone in a different server, you have to use their user ID!` + ); + else if (member.user.id === author.id) + return channel.send("You can't send money to yourself!"); + else if (member.user.bot && process.argv[2] !== "dev") + return channel.send("You can't send money to a bot!"); - const target = member.user; + const target = member.user; - return prompt( - await channel.send( - `Are you sure you want to send ${$(amount).pluralise( - 'credit', - 's', - )} to this person?\n*(This message will automatically be deleted after 10 seconds.)*`, - { - embed: { - color: '#ffff00', - author: { - name: `${target.username}#${target.discriminator}`, - icon_url: target.displayAvatarURL({ - format: 'png', - dynamic: true, - }), - }, - }, - }, - ), - author.id, - () => { - const receiver = Storage.getUser(target.id); - sender.money -= amount; - receiver.money += amount; - Storage.save(); - channel.send(getSendEmbed(author, target, amount)); - }, - ); - } - }, - }), + return prompt( + await channel.send( + `Are you sure you want to send ${$(amount).pluralise( + "credit", + "s" + )} to this person?\n*(This message will automatically be deleted after 10 seconds.)*`, + { + embed: { + color: "#ffff00", + author: { + name: `${target.username}#${target.discriminator}`, + icon_url: target.displayAvatarURL({ + format: "png", + dynamic: true + }) + } + } + } + ), + author.id, + () => { + const receiver = Storage.getUser(target.id); + sender.money -= amount; + receiver.money += amount; + Storage.save(); + channel.send(getSendEmbed(author, target, amount)); + } + ); + } + } + }) }); diff --git a/src/commands/fun/subcommands/eco-shop-items.ts b/src/commands/fun/subcommands/eco-shop-items.ts index b7531cd..107959d 100644 --- a/src/commands/fun/subcommands/eco-shop-items.ts +++ b/src/commands/fun/subcommands/eco-shop-items.ts @@ -1,73 +1,73 @@ -import { Message } from 'discord.js'; -import $ from '../../../core/lib'; +import {Message} from "discord.js"; +import $ from "../../../core/lib"; export interface ShopItem { - cost: number; - title: string; - description: string; - usage: string; - run(message: Message, cost: number, amount: number): void; + cost: number; + title: string; + description: string; + usage: string; + run(message: Message, cost: number, amount: number): void; } export const ShopItems: ShopItem[] = [ - { - cost: 1, - title: 'Hug', - description: 'Hug Monika.', - usage: 'hug', - run(message, cost) { - message.channel.send( - `Transaction of ${cost} Mon completed successfully. <@394808963356688394>`, - ); + { + cost: 1, + title: "Hug", + description: "Hug Monika.", + usage: "hug", + run(message, cost) { + message.channel.send( + `Transaction of ${cost} Mon completed successfully. <@394808963356688394>` + ); + } }, - }, - { - cost: 2, - title: 'Handholding', - description: "Hold Monika's hand.", - usage: 'handhold', - run(message, cost) { - message.channel.send( - `Transaction of ${cost} Mons completed successfully. <@394808963356688394>`, - ); + { + cost: 2, + title: "Handholding", + description: "Hold Monika's hand.", + usage: "handhold", + run(message, cost) { + message.channel.send( + `Transaction of ${cost} Mons completed successfully. <@394808963356688394>` + ); + } }, - }, - { - cost: 1, - title: 'Cute', - description: 'Calls Monika cute.', - usage: 'cute', - run(message) { - message.channel.send('<:MoniCheeseBlushRed:637513137083383826>'); + { + cost: 1, + title: "Cute", + description: "Calls Monika cute.", + usage: "cute", + run(message) { + message.channel.send("<:MoniCheeseBlushRed:637513137083383826>"); + } }, - }, - { - cost: 3, - title: 'Laser Bridge', - description: 'Buys what is technically a laser bridge.', - usage: 'laser bridge', - run(message) { - message.channel.send($(lines).random(), { - files: [ - { - attachment: - 'https://raw.githubusercontent.com/keanuplayz/TravBot/dev/assets/TheUltimateLaser.gif', - }, - ], - }); - }, - }, + { + cost: 3, + title: "Laser Bridge", + description: "Buys what is technically a laser bridge.", + usage: "laser bridge", + run(message) { + message.channel.send($(lines).random(), { + files: [ + { + attachment: + "https://raw.githubusercontent.com/keanuplayz/TravBot/dev/assets/TheUltimateLaser.gif" + } + ] + }); + } + } ]; const lines = [ - "It's technically a laser bridge. No refunds.", - 'You want a laser bridge? You got one!', - "Now what'd they say about building bridges... Oh wait, looks like I nuked the planet again. Whoops!", - 'I saw this redhead the other day who was so excited to buy what I was selling. Needless to say, she was not very happy with me afterward.', - "Sorry, but you'll have to wait until the Laser Bridge Builder leaves early access.", - 'Thank you for your purchase! For you see, this is the legendary laser of obliteration that has been defended and preserved for countless generations!', - 'They say that a certain troll dwells under this laser bridge, waiting for an unlucky person to fall for th- I mean- Thank you for your purchase!', - "Buy?! Hah! How about our new rental service for just under $9.99 a month? But wait, there's more! For just $99.99, you can rent this laser bridge for an entire year and save 16.67% as opposed to renting it monthly!", - 'Good choice. Owning a laser bridge is the penultimate experience that all true seekers strive for!', - 'I can already imagine the reviews...\n"9/10 needs more lasers"', + "It's technically a laser bridge. No refunds.", + "You want a laser bridge? You got one!", + "Now what'd they say about building bridges... Oh wait, looks like I nuked the planet again. Whoops!", + "I saw this redhead the other day who was so excited to buy what I was selling. Needless to say, she was not very happy with me afterward.", + "Sorry, but you'll have to wait until the Laser Bridge Builder leaves early access.", + "Thank you for your purchase! For you see, this is the legendary laser of obliteration that has been defended and preserved for countless generations!", + "They say that a certain troll dwells under this laser bridge, waiting for an unlucky person to fall for th- I mean- Thank you for your purchase!", + "Buy?! Hah! How about our new rental service for just under $9.99 a month? But wait, there's more! For just $99.99, you can rent this laser bridge for an entire year and save 16.67% as opposed to renting it monthly!", + "Good choice. Owning a laser bridge is the penultimate experience that all true seekers strive for!", + 'I can already imagine the reviews...\n"9/10 needs more lasers"' ]; diff --git a/src/commands/fun/subcommands/eco-shop.ts b/src/commands/fun/subcommands/eco-shop.ts index 9c3a5f8..48422a5 100644 --- a/src/commands/fun/subcommands/eco-shop.ts +++ b/src/commands/fun/subcommands/eco-shop.ts @@ -1,100 +1,99 @@ -import Command from '../../../core/command'; -import $ from '../../../core/lib'; -import { Storage, getPrefix } from '../../../core/structures'; -import { isAuthorized, getMoneyEmbed, getSendEmbed } from './eco-utils'; -import { ShopItems, ShopItem } from './eco-shop-items'; -import { EmbedField } from 'discord.js'; +import Command from "../../../core/command"; +import $ from "../../../core/lib"; +import {Storage, getPrefix} from "../../../core/structures"; +import {isAuthorized} from "./eco-utils"; +import {ShopItems, ShopItem} from "./eco-shop-items"; +import {EmbedField} from "discord.js"; export const ShopCommand = new Command({ - description: 'Displays the list of items you can buy in the shop.', - async run({ guild, channel, author }) { - if (isAuthorized(guild, channel)) { - function getShopEmbed(selection: ShopItem[], title = 'Shop') { - const fields: EmbedField[] = []; + description: "Displays the list of items you can buy in the shop.", + async run({guild, channel, author}) { + if (isAuthorized(guild, channel)) { + function getShopEmbed(selection: ShopItem[], title = "Shop") { + const fields: EmbedField[] = []; - for (const item of selection) - fields.push({ - name: `**${item.title}** (${getPrefix(guild)}eco buy ${ - item.usage - })`, - value: `${item.description} Costs ${$(item.cost).pluralise( - 'Mon', - 's', - )}.`, - inline: false, - }); + for (const item of selection) + fields.push({ + name: `**${item.title}** (${getPrefix(guild)}eco buy ${ + item.usage + })`, + value: `${item.description} Costs ${$( + item.cost + ).pluralise("Mon", "s")}.`, + inline: false + }); - return { - embed: { - color: 0xf1c40f, - title: title, - fields: fields, - footer: { - text: 'Mon Shop | TravBot Services', - }, - }, - }; - } + return { + embed: { + color: 0xf1c40f, + title: title, + fields: fields, + footer: { + text: "Mon Shop | TravBot Services" + } + } + }; + } - // In case there's just one page, omit unnecessary details. - if (ShopItems.length <= 5) channel.send(getShopEmbed(ShopItems)); - else { - const shopPages = $(ShopItems).split(5); - const pageAmount = shopPages.length; - const msg = await channel.send( - getShopEmbed(shopPages[0], `Shop (Page 1 of ${pageAmount})`), - ); + // In case there's just one page, omit unnecessary details. + if (ShopItems.length <= 5) channel.send(getShopEmbed(ShopItems)); + else { + const shopPages = $(ShopItems).split(5); + const pageAmount = shopPages.length; + const msg = await channel.send( + getShopEmbed(shopPages[0], `Shop (Page 1 of ${pageAmount})`) + ); - $.paginate(msg, author.id, pageAmount, (page) => { - msg.edit( - getShopEmbed( - shopPages[page], - `Shop (Page ${page + 1} of ${pageAmount})`, - ), - ); - }); - } + $.paginate(msg, author.id, pageAmount, (page) => { + msg.edit( + getShopEmbed( + shopPages[page], + `Shop (Page ${page + 1} of ${pageAmount})` + ) + ); + }); + } + } } - }, }); export const BuyCommand = new Command({ - description: 'Buys an item from the shop.', - usage: '', - async run({ guild, channel, args, message, author }) { - if (isAuthorized(guild, channel)) { - let found = false; + description: "Buys an item from the shop.", + usage: "", + async run({guild, channel, args, message, author}) { + if (isAuthorized(guild, channel)) { + let found = false; - let amount = 1; // The amount the user is buying. + let amount = 1; // The amount the user is buying. - // For now, no shop items support being bought multiple times. Uncomment these 2 lines when it's supported/needed. - //if (/\d+/g.test(args[args.length - 1])) - //amount = parseInt(args.pop()); + // For now, no shop items support being bought multiple times. Uncomment these 2 lines when it's supported/needed. + //if (/\d+/g.test(args[args.length - 1])) + //amount = parseInt(args.pop()); - let requested = args.join(' '); // The item the user is buying. + let requested = args.join(" "); // The item the user is buying. - for (let item of ShopItems) { - if (item.usage === requested) { - const user = Storage.getUser(author.id); - const cost = item.cost * amount; + for (let item of ShopItems) { + if (item.usage === requested) { + const user = Storage.getUser(author.id); + const cost = item.cost * amount; - if (cost > user.money) { - channel.send('Not enough Mons!'); - } else { - user.money -= cost; - Storage.save(); - item.run(message, cost, amount); - } + if (cost > user.money) { + channel.send("Not enough Mons!"); + } else { + user.money -= cost; + Storage.save(); + item.run(message, cost, amount); + } - found = true; - break; + found = true; + break; + } + } + + if (!found) + channel.send( + `There's no item in the shop that goes by \`${requested}\`!` + ); } - } - - if (!found) - channel.send( - `There's no item in the shop that goes by \`${requested}\`!`, - ); } - }, }); diff --git a/src/commands/fun/subcommands/eco-utils.ts b/src/commands/fun/subcommands/eco-utils.ts index 3af6971..fb58435 100644 --- a/src/commands/fun/subcommands/eco-utils.ts +++ b/src/commands/fun/subcommands/eco-utils.ts @@ -1,81 +1,87 @@ -import $ from '../../../core/lib'; -import { Storage } from '../../../core/structures'; -import { User, Guild, TextChannel, DMChannel, NewsChannel } from 'discord.js'; +import $ from "../../../core/lib"; +import {Storage} from "../../../core/structures"; +import {User, Guild, TextChannel, DMChannel, NewsChannel} from "discord.js"; export function getMoneyEmbed(user: User): object { - const profile = Storage.getUser(user.id); + const profile = Storage.getUser(user.id); - return { - embed: { - color: 0xffff00, - author: { - name: user.username, - icon_url: user.displayAvatarURL({ - format: 'png', - dynamic: true, - }), - }, - fields: [ - { - name: 'Balance', - value: $(profile.money).pluralise('credit', 's'), - }, - ], - }, - }; + return { + embed: { + color: 0xffff00, + author: { + name: user.username, + icon_url: user.displayAvatarURL({ + format: "png", + dynamic: true + }) + }, + fields: [ + { + name: "Balance", + value: $(profile.money).pluralise("credit", "s") + } + ] + } + }; } export function getSendEmbed( - sender: User, - receiver: User, - amount: number, + sender: User, + receiver: User, + amount: number ): object { - return { - embed: { - color: 0xffff00, - author: { - name: sender.username, - icon_url: sender.displayAvatarURL({ - format: 'png', - dynamic: true, - }), - }, - title: 'Transaction', - description: `${sender.toString()} has sent ${$(amount).pluralise( - 'credit', - 's', - )} to ${receiver.toString()}!`, - fields: [ - { - name: `Sender: ${sender.username}#${sender.discriminator}`, - value: $(Storage.getUser(sender.id).money).pluralise('credit', 's'), - }, - { - name: `Receiver: ${receiver.username}#${receiver.discriminator}`, - value: $(Storage.getUser(receiver.id).money).pluralise('credit', 's'), - }, - ], - footer: { - text: receiver.username, - icon_url: receiver.displayAvatarURL({ - format: 'png', - dynamic: true, - }), - }, - }, - }; + return { + embed: { + color: 0xffff00, + author: { + name: sender.username, + icon_url: sender.displayAvatarURL({ + format: "png", + dynamic: true + }) + }, + title: "Transaction", + description: `${sender.toString()} has sent ${$(amount).pluralise( + "credit", + "s" + )} to ${receiver.toString()}!`, + fields: [ + { + name: `Sender: ${sender.username}#${sender.discriminator}`, + value: $(Storage.getUser(sender.id).money).pluralise( + "credit", + "s" + ) + }, + { + name: `Receiver: ${receiver.username}#${receiver.discriminator}`, + value: $(Storage.getUser(receiver.id).money).pluralise( + "credit", + "s" + ) + } + ], + footer: { + text: receiver.username, + icon_url: receiver.displayAvatarURL({ + format: "png", + dynamic: true + }) + } + } + }; } export function isAuthorized( - guild: Guild | null, - channel: TextChannel | DMChannel | NewsChannel, + guild: Guild | null, + channel: TextChannel | DMChannel | NewsChannel ): boolean { - if (guild?.id === '637512823676600330' || process.argv[2] === 'dev') - return true; - else { - channel.send( - "Sorry, this command can only be used in Monika's emote server.", - ); - return false; - } + if (guild?.id === "637512823676600330" || process.argv[2] === "dev") + return true; + else { + channel.send( + "Sorry, this command can only be used in Monika's emote server." + ); + return false; + } } diff --git a/src/commands/help.ts b/src/commands/help.ts index 3150df7..0424b10 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,145 +1,153 @@ -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; -import { loadCommands, categories } from '../core/command'; -import { PermissionNames } from '../core/permissions'; +import Command from "../core/command"; +import {CommonLibrary} from "../core/lib"; +import {loadCommands, categories} from "../core/command"; +import {PermissionNames} from "../core/permissions"; export default new Command({ - description: - 'Lists all commands. If a command is specified, their arguments are listed as well.', - usage: '([command, [subcommand/type], ...])', - aliases: ['h'], - async run($: CommonLibrary): Promise { - const commands = await loadCommands(); - let output = `Legend: \`\`, \`[list/of/stuff]\`, \`(optional)\`, \`()\`, \`([optional/list/...])\``; - - for (const [category, headers] of categories) { - output += `\n\n===[ ${category} ]===`; - - for (const header of headers) { - if (header !== 'test') { - const command = commands.get(header); - - if (!command) - return $.warn( - `Command "${header}" of category "${category}" unexpectedly doesn't exist!`, - ); - - output += `\n- \`${header}\`: ${command.description}`; - } - } - } - - $.channel.send(output, { split: true }); - }, - any: new Command({ + description: + "Lists all commands. If a command is specified, their arguments are listed as well.", + usage: "([command, [subcommand/type], ...])", + aliases: ["h"], async run($: CommonLibrary): Promise { - const commands = await loadCommands(); - let header = $.args.shift() as string; - let command = commands.get(header); + const commands = await loadCommands(); + let output = `Legend: \`\`, \`[list/of/stuff]\`, \`(optional)\`, \`()\`, \`([optional/list/...])\``; - if (!command || header === 'test') - return $.channel.send(`No command found by the name \`${header}\`!`); + for (const [category, headers] of categories) { + output += `\n\n===[ ${category} ]===`; - if (command.originalCommandName) header = command.originalCommandName; - else $.warn(`originalCommandName isn't defined for ${header}?!`); + for (const header of headers) { + if (header !== "test") { + const command = commands.get(header); - let permLevel = command.permission ?? Command.PERMISSIONS.NONE; - let usage = command.usage; - let invalid = false; + if (!command) + return $.warn( + `Command "${header}" of category "${category}" unexpectedly doesn't exist!` + ); - let selectedCategory = 'Unknown'; - - for (const [category, headers] of categories) { - if (headers.includes(header)) { - if (selectedCategory !== 'Unknown') - $.warn( - `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.`, - ); - else selectedCategory = category; - } - } - - for (const param of $.args) { - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; - - switch (type) { - case Command.TYPES.SUBCOMMAND: - header += ` ${command.originalCommandName}`; - break; - case Command.TYPES.USER: - header += ' '; - break; - case Command.TYPES.NUMBER: - header += ' '; - break; - case Command.TYPES.ANY: - header += ' '; - break; - default: - header += ` ${param}`; - break; + output += `\n- \`${header}\`: ${command.description}`; + } + } } - if (type === Command.TYPES.NONE) { - invalid = true; - break; - } - } - - if (invalid) - return $.channel.send(`No command found by the name \`${header}\`!`); - - let append = ''; - - if (usage === '') { - const list: string[] = []; - - command.subcommands.forEach((subcmd, subtag) => { - // Don't capture duplicates generated from aliases. - if (subcmd.originalCommandName === subtag) { - const customUsage = subcmd.usage ? ` ${subcmd.usage}` : ''; - list.push( - `- \`${header} ${subtag}${customUsage}\` - ${subcmd.description}`, - ); - } - }); - - const addDynamicType = (cmd: Command | null, type: string) => { - if (cmd) { - const customUsage = cmd.usage ? ` ${cmd.usage}` : ''; - list.push( - `- \`${header} <${type}>${customUsage}\` - ${cmd.description}`, - ); - } - }; - - addDynamicType(command.user, 'user'); - addDynamicType(command.number, 'number'); - addDynamicType(command.any, 'any'); - - append = - 'Usages:' + (list.length > 0 ? `\n${list.join('\n')}` : ' None.'); - } else append = `Usage: \`${header} ${usage}\``; - - let aliases = 'None'; - - if (command.aliases.length > 0) { - aliases = ''; - - for (let i = 0; i < command.aliases.length; i++) { - const alias = command.aliases[i]; - aliases += `\`${alias}\``; - - if (i !== command.aliases.length - 1) aliases += ', '; - } - } - - $.channel.send( - `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${selectedCategory}\`\nPermission Required: \`${PermissionNames[permLevel]}\` (${permLevel})\nDescription: ${command.description}\n${append}`, - { split: true }, - ); + $.channel.send(output, {split: true}); }, - }), + any: new Command({ + async run($: CommonLibrary): Promise { + const commands = await loadCommands(); + let header = $.args.shift() as string; + let command = commands.get(header); + + if (!command || header === "test") + return $.channel.send( + `No command found by the name \`${header}\`!` + ); + + if (command.originalCommandName) + header = command.originalCommandName; + else $.warn(`originalCommandName isn't defined for ${header}?!`); + + let permLevel = command.permission ?? Command.PERMISSIONS.NONE; + let usage = command.usage; + let invalid = false; + + let selectedCategory = "Unknown"; + + for (const [category, headers] of categories) { + if (headers.includes(header)) { + if (selectedCategory !== "Unknown") + $.warn( + `Command "${header}" is somehow in multiple categories. This means that the command loading stage probably failed in properly adding categories.` + ); + else selectedCategory = category; + } + } + + for (const param of $.args) { + const type = command.resolve(param); + command = command.get(param); + permLevel = command.permission ?? permLevel; + + switch (type) { + case Command.TYPES.SUBCOMMAND: + header += ` ${command.originalCommandName}`; + break; + case Command.TYPES.USER: + header += " "; + break; + case Command.TYPES.NUMBER: + header += " "; + break; + case Command.TYPES.ANY: + header += " "; + break; + default: + header += ` ${param}`; + break; + } + + if (type === Command.TYPES.NONE) { + invalid = true; + break; + } + } + + if (invalid) + return $.channel.send( + `No command found by the name \`${header}\`!` + ); + + let append = ""; + + if (usage === "") { + const list: string[] = []; + + command.subcommands.forEach((subcmd, subtag) => { + // Don't capture duplicates generated from aliases. + if (subcmd.originalCommandName === subtag) { + const customUsage = subcmd.usage + ? ` ${subcmd.usage}` + : ""; + list.push( + `- \`${header} ${subtag}${customUsage}\` - ${subcmd.description}` + ); + } + }); + + const addDynamicType = (cmd: Command | null, type: string) => { + if (cmd) { + const customUsage = cmd.usage ? ` ${cmd.usage}` : ""; + list.push( + `- \`${header} <${type}>${customUsage}\` - ${cmd.description}` + ); + } + }; + + addDynamicType(command.user, "user"); + addDynamicType(command.number, "number"); + addDynamicType(command.any, "any"); + + append = + "Usages:" + + (list.length > 0 ? `\n${list.join("\n")}` : " None."); + } else append = `Usage: \`${header} ${usage}\``; + + let aliases = "None"; + + if (command.aliases.length > 0) { + aliases = ""; + + for (let i = 0; i < command.aliases.length; i++) { + const alias = command.aliases[i]; + aliases += `\`${alias}\``; + + if (i !== command.aliases.length - 1) aliases += ", "; + } + } + + $.channel.send( + `Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${selectedCategory}\`\nPermission Required: \`${PermissionNames[permLevel]}\` (${permLevel})\nDescription: ${command.description}\n${append}`, + {split: true} + ); + } + }) }); diff --git a/src/commands/info.ts b/src/commands/info.ts index 18d4a50..c9153a1 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -1,243 +1,288 @@ -import { MessageEmbed, version as djsversion } from 'discord.js'; -/// @ts-ignore -import { version } from '../../package.json'; -import ms from 'ms'; -import os from 'os'; -import Command from '../core/command'; -import { CommonLibrary, formatBytes, trimArray } from '../core/lib'; -import { verificationLevels, filterLevels, regions, flags } from '../defs/info'; -import moment from 'moment'; -import utc from 'moment'; - -export default new Command({ - description: - 'Command to provide all sorts of info about the current server, a user, etc.', - run: 'Please provide an argument.\nFor help, run `%prefix%help info`.', - subcommands: { - avatar: new Command({ - description: "Shows your own, or another user's avatar.", - usage: '()', - async run($: CommonLibrary): Promise { - $.channel.send( - $.author.displayAvatarURL({ dynamic: true, size: 2048 }), - ); - }, - user: new Command({ - description: "Shows your own, or another user's avatar.", - async run($: CommonLibrary): Promise { - $.channel.send( - $.args[0].displayAvatarURL({ dynamic: true, size: 2048 }), - ); - }, - }), - }), - - bot: new Command({ - description: 'Displays info about the bot.', - async run($: CommonLibrary): Promise { - const core = os.cpus()[0]; - const embed = new MessageEmbed() - .setThumbnail( - /// @ts-ignore - $.client.user?.displayAvatarURL({ dynamic: true, size: 2048 }), - ) - .setColor($.guild?.me?.displayHexColor || 'BLUE') - .addField('General', [ - `**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`, - `**❯ Servers:** ${$.client.guilds.cache.size.toLocaleString()}`, - `**❯ Users:** ${$.client.guilds.cache - .reduce((a: any, b: { memberCount: any }) => a + b.memberCount, 0) - .toLocaleString()}`, - `**❯ Channels:** ${$.client.channels.cache.size.toLocaleString()}`, - `**❯ Creation Date:** ${utc($.client.user?.createdTimestamp).format( - 'Do MMMM YYYY HH:mm:ss', - )}`, - `**❯ Node.JS:** ${process.version}`, - `**❯ Version:** v${version}`, - `**❯ Discord.JS:** ${djsversion}`, - '\u200b', - ]) - .addField('System', [ - `**❯ Platform:** ${process.platform}`, - `**❯ Uptime:** ${ms(os.uptime() * 1000, { long: true })}`, - `**❯ CPU:**`, - `\u3000 • Cores: ${os.cpus().length}`, - `\u3000 • Model: ${core.model}`, - `\u3000 • Speed: ${core.speed}MHz`, - `**❯ Memory:**`, - `\u3000 • Total: ${formatBytes(process.memoryUsage().heapTotal)}`, - `\u3000 • Used: ${formatBytes(process.memoryUsage().heapTotal)}`, - ]) - .setTimestamp(); - $.channel.send(embed); - }, - }), - - guild: new Command({ - description: 'Displays info about the current guild.', - async run($: CommonLibrary): Promise { - if ($.guild) { - const roles = $.guild.roles.cache - .sort((a, b) => b.position - a.position) - .map((role) => role.toString()); - const members = $.guild.members.cache; - const channels = $.guild.channels.cache; - const emojis = $.guild.emojis.cache; - - const iconURL = $.guild.iconURL({ dynamic: true }); - const embed = new MessageEmbed() - .setDescription(`**Guild information for __${$.guild.name}__**`) - .setColor('BLUE'); - if (iconURL) - embed - .setThumbnail(iconURL) - .addField('General', [ - `**❯ Name:** ${$.guild.name}`, - `**❯ ID:** ${$.guild.id}`, - `**❯ Owner:** ${$.guild.owner?.user.tag} (${$.guild.ownerID})`, - `**❯ Region:** ${regions[$.guild.region]}`, - `**❯ Boost Tier:** ${ - $.guild.premiumTier ? `Tier ${$.guild.premiumTier}` : 'None' - }`, - `**❯ Explicit Filter:** ${ - filterLevels[$.guild.explicitContentFilter] - }`, - `**❯ Verification Level:** ${ - verificationLevels[$.guild.verificationLevel] - }`, - `**❯ Time Created:** ${moment($.guild.createdTimestamp).format( - 'LT', - )} ${moment($.guild.createdTimestamp).format('LL')} ${moment( - $.guild.createdTimestamp, - ).fromNow()})`, - '\u200b', - ]) - .addField('Statistics', [ - `**❯ Role Count:** ${roles.length}`, - `**❯ Emoji Count:** ${emojis.size}`, - `**❯ Regular Emoji Count:** ${ - emojis.filter((emoji) => !emoji.animated).size - }`, - `**❯ Animated Emoji Count:** ${ - emojis.filter((emoji) => emoji.animated).size - }`, - `**❯ Member Count:** ${$.guild.memberCount}`, - `**❯ Humans:** ${ - members.filter((member) => !member.user.bot).size - }`, - `**❯ Bots:** ${ - members.filter((member) => member.user.bot).size - }`, - `**❯ Text Channels:** ${ - channels.filter((channel) => channel.type === 'text').size - }`, - `**❯ Voice Channels:** ${ - channels.filter((channel) => channel.type === 'voice').size - }`, - `**❯ Boost Count:** ${$.guild.premiumSubscriptionCount || '0'}`, - `\u200b`, - ]) - .addField('Presence', [ - `**❯ Online:** ${ - members.filter( - (member) => member.presence.status === 'online', - ).size - }`, - `**❯ Idle:** ${ - members.filter((member) => member.presence.status === 'idle') - .size - }`, - `**❯ Do Not Disturb:** ${ - members.filter((member) => member.presence.status === 'dnd') - .size - }`, - `**❯ Offline:** ${ - members.filter( - (member) => member.presence.status === 'offline', - ).size - }`, - '\u200b', - ]) - .addField( - `Roles [${roles.length - 1}]`, - roles.length < 10 - ? roles.join(', ') - : roles.length > 10 - ? trimArray(roles) - : 'None', - ) - .setTimestamp(); - - $.channel.send(embed); - } else { - $.channel.send('Please execute this command in a guild.'); - } - }, - }), - }, - user: new Command({ - description: 'Displays info about mentioned user.', - async run($: CommonLibrary): Promise { - // Transforms the User object into a GuildMember object of the current guild. - const member = $.guild?.members.resolve($.args[0]); - - if (!member) - return $.channel.send( - 'No member object was found by that user! Are you sure you used this command in a server?', - ); - - const roles = member.roles.cache - .sort( - (a: { position: number }, b: { position: number }) => - b.position - a.position, - ) - .map((role: { toString: () => any }) => role.toString()) - .slice(0, -1); - // @ts-ignore - Discord.js' typings seem to be outdated here. According to their v12 docs, it's User.fetchFlags() instead of User.flags. - const userFlags = ((await member.user.fetchFlags()) as UserFlags).toArray(); - - const embed = new MessageEmbed() - .setThumbnail( - member.user.displayAvatarURL({ dynamic: true, size: 512 }), - ) - .setColor(member.displayHexColor || 'BLUE') - .addField('User', [ - `**❯ Username:** ${member.user.username}`, - `**❯ Discriminator:** ${member.user.discriminator}`, - `**❯ ID:** ${member.id}`, - `**❯ Flags:** ${userFlags.length ? userFlags.join(', ') : 'None'}`, - `**❯ Avatar:** [Link to avatar](${member.user.displayAvatarURL({ - dynamic: true, - })})`, - `**❯ Time Created:** ${moment(member.user.createdTimestamp).format( - 'LT', - )} ${moment(member.user.createdTimestamp).format('LL')} ${moment( - member.user.createdTimestamp, - ).fromNow()}`, - `**❯ Status:** ${member.user.presence.status}`, - `**❯ Game:** ${ - member.user.presence.activities || 'Not playing a game.' - }`, - ]) - .addField('Member', [ - `**❯ Highest Role:** ${ - member.roles.highest.id === $.guild?.id - ? 'None' - : member.roles.highest.name - }`, - `**❯ Server Join Date:** ${moment(member.joinedAt).format('LL LTS')}`, - `**❯ Hoist Role:** ${ - member.roles.hoist ? member.roles.hoist.name : 'None' - }`, - `**❯ Roles:** [${roles.length}]: ${ - roles.length < 10 - ? roles.join(', ') - : roles.length > 10 - ? this.client.utils.trimArray(roles) - : 'None' - }`, - ]); - $.channel.send(embed); - }, - }), -}); +import {MessageEmbed, version as djsversion} from "discord.js"; +/// @ts-ignore +import {version} from "../../package.json"; +import ms from "ms"; +import os from "os"; +import Command from "../core/command"; +import {CommonLibrary, formatBytes, trimArray} from "../core/lib"; +import {verificationLevels, filterLevels, regions, flags} from "../defs/info"; +import moment from "moment"; +import utc from "moment"; + +export default new Command({ + description: + "Command to provide all sorts of info about the current server, a user, etc.", + run: "Please provide an argument.\nFor help, run `%prefix%help info`.", + subcommands: { + avatar: new Command({ + description: "Shows your own, or another user's avatar.", + usage: "()", + async run($: CommonLibrary): Promise { + $.channel.send( + $.author.displayAvatarURL({dynamic: true, size: 2048}) + ); + }, + user: new Command({ + description: "Shows your own, or another user's avatar.", + async run($: CommonLibrary): Promise { + $.channel.send( + $.args[0].displayAvatarURL({ + dynamic: true, + size: 2048 + }) + ); + } + }) + }), + bot: new Command({ + description: "Displays info about the bot.", + async run($: CommonLibrary): Promise { + const core = os.cpus()[0]; + const embed = new MessageEmbed() + .setThumbnail( + /// @ts-ignore + $.client.user?.displayAvatarURL({ + dynamic: true, + size: 2048 + }) + ) + .setColor($.guild?.me?.displayHexColor || "BLUE") + .addField("General", [ + `**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`, + `**❯ Servers:** ${$.client.guilds.cache.size.toLocaleString()}`, + `**❯ Users:** ${$.client.guilds.cache + .reduce( + (a: any, b: {memberCount: any}) => + a + b.memberCount, + 0 + ) + .toLocaleString()}`, + `**❯ Channels:** ${$.client.channels.cache.size.toLocaleString()}`, + `**❯ Creation Date:** ${utc( + $.client.user?.createdTimestamp + ).format("Do MMMM YYYY HH:mm:ss")}`, + `**❯ Node.JS:** ${process.version}`, + `**❯ Version:** v${version}`, + `**❯ Discord.JS:** ${djsversion}`, + "\u200b" + ]) + .addField("System", [ + `**❯ Platform:** ${process.platform}`, + `**❯ Uptime:** ${ms(os.uptime() * 1000, { + long: true + })}`, + `**❯ CPU:**`, + `\u3000 • Cores: ${os.cpus().length}`, + `\u3000 • Model: ${core.model}`, + `\u3000 • Speed: ${core.speed}MHz`, + `**❯ Memory:**`, + `\u3000 • Total: ${formatBytes( + process.memoryUsage().heapTotal + )}`, + `\u3000 • Used: ${formatBytes( + process.memoryUsage().heapTotal + )}` + ]) + .setTimestamp(); + $.channel.send(embed); + } + }), + guild: new Command({ + description: "Displays info about the current guild.", + async run($: CommonLibrary): Promise { + if ($.guild) { + const roles = $.guild.roles.cache + .sort((a, b) => b.position - a.position) + .map((role) => role.toString()); + const members = $.guild.members.cache; + const channels = $.guild.channels.cache; + const emojis = $.guild.emojis.cache; + const iconURL = $.guild.iconURL({dynamic: true}); + const embed = new MessageEmbed() + .setDescription( + `**Guild information for __${$.guild.name}__**` + ) + .setColor("BLUE"); + if (iconURL) + embed + .setThumbnail(iconURL) + .addField("General", [ + `**❯ Name:** ${$.guild.name}`, + `**❯ ID:** ${$.guild.id}`, + `**❯ Owner:** ${$.guild.owner?.user.tag} (${$.guild.ownerID})`, + `**❯ Region:** ${regions[$.guild.region]}`, + `**❯ Boost Tier:** ${ + $.guild.premiumTier + ? `Tier ${$.guild.premiumTier}` + : "None" + }`, + `**❯ Explicit Filter:** ${ + filterLevels[$.guild.explicitContentFilter] + }`, + `**❯ Verification Level:** ${ + verificationLevels[ + $.guild.verificationLevel + ] + }`, + `**❯ Time Created:** ${moment( + $.guild.createdTimestamp + ).format("LT")} ${moment( + $.guild.createdTimestamp + ).format("LL")} ${moment( + $.guild.createdTimestamp + ).fromNow()})`, + "\u200b" + ]) + .addField("Statistics", [ + `**❯ Role Count:** ${roles.length}`, + `**❯ Emoji Count:** ${emojis.size}`, + `**❯ Regular Emoji Count:** ${ + emojis.filter((emoji) => !emoji.animated) + .size + }`, + `**❯ Animated Emoji Count:** ${ + emojis.filter((emoji) => emoji.animated) + .size + }`, + `**❯ Member Count:** ${$.guild.memberCount}`, + `**❯ Humans:** ${ + members.filter((member) => !member.user.bot) + .size + }`, + `**❯ Bots:** ${ + members.filter((member) => member.user.bot) + .size + }`, + `**❯ Text Channels:** ${ + channels.filter( + (channel) => channel.type === "text" + ).size + }`, + `**❯ Voice Channels:** ${ + channels.filter( + (channel) => channel.type === "voice" + ).size + }`, + `**❯ Boost Count:** ${ + $.guild.premiumSubscriptionCount || "0" + }`, + `\u200b` + ]) + .addField("Presence", [ + `**❯ Online:** ${ + members.filter( + (member) => + member.presence.status === "online" + ).size + }`, + `**❯ Idle:** ${ + members.filter( + (member) => + member.presence.status === "idle" + ).size + }`, + `**❯ Do Not Disturb:** ${ + members.filter( + (member) => + member.presence.status === "dnd" + ).size + }`, + `**❯ Offline:** ${ + members.filter( + (member) => + member.presence.status === "offline" + ).size + }`, + "\u200b" + ]) + .addField( + `Roles [${roles.length - 1}]`, + roles.length < 10 + ? roles.join(", ") + : roles.length > 10 + ? trimArray(roles) + : "None" + ) + .setTimestamp(); + + $.channel.send(embed); + } else { + $.channel.send("Please execute this command in a guild."); + } + } + }) + }, + user: new Command({ + description: "Displays info about mentioned user.", + async run($: CommonLibrary): Promise { + // Transforms the User object into a GuildMember object of the current guild. + const member = $.guild?.members.resolve($.args[0]); + + if (!member) + return $.channel.send( + "No member object was found by that user! Are you sure you used this command in a server?" + ); + + const roles = member.roles.cache + .sort( + (a: {position: number}, b: {position: number}) => + b.position - a.position + ) + .map((role: {toString: () => any}) => role.toString()) + .slice(0, -1); + // @ts-ignore - Discord.js' typings seem to be outdated here. According to their v12 docs, it's User.fetchFlags() instead of User.flags. + const userFlags = ((await member.user.fetchFlags()) as UserFlags).toArray(); + + const embed = new MessageEmbed() + .setThumbnail( + member.user.displayAvatarURL({dynamic: true, size: 512}) + ) + .setColor(member.displayHexColor || "BLUE") + .addField("User", [ + `**❯ Username:** ${member.user.username}`, + `**❯ Discriminator:** ${member.user.discriminator}`, + `**❯ ID:** ${member.id}`, + `**❯ Flags:** ${ + userFlags.length ? userFlags.join(", ") : "None" + }`, + `**❯ Avatar:** [Link to avatar](${member.user.displayAvatarURL( + { + dynamic: true + } + )})`, + `**❯ Time Created:** ${moment( + member.user.createdTimestamp + ).format("LT")} ${moment( + member.user.createdTimestamp + ).format("LL")} ${moment( + member.user.createdTimestamp + ).fromNow()}`, + `**❯ Status:** ${member.user.presence.status}`, + `**❯ Game:** ${ + member.user.presence.activities || "Not playing a game." + }` + ]) + .addField("Member", [ + `**❯ Highest Role:** ${ + member.roles.highest.id === $.guild?.id + ? "None" + : member.roles.highest.name + }`, + `**❯ Server Join Date:** ${moment(member.joinedAt).format( + "LL LTS" + )}`, + `**❯ Hoist Role:** ${ + member.roles.hoist ? member.roles.hoist.name : "None" + }`, + `**❯ Roles:** [${roles.length}]: ${ + roles.length < 10 + ? roles.join(", ") + : roles.length > 10 + ? this.client.utils.trimArray(roles) + : "None" + }` + ]); + $.channel.send(embed); + } + }) +}); diff --git a/src/commands/scanemotes.ts b/src/commands/scanemotes.ts index 107bf6f..ed93a3f 100644 --- a/src/commands/scanemotes.ts +++ b/src/commands/scanemotes.ts @@ -1,191 +1,196 @@ -import Command from '../core/command'; -import { CommonLibrary } from '../core/lib'; -import moment from 'moment'; -import { Collection, TextChannel } from 'discord.js'; +import Command from "../core/command"; +import {CommonLibrary} from "../core/lib"; +import moment from "moment"; +import {Collection, TextChannel} from "discord.js"; -const lastUsedTimestamps: { [id: string]: number } = {}; +const lastUsedTimestamps: {[id: string]: number} = {}; export default new Command({ - description: - 'Scans all text channels in the current guild and returns the number of times each emoji specific to the guild has been used. Has a cooldown of 24 hours per guild.', - async run($: CommonLibrary): Promise { - if (!$.guild) - return $.channel.send(`You must use this command on a server!`); + description: + "Scans all text channels in the current guild and returns the number of times each emoji specific to the guild has been used. Has a cooldown of 24 hours per guild.", + async run($: CommonLibrary): Promise { + if (!$.guild) + return $.channel.send(`You must use this command on a server!`); - // Test if the command is on cooldown. This isn't the strictest cooldown possible, because in the event that the bot crashes, the cooldown will be reset. But for all intends and purposes, it's a good enough cooldown. It's a per-server cooldown. - const startTime = Date.now(); - const cooldown = 86400000; // 24 hours - const lastUsedTimestamp = lastUsedTimestamps[$.guild.id] ?? 0; - const difference = startTime - lastUsedTimestamp; - const howLong = moment(startTime).to(lastUsedTimestamp + cooldown); + // Test if the command is on cooldown. This isn't the strictest cooldown possible, because in the event that the bot crashes, the cooldown will be reset. But for all intends and purposes, it's a good enough cooldown. It's a per-server cooldown. + const startTime = Date.now(); + const cooldown = 86400000; // 24 hours + const lastUsedTimestamp = lastUsedTimestamps[$.guild.id] ?? 0; + const difference = startTime - lastUsedTimestamp; + const howLong = moment(startTime).to(lastUsedTimestamp + cooldown); - // If it's been less than an hour since the command was last used, prevent it from executing. - if (difference < cooldown) - return $.channel.send( - `This command requires a day to cooldown. You'll be able to activate this command ${howLong}.`, - ); - else lastUsedTimestamps[$.guild.id] = startTime; + // If it's been less than an hour since the command was last used, prevent it from executing. + if (difference < cooldown) + return $.channel.send( + `This command requires a day to cooldown. You'll be able to activate this command ${howLong}.` + ); + else lastUsedTimestamps[$.guild.id] = startTime; - const stats: { - [id: string]: { - name: string; - formatted: string; - users: number; - bots: number; - }; - } = {}; - let totalUserEmoteUsage = 0; - // IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise. - const allTextChannelsInCurrentGuild = $.guild.channels.cache.filter( - (channel) => channel.type === 'text' && channel.viewable, - ) as Collection; - let messagesSearched = 0; - let channelsSearched = 0; - let currentChannelName = ''; - const totalChannels = allTextChannelsInCurrentGuild.size; - const statusMessage = await $.channel.send('Gathering emotes...'); - let warnings = 0; - $.channel.startTyping(); + const stats: { + [id: string]: { + name: string; + formatted: string; + users: number; + bots: number; + }; + } = {}; + let totalUserEmoteUsage = 0; + // IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise. + const allTextChannelsInCurrentGuild = $.guild.channels.cache.filter( + (channel) => channel.type === "text" && channel.viewable + ) as Collection; + let messagesSearched = 0; + let channelsSearched = 0; + let currentChannelName = ""; + const totalChannels = allTextChannelsInCurrentGuild.size; + const statusMessage = await $.channel.send("Gathering emotes..."); + let warnings = 0; + $.channel.startTyping(); - // Initialize the emote stats object with every emote in the current guild. - // The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with. - for (let emote of $.guild.emojis.cache.values()) { - // If you don't include the "a" for animated emotes, it'll not only not show up, but also cause all other emotes in the same message to not show up. The emote name is self-correcting but it's better to keep the right value since it'll be used to calculate message lengths that fit. - stats[emote.id] = { - name: emote.name, - formatted: `<${emote.animated ? 'a' : ''}:${emote.name}:${emote.id}>`, - users: 0, - bots: 0, - }; - } - - const interval = setInterval(() => { - statusMessage.edit( - `Searching channel \`${currentChannelName}\`... (${messagesSearched} messages scanned, ${channelsSearched}/${totalChannels} channels scanned)`, - ); - }, 5000); - - for (const channel of allTextChannelsInCurrentGuild.values()) { - currentChannelName = channel.name; - let selected = channel.lastMessageID ?? $.message.id; - let continueLoop = true; - - while (continueLoop) { - // Unfortunately, any kind of .fetch call is limited to 100 items at once by Discord's API. - const messages = await channel.messages.fetch({ - limit: 100, - before: selected, - }); - - if (messages.size > 0) { - for (const msg of messages.values()) { - // It's very important to not capture an array of matches then do \d+ on each item because emote names can have numbers in them, causing the bot to not count them correctly. - const search = //g; - const text = msg.content; - let match: RegExpExecArray | null; - - while ((match = search.exec(text))) { - const emoteID = match[1]; - - if (emoteID in stats) { - if (msg.author.bot) stats[emoteID].bots++; - else { - stats[emoteID].users++; - totalUserEmoteUsage++; - } - } - } - - for (const reaction of msg.reactions.cache.values()) { - const emoteID = reaction.emoji.id; - let continueReactionLoop = true; - let lastUserID: string | undefined; - let userReactions = 0; - let botReactions = 0; - - // An emote's ID will be null if it's a unicode emote. - if (emoteID && emoteID in stats) { - // There is a simple count property on a reaction, but that doesn't separate users from bots. - // So instead, I'll use that property to check for inconsistencies. - while (continueReactionLoop) { - // After logging users, it seems like the order is strictly numerical. As long as that stays consistent, this should work fine. - const users = await reaction.users.fetch({ - limit: 100, - after: lastUserID, - }); - - if (users.size > 0) { - for (const user of users.values()) { - if (user.bot) { - stats[emoteID].bots++; - botReactions++; - } else { - stats[emoteID].users++; - totalUserEmoteUsage++; - userReactions++; - } - - lastUserID = user.id; - } - } else { - // Then halt the loop and send warnings of any inconsistencies. - continueReactionLoop = false; - - if (reaction.count !== userReactions + botReactions) { - $.warn( - `[Channel: ${channel.id}, Message: ${msg.id}] A reaction count of ${reaction.count} was expected but was given ${userReactions} user reactions and ${botReactions} bot reactions.`, - ); - warnings++; - } - } - } - } - } - - selected = msg.id; - messagesSearched++; - } - } else { - continueLoop = false; - channelsSearched++; + // Initialize the emote stats object with every emote in the current guild. + // The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with. + for (let emote of $.guild.emojis.cache.values()) { + // If you don't include the "a" for animated emotes, it'll not only not show up, but also cause all other emotes in the same message to not show up. The emote name is self-correcting but it's better to keep the right value since it'll be used to calculate message lengths that fit. + stats[emote.id] = { + name: emote.name, + formatted: `<${emote.animated ? "a" : ""}:${emote.name}:${ + emote.id + }>`, + users: 0, + bots: 0 + }; } - } + + const interval = setInterval(() => { + statusMessage.edit( + `Searching channel \`${currentChannelName}\`... (${messagesSearched} messages scanned, ${channelsSearched}/${totalChannels} channels scanned)` + ); + }, 5000); + + for (const channel of allTextChannelsInCurrentGuild.values()) { + currentChannelName = channel.name; + let selected = channel.lastMessageID ?? $.message.id; + let continueLoop = true; + + while (continueLoop) { + // Unfortunately, any kind of .fetch call is limited to 100 items at once by Discord's API. + const messages = await channel.messages.fetch({ + limit: 100, + before: selected + }); + + if (messages.size > 0) { + for (const msg of messages.values()) { + // It's very important to not capture an array of matches then do \d+ on each item because emote names can have numbers in them, causing the bot to not count them correctly. + const search = //g; + const text = msg.content; + let match: RegExpExecArray | null; + + while ((match = search.exec(text))) { + const emoteID = match[1]; + + if (emoteID in stats) { + if (msg.author.bot) stats[emoteID].bots++; + else { + stats[emoteID].users++; + totalUserEmoteUsage++; + } + } + } + + for (const reaction of msg.reactions.cache.values()) { + const emoteID = reaction.emoji.id; + let continueReactionLoop = true; + let lastUserID: string | undefined; + let userReactions = 0; + let botReactions = 0; + + // An emote's ID will be null if it's a unicode emote. + if (emoteID && emoteID in stats) { + // There is a simple count property on a reaction, but that doesn't separate users from bots. + // So instead, I'll use that property to check for inconsistencies. + while (continueReactionLoop) { + // After logging users, it seems like the order is strictly numerical. As long as that stays consistent, this should work fine. + const users = await reaction.users.fetch({ + limit: 100, + after: lastUserID + }); + + if (users.size > 0) { + for (const user of users.values()) { + if (user.bot) { + stats[emoteID].bots++; + botReactions++; + } else { + stats[emoteID].users++; + totalUserEmoteUsage++; + userReactions++; + } + + lastUserID = user.id; + } + } else { + // Then halt the loop and send warnings of any inconsistencies. + continueReactionLoop = false; + + if ( + reaction.count !== + userReactions + botReactions + ) { + $.warn( + `[Channel: ${channel.id}, Message: ${msg.id}] A reaction count of ${reaction.count} was expected but was given ${userReactions} user reactions and ${botReactions} bot reactions.` + ); + warnings++; + } + } + } + } + } + + selected = msg.id; + messagesSearched++; + } + } else { + continueLoop = false; + channelsSearched++; + } + } + } + + // Mark the operation as ended. + const finishTime = Date.now(); + clearInterval(interval); + statusMessage.edit( + `Finished operation in ${moment + .duration(finishTime - startTime) + .humanize()} with ${$(warnings).pluralise( + "inconsistenc", + "ies", + "y" + )}.` + ); + $.log(`Finished operation in ${finishTime - startTime} ms.`); + $.channel.stopTyping(); + + // Display stats on emote usage. + // This can work outside the loop now that it's synchronous, and now it's clearer what code is meant to execute at the end. + let sortedEmoteIDs = Object.keys(stats).sort( + (a, b) => stats[b].users - stats[a].users + ); + const lines: string[] = []; + let rank = 1; + + // It's better to send all the lines at once rather than paginate the data because it's quite a memory-intensive task to search all the messages in a server for it, and I wouldn't want to activate the command again just to get to another page. + for (const emoteID of sortedEmoteIDs) { + const emote = stats[emoteID]; + const botInfo = emote.bots > 0 ? ` (Bots: ${emote.bots})` : ""; + lines.push( + `\`#${rank++}\` ${emote.formatted} x ${emote.users} - ${( + (emote.users / totalUserEmoteUsage) * 100 || 0 + ).toFixed(3)}%` + botInfo + ); + } + + $.channel.send(lines, {split: true}).catch($.handler.bind($)); } - - // Mark the operation as ended. - const finishTime = Date.now(); - clearInterval(interval); - statusMessage.edit( - `Finished operation in ${moment - .duration(finishTime - startTime) - .humanize()} with ${$(warnings).pluralise( - 'inconsistenc', - 'ies', - 'y', - )}.`, - ); - $.log(`Finished operation in ${finishTime - startTime} ms.`); - $.channel.stopTyping(); - - // Display stats on emote usage. - // This can work outside the loop now that it's synchronous, and now it's clearer what code is meant to execute at the end. - let sortedEmoteIDs = Object.keys(stats).sort( - (a, b) => stats[b].users - stats[a].users, - ); - const lines: string[] = []; - let rank = 1; - - // It's better to send all the lines at once rather than paginate the data because it's quite a memory-intensive task to search all the messages in a server for it, and I wouldn't want to activate the command again just to get to another page. - for (const emoteID of sortedEmoteIDs) { - const emote = stats[emoteID]; - const botInfo = emote.bots > 0 ? ` (Bots: ${emote.bots})` : ''; - lines.push( - `\`#${rank++}\` ${emote.formatted} x ${emote.users} - ${( - (emote.users / totalUserEmoteUsage) * 100 || 0 - ).toFixed(3)}%` + botInfo, - ); - } - - $.channel.send(lines, { split: true }).catch($.handler.bind($)); - }, }); diff --git a/src/commands/utilities/desc.ts b/src/commands/utilities/desc.ts index 5b96306..f375a2c 100644 --- a/src/commands/utilities/desc.ts +++ b/src/commands/utilities/desc.ts @@ -1,29 +1,31 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; export default new Command({ description: "Renames current voice channel.", usage: "", async run($: CommonLibrary): Promise { const voiceChannel = $.message.member?.voice.channel; + if (!voiceChannel) - return $.channel.send('You are not in a voice channel.'); - if (!$.guild?.me?.hasPermission('MANAGE_CHANNELS')) - return $.channel.send( - 'I am lacking the required permissions to perform this action.', - ); + return $.channel.send("You are not in a voice channel."); + + if (!$.guild?.me?.hasPermission("MANAGE_CHANNELS")) + return $.channel.send( + "I am lacking the required permissions to perform this action." + ); + if ($.args.length === 0) - return $.channel.send( - 'Please provide a new voice channel name.', - ); + return $.channel.send("Please provide a new voice channel name."); + const changeVC = $.guild.channels.resolve(voiceChannel.id); $.channel - .send( - `Changed channel name from "${voiceChannel}" to "${$.args.join( - ' ', - )}".`, - ) - /// @ts-ignore - .then(changeVC?.setName($.args.join(' '))); + .send( + `Changed channel name from "${voiceChannel}" to "${$.args.join( + " " + )}".` + ) + /// @ts-ignore + .then(changeVC?.setName($.args.join(" "))); } -}) \ No newline at end of file +}); diff --git a/src/commands/utilities/emote.ts b/src/commands/utilities/emote.ts index c01129e..bc0291c 100644 --- a/src/commands/utilities/emote.ts +++ b/src/commands/utilities/emote.ts @@ -1,21 +1,21 @@ -import { MessageEmbed } from 'discord.js'; -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; +import {MessageEmbed} from "discord.js"; +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; export default new Command({ - description: 'Send the specified emote.', - run: 'Please provide a command name.', - any: new Command({ - description: 'The emote to send.', - usage: '', - async run($: CommonLibrary): Promise { - const search = $.args[0].toLowerCase(); - const emote = $.client.emojis.cache.find((emote) => - emote.name.toLowerCase().includes(search), - ); - if (!emote) return $.channel.send("That's not a valid emote name!"); - $.message.delete(); - $.channel.send(`${emote}`); - }, - }), + description: "Send the specified emote.", + run: "Please provide a command name.", + any: new Command({ + description: "The emote to send.", + usage: "", + async run($: CommonLibrary): Promise { + const search = $.args[0].toLowerCase(); + const emote = $.client.emojis.cache.find((emote) => + emote.name.toLowerCase().includes(search) + ); + if (!emote) return $.channel.send("That's not a valid emote name!"); + $.message.delete(); + $.channel.send(`${emote}`); + } + }) }); diff --git a/src/commands/utilities/lsemotes.ts b/src/commands/utilities/lsemotes.ts index 838d62e..a05106d 100644 --- a/src/commands/utilities/lsemotes.ts +++ b/src/commands/utilities/lsemotes.ts @@ -1,32 +1,36 @@ -import { MessageEmbed } from 'discord.js'; -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; +import {MessageEmbed} from "discord.js"; +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; export default new Command({ - description: "Lists all emotes the bot has in it's registry,", - endpoint: true, - async run($: CommonLibrary): Promise { - const nsfw: string | string[] = []; - const pages = $.client.emojis.cache - .filter((x) => !nsfw.includes(x.guild.id), this) - .array(); - const pagesSplit = $(pages).split(20); - $.log(pagesSplit); - var embed = new MessageEmbed().setTitle('**Emoji list!**').setColor('AQUA'); - let desc = ''; - for (const emote of pagesSplit[0]) { - desc += `${emote} | ${emote.name}\n`; - } - embed.setDescription(desc); - const msg = await $.channel.send({ embed }); + description: "Lists all emotes the bot has in it's registry,", + endpoint: true, + async run($: CommonLibrary): Promise { + const nsfw: string | string[] = []; + const pages = $.client.emojis.cache + .filter((x) => !nsfw.includes(x.guild.id), this) + .array(); + const pagesSplit = $(pages).split(20); + $.log(pagesSplit); + var embed = new MessageEmbed() + .setTitle("**Emoji list!**") + .setColor("AQUA"); + let desc = ""; - $.paginate(msg, $.author.id, pages.length, (page) => { - let desc = ''; - for (const emote of pagesSplit[page]) { - desc += `${emote} | ${emote.name}\n`; - } - embed.setDescription(desc); - msg.edit(embed); - }); - }, + for (const emote of pagesSplit[0]) { + desc += `${emote} | ${emote.name}\n`; + } + + embed.setDescription(desc); + const msg = await $.channel.send({embed}); + + $.paginate(msg, $.author.id, pages.length, (page) => { + let desc = ""; + for (const emote of pagesSplit[page]) { + desc += `${emote} | ${emote.name}\n`; + } + embed.setDescription(desc); + msg.edit(embed); + }); + } }); diff --git a/src/commands/utilities/react.ts b/src/commands/utilities/react.ts index d7135d4..f7f6080 100644 --- a/src/commands/utilities/react.ts +++ b/src/commands/utilities/react.ts @@ -1,68 +1,72 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; - -export default new Command({ - description: - 'Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.', - usage: 'react ()', - async run($: CommonLibrary): Promise { - let target; - let distance = 1; - - if ($.args.length >= 2) { - const last = $.args[$.args.length - 1]; - - if (/\d{17,19}/g.test(last)) { - try { - target = await $.channel.messages.fetch(last); - } catch { - return $.channel.send( - `No valid message found by the ID \`${last}\`!`, - ); - } - $.args.pop(); - } - // The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this. - else if (/^\d+$/g.test(last)) { - distance = parseInt(last); - - if (distance >= 0 && distance <= 99) $.args.pop(); - else return $.channel.send('Your distance must be between 0 and 99!'); - } - } - - if (!target) { - // Messages are ordered from latest to earliest. - // You also have to add 1 as well because fetchMessages includes your own message. - target = ( - await $.message.channel.messages.fetch({ - limit: distance + 1, - }) - ).last(); - } - - let anyEmoteIsValid = false; - - for (const search of $.args) { - const emoji = $.client.emojis.cache.find( - (emoji) => emoji.name === search, - ); - - if (emoji) { - // Call the delete function only once to avoid unnecessary errors. - if (!anyEmoteIsValid && distance !== 0) $.message.delete(); - anyEmoteIsValid = true; - const reaction = await target?.react(emoji); - - // This part is called with a promise because you don't want to wait 5 seconds between each reaction. - - setTimeout(() => { - /// @ts-ignore - reaction.users.remove($.client.user); - }, 5000); - } - } - - if (!anyEmoteIsValid && !$.message.deleted) $.message.react('❓'); - }, -}); +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; + +export default new Command({ + description: + "Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.", + usage: "react ()", + async run($: CommonLibrary): Promise { + let target; + let distance = 1; + + if ($.args.length >= 2) { + const last = $.args[$.args.length - 1]; + + if (/\d{17,19}/g.test(last)) { + try { + target = await $.channel.messages.fetch(last); + } catch { + return $.channel.send( + `No valid message found by the ID \`${last}\`!` + ); + } + + $.args.pop(); + } + // The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this. + else if (/^\d+$/g.test(last)) { + distance = parseInt(last); + + if (distance >= 0 && distance <= 99) $.args.pop(); + else + return $.channel.send( + "Your distance must be between 0 and 99!" + ); + } + } + + if (!target) { + // Messages are ordered from latest to earliest. + // You also have to add 1 as well because fetchMessages includes your own message. + target = ( + await $.message.channel.messages.fetch({ + limit: distance + 1 + }) + ).last(); + } + + let anyEmoteIsValid = false; + + for (const search of $.args) { + const emoji = $.client.emojis.cache.find( + (emoji) => emoji.name === search + ); + + if (emoji) { + // Call the delete function only once to avoid unnecessary errors. + if (!anyEmoteIsValid && distance !== 0) $.message.delete(); + anyEmoteIsValid = true; + const reaction = await target?.react(emoji); + + // This part is called with a promise because you don't want to wait 5 seconds between each reaction. + + setTimeout(() => { + /// @ts-ignore + reaction.users.remove($.client.user); + }, 5000); + } + } + + if (!anyEmoteIsValid && !$.message.deleted) $.message.react("❓"); + } +}); diff --git a/src/commands/utilities/say.ts b/src/commands/utilities/say.ts index 05695eb..f4d74f0 100644 --- a/src/commands/utilities/say.ts +++ b/src/commands/utilities/say.ts @@ -1,14 +1,14 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; - -export default new Command({ - description: "Repeats your message.", - usage: "", - run: "Please provide a message for me to say!", - any: new Command({ - description: "Message to repeat.", - async run($: CommonLibrary): Promise { - $.channel.send(`*${$.author} says:*\n${$.args.join(' ')}`); - } - }) -}) \ No newline at end of file +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; + +export default new Command({ + description: "Repeats your message.", + usage: "", + run: "Please provide a message for me to say!", + any: new Command({ + description: "Message to repeat.", + async run($: CommonLibrary): Promise { + $.channel.send(`*${$.author} says:*\n${$.args.join(" ")}`); + } + }) +}); diff --git a/src/commands/utilities/shorten.ts b/src/commands/utilities/shorten.ts index e58b528..26b8983 100644 --- a/src/commands/utilities/shorten.ts +++ b/src/commands/utilities/shorten.ts @@ -1,25 +1,25 @@ -import Command from '../../core/command'; -import { CommonLibrary } from '../../core/lib'; -import * as https from 'https'; - -export default new Command({ - description: 'Shortens a given URL.', - run: 'Please provide a URL.', - any: new Command({ - async run($: CommonLibrary): Promise { - https.get( - 'https://is.gd/create.php?format=simple&url=' + - encodeURIComponent($.args[0]), - function (res) { - var body = ''; - res.on('data', function (chunk) { - body += chunk; - }); - res.on('end', function () { - $.channel.send(`<${body}>`); - }); - }, - ); - }, - }), -}); +import Command from "../../core/command"; +import {CommonLibrary} from "../../core/lib"; +import * as https from "https"; + +export default new Command({ + description: "Shortens a given URL.", + run: "Please provide a URL.", + any: new Command({ + async run($: CommonLibrary): Promise { + https.get( + "https://is.gd/create.php?format=simple&url=" + + encodeURIComponent($.args[0]), + function (res) { + var body = ""; + res.on("data", function (chunk) { + body += chunk; + }); + res.on("end", function () { + $.channel.send(`<${body}>`); + }); + } + ); + } + }) +}); diff --git a/src/core/command.ts b/src/core/command.ts index bdf0b3b..e62e70a 100644 --- a/src/core/command.ts +++ b/src/core/command.ts @@ -1,155 +1,157 @@ -import $, { isType, parseVars, CommonLibrary } from './lib'; -import { Collection } from 'discord.js'; -import { generateHandler } from './storage'; -import { promises as ffs, existsSync, writeFile } from 'fs'; -import { PERMISSIONS } from './permissions'; -import { getPrefix } from '../core/structures'; +import $, {isType, parseVars, CommonLibrary} from "./lib"; +import {Collection} from "discord.js"; +import {generateHandler} from "./storage"; +import {promises as ffs, existsSync, writeFile} from "fs"; +import {PERMISSIONS} from "./permissions"; +import {getPrefix} from "../core/structures"; interface CommandOptions { - description?: string; - endpoint?: boolean; - usage?: string; - permission?: PERMISSIONS | null; - aliases?: string[]; - run?: (($: CommonLibrary) => Promise) | string; - subcommands?: { [key: string]: Command }; - user?: Command; - number?: Command; - any?: Command; + description?: string; + endpoint?: boolean; + usage?: string; + permission?: PERMISSIONS | null; + aliases?: string[]; + run?: (($: CommonLibrary) => Promise) | string; + subcommands?: {[key: string]: Command}; + user?: Command; + number?: Command; + any?: Command; } export enum TYPES { - SUBCOMMAND, - USER, - NUMBER, - ANY, - NONE, + SUBCOMMAND, + USER, + NUMBER, + ANY, + NONE } export default class Command { - public readonly description: string; - public readonly endpoint: boolean; - public readonly usage: string; - public readonly permission: PERMISSIONS | null; - public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. - public originalCommandName: string | null; // If the command is an alias, what's the original name? - public run: (($: CommonLibrary) => Promise) | string; - public readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. - public user: Command | null; - public number: Command | null; - public any: Command | null; - public static readonly TYPES = TYPES; - public static readonly PERMISSIONS = PERMISSIONS; + public readonly description: string; + public readonly endpoint: boolean; + public readonly usage: string; + public readonly permission: PERMISSIONS | null; + public readonly aliases: string[]; // This is to keep the array intact for parent Command instances to use. It'll also be used when loading top-level aliases. + public originalCommandName: string | null; // If the command is an alias, what's the original name? + public run: (($: CommonLibrary) => Promise) | string; + public readonly subcommands: Collection; // This is the final data structure you'll actually use to work with the commands the aliases point to. + public user: Command | null; + public number: Command | null; + public any: Command | null; + public static readonly TYPES = TYPES; + public static readonly PERMISSIONS = PERMISSIONS; - constructor(options?: CommandOptions) { - this.description = options?.description || 'No description.'; - this.endpoint = options?.endpoint || false; - this.usage = options?.usage || ''; - this.permission = options?.permission ?? null; - this.aliases = options?.aliases ?? []; - this.originalCommandName = null; - this.run = options?.run || 'No action was set on this command!'; - this.subcommands = new Collection(); // Populate this collection after setting subcommands. - this.user = options?.user || null; - this.number = options?.number || null; - this.any = options?.any || null; + constructor(options?: CommandOptions) { + this.description = options?.description || "No description."; + this.endpoint = options?.endpoint || false; + this.usage = options?.usage || ""; + this.permission = options?.permission ?? null; + this.aliases = options?.aliases ?? []; + this.originalCommandName = null; + this.run = options?.run || "No action was set on this command!"; + this.subcommands = new Collection(); // Populate this collection after setting subcommands. + this.user = options?.user || null; + this.number = options?.number || null; + this.any = options?.any || null; - if (options?.subcommands) { - const baseSubcommands = Object.keys(options.subcommands); + if (options?.subcommands) { + const baseSubcommands = Object.keys(options.subcommands); - // Loop once to set the base subcommands. - for (const name in options.subcommands) - this.subcommands.set(name, options.subcommands[name]); + // Loop once to set the base subcommands. + for (const name in options.subcommands) + this.subcommands.set(name, options.subcommands[name]); - // Then loop again to make aliases point to the base subcommands and warn if something's not right. - // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. - for (const name in options.subcommands) { - const subcmd = options.subcommands[name]; - subcmd.originalCommandName = name; - const aliases = subcmd.aliases; + // Then loop again to make aliases point to the base subcommands and warn if something's not right. + // This shouldn't be a problem because I'm hoping that JS stores these as references that point to the same object. + for (const name in options.subcommands) { + const subcmd = options.subcommands[name]; + subcmd.originalCommandName = name; + const aliases = subcmd.aliases; - for (const alias of aliases) { - if (baseSubcommands.includes(alias)) - $.warn( - `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)`, - ); - else if (this.subcommands.has(alias)) - $.warn( - `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)`, - ); - else this.subcommands.set(alias, subcmd); + for (const alias of aliases) { + if (baseSubcommands.includes(alias)) + $.warn( + `"${alias}" in subcommand "${name}" was attempted to be declared as an alias but it already exists in the base commands! (Look at the next "Loading Command" line to see which command is affected.)` + ); + else if (this.subcommands.has(alias)) + $.warn( + `Duplicate alias "${alias}" at subcommand "${name}"! (Look at the next "Loading Command" line to see which command is affected.)` + ); + else this.subcommands.set(alias, subcmd); + } + } } - } + + if (this.user && this.user.aliases.length > 0) + $.warn( + `There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` + ); + + if (this.number && this.number.aliases.length > 0) + $.warn( + `There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` + ); + + if (this.any && this.any.aliases.length > 0) + $.warn( + `There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)` + ); } - if (this.user && this.user.aliases.length > 0) - $.warn( - `There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`, - ); - if (this.number && this.number.aliases.length > 0) - $.warn( - `There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`, - ); - if (this.any && this.any.aliases.length > 0) - $.warn( - `There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`, - ); - } - - public execute($: CommonLibrary) { - if (isType(this.run, String)) { - $.channel.send( - parseVars( - this.run as string, - { - author: $.author.toString(), - prefix: getPrefix($.guild), - }, - '???', - ), - ); - } else (this.run as Function)($).catch($.handler.bind($)); - } - - public resolve(param: string): TYPES { - if (this.subcommands.has(param)) return TYPES.SUBCOMMAND; - // Any Discord ID format will automatically format to a user ID. - else if (this.user && /\d{17,19}/.test(param)) return TYPES.USER; - // Disallow infinity and allow for 0. - else if ( - this.number && - (Number(param) || param === '0') && - !param.includes('Infinity') - ) - return TYPES.NUMBER; - else if (this.any) return TYPES.ANY; - else return TYPES.NONE; - } - - public get(param: string): Command { - const type = this.resolve(param); - let command: Command; - - switch (type) { - case TYPES.SUBCOMMAND: - command = this.subcommands.get(param) as Command; - break; - case TYPES.USER: - command = this.user as Command; - break; - case TYPES.NUMBER: - command = this.number as Command; - break; - case TYPES.ANY: - command = this.any as Command; - break; - default: - command = this; - break; + public execute($: CommonLibrary) { + if (isType(this.run, String)) { + $.channel.send( + parseVars( + this.run as string, + { + author: $.author.toString(), + prefix: getPrefix($.guild) + }, + "???" + ) + ); + } else (this.run as Function)($).catch($.handler.bind($)); } - return command; - } + public resolve(param: string): TYPES { + if (this.subcommands.has(param)) return TYPES.SUBCOMMAND; + // Any Discord ID format will automatically format to a user ID. + else if (this.user && /\d{17,19}/.test(param)) return TYPES.USER; + // Disallow infinity and allow for 0. + else if ( + this.number && + (Number(param) || param === "0") && + !param.includes("Infinity") + ) + return TYPES.NUMBER; + else if (this.any) return TYPES.ANY; + else return TYPES.NONE; + } + + public get(param: string): Command { + const type = this.resolve(param); + let command: Command; + + switch (type) { + case TYPES.SUBCOMMAND: + command = this.subcommands.get(param) as Command; + break; + case TYPES.USER: + command = this.user as Command; + break; + case TYPES.NUMBER: + command = this.number as Command; + break; + case TYPES.ANY: + command = this.any as Command; + break; + default: + command = this; + break; + } + + return command; + } } let commands: Collection | null = null; @@ -158,96 +160,95 @@ export const aliases: Collection = new Collection(); // Top-leve /** Returns the cache of the commands if it exists and searches the directory if not. */ export async function loadCommands(): Promise> { - if (commands) return commands; + if (commands) return commands; - if (process.argv[2] === 'dev' && !existsSync('src/commands/test.ts')) - writeFile( - 'src/commands/test.ts', - template, - generateHandler( - '"test.ts" (testing/template command) successfully generated.', - ), - ); + if (process.argv[2] === "dev" && !existsSync("src/commands/test.ts")) + writeFile( + "src/commands/test.ts", + template, + generateHandler( + '"test.ts" (testing/template command) successfully generated.' + ) + ); - commands = new Collection(); - const dir = await ffs.opendir('dist/commands'); - const listMisc: string[] = []; - let selected; + commands = new Collection(); + const dir = await ffs.opendir("dist/commands"); + const listMisc: string[] = []; + let selected; - // There will only be one level of directory searching (per category). - while ((selected = await dir.read())) { - if (selected.isDirectory()) { - if (selected.name === 'subcommands') continue; + // There will only be one level of directory searching (per category). + while ((selected = await dir.read())) { + if (selected.isDirectory()) { + if (selected.name === "subcommands") continue; - const subdir = await ffs.opendir(`dist/commands/${selected.name}`); - const category = $(selected.name).toTitleCase(); - const list: string[] = []; - let cmd; + const subdir = await ffs.opendir(`dist/commands/${selected.name}`); + const category = $(selected.name).toTitleCase(); + const list: string[] = []; + let cmd; - while ((cmd = await subdir.read())) { - if (cmd.isDirectory()) { - if (cmd.name === 'subcommands') continue; - else - $.warn( - `You can't have multiple levels of directories! From: "dist/commands/${cmd.name}"`, - ); - } else loadCommand(cmd.name, list, selected.name); - } + while ((cmd = await subdir.read())) { + if (cmd.isDirectory()) { + if (cmd.name === "subcommands") continue; + else + $.warn( + `You can't have multiple levels of directories! From: "dist/commands/${cmd.name}"` + ); + } else loadCommand(cmd.name, list, selected.name); + } - subdir.close(); - categories.set(category, list); - } else loadCommand(selected.name, listMisc); - } + subdir.close(); + categories.set(category, list); + } else loadCommand(selected.name, listMisc); + } - dir.close(); - categories.set('Miscellaneous', listMisc); + dir.close(); + categories.set("Miscellaneous", listMisc); - return commands; + return commands; } async function loadCommand( - filename: string, - list: string[], - category?: string, + filename: string, + list: string[], + category?: string ) { - if (!commands) - return $.error( - `Function "loadCommand" was called without first initializing commands!`, + if (!commands) + return $.error( + `Function "loadCommand" was called without first initializing commands!` + ); + + const prefix = category ?? ""; + const header = filename.substring(0, filename.indexOf(".js")); + const command = (await import(`../commands/${prefix}/${header}`)) + .default as Command | undefined; + + if (!command) + return $.warn( + `Command "${header}" has no default export which is a Command instance!` + ); + + command.originalCommandName = header; + list.push(header); + + if (commands.has(header)) + $.warn( + `Command "${header}" already exists! Make sure to make each command uniquely identifiable across categories!` + ); + else commands.set(header, command); + + for (const alias of command.aliases) { + if (commands.has(alias)) + $.warn( + `Top-level alias "${alias}" from command "${header}" already exists either as a command or alias!` + ); + else commands.set(alias, command); + } + + $.log( + `Loading Command: ${header} (${ + category ? $(category).toTitleCase() : "Miscellaneous" + })` ); - - const prefix = category ?? ''; - const header = filename.substring(0, filename.indexOf('.js')); - const command = (await import(`../commands/${prefix}/${header}`)).default as - | Command - | undefined; - - if (!command) - return $.warn( - `Command "${header}" has no default export which is a Command instance!`, - ); - - command.originalCommandName = header; - list.push(header); - - if (commands.has(header)) - $.warn( - `Command "${header}" already exists! Make sure to make each command uniquely identifiable across categories!`, - ); - else commands.set(header, command); - - for (const alias of command.aliases) { - if (commands.has(alias)) - $.warn( - `Top-level alias "${alias}" from command "${header}" already exists either as a command or alias!`, - ); - else commands.set(alias, command); - } - - $.log( - `Loading Command: ${header} (${ - category ? $(category).toTitleCase() : 'Miscellaneous' - })`, - ); } // The template should be built with a reductionist mentality. diff --git a/src/core/event.ts b/src/core/event.ts index fac4bc8..e84d5a6 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -1,41 +1,41 @@ -import { Client, ClientEvents, Constants } from 'discord.js'; -import Storage from './storage'; -import $ from './lib'; +import {Client, ClientEvents, Constants} from "discord.js"; +import Storage from "./storage"; +import $ from "./lib"; interface EventOptions { - readonly on?: (...args: ClientEvents[K]) => void; - readonly once?: (...args: ClientEvents[K]) => void; + readonly on?: (...args: ClientEvents[K]) => void; + readonly once?: (...args: ClientEvents[K]) => void; } export default class Event { - private readonly on?: (...args: ClientEvents[K]) => void; - private readonly once?: (...args: ClientEvents[K]) => void; + private readonly on?: (...args: ClientEvents[K]) => void; + private readonly once?: (...args: ClientEvents[K]) => void; - constructor(options: EventOptions) { - this.on = options.on; - this.once = options.once; - } + constructor(options: EventOptions) { + this.on = options.on; + this.once = options.once; + } - // For this function, I'm going to assume that the event is used with the correct arguments and that the event tag is checked in "storage.ts". - public attach(client: Client, event: K) { - if (this.on) client.on(event, this.on); - if (this.once) client.once(event, this.once); - } + // For this function, I'm going to assume that the event is used with the correct arguments and that the event tag is checked in "storage.ts". + public attach(client: Client, event: K) { + if (this.on) client.on(event, this.on); + if (this.once) client.once(event, this.once); + } } export async function loadEvents(client: Client) { - for (const file of Storage.open('dist/events', (filename: string) => - filename.endsWith('.js'), - )) { - const header = file.substring(0, file.indexOf('.js')); - const event = (await import(`../events/${header}`)).default; + for (const file of Storage.open("dist/events", (filename: string) => + filename.endsWith(".js") + )) { + const header = file.substring(0, file.indexOf(".js")); + const event = (await import(`../events/${header}`)).default; - if ((Object.values(Constants.Events) as string[]).includes(header)) { - event.attach(client, header); - $.log(`Loading Event: ${header}`); - } else - $.warn( - `"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)`, - ); - } + if ((Object.values(Constants.Events) as string[]).includes(header)) { + event.attach(client, header); + $.log(`Loading Event: ${header}`); + } else + $.warn( + `"${header}" is not a valid event type! Did you misspell it? (Note: If you fixed the issue, delete "dist" because the compiler won't automatically delete any extra files.)` + ); + } } diff --git a/src/core/lib.ts b/src/core/lib.ts index b30bcb5..21ea8b8 100644 --- a/src/core/lib.ts +++ b/src/core/lib.ts @@ -1,506 +1,515 @@ -import { - GenericWrapper, - NumberWrapper, - StringWrapper, - ArrayWrapper, -} from './wrappers'; -import { - Client, - Message, - TextChannel, - DMChannel, - NewsChannel, - Guild, - User, - GuildMember, - Permissions, -} from 'discord.js'; -import chalk from 'chalk'; -import { get } from 'https'; -import FileManager from './storage'; -import { eventListeners } from '../events/messageReactionRemove'; -import { client } from '../index'; - -/** A type that describes what the library module does. */ -export interface CommonLibrary { - // Wrapper Object // - /** Wraps the value you enter with an object that provides extra functionality and provides common utility functions. */ - (value: number): NumberWrapper; - (value: string): StringWrapper; - (value: T[]): ArrayWrapper; - (value: T): GenericWrapper; - - // Common Library Functions // - /** .catch($.handler.bind($)) or .catch(error => $.handler(error)) */ - handler: (error: Error) => void; - log: (...args: any[]) => void; - warn: (...args: any[]) => void; - error: (...args: any[]) => void; - debug: (...args: any[]) => void; - ready: (...args: any[]) => void; - paginate: ( - message: Message, - senderID: string, - total: number, - callback: (page: number) => void, - duration?: number, - ) => void; - prompt: ( - message: Message, - senderID: string, - onConfirm: () => void, - duration?: number, - ) => void; - getMemberByUsername: ( - guild: Guild, - username: string, - ) => Promise; - callMemberByUsername: ( - message: Message, - username: string, - onSuccess: (member: GuildMember) => void, - ) => Promise; - - // Dynamic Properties // - args: any[]; - client: Client; - message: Message; - channel: TextChannel | DMChannel | NewsChannel; - guild: Guild | null; - author: User; - member: GuildMember | null; -} - -export default function $(value: number): NumberWrapper; -export default function $(value: string): StringWrapper; -export default function $(value: T[]): ArrayWrapper; -export default function $(value: T): GenericWrapper; -export default function $(value: any) { - if (isType(value, Number)) return new NumberWrapper(value); - else if (isType(value, String)) return new StringWrapper(value); - else if (isType(value, Array)) return new ArrayWrapper(value); - else return new GenericWrapper(value); -} - -// If you use promises, use this function to display the error in chat. -// Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). -// Case #2: $.channel.send("").catch($.handler.bind($)); --> Manually caught by the user. -$.handler = function (this: CommonLibrary, error: Error) { - if (this) - this.channel.send( - `There was an error while trying to execute that command!\`\`\`${ - error.stack ?? error - }\`\`\``, - ); - else - $.warn( - 'No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!', - ); - - $.error(error); -}; - -// Logs with different levels of verbosity. -export const logs: { [type: string]: string } = { - error: '', - warn: '', - info: '', - verbose: '', -}; - -let enabled = true; -export function setConsoleActivated(activated: boolean) { - enabled = activated; -} - -// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log. -// General Purpose Logger -$.log = (...args: any[]) => { - if (enabled) - console.log( - chalk.white.bgGray(formatTimestamp()), - chalk.black.bgWhite('INFO'), - ...args, - ); - const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(' ')}\n`; - logs.info += text; - logs.verbose += text; -}; -// "It'll still work, but you should really check up on this." -$.warn = (...args: any[]) => { - if (enabled) - console.warn( - chalk.white.bgGray(formatTimestamp()), - chalk.black.bgYellow('WARN'), - ...args, - ); - const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(' ')}\n`; - logs.warn += text; - logs.info += text; - logs.verbose += text; -}; -// Used for anything which prevents the program from actually running. -$.error = (...args: any[]) => { - if (enabled) - console.error( - chalk.white.bgGray(formatTimestamp()), - chalk.white.bgRed('ERROR'), - ...args, - ); - const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(' ')}\n`; - logs.error += text; - logs.warn += text; - logs.info += text; - logs.verbose += text; -}; -// Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose". -// $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> /::(.)() = -// Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests. -$.debug = (...args: any[]) => { - if (process.argv[2] === 'dev' && enabled) - console.debug( - chalk.white.bgGray(formatTimestamp()), - chalk.white.bgBlue('DEBUG'), - ...args, - ); - const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(' ')}\n`; - logs.verbose += text; -}; -// Used once at the start of the program when the bot loads. -$.ready = (...args: any[]) => { - if (enabled) - console.log( - chalk.white.bgGray(formatTimestamp()), - chalk.black.bgGreen('READY'), - ...args, - ); - const text = `[${formatUTCTimestamp()}] [READY] ${args.join(' ')}\n`; - logs.info += text; - logs.verbose += text; -}; - -export function formatTimestamp(now = new Date()) { - const year = now.getFullYear(); - const month = (now.getMonth() + 1).toString().padStart(2, '0'); - const day = now.getDate().toString().padStart(2, '0'); - const hour = now.getHours().toString().padStart(2, '0'); - const minute = now.getMinutes().toString().padStart(2, '0'); - const second = now.getSeconds().toString().padStart(2, '0'); - return `${year}-${month}-${day} ${hour}:${minute}:${second}`; -} - -export function formatUTCTimestamp(now = new Date()) { - const year = now.getUTCFullYear(); - const month = (now.getUTCMonth() + 1).toString().padStart(2, '0'); - const day = now.getUTCDate().toString().padStart(2, '0'); - const hour = now.getUTCHours().toString().padStart(2, '0'); - const minute = now.getUTCMinutes().toString().padStart(2, '0'); - const second = now.getUTCSeconds().toString().padStart(2, '0'); - return `${year}-${month}-${day} ${hour}:${minute}:${second}`; -} - -export function botHasPermission( - guild: Guild | null, - permission: number, -): boolean { - return !!( - client.user && - guild?.members.resolve(client.user)?.hasPermission(permission) - ); -} - -// Pagination function that allows for customization via a callback. -// Define your own pages outside the function because this only manages the actual turning of pages. -$.paginate = async ( - message: Message, - senderID: string, - total: number, - callback: (page: number) => void, - duration = 60000, -) => { - let page = 0; - const turn = (amount: number) => { - page += amount; - - if (page < 0) page += total; - else if (page >= total) page -= total; - - callback(page); - }; - const handle = (emote: string, reacterID: string) => { - switch (emote) { - case '⬅️': - turn(-1); - break; - case '➡️': - turn(1); - break; - } - }; - - // Listen for reactions and call the handler. - await message.react('⬅️'); - await message.react('➡️'); - eventListeners.set(message.id, handle); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. - // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. - const canDeleteEmotes = botHasPermission( - message.guild, - Permissions.FLAGS.MANAGE_MESSAGES, - ); - handle(reaction.emoji.name, user.id); - - if (canDeleteEmotes) reaction.users.remove(user); - } - return false; - }, - { time: duration }, - ); - // When time's up, remove the bot's own reactions. - eventListeners.delete(message.id); - message.reactions.cache.get('⬅️')?.users.remove(message.author); - message.reactions.cache.get('➡️')?.users.remove(message.author); -}; - -// Waits for the sender to either confirm an action or let it pass (and delete the message). -$.prompt = async ( - message: Message, - senderID: string, - onConfirm: () => void, - duration = 10000, -) => { - let isDeleted = false; - - message.react('✅'); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - if (reaction.emoji.name === '✅') onConfirm(); - isDeleted = true; - message.delete(); - } - - // CollectorFilter requires a boolean to be returned. - // My guess is that the return value of awaitReactions can be altered by making a boolean filter. - // However, because that's not my concern with this command, I don't have to worry about it. - // May as well just set it to false because I'm not concerned with collecting any reactions. - return false; - }, - { time: duration }, - ); - - if (!isDeleted) message.delete(); -}; - -$.getMemberByUsername = async (guild: Guild, username: string) => { - return ( - await guild.members.fetch({ - query: username, - limit: 1, - }) - ).first(); -}; - -/** Convenience function to handle false cases automatically. */ -$.callMemberByUsername = async ( - message: Message, - username: string, - onSuccess: (member: GuildMember) => void, -) => { - const guild = message.guild; - const send = message.channel.send; - - if (guild) { - const member = await $.getMemberByUsername(guild, username); - - if (member) onSuccess(member); - else send(`Couldn't find a user by the name of \`${username}\`!`); - } else send('You must execute this command in a server!'); -}; - -/** - * Splits a command by spaces while accounting for quotes which capture string arguments. - * - `\"` = `"` - * - `\\` = `\` - */ -export function parseArgs(line: string): string[] { - let result = []; - let selection = ''; - let inString = false; - let isEscaped = false; - - for (let c of line) { - if (isEscaped) { - if (['"', '\\'].includes(c)) selection += c; - else selection += '\\' + c; - - isEscaped = false; - } else if (c === '\\') isEscaped = true; - else if (c === '"') inString = !inString; - else if (c === ' ' && !inString) { - result.push(selection); - selection = ''; - } else selection += c; - } - - if (selection.length > 0) result.push(selection); - - return result; -} - -/** - * Allows you to store a template string with variable markers and parse it later. - * - Use `%name%` for variables - * - `%%` = `%` - * - If the invalid token is null/undefined, nothing is changed. - */ -export function parseVars( - line: string, - definitions: { [key: string]: string }, - invalid: string | null = '', -): string { - let result = ''; - let inVariable = false; - let token = ''; - - for (const c of line) { - if (c === '%') { - if (inVariable) { - if (token === '') result += '%'; - else { - if (token in definitions) result += definitions[token]; - else if (invalid === null) result += `%${token}%`; - else result += invalid; - - token = ''; - } - } - - inVariable = !inVariable; - } else if (inVariable) token += c; - else result += c; - } - - return result; -} - -export function isType(value: any, type: any): boolean { - if (value === undefined && type === undefined) return true; - else if (value === null && type === null) return true; - else - return value !== undefined && value !== null && value.constructor === type; -} - -/** - * Checks a value to see if it matches the fallback's type, otherwise returns the fallback. - * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). - * If at any point the value doesn't match the data structure provided, the fallback is returned. - * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! - */ -export function select( - value: any, - fallback: T, - type: Function, - isArray = false, -): T { - if (isArray && isType(value, Array)) { - for (let item of value) if (!isType(item, type)) return fallback; - return value; - } else { - if (isType(value, type)) return value; - else return fallback; - } -} - -export function clean(text: any) { - if (typeof text === 'string') - return text - .replace(/`/g, '`' + String.fromCharCode(8203)) - .replace(/@/g, '@' + String.fromCharCode(8203)); - else return text; -} - -export function trimArray(arr: any, maxLen = 10) { - if (arr.length > maxLen) { - const len = arr.length - maxLen; - arr = arr.slice(0, maxLen); - arr.push(`${len} more...`); - } - return arr; -} - -export function formatBytes(bytes: any) { - if (bytes === 0) return '0 Bytes'; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; -} - -export function getContent(url: any) { - return new Promise((resolve, reject) => { - get( - url, - (res: { - resume?: any; - setEncoding?: any; - on?: any; - statusCode?: any; - }) => { - const { statusCode } = res; - if (statusCode !== 200) { - res.resume(); - reject(`Request failed. Status code: ${statusCode}`); - } - res.setEncoding('utf8'); - let rawData = ''; - res.on('data', (chunk: string) => { - rawData += chunk; - }); - res.on('end', () => { - try { - const parsedData = JSON.parse(rawData); - resolve(parsedData); - } catch (e) { - reject(`Error: ${e.message}`); - } - }); - }, - ).on('error', (err: { message: any }) => { - reject(`Error: ${err.message}`); - }); - }); -} - -export interface GenericJSON { - [key: string]: any; -} - -export abstract class GenericStructure { - private __meta__ = 'generic'; - - constructor(tag?: string) { - this.__meta__ = tag || this.__meta__; - } - - public save(asynchronous = true) { - const tag = this.__meta__; - /// @ts-ignore - delete this.__meta__; - FileManager.write(tag, this, asynchronous); - this.__meta__ = tag; - } -} - -// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). -// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). -export const Random = { - num: (min: number, max: number) => Math.random() * (max - min) + min, - int: (min: number, max: number) => Math.floor(Random.num(min, max)), - chance: (decimal: number) => Math.random() < decimal, - sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), - deviation: (base: number, deviation: number) => - Random.num(base - deviation, base + deviation), -}; +import { + GenericWrapper, + NumberWrapper, + StringWrapper, + ArrayWrapper +} from "./wrappers"; +import { + Client, + Message, + TextChannel, + DMChannel, + NewsChannel, + Guild, + User, + GuildMember, + Permissions +} from "discord.js"; +import chalk from "chalk"; +import {get} from "https"; +import FileManager from "./storage"; +import {eventListeners} from "../events/messageReactionRemove"; +import {client} from "../index"; + +/** A type that describes what the library module does. */ +export interface CommonLibrary { + // Wrapper Object // + /** Wraps the value you enter with an object that provides extra functionality and provides common utility functions. */ + (value: number): NumberWrapper; + (value: string): StringWrapper; + (value: T[]): ArrayWrapper; + (value: T): GenericWrapper; + + // Common Library Functions // + /** .catch($.handler.bind($)) or .catch(error => $.handler(error)) */ + handler: (error: Error) => void; + log: (...args: any[]) => void; + warn: (...args: any[]) => void; + error: (...args: any[]) => void; + debug: (...args: any[]) => void; + ready: (...args: any[]) => void; + paginate: ( + message: Message, + senderID: string, + total: number, + callback: (page: number) => void, + duration?: number + ) => void; + prompt: ( + message: Message, + senderID: string, + onConfirm: () => void, + duration?: number + ) => void; + getMemberByUsername: ( + guild: Guild, + username: string + ) => Promise; + callMemberByUsername: ( + message: Message, + username: string, + onSuccess: (member: GuildMember) => void + ) => Promise; + + // Dynamic Properties // + args: any[]; + client: Client; + message: Message; + channel: TextChannel | DMChannel | NewsChannel; + guild: Guild | null; + author: User; + member: GuildMember | null; +} + +export default function $(value: number): NumberWrapper; +export default function $(value: string): StringWrapper; +export default function $(value: T[]): ArrayWrapper; +export default function $(value: T): GenericWrapper; +export default function $(value: any) { + if (isType(value, Number)) return new NumberWrapper(value); + else if (isType(value, String)) return new StringWrapper(value); + else if (isType(value, Array)) return new ArrayWrapper(value); + else return new GenericWrapper(value); +} + +// If you use promises, use this function to display the error in chat. +// Case #1: await $.channel.send(""); --> Automatically caught by Command.execute(). +// Case #2: $.channel.send("").catch($.handler.bind($)); --> Manually caught by the user. +$.handler = function (this: CommonLibrary, error: Error) { + if (this) + this.channel.send( + `There was an error while trying to execute that command!\`\`\`${ + error.stack ?? error + }\`\`\`` + ); + else + $.warn( + "No context was attached to $.handler! Make sure to use .catch($.handler.bind($)) or .catch(error => $.handler(error)) instead!" + ); + + $.error(error); +}; + +// Logs with different levels of verbosity. +export const logs: {[type: string]: string} = { + error: "", + warn: "", + info: "", + verbose: "" +}; + +let enabled = true; + +export function setConsoleActivated(activated: boolean) { + enabled = activated; +} + +// The custom console. In order of verbosity, error, warn, log, and debug. Ready is a variation of log. +// General Purpose Logger +$.log = (...args: any[]) => { + if (enabled) + console.log( + chalk.white.bgGray(formatTimestamp()), + chalk.black.bgWhite("INFO"), + ...args + ); + + const text = `[${formatUTCTimestamp()}] [INFO] ${args.join(" ")}\n`; + logs.info += text; + logs.verbose += text; +}; +// "It'll still work, but you should really check up on this." +$.warn = (...args: any[]) => { + if (enabled) + console.warn( + chalk.white.bgGray(formatTimestamp()), + chalk.black.bgYellow("WARN"), + ...args + ); + + const text = `[${formatUTCTimestamp()}] [WARN] ${args.join(" ")}\n`; + logs.warn += text; + logs.info += text; + logs.verbose += text; +}; +// Used for anything which prevents the program from actually running. +$.error = (...args: any[]) => { + if (enabled) + console.error( + chalk.white.bgGray(formatTimestamp()), + chalk.white.bgRed("ERROR"), + ...args + ); + + const text = `[${formatUTCTimestamp()}] [ERROR] ${args.join(" ")}\n`; + logs.error += text; + logs.warn += text; + logs.info += text; + logs.verbose += text; +}; +// Be as verbose as possible. If anything might help when debugging an error, then include it. This only shows in your console if you run this with "dev", but you can still get it from "logs.verbose". +// $.debug(`core/lib::parseArgs("testing \"in progress\"") = ["testing", "in progress"]`) --> /::(.)() = +// Would probably be more suited for debugging program logic rather than function logic, which can be checked using unit tests. +$.debug = (...args: any[]) => { + if (process.argv[2] === "dev" && enabled) + console.debug( + chalk.white.bgGray(formatTimestamp()), + chalk.white.bgBlue("DEBUG"), + ...args + ); + + const text = `[${formatUTCTimestamp()}] [DEBUG] ${args.join(" ")}\n`; + logs.verbose += text; +}; +// Used once at the start of the program when the bot loads. +$.ready = (...args: any[]) => { + if (enabled) + console.log( + chalk.white.bgGray(formatTimestamp()), + chalk.black.bgGreen("READY"), + ...args + ); + + const text = `[${formatUTCTimestamp()}] [READY] ${args.join(" ")}\n`; + logs.info += text; + logs.verbose += text; +}; + +export function formatTimestamp(now = new Date()) { + const year = now.getFullYear(); + const month = (now.getMonth() + 1).toString().padStart(2, "0"); + const day = now.getDate().toString().padStart(2, "0"); + const hour = now.getHours().toString().padStart(2, "0"); + const minute = now.getMinutes().toString().padStart(2, "0"); + const second = now.getSeconds().toString().padStart(2, "0"); + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; +} + +export function formatUTCTimestamp(now = new Date()) { + const year = now.getUTCFullYear(); + const month = (now.getUTCMonth() + 1).toString().padStart(2, "0"); + const day = now.getUTCDate().toString().padStart(2, "0"); + const hour = now.getUTCHours().toString().padStart(2, "0"); + const minute = now.getUTCMinutes().toString().padStart(2, "0"); + const second = now.getUTCSeconds().toString().padStart(2, "0"); + return `${year}-${month}-${day} ${hour}:${minute}:${second}`; +} + +export function botHasPermission( + guild: Guild | null, + permission: number +): boolean { + return !!( + client.user && + guild?.members.resolve(client.user)?.hasPermission(permission) + ); +} + +// Pagination function that allows for customization via a callback. +// Define your own pages outside the function because this only manages the actual turning of pages. +$.paginate = async ( + message: Message, + senderID: string, + total: number, + callback: (page: number) => void, + duration = 60000 +) => { + let page = 0; + const turn = (amount: number) => { + page += amount; + + if (page < 0) page += total; + else if (page >= total) page -= total; + + callback(page); + }; + const handle = (emote: string, reacterID: string) => { + switch (emote) { + case "⬅️": + turn(-1); + break; + case "➡️": + turn(1); + break; + } + }; + + // Listen for reactions and call the handler. + await message.react("⬅️"); + await message.react("➡️"); + eventListeners.set(message.id, handle); + await message.awaitReactions( + (reaction, user) => { + if (user.id === senderID) { + // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. + // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. + const canDeleteEmotes = botHasPermission( + message.guild, + Permissions.FLAGS.MANAGE_MESSAGES + ); + handle(reaction.emoji.name, user.id); + + if (canDeleteEmotes) reaction.users.remove(user); + } + + return false; + }, + {time: duration} + ); + // When time's up, remove the bot's own reactions. + eventListeners.delete(message.id); + message.reactions.cache.get("⬅️")?.users.remove(message.author); + message.reactions.cache.get("➡️")?.users.remove(message.author); +}; + +// Waits for the sender to either confirm an action or let it pass (and delete the message). +$.prompt = async ( + message: Message, + senderID: string, + onConfirm: () => void, + duration = 10000 +) => { + let isDeleted = false; + + message.react("✅"); + await message.awaitReactions( + (reaction, user) => { + if (user.id === senderID) { + if (reaction.emoji.name === "✅") onConfirm(); + isDeleted = true; + message.delete(); + } + + // CollectorFilter requires a boolean to be returned. + // My guess is that the return value of awaitReactions can be altered by making a boolean filter. + // However, because that's not my concern with this command, I don't have to worry about it. + // May as well just set it to false because I'm not concerned with collecting any reactions. + return false; + }, + {time: duration} + ); + + if (!isDeleted) message.delete(); +}; + +$.getMemberByUsername = async (guild: Guild, username: string) => { + return ( + await guild.members.fetch({ + query: username, + limit: 1 + }) + ).first(); +}; + +/** Convenience function to handle false cases automatically. */ +$.callMemberByUsername = async ( + message: Message, + username: string, + onSuccess: (member: GuildMember) => void +) => { + const guild = message.guild; + const send = message.channel.send; + + if (guild) { + const member = await $.getMemberByUsername(guild, username); + + if (member) onSuccess(member); + else send(`Couldn't find a user by the name of \`${username}\`!`); + } else send("You must execute this command in a server!"); +}; + +/** + * Splits a command by spaces while accounting for quotes which capture string arguments. + * - `\"` = `"` + * - `\\` = `\` + */ +export function parseArgs(line: string): string[] { + let result = []; + let selection = ""; + let inString = false; + let isEscaped = false; + + for (let c of line) { + if (isEscaped) { + if (['"', "\\"].includes(c)) selection += c; + else selection += "\\" + c; + + isEscaped = false; + } else if (c === "\\") isEscaped = true; + else if (c === '"') inString = !inString; + else if (c === " " && !inString) { + result.push(selection); + selection = ""; + } else selection += c; + } + + if (selection.length > 0) result.push(selection); + + return result; +} + +/** + * Allows you to store a template string with variable markers and parse it later. + * - Use `%name%` for variables + * - `%%` = `%` + * - If the invalid token is null/undefined, nothing is changed. + */ +export function parseVars( + line: string, + definitions: {[key: string]: string}, + invalid: string | null = "" +): string { + let result = ""; + let inVariable = false; + let token = ""; + + for (const c of line) { + if (c === "%") { + if (inVariable) { + if (token === "") result += "%"; + else { + if (token in definitions) result += definitions[token]; + else if (invalid === null) result += `%${token}%`; + else result += invalid; + + token = ""; + } + } + + inVariable = !inVariable; + } else if (inVariable) token += c; + else result += c; + } + + return result; +} + +export function isType(value: any, type: any): boolean { + if (value === undefined && type === undefined) return true; + else if (value === null && type === null) return true; + else + return ( + value !== undefined && value !== null && value.constructor === type + ); +} + +/** + * Checks a value to see if it matches the fallback's type, otherwise returns the fallback. + * For the purposes of the templates system, this function will only check array types, objects should be checked under their own type (as you'd do anyway with something like a User object). + * If at any point the value doesn't match the data structure provided, the fallback is returned. + * Warning: Type checking is based on the fallback's type. Be sure that the "type" parameter is accurate to this! + */ +export function select( + value: any, + fallback: T, + type: Function, + isArray = false +): T { + if (isArray && isType(value, Array)) { + for (let item of value) if (!isType(item, type)) return fallback; + return value; + } else { + if (isType(value, type)) return value; + else return fallback; + } +} + +export function clean(text: any) { + if (typeof text === "string") + return text + .replace(/`/g, "`" + String.fromCharCode(8203)) + .replace(/@/g, "@" + String.fromCharCode(8203)); + else return text; +} + +export function trimArray(arr: any, maxLen = 10) { + if (arr.length > maxLen) { + const len = arr.length - maxLen; + arr = arr.slice(0, maxLen); + arr.push(`${len} more...`); + } + return arr; +} + +export function formatBytes(bytes: any) { + if (bytes === 0) return "0 Bytes"; + const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + return `${parseFloat((bytes / Math.pow(1024, i)).toFixed(2))} ${sizes[i]}`; +} + +export function getContent(url: any) { + return new Promise((resolve, reject) => { + get( + url, + (res: { + resume?: any; + setEncoding?: any; + on?: any; + statusCode?: any; + }) => { + const {statusCode} = res; + if (statusCode !== 200) { + res.resume(); + reject(`Request failed. Status code: ${statusCode}`); + } + res.setEncoding("utf8"); + let rawData = ""; + res.on("data", (chunk: string) => { + rawData += chunk; + }); + res.on("end", () => { + try { + const parsedData = JSON.parse(rawData); + resolve(parsedData); + } catch (e) { + reject(`Error: ${e.message}`); + } + }); + } + ).on("error", (err: {message: any}) => { + reject(`Error: ${err.message}`); + }); + }); +} + +export interface GenericJSON { + [key: string]: any; +} + +export abstract class GenericStructure { + private __meta__ = "generic"; + + constructor(tag?: string) { + this.__meta__ = tag || this.__meta__; + } + + public save(asynchronous = true) { + const tag = this.__meta__; + /// @ts-ignore + delete this.__meta__; + FileManager.write(tag, this, asynchronous); + this.__meta__ = tag; + } +} + +// A 50% chance would be "Math.random() < 0.5" because Math.random() can be [0, 1), so to make two equal ranges, you'd need [0, 0.5)U[0.5, 1). +// Similar logic would follow for any other percentage. Math.random() < 1 is always true (100% chance) and Math.random() < 0 is always false (0% chance). +export const Random = { + num: (min: number, max: number) => Math.random() * (max - min) + min, + int: (min: number, max: number) => Math.floor(Random.num(min, max)), + chance: (decimal: number) => Math.random() < decimal, + sign: (number = 1) => number * (Random.chance(0.5) ? -1 : 1), + deviation: (base: number, deviation: number) => + Random.num(base - deviation, base + deviation) +}; diff --git a/src/core/permissions.ts b/src/core/permissions.ts index d190526..188ca9a 100644 --- a/src/core/permissions.ts +++ b/src/core/permissions.ts @@ -1,80 +1,81 @@ -import { GuildMember, Permissions } from 'discord.js'; -import { Config } from './structures'; -import $ from './lib'; +import {GuildMember, Permissions} from "discord.js"; +import {Config} from "./structures"; +import $ from "./lib"; export enum PERMISSIONS { - NONE, - MOD, - ADMIN, - OWNER, - BOT_SUPPORT, - BOT_ADMIN, - BOT_OWNER, + NONE, + MOD, + ADMIN, + OWNER, + BOT_SUPPORT, + BOT_ADMIN, + BOT_OWNER } + export const PermissionNames = [ - 'User', - 'Moderator', - 'Administrator', - 'Server Owner', - 'Bot Support', - 'Bot Admin', - 'Bot Owner', + "User", + "Moderator", + "Administrator", + "Server Owner", + "Bot Support", + "Bot Admin", + "Bot Owner" ]; // Here is where you enter in the functions that check for permissions. const PermissionChecker: ((member: GuildMember) => boolean)[] = [ - // NONE // - () => true, + // NONE // + () => true, - // MOD // - (member) => - member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || - member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || - member.hasPermission(Permissions.FLAGS.KICK_MEMBERS) || - member.hasPermission(Permissions.FLAGS.BAN_MEMBERS), + // MOD // + (member) => + member.hasPermission(Permissions.FLAGS.MANAGE_ROLES) || + member.hasPermission(Permissions.FLAGS.MANAGE_MESSAGES) || + member.hasPermission(Permissions.FLAGS.KICK_MEMBERS) || + member.hasPermission(Permissions.FLAGS.BAN_MEMBERS), - // ADMIN // - (member) => member.hasPermission(Permissions.FLAGS.ADMINISTRATOR), + // ADMIN // + (member) => member.hasPermission(Permissions.FLAGS.ADMINISTRATOR), - // OWNER // - (member) => member.guild.ownerID === member.id, + // OWNER // + (member) => member.guild.ownerID === member.id, - // BOT_SUPPORT // - (member) => Config.support.includes(member.id), + // BOT_SUPPORT // + (member) => Config.support.includes(member.id), - // BOT_ADMIN // - (member) => Config.admins.includes(member.id), + // BOT_ADMIN // + (member) => Config.admins.includes(member.id), - // BOT_OWNER // - (member) => Config.owner === member.id, + // BOT_OWNER // + (member) => Config.owner === member.id ]; // After checking the lengths of these three objects, use this as the length for consistency. const length = Object.keys(PERMISSIONS).length / 2; export function hasPermission( - member: GuildMember, - permission: PERMISSIONS, + member: GuildMember, + permission: PERMISSIONS ): boolean { - for (let i = length - 1; i >= permission; i--) - if (PermissionChecker[i](member)) return true; - return false; + for (let i = length - 1; i >= permission; i--) + if (PermissionChecker[i](member)) return true; + return false; } export function getPermissionLevel(member: GuildMember): number { - for (let i = length - 1; i >= 0; i--) - if (PermissionChecker[i](member)) return i; - return 0; + for (let i = length - 1; i >= 0; i--) + if (PermissionChecker[i](member)) return i; + return 0; } // Length Checking (() => { - const lenNames = PermissionNames.length; - const lenChecker = PermissionChecker.length; + const lenNames = PermissionNames.length; + const lenChecker = PermissionChecker.length; - // By transitive property, lenNames and lenChecker have to be equal to each other as well. - if (length !== lenNames || length !== lenChecker) - $.error( - `Permission object lengths aren't equal! Enum Length (${length}), Names Length (${lenNames}), and Functions Length (${lenChecker}). This WILL cause problems!`, - ); + // By transitive property, lenNames and lenChecker have to be equal to each other as well. + if (length !== lenNames || length !== lenChecker) + $.error( + `Permission object lengths aren't equal! Enum Length (${length}), Names Length (${lenNames}), and Functions Length (${lenChecker}). This WILL cause problems!` + ); })(); diff --git a/src/core/storage.ts b/src/core/storage.ts index 8048993..db3bc21 100644 --- a/src/core/storage.ts +++ b/src/core/storage.ts @@ -1,85 +1,87 @@ -import fs from 'fs'; -import $ from './lib'; +import fs from "fs"; +import $ from "./lib"; const Storage = { - read(header: string): object { - this.open('data'); - const path = `data/${header}.json`; - let data = {}; + read(header: string): object { + this.open("data"); + const path = `data/${header}.json`; + let data = {}; - if (fs.existsSync(path)) { - const file = fs.readFileSync(path, 'utf-8'); + if (fs.existsSync(path)) { + const file = fs.readFileSync(path, "utf-8"); - try { - data = JSON.parse(file); - } catch (error) { - if (process.argv[2] !== 'dev') { - $.warn( - `Malformed JSON data (header: ${header}), backing it up.`, - file, - ); - fs.writeFile( - `${path}.backup`, - file, - generateHandler( - `Backup file of "${header}" successfully written as ${file}.`, - ), - ); + try { + data = JSON.parse(file); + } catch (error) { + if (process.argv[2] !== "dev") { + $.warn( + `Malformed JSON data (header: ${header}), backing it up.`, + file + ); + fs.writeFile( + `${path}.backup`, + file, + generateHandler( + `Backup file of "${header}" successfully written as ${file}.` + ) + ); + } + } } - } + + return data; + }, + write(header: string, data: object, asynchronous = true) { + this.open("data"); + const path = `data/${header}.json`; + + if (process.argv[2] === "dev" || header === "config") { + const result = JSON.stringify(data, null, "\t"); + + if (asynchronous) + fs.writeFile( + path, + result, + generateHandler( + `"${header}" sucessfully spaced and written.` + ) + ); + else fs.writeFileSync(path, result); + } else { + const result = JSON.stringify(data); + + if (asynchronous) + fs.writeFile( + path, + result, + generateHandler(`"${header}" sucessfully written.`) + ); + else fs.writeFileSync(path, result); + } + }, + open( + path: string, + filter?: (value: string, index: number, array: string[]) => unknown + ): string[] { + if (!fs.existsSync(path)) fs.mkdirSync(path); + + let directory = fs.readdirSync(path); + + if (filter) directory = directory.filter(filter); + + return directory; + }, + close(path: string) { + if (fs.existsSync(path) && fs.readdirSync(path).length === 0) + fs.rmdir(path, generateHandler(`"${path}" successfully closed.`)); } - - return data; - }, - write(header: string, data: object, asynchronous = true) { - this.open('data'); - const path = `data/${header}.json`; - - if (process.argv[2] === 'dev' || header === 'config') { - const result = JSON.stringify(data, null, '\t'); - - if (asynchronous) - fs.writeFile( - path, - result, - generateHandler(`"${header}" sucessfully spaced and written.`), - ); - else fs.writeFileSync(path, result); - } else { - const result = JSON.stringify(data); - - if (asynchronous) - fs.writeFile( - path, - result, - generateHandler(`"${header}" sucessfully written.`), - ); - else fs.writeFileSync(path, result); - } - }, - open( - path: string, - filter?: (value: string, index: number, array: string[]) => unknown, - ): string[] { - if (!fs.existsSync(path)) fs.mkdirSync(path); - - let directory = fs.readdirSync(path); - - if (filter) directory = directory.filter(filter); - - return directory; - }, - close(path: string) { - if (fs.existsSync(path) && fs.readdirSync(path).length === 0) - fs.rmdir(path, generateHandler(`"${path}" successfully closed.`)); - }, }; export function generateHandler(message: string) { - return (error: Error | null) => { - if (error) $.error(error); - else $.debug(message); - }; + return (error: Error | null) => { + if (error) $.error(error); + else $.debug(message); + }; } export default Storage; diff --git a/src/core/structures.ts b/src/core/structures.ts index e958714..243ce34 100644 --- a/src/core/structures.ts +++ b/src/core/structures.ts @@ -1,112 +1,114 @@ -import FileManager from './storage'; -import $, { select, GenericJSON, GenericStructure } from './lib'; -import { watch } from 'fs'; -import { Guild as DiscordGuild } from 'discord.js'; +import FileManager from "./storage"; +import $, {select, GenericJSON, GenericStructure} from "./lib"; +import {watch} from "fs"; +import {Guild as DiscordGuild} from "discord.js"; class ConfigStructure extends GenericStructure { - public token: string; - public prefix: string; - public owner: string; - public admins: string[]; - public support: string[]; + public token: string; + public prefix: string; + public owner: string; + public admins: string[]; + public support: string[]; - constructor(data: GenericJSON) { - super('config'); - this.token = select(data.token, '', String); - this.prefix = select(data.prefix, '$', String); - this.owner = select(data.owner, '', String); - this.admins = select(data.admins, [], String, true); - this.support = select(data.support, [], String, true); - } + constructor(data: GenericJSON) { + super("config"); + this.token = select(data.token, "", String); + this.prefix = select(data.prefix, "$", String); + this.owner = select(data.owner, "", String); + this.admins = select(data.admins, [], String, true); + this.support = select(data.support, [], String, true); + } } class User { - public money: number; - public lastReceived: number; + public money: number; + public lastReceived: number; - constructor(data?: GenericJSON) { - this.money = select(data?.money, 0, Number); - this.lastReceived = select(data?.lastReceived, -1, Number); - } + constructor(data?: GenericJSON) { + this.money = select(data?.money, 0, Number); + this.lastReceived = select(data?.lastReceived, -1, Number); + } } class Guild { - public prefix: string | null; + public prefix: string | null; - constructor(data?: GenericJSON) { - this.prefix = select(data?.prefix, null, String); - } + constructor(data?: GenericJSON) { + this.prefix = select(data?.prefix, null, String); + } } class StorageStructure extends GenericStructure { - public users: { [id: string]: User }; - public guilds: { [id: string]: Guild }; + public users: {[id: string]: User}; + public guilds: {[id: string]: Guild}; - constructor(data: GenericJSON) { - super('storage'); - this.users = {}; - this.guilds = {}; + constructor(data: GenericJSON) { + super("storage"); + this.users = {}; + this.guilds = {}; - for (let id in data.users) - if (/\d{17,19}/g.test(id)) this.users[id] = new User(data.users[id]); + for (let id in data.users) + if (/\d{17,19}/g.test(id)) + this.users[id] = new User(data.users[id]); - for (let id in data.guilds) - if (/\d{17,19}/g.test(id)) this.guilds[id] = new Guild(data.guilds[id]); - } - - /** Gets a user's profile if they exist and generate one if not. */ - public getUser(id: string): User { - if (!/\d{17,19}/g.test(id)) - $.warn( - `"${id}" is not a valid user ID! It will be erased when the data loads again.`, - ); - - if (id in this.users) return this.users[id]; - else { - const user = new User(); - this.users[id] = user; - return user; + for (let id in data.guilds) + if (/\d{17,19}/g.test(id)) + this.guilds[id] = new Guild(data.guilds[id]); } - } - /** Gets a guild's settings if they exist and generate one if not. */ - public getGuild(id: string): Guild { - if (!/\d{17,19}/g.test(id)) - $.warn( - `"${id}" is not a valid guild ID! It will be erased when the data loads again.`, - ); + /** Gets a user's profile if they exist and generate one if not. */ + public getUser(id: string): User { + if (!/\d{17,19}/g.test(id)) + $.warn( + `"${id}" is not a valid user ID! It will be erased when the data loads again.` + ); - if (id in this.guilds) return this.guilds[id]; - else { - const guild = new Guild(); - this.guilds[id] = guild; - return guild; + if (id in this.users) return this.users[id]; + else { + const user = new User(); + this.users[id] = user; + return user; + } + } + + /** Gets a guild's settings if they exist and generate one if not. */ + public getGuild(id: string): Guild { + if (!/\d{17,19}/g.test(id)) + $.warn( + `"${id}" is not a valid guild ID! It will be erased when the data loads again.` + ); + + if (id in this.guilds) return this.guilds[id]; + else { + const guild = new Guild(); + this.guilds[id] = guild; + return guild; + } } - } } // Exports instances. Don't worry, importing it from different files will load the same instance. -export let Config = new ConfigStructure(FileManager.read('config')); -export let Storage = new StorageStructure(FileManager.read('storage')); +export let Config = new ConfigStructure(FileManager.read("config")); +export let Storage = new StorageStructure(FileManager.read("storage")); // This part will allow the user to manually edit any JSON files they want while the program is running which'll update the program's cache. // However, fs.watch is a buggy mess that should be avoided in production. While it helps test out stuff for development, it's not a good idea to have it running outside of development as it causes all sorts of issues. -if (process.argv[2] === 'dev') { - watch('data', (event, filename) => { - $.debug('File Watcher:', event, filename); - const header = filename.substring(0, filename.indexOf('.json')); +if (process.argv[2] === "dev") { + watch("data", (event, filename) => { + $.debug("File Watcher:", event, filename); + const header = filename.substring(0, filename.indexOf(".json")); - switch (header) { - case 'config': - Config = new ConfigStructure(FileManager.read('config')); - break; - case 'storage': - Storage = new StorageStructure(FileManager.read('storage')); - break; - } - }); + switch (header) { + case "config": + Config = new ConfigStructure(FileManager.read("config")); + break; + case "storage": + Storage = new StorageStructure(FileManager.read("storage")); + break; + } + }); } export function getPrefix(guild: DiscordGuild | null): string { - return Storage.getGuild(guild?.id || 'N/A').prefix ?? Config.prefix; + return Storage.getGuild(guild?.id || "N/A").prefix ?? Config.prefix; } diff --git a/src/core/wrappers.ts b/src/core/wrappers.ts index 96df202..9c103a4 100644 --- a/src/core/wrappers.ts +++ b/src/core/wrappers.ts @@ -1,87 +1,94 @@ export class GenericWrapper { - protected readonly value: T; + protected readonly value: T; - public constructor(value: T) { - this.value = value; - } + public constructor(value: T) { + this.value = value; + } } export class NumberWrapper extends GenericWrapper { - /** - * Pluralises a word and chooses a suffix attached to the root provided. - * - pluralise("credit", "s") = credit/credits - * - pluralise("part", "ies", "y") = party/parties - * - pluralise("sheep") = sheep - */ - public pluralise( - word: string, - plural = '', - singular = '', - excludeNumber = false, - ): string { - let result = excludeNumber ? '' : `${this.value} `; + /** + * Pluralises a word and chooses a suffix attached to the root provided. + * - pluralise("credit", "s") = credit/credits + * - pluralise("part", "ies", "y") = party/parties + * - pluralise("sheep") = sheep + */ + public pluralise( + word: string, + plural = "", + singular = "", + excludeNumber = false + ): string { + let result = excludeNumber ? "" : `${this.value} `; - if (this.value === 1) result += word + singular; - else result += word + plural; + if (this.value === 1) result += word + singular; + else result += word + plural; - return result; - } + return result; + } - /** - * Pluralises a word for changes. - * - (-1).pluraliseSigned() = '-1 credits' - * - (0).pluraliseSigned() = '+0 credits' - * - (1).pluraliseSigned() = '+1 credit' - */ - public pluraliseSigned( - word: string, - plural = '', - singular = '', - excludeNumber = false, - ): string { - const sign = this.value >= 0 ? '+' : ''; - return `${sign}${this.pluralise(word, plural, singular, excludeNumber)}`; - } + /** + * Pluralises a word for changes. + * - (-1).pluraliseSigned() = '-1 credits' + * - (0).pluraliseSigned() = '+0 credits' + * - (1).pluraliseSigned() = '+1 credit' + */ + public pluraliseSigned( + word: string, + plural = "", + singular = "", + excludeNumber = false + ): string { + const sign = this.value >= 0 ? "+" : ""; + return `${sign}${this.pluralise( + word, + plural, + singular, + excludeNumber + )}`; + } } export class StringWrapper extends GenericWrapper { - public replaceAll(before: string, after: string): string { - let result = this.value; + public replaceAll(before: string, after: string): string { + let result = this.value; - while (result.indexOf(before) !== -1) - result = result.replace(before, after); + while (result.indexOf(before) !== -1) + result = result.replace(before, after); - return result; - } + return result; + } - public toTitleCase(): string { - return this.value.replace( - /([^\W_]+[^\s-]*) */g, - (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(), - ); - } + public toTitleCase(): string { + return this.value.replace( + /([^\W_]+[^\s-]*) */g, + (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase() + ); + } } export class ArrayWrapper extends GenericWrapper { - /** Returns a random element from this array. */ - public random(): T { - return this.value[Math.floor(Math.random() * this.value.length)]; - } + /** Returns a random element from this array. */ + public random(): T { + return this.value[Math.floor(Math.random() * this.value.length)]; + } - /** - * Splits up this array into a specified length. - * `$([1,2,3,4,5,6,7,8,9,10]).split(3)` = `[[1,2,3],[4,5,6],[7,8,9],[10]]` - */ - public split(lengthOfEachSection: number): T[][] { - const amountOfSections = Math.ceil(this.value.length / lengthOfEachSection); - const sections: T[][] = new Array(amountOfSections); + /** + * Splits up this array into a specified length. + * `$([1,2,3,4,5,6,7,8,9,10]).split(3)` = `[[1,2,3],[4,5,6],[7,8,9],[10]]` + */ + public split(lengthOfEachSection: number): T[][] { + const amountOfSections = Math.ceil( + this.value.length / lengthOfEachSection + ); + const sections: T[][] = new Array(amountOfSections); - for (let index = 0; index < amountOfSections; index++) - sections[index] = this.value.slice( - index * lengthOfEachSection, - (index + 1) * lengthOfEachSection, - ); + for (let index = 0; index < amountOfSections; index++) + sections[index] = this.value.slice( + index * lengthOfEachSection, + (index + 1) * lengthOfEachSection + ); - return sections; - } + return sections; + } } diff --git a/src/defs/info.ts b/src/defs/info.ts index 30b4786..abb13a5 100644 --- a/src/defs/info.ts +++ b/src/defs/info.ts @@ -1,45 +1,47 @@ // Flags a user can have. // They're basically your profile badges. -export const flags: { [index: string]: any } = { - DISCORD_EMPLOYEE: 'Discord Employee', - DISCORD_PARTNER: 'Discord Partner', - BUGHUNTER_LEVEL_1: 'Bug Hunter (Level 1)', - BUGHUNTER_LEVEL_2: 'Bug Hunter (Level 2)', - HYPESQUAD_EVENTS: 'HypeSquad Events', - HOUSE_BRAVERY: 'House of Bravery', - HOUSE_BRILLIANCE: 'House of Brilliance', - HOUSE_BALANCE: 'House of Balance', - EARLY_SUPPORTER: 'Early Supporter', - TEAM_USER: 'Team User', - SYSTEM: 'System', - VERIFIED_BOT: 'Verified Bot', - VERIFIED_DEVELOPER: 'Verified Bot Developer', +export const flags: {[index: string]: any} = { + DISCORD_EMPLOYEE: "Discord Employee", + DISCORD_PARTNER: "Discord Partner", + BUGHUNTER_LEVEL_1: "Bug Hunter (Level 1)", + BUGHUNTER_LEVEL_2: "Bug Hunter (Level 2)", + HYPESQUAD_EVENTS: "HypeSquad Events", + HOUSE_BRAVERY: "House of Bravery", + HOUSE_BRILLIANCE: "House of Brilliance", + HOUSE_BALANCE: "House of Balance", + EARLY_SUPPORTER: "Early Supporter", + TEAM_USER: "Team User", + SYSTEM: "System", + VERIFIED_BOT: "Verified Bot", + VERIFIED_DEVELOPER: "Verified Bot Developer" }; -export const filterLevels: { [index: string]: any } = { - DISABLED: 'Off', - MEMBERS_WITHOUT_ROLES: 'No Role', - ALL_MEMBERS: 'Everyone', +export const filterLevels: {[index: string]: any} = { + DISABLED: "Off", + MEMBERS_WITHOUT_ROLES: "No Role", + ALL_MEMBERS: "Everyone" }; -export const verificationLevels: { [index: string]: any } = { - NONE: 'None', - LOW: 'Low', - MEDIUM: 'Medium', - HIGH: '(╯°□°)╯︵ ┻━┻', - VERY_HIGH: '┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻', + +export const verificationLevels: {[index: string]: any} = { + NONE: "None", + LOW: "Low", + MEDIUM: "Medium", + HIGH: "(╯°□°)╯︵ ┻━┻", + VERY_HIGH: "┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻" }; -export const regions: { [index: string]: any } = { - brazil: 'Brazil', - europe: 'Europe', - hongkong: 'Hong Kong', - india: 'India', - japan: 'Japan', - russia: 'Russia', - singapore: 'Singapore', - southafrica: 'South Africa', - sydney: 'Sydney', - 'us-central': 'US Central', - 'us-east': 'US East', - 'us-west': 'US West', - 'us-south': 'US South', + +export const regions: {[index: string]: any} = { + brazil: "Brazil", + europe: "Europe", + hongkong: "Hong Kong", + india: "India", + japan: "Japan", + russia: "Russia", + singapore: "Singapore", + southafrica: "South Africa", + sydney: "Sydney", + "us-central": "US Central", + "us-east": "US East", + "us-west": "US West", + "us-south": "US South" }; diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts index a1fbd89..656dcc9 100644 --- a/src/events/channelCreate.ts +++ b/src/events/channelCreate.ts @@ -1,16 +1,16 @@ -import Event from '../core/event'; -import { client } from '../index'; -import $ from '../core/lib'; -import * as discord from 'discord.js'; +import Event from "../core/event"; +import {client} from "../index"; +import $ from "../core/lib"; +import * as discord from "discord.js"; -export default new Event<'channelCreate'>({ - async on(channel) { - const botGuilds = client.guilds; - if (channel instanceof discord.GuildChannel) { - const createdGuild = await botGuilds.fetch(channel.guild.id); - $.log( - `Channel created in '${createdGuild.name}' called '#${channel.name}'`, - ); +export default new Event<"channelCreate">({ + async on(channel) { + const botGuilds = client.guilds; + if (channel instanceof discord.GuildChannel) { + const createdGuild = await botGuilds.fetch(channel.guild.id); + $.log( + `Channel created in '${createdGuild.name}' called '#${channel.name}'` + ); + } } - }, }); diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts index c7836d8..656a9e3 100644 --- a/src/events/channelDelete.ts +++ b/src/events/channelDelete.ts @@ -1,16 +1,16 @@ -import Event from '../core/event'; -import { client } from '../index'; -import $ from '../core/lib'; -import * as discord from 'discord.js'; +import Event from "../core/event"; +import {client} from "../index"; +import $ from "../core/lib"; +import * as discord from "discord.js"; -export default new Event<'channelDelete'>({ - async on(channel) { - const botGuilds = client.guilds; - if (channel instanceof discord.GuildChannel) { - const createdGuild = await botGuilds.fetch(channel.guild.id); - $.log( - `Channel deleted in '${createdGuild.name}' called '#${channel.name}'`, - ); +export default new Event<"channelDelete">({ + async on(channel) { + const botGuilds = client.guilds; + if (channel instanceof discord.GuildChannel) { + const createdGuild = await botGuilds.fetch(channel.guild.id); + $.log( + `Channel deleted in '${createdGuild.name}' called '#${channel.name}'` + ); + } } - }, }); diff --git a/src/events/message.ts b/src/events/message.ts index dab5e7b..7bb5285 100644 --- a/src/events/message.ts +++ b/src/events/message.ts @@ -1,137 +1,143 @@ -import Event from '../core/event'; -import Command, { loadCommands } from '../core/command'; +import Event from "../core/event"; +import Command, {loadCommands} from "../core/command"; import { - hasPermission, - getPermissionLevel, - PermissionNames, -} from '../core/permissions'; -import { Permissions, Collection } from 'discord.js'; -import { getPrefix } from '../core/structures'; -import $ from '../core/lib'; + hasPermission, + getPermissionLevel, + PermissionNames +} from "../core/permissions"; +import {Permissions, Collection} from "discord.js"; +import {getPrefix} from "../core/structures"; +import $ from "../core/lib"; // It's a rather hacky solution, but since there's no top-level await, I just have to make the loading conditional. let commands: Collection | null = null; -export default new Event<'message'>({ - async on(message) { - // Load commands if it hasn't already done so. Luckily, it's called once at most. - if (!commands) commands = await loadCommands(); +export default new Event<"message">({ + async on(message) { + // Load commands if it hasn't already done so. Luckily, it's called once at most. + if (!commands) commands = await loadCommands(); - // Message Setup // - if (message.author.bot) return; + // Message Setup // + if (message.author.bot) return; - const prefix = getPrefix(message.guild); + const prefix = getPrefix(message.guild); - if (!message.content.startsWith(prefix)) { - if (message.client.user && message.mentions.has(message.client.user)) - message.channel.send( - `${message.author.toString()}, my prefix on this guild is \`${prefix}\`.`, - ); - return; - } - - const [header, ...args] = message.content - .substring(prefix.length) - .split(/ +/); - - if (!commands.has(header)) return; - - if ( - message.channel.type === 'text' && - !message.channel - .permissionsFor(message.client.user || '') - ?.has(Permissions.FLAGS.SEND_MESSAGES) - ) { - let status; - - if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR)) - status = - "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended."; - else - status = - "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong."; - - return message.author.send( - `I don't have permission to send messages in ${message.channel.toString()}. ${status}`, - ); - } - - $.log( - `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".`, - ); - - // Subcommand Recursion // - let command = commands.get(header); - if (!command) - return $.warn( - `Command "${header}" was called but for some reason it's still undefined!`, - ); - const params: any[] = []; - let isEndpoint = false; - let permLevel = command.permission ?? Command.PERMISSIONS.NONE; - - for (let param of args) { - if (command.endpoint) { - if ( - command.subcommands.size > 0 || - command.user || - command.number || - command.any - ) - $.warn( - `An endpoint cannot have subcommands! Check ${prefix}${header} again.`, - ); - isEndpoint = true; - break; - } - - const type = command.resolve(param); - command = command.get(param); - permLevel = command.permission ?? permLevel; - - if (type === Command.TYPES.USER) { - const id = param.match(/\d+/g)![0]; - try { - params.push(await message.client.users.fetch(id)); - } catch (error) { - return message.channel.send(`No user found by the ID \`${id}\`!`); + if (!message.content.startsWith(prefix)) { + if ( + message.client.user && + message.mentions.has(message.client.user) + ) + message.channel.send( + `${message.author.toString()}, my prefix on this guild is \`${prefix}\`.` + ); + return; } - } else if (type === Command.TYPES.NUMBER) params.push(Number(param)); - else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); + + const [header, ...args] = message.content + .substring(prefix.length) + .split(/ +/); + + if (!commands.has(header)) return; + + if ( + message.channel.type === "text" && + !message.channel + .permissionsFor(message.client.user || "") + ?.has(Permissions.FLAGS.SEND_MESSAGES) + ) { + let status; + + if (message.member?.hasPermission(Permissions.FLAGS.ADMINISTRATOR)) + status = + "Because you're a server admin, you have the ability to change that channel's permissions to match if that's what you intended."; + else + status = + "Try using a different channel or contacting a server admin to change permissions of that channel if you think something's wrong."; + + return message.author.send( + `I don't have permission to send messages in ${message.channel.toString()}. ${status}` + ); + } + + $.log( + `${message.author.username}#${message.author.discriminator} executed the command "${header}" with arguments "${args}".` + ); + + // Subcommand Recursion // + let command = commands.get(header); + if (!command) + return $.warn( + `Command "${header}" was called but for some reason it's still undefined!` + ); + const params: any[] = []; + let isEndpoint = false; + let permLevel = command.permission ?? Command.PERMISSIONS.NONE; + + for (let param of args) { + if (command.endpoint) { + if ( + command.subcommands.size > 0 || + command.user || + command.number || + command.any + ) + $.warn( + `An endpoint cannot have subcommands! Check ${prefix}${header} again.` + ); + isEndpoint = true; + break; + } + + const type = command.resolve(param); + command = command.get(param); + permLevel = command.permission ?? permLevel; + + if (type === Command.TYPES.USER) { + const id = param.match(/\d+/g)![0]; + try { + params.push(await message.client.users.fetch(id)); + } catch (error) { + return message.channel.send( + `No user found by the ID \`${id}\`!` + ); + } + } else if (type === Command.TYPES.NUMBER) + params.push(Number(param)); + else if (type !== Command.TYPES.SUBCOMMAND) params.push(param); + } + + if (!message.member) + return $.warn( + "This command was likely called from a DM channel meaning the member object is null." + ); + + if (!hasPermission(message.member, permLevel)) { + const userPermLevel = getPermissionLevel(message.member); + return message.channel.send( + `You don't have access to this command! Your permission level is \`${PermissionNames[userPermLevel]}\` (${userPermLevel}), but this command requires a permission level of \`${PermissionNames[permLevel]}\` (${permLevel}).` + ); + } + + if (isEndpoint) return message.channel.send("Too many arguments!"); + + // Execute with dynamic library attached. // + // The purpose of using $.bind($) is to clone the function so as to not modify the original $. + // The cloned function doesn't copy the properties, so Object.assign() is used. + // Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one. + command.execute( + Object.assign( + $.bind($), + { + args: params, + author: message.author, + channel: message.channel, + client: message.client, + guild: message.guild, + member: message.member, + message: message + }, + $ + ) + ); } - - if (!message.member) - return $.warn( - 'This command was likely called from a DM channel meaning the member object is null.', - ); - - if (!hasPermission(message.member, permLevel)) { - const userPermLevel = getPermissionLevel(message.member); - return message.channel.send( - `You don't have access to this command! Your permission level is \`${PermissionNames[userPermLevel]}\` (${userPermLevel}), but this command requires a permission level of \`${PermissionNames[permLevel]}\` (${permLevel}).`, - ); - } - - if (isEndpoint) return message.channel.send('Too many arguments!'); - - // Execute with dynamic library attached. // - // The purpose of using $.bind($) is to clone the function so as to not modify the original $. - // The cloned function doesn't copy the properties, so Object.assign() is used. - // Object.assign() modifies the first element and returns that, the second element applies its properties and the third element applies its own overriding the second one. - command.execute( - Object.assign( - $.bind($), - { - args: params, - author: message.author, - channel: message.channel, - client: message.client, - guild: message.guild, - member: message.member, - message: message, - }, - $, - ), - ); - }, }); diff --git a/src/events/messageReactionRemove.ts b/src/events/messageReactionRemove.ts index 8957a24..15b77a2 100644 --- a/src/events/messageReactionRemove.ts +++ b/src/events/messageReactionRemove.ts @@ -1,24 +1,24 @@ -import Event from '../core/event'; -import { Permissions } from 'discord.js'; -import { botHasPermission } from '../core/lib'; +import Event from "../core/event"; +import {Permissions} from "discord.js"; +import {botHasPermission} from "../core/lib"; // A list of message ID and callback pairs. You get the emote name and ID of the user reacting. export const eventListeners: Map< - string, - (emote: string, id: string) => void + string, + (emote: string, id: string) => void > = new Map(); // Attached to the client, there can be one event listener attached to a message ID which is executed if present. -export default new Event<'messageReactionRemove'>({ - on(reaction, user) { - const canDeleteEmotes = botHasPermission( - reaction.message.guild, - Permissions.FLAGS.MANAGE_MESSAGES, - ); +export default new Event<"messageReactionRemove">({ + on(reaction, user) { + const canDeleteEmotes = botHasPermission( + reaction.message.guild, + Permissions.FLAGS.MANAGE_MESSAGES + ); - if (!canDeleteEmotes) { - const callback = eventListeners.get(reaction.message.id); - callback && callback(reaction.emoji.name, user.id); + if (!canDeleteEmotes) { + const callback = eventListeners.get(reaction.message.id); + callback && callback(reaction.emoji.name, user.id); + } } - }, }); diff --git a/src/events/ready.ts b/src/events/ready.ts index 10ca87a..364b98f 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,18 +1,18 @@ -import Event from '../core/event'; -import { client } from '../index'; -import $ from '../core/lib'; -import { Config } from '../core/structures'; +import Event from "../core/event"; +import {client} from "../index"; +import $ from "../core/lib"; +import {Config} from "../core/structures"; -export default new Event<'ready'>({ - once() { - if (client.user) { - $.ready( - `Logged in as ${client.user.username}#${client.user.discriminator}.`, - ); - client.user.setActivity({ - type: 'LISTENING', - name: `${Config.prefix}help`, - }); +export default new Event<"ready">({ + once() { + if (client.user) { + $.ready( + `Logged in as ${client.user.username}#${client.user.discriminator}.` + ); + client.user.setActivity({ + type: "LISTENING", + name: `${Config.prefix}help` + }); + } } - }, }); diff --git a/src/index.ts b/src/index.ts index 207bb4b..9721784 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,38 +1,38 @@ -import { Client } from 'discord.js'; -import setup from './setup'; -import { Config } from './core/structures'; -import { loadCommands } from './core/command'; -import { loadEvents } from './core/event'; -import 'discord.js-lavalink-lib'; -import LavalinkMusic from 'discord.js-lavalink-lib'; +import {Client} from "discord.js"; +import setup from "./setup"; +import {Config} from "./core/structures"; +import {loadCommands} from "./core/command"; +import {loadEvents} from "./core/event"; +import "discord.js-lavalink-lib"; +import LavalinkMusic from "discord.js-lavalink-lib"; // This is here in order to make it much less of a headache to access the client from other files. // This of course won't actually do anything until the setup process is complete and it logs in. export const client = new Client(); (client as any).music = LavalinkMusic(client, { - lavalink: { - restnode: { - host: 'localhost', - port: 2333, - password: 'youshallnotpass', + lavalink: { + restnode: { + host: "localhost", + port: 2333, + password: "youshallnotpass" + }, + nodes: [ + { + host: "localhost", + port: 2333, + password: "youshallnotpass" + } + ] }, - nodes: [ - { - host: 'localhost', - port: 2333, - password: 'youshallnotpass', - }, - ], - }, - prefix: '!!', - helpCmd: 'mhelp', - admins: ['717352467280691331'], + prefix: "!!", + helpCmd: "mhelp", + admins: ["717352467280691331"] }); // Begin the command loading here rather than when it's needed like in the message event. setup.init().then(() => { - loadCommands(); - loadEvents(client); - client.login(Config.token).catch(setup.again); + loadCommands(); + loadEvents(client); + client.login(Config.token).catch(setup.again); }); diff --git a/src/setup.ts b/src/setup.ts index 6559236..d2d9fbd 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -1,64 +1,65 @@ -import { existsSync as exists } from 'fs'; -import inquirer from 'inquirer'; -import Storage from './core/storage'; -import { Config } from './core/structures'; -import $, { setConsoleActivated } from './core/lib'; +import {existsSync as exists} from "fs"; +import inquirer from "inquirer"; +import Storage from "./core/storage"; +import {Config} from "./core/structures"; +import $, {setConsoleActivated} from "./core/lib"; // This file is called (or at least should be called) automatically as long as a config file doesn't exist yet. // And that file won't be written until the data is successfully initialized. const prompts = [ - { - type: 'password', - name: 'token', - message: "What's your bot's token?", - mask: true, - }, - { - type: 'input', - name: 'prefix', - message: "What do you want your bot's prefix to be?", - default: '$', - }, - { - type: 'input', - name: 'owner', - message: "Enter the owner's user ID here.", - }, - { - type: 'input', - name: 'admins', - message: 'Enter a list of bot admins (by their IDs) separated by spaces.', - }, - { - type: 'input', - name: 'support', - message: - 'Enter a list of bot troubleshooters (by their IDs) separated by spaces.', - }, + { + type: "password", + name: "token", + message: "What's your bot's token?", + mask: true + }, + { + type: "input", + name: "prefix", + message: "What do you want your bot's prefix to be?", + default: "$" + }, + { + type: "input", + name: "owner", + message: "Enter the owner's user ID here." + }, + { + type: "input", + name: "admins", + message: + "Enter a list of bot admins (by their IDs) separated by spaces." + }, + { + type: "input", + name: "support", + message: + "Enter a list of bot troubleshooters (by their IDs) separated by spaces." + } ]; export default { - async init() { - while (!exists('data/config.json')) { - const answers = await inquirer.prompt(prompts); - Storage.open('data'); - Config.token = answers.token as string; - Config.prefix = answers.prefix as string; - Config.owner = answers.owner as string; - const admins = answers.admins as string; - Config.admins = admins !== '' ? admins.split(' ') : []; - const support = answers.support as string; - Config.support = support !== '' ? support.split(' ') : []; - Config.save(false); + async init() { + while (!exists("data/config.json")) { + const answers = await inquirer.prompt(prompts); + Storage.open("data"); + Config.token = answers.token as string; + Config.prefix = answers.prefix as string; + Config.owner = answers.owner as string; + const admins = answers.admins as string; + Config.admins = admins !== "" ? admins.split(" ") : []; + const support = answers.support as string; + Config.support = support !== "" ? support.split(" ") : []; + Config.save(false); + } + }, + /** Prompt the user to set their token again. */ + async again() { + $.error("It seems that the token you provided is invalid."); + setConsoleActivated(false); + const answers = await inquirer.prompt(prompts.slice(0, 1)); + Config.token = answers.token as string; + Config.save(false); + process.exit(); } - }, - /** Prompt the user to set their token again. */ - async again() { - $.error('It seems that the token you provided is invalid.'); - setConsoleActivated(false); - const answers = await inquirer.prompt(prompts.slice(0, 1)); - Config.token = answers.token as string; - Config.save(false); - process.exit(); - }, }; diff --git a/test/wrappers.ts b/test/wrappers.ts index 9597e77..7d995f3 100644 --- a/test/wrappers.ts +++ b/test/wrappers.ts @@ -1,111 +1,107 @@ -import { strict as assert } from 'assert'; -import { - NumberWrapper, - StringWrapper, - ArrayWrapper, -} from '../src/core/wrappers'; +import {strict as assert} from "assert"; +import {NumberWrapper, StringWrapper, ArrayWrapper} from "../src/core/wrappers"; // I can't figure out a way to run the test suite while running the bot. -describe('Wrappers', () => { - describe('NumberWrapper', () => { - describe('#pluralise()', () => { - it('should return "5 credits"', () => { - assert.strictEqual( - new NumberWrapper(5).pluralise('credit', 's'), - '5 credits', - ); - }); +describe("Wrappers", () => { + describe("NumberWrapper", () => { + describe("#pluralise()", () => { + it('should return "5 credits"', () => { + assert.strictEqual( + new NumberWrapper(5).pluralise("credit", "s"), + "5 credits" + ); + }); - it('should return "1 credit"', () => { - assert.strictEqual( - new NumberWrapper(1).pluralise('credit', 's'), - '1 credit', - ); - }); + it('should return "1 credit"', () => { + assert.strictEqual( + new NumberWrapper(1).pluralise("credit", "s"), + "1 credit" + ); + }); - it('should return "-1 credits"', () => { - assert.strictEqual( - new NumberWrapper(-1).pluralise('credit', 's'), - '-1 credits', - ); - }); + it('should return "-1 credits"', () => { + assert.strictEqual( + new NumberWrapper(-1).pluralise("credit", "s"), + "-1 credits" + ); + }); - it('should be able to work with a plural suffix', () => { - assert.strictEqual( - new NumberWrapper(2).pluralise('part', 'ies', 'y'), - '2 parties', - ); - }); + it("should be able to work with a plural suffix", () => { + assert.strictEqual( + new NumberWrapper(2).pluralise("part", "ies", "y"), + "2 parties" + ); + }); - it('should be able to work with a singular suffix', () => { - assert.strictEqual( - new NumberWrapper(1).pluralise('part', 'ies', 'y'), - '1 party', - ); - }); + it("should be able to work with a singular suffix", () => { + assert.strictEqual( + new NumberWrapper(1).pluralise("part", "ies", "y"), + "1 party" + ); + }); - it('should be able to exclude the number', () => { - assert.strictEqual( - new NumberWrapper(1).pluralise('credit', 's', '', true), - 'credit', - ); - }); + it("should be able to exclude the number", () => { + assert.strictEqual( + new NumberWrapper(1).pluralise("credit", "s", "", true), + "credit" + ); + }); + }); + + describe("#pluraliseSigned()", () => { + it('should return "-1 credits"', () => { + assert.strictEqual( + new NumberWrapper(-1).pluraliseSigned("credit", "s"), + "-1 credits" + ); + }); + + it('should return "+0 credits"', () => { + assert.strictEqual( + new NumberWrapper(0).pluraliseSigned("credit", "s"), + "+0 credits" + ); + }); + + it('should return "+1 credit"', () => { + assert.strictEqual( + new NumberWrapper(1).pluraliseSigned("credit", "s"), + "+1 credit" + ); + }); + }); }); - describe('#pluraliseSigned()', () => { - it('should return "-1 credits"', () => { - assert.strictEqual( - new NumberWrapper(-1).pluraliseSigned('credit', 's'), - '-1 credits', - ); - }); + describe("StringWrapper", () => { + describe("#replaceAll()", () => { + it('should convert "test" to "zesz"', () => { + assert.strictEqual( + new StringWrapper("test").replaceAll("t", "z"), + "zesz" + ); + }); + }); - it('should return "+0 credits"', () => { - assert.strictEqual( - new NumberWrapper(0).pluraliseSigned('credit', 's'), - '+0 credits', - ); - }); - - it('should return "+1 credit"', () => { - assert.strictEqual( - new NumberWrapper(1).pluraliseSigned('credit', 's'), - '+1 credit', - ); - }); - }); - }); - - describe('StringWrapper', () => { - describe('#replaceAll()', () => { - it('should convert "test" to "zesz"', () => { - assert.strictEqual( - new StringWrapper('test').replaceAll('t', 'z'), - 'zesz', - ); - }); + describe("#toTitleCase()", () => { + it("should capitalize the first letter of each word", () => { + assert.strictEqual( + new StringWrapper( + "yeetus deletus find salvation from jesus" + ).toTitleCase(), + "Yeetus Deletus Find Salvation From Jesus" + ); + }); + }); }); - describe('#toTitleCase()', () => { - it('should capitalize the first letter of each word', () => { - assert.strictEqual( - new StringWrapper( - 'yeetus deletus find salvation from jesus', - ).toTitleCase(), - 'Yeetus Deletus Find Salvation From Jesus', - ); - }); + describe("ArrayWrapper", () => { + describe("#split()", () => { + it("should split [1,2,3,4,5,6,7,8,9,10] into [[1,2,3],[4,5,6],[7,8,9],[10]]", () => { + assert.deepStrictEqual( + new ArrayWrapper([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).split(3), + [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] + ); + }); + }); }); - }); - - describe('ArrayWrapper', () => { - describe('#split()', () => { - it('should split [1,2,3,4,5,6,7,8,9,10] into [[1,2,3],[4,5,6],[7,8,9],[10]]', () => { - assert.deepStrictEqual( - new ArrayWrapper([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).split(3), - [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], - ); - }); - }); - }); }); diff --git a/tsconfig.json b/tsconfig.json index 74603be..ba4dd11 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,17 @@ { - "compilerOptions": { - "rootDir": "src", - "outDir": "dist", - "target": "ES6", - "module": "CommonJS", - "moduleResolution": "node", - "esModuleInterop": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictPropertyInitialization": true, - "removeComments": true - }, - "exclude": ["test"] + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "target": "ES6", + "module": "CommonJS", + "moduleResolution": "node", + "esModuleInterop": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictPropertyInitialization": true, + "removeComments": true + }, + "exclude": ["test"] }