diff --git a/.env b/.env index 6bc8826..6806c45 100644 --- a/.env +++ b/.env @@ -16,6 +16,7 @@ BOT_SOUNDCLOUD_CLIENT_ID=undefined BOT_SOUNDCLOUD_TOKEN=undefined BOT_YANDEXMUSIC_TOKEN=undefined +BOT_YANDEXMUSIC_UID=undefined MONGO_URI=undefined MONGO_DATABASE_NAME=undefined diff --git a/.eslintrc.json b/.eslintrc.json index a64efde..52940fa 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,10 +1,7 @@ { "root": true, "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "prettier" - ], + "plugins": ["@typescript-eslint", "prettier"], "env": { "commonjs": true, "es2021": true, @@ -22,6 +19,6 @@ "rules": { "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-non-null-assertion": 0, - "prettier/prettier": 2 + "prettier/prettier": ["error", { "endOfLine": "auto" }] } } diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..b6599f3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,61 @@ +name: Bug Report +description: Bug report for anything +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Please also ensure you have searched for any existing reports of this issue before you begin - if one exists, please leave a thumbs-up and/or a comment on that one, rather than knowingly filing a duplicate report, as duplicates just slow things down. + + Note that most fields are required to be filled-in before you can submit this report, as it is all essential information which we will require in order to confirm and fix your issue. + + - type: dropdown + id: user-type + attributes: + label: Who are you? User (who just use bot from discord)/developer/admin (who download all bot stuff to host)? + description: If you are on an older version, update and confirm your issue stil exists! + multiple: true + options: + - User + - Developer + - Admin + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: Write a concise yet detailed description of the issue!) + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps To Reproduce + description: Add detailed, ordered steps for someone else to recreate the issue into the gaps below (add as many steps into the middle as you require) + value: | + 1. Use command + 2. + 3. + 4. See the issue + validations: + required: true + + - type: dropdown + id: repro-rate + attributes: + label: How reliably can you recreate this issue using your steps above? + options: + - Always + - Most Times + - Only Sometimes + - Rarely + - Only Ever Happened Once + validations: + required: true + + - type: markdown + attributes: + value: Thanks for taking the time to fill out this bug report! diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ddbc450 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false +contact_links: + - name: Frequently Asked Questions + url: https://github.com/AlexInCube/AICoTest/wiki/Frequently-Asked-Questions + about: AICoTest Frequently Asked Questions + diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..cacec3a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,35 @@ +name: Feature Request +description: Suggest an improvement for AICoTest! +labels: ["feature"] +body: + - type: textarea + id: problem + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: false + + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + validations: + required: false diff --git a/.github/workflows/wiki.yaml b/.github/workflows/wiki.yaml new file mode 100644 index 0000000..54d2d7f --- /dev/null +++ b/.github/workflows/wiki.yaml @@ -0,0 +1,15 @@ +name: Wiki +on: + push: + branches: ['master'] + paths: + - wiki/** + - .github/workflows/wiki.yml +permissions: + contents: write +jobs: + wiki: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: Andrew-Chen-Wang/github-wiki-action@v4 diff --git a/.prettierrc b/.prettierrc index 83289e0..262579f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "semi": true, "trailingComma": "none", "singleQuote": true, - "printWidth": 100 + "printWidth": 100, + "endOfLine": "auto" } diff --git a/Dockerfile b/Dockerfile index da0fa68..0fc98ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,17 @@ RUN pnpm run build RUN pnpm prune --prod FROM base as prod + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 aicbot + WORKDIR /bot COPY --from=build /botbuild/build ./build COPY --from=build /botbuild/node_modules ./node_modules COPY --from=build /botbuild/package.json . + +RUN chown -R aicbot:nodejs /bot + +USER aicbot + CMD ["npm", "run", "production"] diff --git a/README.md b/README.md index 9a349e5..fd7255b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- BotLogo + BotLogo

AICTest

@@ -7,72 +7,7 @@ Cool audiobot for Discord created by @Ale ## 🌟 Features - Command /alcotest which shows your alcohol count in blood -- Audioplayer based on [Distube](https://github.com/skick1234/DisTube) with buttons ![image](https://i.imgur.com/zqQ6ViY.png) +- Audioplayer based on [Distube](https://github.com/skick1234/DisTube) with buttons ![play-audioplayer](/wiki/images/commands/play-audioplayer.png) - Support YouTube, Spotify, Soundcloud, any HTTP-stream and Discord Attachments (/playfile support MP3/WAV/OGG) - Support Slash and Text commands (with customizable prefix per server using /setprefix) - Localization (English and Russian are currently supported) - -## 🎛️ Requirements -- Node.js 20 or higher -- MongoDB 6.0 or higher -- ffmpeg latest - -## How to run bot? -### ⚙️ Configure .env -You can use Docker image or install things from "Requirements" and source code locally. -But in both cases, you need to configure .env file. - -- Create .env.production -- Fill all fields in .env.production. If the field is marked as (Optional), you can skip it. -- (Required) To get Discord Token, follow this [guide](https://discordjs.guide/preparations/setting-up-a-bot-application.html#creating-your-bot). After following the guide, you need to enable privileged intents in [Discord Developer Portal.](https://github.com/AlexInCube/AlCoTest/assets/25522245/fdbcdcf1-9501-47f0-93bf-7e76806f623f) -- (Optional) To get Spotify Secret and ID, follow this [guide](https://stevesie.com/docs/pages/spotify-client-id-secret-developer-api) -- (Optional) To get Yandex Music token, follow this [guide](https://github.com/MarshalX/yandex-music-api/discussions/513) -``` -BOT_VERBOSE_LOGGING= (Optional) The bot will give more information about what it is doing to the console, useful for debugging. By default is: false - -BOT_COMMAND_PREFIX= (Required) Used only for text commands, for example: // -BOT_LANGUAGE= (Optional) Supported values: en ru. By default is: en - -MONGO_URI= (Required) If you run bot locally, use mongodb://localhost:27017. If you run in Docker, use mongodb://mongo:27017 -MONGO_DATABASE_NAME= (Required) Database name in MongoDB, for example: aicbot - -BOT_DISCORD_TOKEN= (Required) -BOT_DISCORD_CLIENT_ID= (Required) -BOT_DISCORD_OVERPOWERED_ID= (Required) This need to retrieve reports in direct message - -BOT_SPOTIFY_CLIENT_SECRET= (Optional) -BOT_SPOTIFY_CLIENT_ID= (Optional) - -BOT_YANDEXMUSIC_TOKEN= (Optional) -``` - -### 🍪 Youtube Cookie -Also, it is preferable to provide cookies for YouTube. -This will allow you to play 18+ videos and bypass YouTube rate limiting error (429 Error). -I highly recommend that you create a new Google account from which you get the cookie. -You cannot watch videos in your browser from this account, -otherwise your cookie will be reset, and you will need to retrieve it again. - -- Install an extension for extracting cookie, [EditThisCookie](https://www.editthiscookie.com/blog/2014/03/install-editthiscookie/) -- Go to [YouTube](https://www.youtube.com/) -- Log in to your account. (You should use a new account for this purpose) -- Click on the extension icon and click "Export" button. -- Create file yt-cookies.json and paste cookie in this file - -### 🖥️ Run locally -- Install things from "Requirements" section -- Follow the "Configure .env" section and put .env.production in folder with repository. -- Follow the "YouTube Cookie" section and put yt-cookies.json in the folder with repository. -- Run commands below - -```npm -npm install -npm run build -npm run production -``` - -### 🐋 Run in Docker -- Copy docker-compose.yml, Dockerfile in empty folder -- Follow the "Configure .env" section and put .env.production in folder with Dockerfile etc. -- Follow the "YouTube Cookie" section and put yt-cookies.json in the folder with Dockerfile etc. -- Run command ```docker-compose up --detach --force-recreate``` in folder with files diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..9ed99b8 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,17 @@ +{ + 'targets': [ + { + 'target_name': 'bindings', + 'sources': [ 'bindings.node' ], + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' + } + }] + ] + } + ] +} diff --git a/docker-compose.yml b/docker-compose.yml index fb02ffe..a2c8839 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: volumes: - ./data:/data/db ports: - - '27018:27017' + - '27017:27017' aicbot: container_name: aicbot image: alexincube/aicotest:latest diff --git a/icons/audioplayer/player/play.png b/icons/audioplayer/player/play.png new file mode 100644 index 0000000..a18dd6e Binary files /dev/null and b/icons/audioplayer/player/play.png differ diff --git a/icons/audioplayer/services/apple-music.png b/icons/audioplayer/services/apple-music.png new file mode 100644 index 0000000..1adfdb3 Binary files /dev/null and b/icons/audioplayer/services/apple-music.png differ diff --git a/icons/audioplayer/services/attach-file.png b/icons/audioplayer/services/attach-file.png new file mode 100644 index 0000000..ef806ee Binary files /dev/null and b/icons/audioplayer/services/attach-file.png differ diff --git a/icons/audioplayer/services/audio-waves.png b/icons/audioplayer/services/audio-waves.png new file mode 100644 index 0000000..749525d Binary files /dev/null and b/icons/audioplayer/services/audio-waves.png differ diff --git a/icons/audioplayer/services/soundcloud.png b/icons/audioplayer/services/soundcloud.png new file mode 100644 index 0000000..f0243e1 Binary files /dev/null and b/icons/audioplayer/services/soundcloud.png differ diff --git a/icons/audioplayer/services/spotify.png b/icons/audioplayer/services/spotify.png new file mode 100644 index 0000000..932dd00 Binary files /dev/null and b/icons/audioplayer/services/spotify.png differ diff --git a/icons/audioplayer/services/yandex-music.png b/icons/audioplayer/services/yandex-music.png new file mode 100644 index 0000000..ce681bd Binary files /dev/null and b/icons/audioplayer/services/yandex-music.png differ diff --git a/icons/audioplayer/services/youtube.png b/icons/audioplayer/services/youtube.png new file mode 100644 index 0000000..cf640c6 Binary files /dev/null and b/icons/audioplayer/services/youtube.png differ diff --git a/icons/error.png b/icons/error.png new file mode 100644 index 0000000..f3f43b9 Binary files /dev/null and b/icons/error.png differ diff --git a/src/logo.png b/icons/logo.png similarity index 100% rename from src/logo.png rename to icons/logo.png diff --git a/icons/warning.png b/icons/warning.png new file mode 100644 index 0000000..98fc5f3 Binary files /dev/null and b/icons/warning.png differ diff --git a/package.json b/package.json index 014826d..3b7cc37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aicbot", - "version": "2.5.0", + "version": "3.0.1", "description": "Discord Bot for playing music", "main": "build/main.js", "scripts": { @@ -13,29 +13,34 @@ "author": "AlexInCube", "license": "ISC", "dependencies": { - "@discordjs/opus": "^0.9.0", "@discordjs/rest": "^1.7.1", "@discordjs/voice": "^0.16.1", - "@distube/soundcloud": "^1.3.5", - "@distube/spotify": "^1.6.1", - "@distube/yt-dlp": "^1.1.3", - "@distube/ytdl-core": "^4.13.3", + "@distube/direct-link": "^1.0.1", + "@distube/file": "^1.0.1", + "@distube/soundcloud": "^2.0.2", + "@distube/spotify": "^2.0.2", + "@distube/youtube": "^1.0.2", + "@distube/yt-dlp": "^2.0.1", + "@distube/ytdl-core": "^4.13.4", + "@distube/ytsr": "^2.0.4", "cross-env": "7.0.3", - "discord.js": "14.14.1", - "distube": "^4.2.2", - "distube-yandex-music-plugin": "^0.2.1", + "discord.js": "^14.15.3", + "distube": "^5.0.2", + "distube-apple-music": "^0.1.0", + "distube-yandex-music-plugin": "^1.0.4", "dotenv": "^16.4.5", "i18next": "^22.5.1", "i18next-fs-backend": "^2.3.1", - "mongoose": "^7.6.12", + "mongoose": "^7.7.0", "node-os-utils": "^1.3.7", + "opusscript": "^0.1.1", "prism-media": "^1.3.5", "sodium-native": "^4.1.1", "uuid": "^9.0.1", "zod": "^3.23.8" }, "devDependencies": { - "@types/node": "^20.12.12", + "@types/node": "^20.14.10", "@types/node-os-utils": "^1.3.4", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.62.0", @@ -43,7 +48,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "prettier": "^3.2.5", - "typescript": "^5.4.5" + "prettier": "^3.3.2", + "typescript": "^5.5.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d300e86..ba2b7a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,488 +1,310 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - '@discordjs/opus': - specifier: ^0.9.0 - version: 0.9.0 - '@discordjs/rest': - specifier: ^1.7.1 - version: 1.7.1 - '@discordjs/voice': - specifier: ^0.16.1 - version: 0.16.1(@discordjs/opus@0.9.0) - '@distube/soundcloud': - specifier: ^1.3.5 - version: 1.3.5(distube@4.2.2) - '@distube/spotify': - specifier: ^1.6.1 - version: 1.6.1(distube@4.2.2) - '@distube/yt-dlp': - specifier: ^1.1.3 - version: 1.1.3(distube@4.2.2) - '@distube/ytdl-core': - specifier: ^4.13.3 - version: 4.13.3 - cross-env: - specifier: 7.0.3 - version: 7.0.3 - discord.js: - specifier: 14.14.1 - version: 14.14.1 - distube: - specifier: ^4.2.2 - version: 4.2.2(@discordjs/voice@0.16.1)(discord.js@14.14.1) - distube-yandex-music-plugin: - specifier: ^0.2.1 - version: 0.2.1(distube@4.2.2) - dotenv: - specifier: ^16.4.5 - version: 16.4.5 - i18next: - specifier: ^22.5.1 - version: 22.5.1 - i18next-fs-backend: - specifier: ^2.3.1 - version: 2.3.1 - mongoose: - specifier: ^7.6.12 - version: 7.6.12 - node-os-utils: - specifier: ^1.3.7 - version: 1.3.7 - prism-media: - specifier: ^1.3.5 - version: 1.3.5(@discordjs/opus@0.9.0) - sodium-native: - specifier: ^4.1.1 - version: 4.1.1 - uuid: - specifier: ^9.0.1 - version: 9.0.1 - zod: - specifier: ^3.23.8 - version: 3.23.8 - -devDependencies: - '@types/node': - specifier: ^20.12.12 - version: 20.12.12 - '@types/node-os-utils': - specifier: ^1.3.4 - version: 1.3.4 - '@types/uuid': - specifier: ^9.0.8 - version: 9.0.8 - '@typescript-eslint/eslint-plugin': - specifier: ^5.62.0 - version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/parser': - specifier: ^5.62.0 - version: 5.62.0(eslint@8.57.0)(typescript@5.4.5) - eslint: - specifier: ^8.57.0 - version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.1.3 - version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5) - prettier: - specifier: ^3.2.5 - version: 3.2.5 - typescript: - specifier: ^5.4.5 - version: 5.4.5 +importers: + + .: + dependencies: + '@discordjs/rest': + specifier: ^1.7.1 + version: 1.7.1 + '@discordjs/voice': + specifier: ^0.16.1 + version: 0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1) + '@distube/direct-link': + specifier: ^1.0.1 + version: 1.0.1(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + '@distube/file': + specifier: ^1.0.1 + version: 1.0.1(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + '@distube/soundcloud': + specifier: ^2.0.2 + version: 2.0.2(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + '@distube/spotify': + specifier: ^2.0.2 + version: 2.0.2(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + '@distube/youtube': + specifier: ^1.0.2 + version: 1.0.2(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + '@distube/yt-dlp': + specifier: ^2.0.1 + version: 2.0.1(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + '@distube/ytdl-core': + specifier: ^4.13.4 + version: 4.13.4 + '@distube/ytsr': + specifier: ^2.0.4 + version: 2.0.4 + cross-env: + specifier: 7.0.3 + version: 7.0.3 + discord.js: + specifier: ^14.15.3 + version: 14.15.3 + distube: + specifier: ^5.0.2 + version: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + distube-apple-music: + specifier: ^0.1.0 + version: 0.1.0(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + distube-yandex-music-plugin: + specifier: ^1.0.4 + version: 1.0.4(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)) + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + i18next: + specifier: ^22.5.1 + version: 22.5.1 + i18next-fs-backend: + specifier: ^2.3.1 + version: 2.3.1 + mongoose: + specifier: ^7.7.0 + version: 7.7.0 + node-os-utils: + specifier: ^1.3.7 + version: 1.3.7 + opusscript: + specifier: ^0.1.1 + version: 0.1.1 + prism-media: + specifier: ^1.3.5 + version: 1.3.5(@discordjs/opus@0.9.0)(opusscript@0.1.1) + sodium-native: + specifier: ^4.1.1 + version: 4.1.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@types/node': + specifier: ^20.14.10 + version: 20.14.10 + '@types/node-os-utils': + specifier: ^1.3.4 + version: 1.3.4 + '@types/uuid': + specifier: ^9.0.8 + version: 9.0.8 + '@typescript-eslint/eslint-plugin': + specifier: ^5.62.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/parser': + specifier: ^5.62.0 + version: 5.62.0(eslint@8.57.0)(typescript@5.5.3) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.0) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.2) + prettier: + specifier: ^3.3.2 + version: 3.3.2 + typescript: + specifier: ^5.5.3 + version: 5.5.3 packages: - /@babel/runtime@7.24.6: - resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} + '@babel/runtime@7.24.7': + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: false - /@discordjs/builders@1.8.1: - resolution: {integrity: sha512-GkF+HM01FHy+NSoTaUPR8z44otfQgJ1AIsRxclYGUZDyUbdZEFyD/5QVv2Y1Flx6M+B0bQLzg2M9CJv5lGTqpA==} + '@discordjs/builders@1.8.2': + resolution: {integrity: sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g==} engines: {node: '>=16.11.0'} - dependencies: - '@discordjs/formatters': 0.4.0 - '@discordjs/util': 1.1.0 - '@sapphire/shapeshift': 3.9.7 - discord-api-types: 0.37.83 - fast-deep-equal: 3.1.3 - ts-mixer: 6.0.4 - tslib: 2.6.2 - dev: false - /@discordjs/collection@1.5.3: + '@discordjs/collection@1.5.3': resolution: {integrity: sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==} engines: {node: '>=16.11.0'} - dev: false - /@discordjs/collection@2.1.0: + '@discordjs/collection@2.1.0': resolution: {integrity: sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==} engines: {node: '>=18'} - dev: false - - /@discordjs/formatters@0.3.3: - resolution: {integrity: sha512-wTcI1Q5cps1eSGhl6+6AzzZkBBlVrBdc9IUhJbijRgVjCNIIIZPgqnUj3ntFODsHrdbGU8BEG9XmDQmgEEYn3w==} - engines: {node: '>=16.11.0'} - dependencies: - discord-api-types: 0.37.61 - dev: false - /@discordjs/formatters@0.4.0: + '@discordjs/formatters@0.4.0': resolution: {integrity: sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ==} engines: {node: '>=16.11.0'} - dependencies: - discord-api-types: 0.37.83 - dev: false - /@discordjs/node-pre-gyp@0.4.5: + '@discordjs/node-pre-gyp@0.4.5': resolution: {integrity: sha512-YJOVVZ545x24mHzANfYoy0BJX5PDyeZlpiJjDkUBM/V/Ao7TFX9lcUvCN4nr0tbr5ubeaXxtEBILUrHtTphVeQ==} hasBin: true - dependencies: - detect-libc: 2.0.3 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.7.0 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.6.2 - tar: 6.2.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /@discordjs/opus@0.9.0: + '@discordjs/opus@0.9.0': resolution: {integrity: sha512-NEE76A96FtQ5YuoAVlOlB3ryMPrkXbUCTQICHGKb8ShtjXyubGicjRMouHtP1RpuDdm16cDa+oI3aAMo1zQRUQ==} engines: {node: '>=12.0.0'} - requiresBuild: true - dependencies: - '@discordjs/node-pre-gyp': 0.4.5 - node-addon-api: 5.1.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /@discordjs/rest@1.7.1: + '@discordjs/rest@1.7.1': resolution: {integrity: sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ==} engines: {node: '>=16.9.0'} - dependencies: - '@discordjs/collection': 1.5.3 - '@discordjs/util': 0.3.1 - '@sapphire/async-queue': 1.5.2 - '@sapphire/snowflake': 3.5.3 - discord-api-types: 0.37.85 - file-type: 18.7.0 - tslib: 2.6.2 - undici: 5.28.4 - dev: false - /@discordjs/rest@2.3.0: + '@discordjs/rest@2.3.0': resolution: {integrity: sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==} engines: {node: '>=16.11.0'} - dependencies: - '@discordjs/collection': 2.1.0 - '@discordjs/util': 1.1.0 - '@sapphire/async-queue': 1.5.2 - '@sapphire/snowflake': 3.5.3 - '@vladfrangu/async_event_emitter': 2.2.4 - discord-api-types: 0.37.83 - magic-bytes.js: 1.10.0 - tslib: 2.6.2 - undici: 6.13.0 - dev: false - /@discordjs/util@0.3.1: + '@discordjs/util@0.3.1': resolution: {integrity: sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==} engines: {node: '>=16.9.0'} - dev: false - /@discordjs/util@1.1.0: + '@discordjs/util@1.1.0': resolution: {integrity: sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg==} engines: {node: '>=16.11.0'} - dev: false - /@discordjs/voice@0.16.1(@discordjs/opus@0.9.0): + '@discordjs/voice@0.16.1': resolution: {integrity: sha512-uiWiW0Ta6K473yf8zs13RfKuPqm/xU4m4dAidMkIdwqgy1CztbbZBtPLfDkVSKzpW7s6m072C+uQcs4LwF3FhA==} engines: {node: '>=16.11.0'} - dependencies: - '@types/ws': 8.5.10 - discord-api-types: 0.37.61 - prism-media: 1.3.5(@discordjs/opus@0.9.0) - tslib: 2.6.2 - ws: 8.17.0 - transitivePeerDependencies: - - '@discordjs/opus' - - bufferutil - - ffmpeg-static - - node-opus - - opusscript - - utf-8-validate - dev: false - /@discordjs/ws@1.1.0: - resolution: {integrity: sha512-O97DIeSvfNTn5wz5vaER6ciyUsr7nOqSEtsLoMhhIgeFkhnxLRqSr00/Fpq2/ppLgjDGLbQCDzIK7ilGoB/M7A==} + '@discordjs/ws@1.1.1': + resolution: {integrity: sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==} engines: {node: '>=16.11.0'} - dependencies: - '@discordjs/collection': 2.1.0 - '@discordjs/rest': 2.3.0 - '@discordjs/util': 1.1.0 - '@sapphire/async-queue': 1.5.2 - '@types/ws': 8.5.10 - '@vladfrangu/async_event_emitter': 2.2.4 - discord-api-types: 0.37.83 - tslib: 2.6.2 - ws: 8.17.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - /@distube/soundcloud@1.3.5(distube@4.2.2): - resolution: {integrity: sha512-d/Va+DrBCpwFB7aAEwofAbQ8OWmJViHJtl/OsIfHsNBhh/A5Agp1I0P2Go8dJTQtOhNhKUxgR7Pa1jOxP7m9Xw==} + '@distube/direct-link@1.0.1': + resolution: {integrity: sha512-NbUzRj1mVOkpmcUrjQ1KjfYDQEeS8uRWGPcgunNmRGdI+BQ5BG+pcHK1r6wMVwzaIdQrpg3X6Sh/T7uG3bdImg==} peerDependencies: - distube: 3||4 - dependencies: - distube: 4.2.2(@discordjs/voice@0.16.1)(discord.js@14.14.1) - soundcloud.ts: 0.5.3 - dev: false + distube: '5' - /@distube/spotify@1.6.1(distube@4.2.2): - resolution: {integrity: sha512-nuBftbp05y+pMFcJBlaewauz1LXqrjv5PluIFtlaq50cNx4smton1l3rZxFoqbYQJaFx1cfnbVxwqCUBNEkpIQ==} + '@distube/file@1.0.1': + resolution: {integrity: sha512-XdWxh4zNI1hdNBf7tAnCL07Mytl63xxVhtF3M3jrh/aklqY9MXlI+bqogmuCa2j8bqTE3XJ/TBwgIhNoyy//JA==} peerDependencies: - distube: ^3.3.1||4 - dependencies: - distube: 4.2.2(@discordjs/voice@0.16.1)(discord.js@14.14.1) - spotify-uri: 4.1.0 - spotify-url-info: 3.2.15 - spotify-web-api-node: 5.0.2 - undici: 6.18.1 - transitivePeerDependencies: - - supports-color - dev: false + distube: '5' - /@distube/yt-dlp@1.1.3(distube@4.2.2): - resolution: {integrity: sha512-kInFaIgNK7bhUJwY5o/ejBpwHdtLEH9l/GPBl9G7ddMKFYx7/MA5vKZ2F3oZQVMq9pzphPMRpSGi4UBT9L6O2g==} - engines: {node: '>=12.0.0'} - requiresBuild: true + '@distube/soundcloud@2.0.2': + resolution: {integrity: sha512-+SgxJ9jRA0SxInWj23rsvi12nzJ7tNpo42A5SRgeoHVvDp7XgBgTEDg2vvVfnj1WaVIptulZsD7gHDAudURODA==} peerDependencies: - distube: 3.x||4.x - dependencies: - dargs: 7.0.0 - distube: 4.2.2(@discordjs/voice@0.16.1)(discord.js@14.14.1) - execa: 5.1.1 - mkdirp: 1.0.4 - undici: 5.28.4 - dev: false + distube: '5' - /@distube/ytdl-core@4.13.3: - resolution: {integrity: sha512-WHVzp0NyUkmdxRkfU8tN7eRquL7bnia2U/EDNWVupCptRo7EToTdBKHwJrDFqvavbXsdqLG/kR1r+1LaPglrFQ==} - engines: {node: '>=12'} - dependencies: - http-cookie-agent: 5.0.4(tough-cookie@4.1.4)(undici@5.28.4) - m3u8stream: 0.8.6 - miniget: 4.2.3 - sax: 1.3.0 - tough-cookie: 4.1.4 - undici: 5.28.4 - transitivePeerDependencies: - - deasync - - supports-color - dev: false + '@distube/spotify@2.0.2': + resolution: {integrity: sha512-rIncX05PhedMZuZyhgz7dt9y/nxir9KPmxUo6sBxfITQcztkXlG3r0EhKivp9+BRCQlEBO/GitRTHVwhRKtmLg==} + peerDependencies: + distube: '5' + + '@distube/youtube@1.0.2': + resolution: {integrity: sha512-vNOE7kGhg3pC87iqT3Q4eu+yTae8Nr3J1XDR67YMH9HAg1RsPjwM7TvfabZoGxIUiPl4ua/PG6s8Nfxf2U9cxQ==} + peerDependencies: + distube: '5' - /@distube/ytpl@1.2.1: + '@distube/yt-dlp@2.0.1': + resolution: {integrity: sha512-9c16lRU6jbyal38UUr5E36+2lp36s0DaJySOtFjuAPgaJkp2xvKvyd+s4rFZSqVQGJO5GOhBiH+HD115SKfKAw==} + peerDependencies: + distube: '5' + + '@distube/ytdl-core@4.13.4': + resolution: {integrity: sha512-AMuKFWN8MHq+gtlhMq2O4jMm7KNtFbXpFMxK6NgDsyMxZT+obH+wVCu/2sj+ZBn6HOnGofvrmb6kAx0P59+lFw==} + engines: {node: '>=16'} + + '@distube/ytpl@1.2.1': resolution: {integrity: sha512-cp9nDYyGTZ2DNHURQXS76ptKWijLMspoxJEj03i++9tJf0LFGTWkskL3sLwcY4qQPLXjlH9FAXwwpjVttdXvZQ==} engines: {node: '>=8'} - dependencies: - undici: 5.28.4 - dev: false - /@distube/ytsr@2.0.0: - resolution: {integrity: sha512-N9z8IMbBCQ/gNnJmBgc0TBOU7tdl2nYDOnT6adN1utzIlrKWa2Ux+3UdAPV38f/qRrWohcmyMHPbSbex80ap3A==} - engines: {node: '>=18.0'} - dependencies: - undici: 6.0.1 - dev: false + '@distube/ytsr@2.0.4': + resolution: {integrity: sha512-OiSWgARQ9LTj+dXt3jmMFzUH4l86VVCD4dVC4hEHNXdqp+DyU4QEzc+W6YY6//kWkvzTaUxOo7JUY7lBzwIF0A==} + engines: {node: '>=14.0'} - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/regexpp@4.10.0: - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - /@eslint/eslintrc@2.1.4: + '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.1 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - /@eslint/js@8.57.0: + '@eslint/js@8.57.0': resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /@fastify/busboy@2.1.1: + '@fastify/busboy@2.1.1': resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - dev: false - /@humanwhocodes/config-array@0.11.14: + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true + deprecated: Use @eslint/config-array instead - /@humanwhocodes/module-importer@1.0.1: + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - dev: true - /@humanwhocodes/object-schema@2.0.3: + '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - dev: true + deprecated: Use @eslint/object-schema instead - /@mongodb-js/saslprep@1.1.7: + '@mongodb-js/saslprep@1.1.7': resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} - requiresBuild: true - dependencies: - sparse-bitfield: 3.0.3 - dev: false - optional: true - /@nodelib/fs.scandir@2.1.5: + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - /@nodelib/fs.stat@2.0.5: + '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true - /@nodelib/fs.walk@1.2.8: + '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - dev: true - /@pkgr/core@0.1.1: + '@pkgr/core@0.1.1': resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dev: true - /@sapphire/async-queue@1.5.2: + '@sapphire/async-queue@1.5.2': resolution: {integrity: sha512-7X7FFAA4DngXUl95+hYbUF19bp1LGiffjJtu7ygrZrbdCSsdDDBaSjB7Akw0ZbOu6k0xpXyljnJ6/RZUvLfRdg==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - dev: false - /@sapphire/shapeshift@3.9.7: + '@sapphire/shapeshift@3.9.7': resolution: {integrity: sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==} engines: {node: '>=v16'} - dependencies: - fast-deep-equal: 3.1.3 - lodash: 4.17.21 - dev: false - - /@sapphire/snowflake@3.5.1: - resolution: {integrity: sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA==} - engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - dev: false - /@sapphire/snowflake@3.5.3: + '@sapphire/snowflake@3.5.3': resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - dev: false - /@tokenizer/token@0.3.0: + '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - dev: false - /@types/json-schema@7.0.15: + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true - /@types/node-os-utils@1.3.4: + '@types/node-os-utils@1.3.4': resolution: {integrity: sha512-BCUYrbdoO4FUbx6MB9atLNFnkxdliFaxdiTJMIPPiecXIApc5zf4NIqV5G1jWv/ReZvtYyHLs40RkBjHX+vykA==} - dev: true - /@types/node@20.12.12: - resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} - dependencies: - undici-types: 5.26.5 + '@types/node@20.14.10': + resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} - /@types/semver@7.5.8: + '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true - /@types/uuid@9.0.8: + '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - dev: true - /@types/webidl-conversions@7.0.3: + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false - /@types/whatwg-url@8.2.2: + '@types/whatwg-url@8.2.2': resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} - dependencies: - '@types/node': 20.12.12 - '@types/webidl-conversions': 7.0.3 - dev: false - /@types/ws@8.5.10: + '@types/ws@8.5.10': resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} - dependencies: - '@types/node': 20.12.12 - dev: false - - /@types/ws@8.5.9: - resolution: {integrity: sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==} - dependencies: - '@types/node': 20.12.12 - dev: false - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5): + '@typescript-eslint/eslint-plugin@5.62.0': resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -492,25 +314,8 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare-lite: 1.4.0 - semver: 7.6.2 - tsutils: 3.21.0(typescript@5.4.5) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5): + '@typescript-eslint/parser@5.62.0': resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -519,26 +324,12 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - debug: 4.3.4 - eslint: 8.57.0 - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/scope-manager@5.62.0: + '@typescript-eslint/scope-manager@5.62.0': resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): + '@typescript-eslint/type-utils@5.62.0': resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -547,23 +338,12 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4 - eslint: 8.57.0 - tsutils: 3.21.0(typescript@5.4.5) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/types@5.62.0: + '@typescript-eslint/types@5.62.0': resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5): + '@typescript-eslint/typescript-estree@5.62.0': resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -571,418 +351,269 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.6.2 - tsutils: 3.21.0(typescript@5.4.5) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): + '@typescript-eslint/utils@5.62.0': resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) - eslint: 8.57.0 - eslint-scope: 5.1.1 - semver: 7.6.2 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@typescript-eslint/visitor-keys@5.62.0: + '@typescript-eslint/visitor-keys@5.62.0': resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.3 - dev: true - /@ungap/structured-clone@1.2.0: + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - dev: true - /@vladfrangu/async_event_emitter@2.2.4: - resolution: {integrity: sha512-ButUPz9E9cXMLgvAW8aLAKKJJsPu1dY1/l/E8xzLFuysowXygs6GBcyunK9rnGC4zTsnIc2mQo71rGw9U+Ykug==} + '@vladfrangu/async_event_emitter@2.4.1': + resolution: {integrity: sha512-cedU1DrzO4oliUigSAOqSgts6wEfGGSbpO1hYxvKbz8sr7a0meyP3GxnL6hIUtBK0nMG6zHfIYWcqOIb+MRI7w==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} - dev: false - /abbrev@1.1.1: + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: false - /acorn-jsx@5.3.2(acorn@8.11.3): + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.11.3 - dev: true - /acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true - dev: true - /agent-base@6.0.2: + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - /agent-base@7.1.1: + agent-base@7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - /ajv@6.12.6: + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - /ansi-regex@5.0.1: + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - /ansi-styles@4.3.0: + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - dev: true - /aproba@2.0.0: + aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: false - /are-we-there-yet@2.0.0: + are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} deprecated: This package is no longer supported. - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - dev: false - /argparse@2.0.1: + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - /array-union@2.1.0: + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - dev: true - /asynckit@0.4.0: + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false - /axios@1.7.2: + axios@1.7.2: resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} - dependencies: - follow-redirects: 1.15.6 - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - dev: false - /balanced-match@1.0.2: + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - /brace-expansion@1.1.11: + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - /braces@3.0.3: + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - dependencies: - fill-range: 7.1.1 - dev: true - /bson@5.5.1: + bson@5.5.1: resolution: {integrity: sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==} engines: {node: '>=14.20.1'} - dev: false - /call-bind@1.0.7: + call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - dev: false - /callsites@3.1.0: + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - dev: true - /chalk@4.1.2: + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /chownr@2.0.0: + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - dev: false - /color-convert@2.0.1: + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - dev: true - /color-name@1.1.4: + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true - /color-support@1.1.3: + color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true - dev: false - /combined-stream@1.0.8: + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: false - /component-emitter@1.3.1: + component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - dev: false - /concat-map@0.0.1: + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - /console-control-strings@1.1.0: + console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: false - /cookiejar@2.1.4: + cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - dev: false - /cross-env@7.0.3: + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} hasBin: true - dependencies: - cross-spawn: 7.0.3 - dev: false - /cross-spawn@7.0.3: + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - /crypto@1.0.1: - resolution: {integrity: sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==} - deprecated: This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in. - dev: false + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} - /dargs@7.0.0: + dargs@7.0.0: resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} engines: {node: '>=8'} - dev: false - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - dependencies: - ms: 2.1.2 - /deep-is@0.1.4: + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true - /define-data-property@1.1.4: + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - dev: false - /delayed-stream@1.0.0: + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: false - /delegates@1.0.0: + delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: false - /detect-libc@2.0.3: + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} - dev: false - /dir-glob@3.0.1: + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - dev: true - /discord-api-types@0.37.61: + discord-api-types@0.37.61: resolution: {integrity: sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw==} - dev: false - /discord-api-types@0.37.83: + discord-api-types@0.37.83: resolution: {integrity: sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==} - dev: false - /discord-api-types@0.37.85: - resolution: {integrity: sha512-T75aB9JEw9X0rlMChEMHbr9JlXMqdmdKiZVjBeKs91cJo28IuAIldj+VZoC+I+Q9gCaRjHlwRkcksy2XZY6c3A==} - dev: false + discord-api-types@0.37.92: + resolution: {integrity: sha512-7xnedbQRLRef/O+4jKPyIFwl6YqoyihOG3OSneiRmVJMBk30ph2YuZGcHjeX1Kk/a3yQWeyCKe4RZJB3iECcxg==} - /discord.js@14.14.1: - resolution: {integrity: sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w==} + discord.js@14.15.3: + resolution: {integrity: sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ==} engines: {node: '>=16.11.0'} - dependencies: - '@discordjs/builders': 1.8.1 - '@discordjs/collection': 1.5.3 - '@discordjs/formatters': 0.3.3 - '@discordjs/rest': 2.3.0 - '@discordjs/util': 1.1.0 - '@discordjs/ws': 1.1.0 - '@sapphire/snowflake': 3.5.1 - '@types/ws': 8.5.9 - discord-api-types: 0.37.61 - fast-deep-equal: 3.1.3 - lodash.snakecase: 4.1.1 - tslib: 2.6.2 - undici: 5.27.2 - ws: 8.14.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: false - /distube-yandex-music-plugin@0.2.1(distube@4.2.2): - resolution: {integrity: sha512-irCjn3t3JctfxLR86Mi9twcvDs8hTouYArpFR2nV/SkW59z1SA/0XUfqAJ0pBBW/aSADR06RqJi/WMH4MIWVPw==} + distube-apple-music@0.1.0: + resolution: {integrity: sha512-f+YMXZ5+9RX7HTvtp0V51A987RwiniiCyDzwy0ZFVFgWQq0EPqziYEmZ5D1RSZ6c5s6+YCc9vIC8KPulezdhIw==} peerDependencies: - distube: ^3.3.1||4 - dependencies: - axios: 1.7.2 - distube: 4.2.2(@discordjs/voice@0.16.1)(discord.js@14.14.1) - yandex-music-client: 0.4.3 - transitivePeerDependencies: - - debug - dev: false + distube: '5' - /distube@4.2.2(@discordjs/voice@0.16.1)(discord.js@14.14.1): - resolution: {integrity: sha512-DBfyScFM66RFGWzPlf+mXy4uU4wfF33qyCr+ojDjHsVYy7m7CJo3e9tkpz/PhIo8OdU/gb4TDiUHc8Pj0r5B/Q==} + distube-yandex-music-plugin@1.0.4: + resolution: {integrity: sha512-A3VnMHqyoKxsp/2S1ZSuYpSOQboY2H2kr4zwIY7OYoVBAy4ZDbzKC2IWB47F49n+bz6kgi4tnps5WI+8wg2DRg==} + peerDependencies: + distube: '5' + + distube@5.0.2: + resolution: {integrity: sha512-VR+js/RS+sbxQIGaewh1jZtQDeSE1x05V6CFDb7/KATXSln+nUemIh/RTqKPuPeFJhcPcYGdZWPvEliDU/RS/g==} engines: {node: '>=18.17'} peerDependencies: '@discordjs/voice': '*' discord.js: '14' - dependencies: - '@discordjs/voice': 0.16.1(@discordjs/opus@0.9.0) - '@distube/ytdl-core': 4.13.3 - '@distube/ytpl': 1.2.1 - '@distube/ytsr': 2.0.0 - discord.js: 14.14.1 - tiny-typed-emitter: 2.1.0 - tough-cookie: 4.1.4 - tslib: 2.6.2 - undici: 6.18.1 - transitivePeerDependencies: - - deasync - - supports-color - dev: false - /doctrine@3.0.0: + doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - dev: true - /dotenv@16.4.5: + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} - dev: false - /emoji-regex@8.0.0: + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: false - /es-define-property@1.0.0: + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.4 - dev: false - /es-errors@1.3.0: + es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - dev: false - /escape-string-regexp@4.0.0: + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - dev: true - /eslint-config-prettier@9.1.0(eslint@8.57.0): + eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' - dependencies: - eslint: 8.57.0 - dev: true - /eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5): + eslint-plugin-prettier@5.1.3: resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -995,216 +626,98 @@ packages: optional: true eslint-config-prettier: optional: true - dependencies: - eslint: 8.57.0 - eslint-config-prettier: 9.1.0(eslint@8.57.0) - prettier: 3.2.5 - prettier-linter-helpers: 1.0.0 - synckit: 0.8.8 - dev: true - /eslint-scope@5.1.1: + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - dev: true - /eslint-scope@7.2.2: + eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - /eslint-visitor-keys@3.4.3: + eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint@8.57.0: + eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - /espree@9.6.1: + espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 - dev: true - /esquery@1.5.0: + esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - /estraverse@4.3.0: + estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} - dev: true - /estraverse@5.3.0: + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - dev: true - /esutils@2.0.3: + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - dev: true - - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: false - /fast-deep-equal@3.1.3: + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - /fast-diff@1.3.0: + fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true - /fast-glob@3.3.2: + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.7 - dev: true - /fast-json-stable-stringify@2.1.0: + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true - /fast-levenshtein@2.0.6: + fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - /fast-safe-stringify@2.1.1: + fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - dev: false - /fastq@1.17.1: + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - dependencies: - reusify: 1.0.4 - dev: true - /file-entry-cache@6.0.1: + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.2.0 - dev: true - /file-type@18.7.0: + file-type@18.7.0: resolution: {integrity: sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==} engines: {node: '>=14.16'} - dependencies: - readable-web-to-node-stream: 3.0.2 - strtok3: 7.0.0 - token-types: 5.0.1 - dev: false - /fill-range@7.1.1: + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: true - /find-up@5.0.0: + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: true - /flat-cache@3.2.0: + flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.3.1 - keyv: 4.5.4 - rimraf: 3.0.2 - dev: true - /flatted@3.3.1: + flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - dev: true - /follow-redirects@1.15.6: + follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} peerDependencies: @@ -1212,462 +725,276 @@ packages: peerDependenciesMeta: debug: optional: true - dev: false - /form-data@3.0.1: + form-data@3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - /form-data@4.0.0: + form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - /formidable@1.2.6: + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + formidable@1.2.6: resolution: {integrity: sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==} deprecated: 'Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau' - dev: false - /fs-minipass@2.1.0: + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - /fs.realpath@1.0.0: + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - /function-bind@1.1.2: + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - dev: false - /gauge@3.0.2: + gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} deprecated: This package is no longer supported. - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: false - /get-intrinsic@1.2.4: + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - dev: false - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: false - - /glob-parent@5.1.2: + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: true - /glob-parent@6.0.2: + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - dev: true - /glob@7.2.3: + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - /globals@13.24.0: + globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true - /globby@11.1.0: + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 3.0.0 - dev: true - /gopd@1.0.1: + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.4 - dev: false - /graphemer@1.4.0: + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true - /has-flag@4.0.0: + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true - /has-property-descriptors@1.0.2: + has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - dependencies: - es-define-property: 1.0.0 - dev: false - /has-proto@1.0.3: + has-proto@1.0.3: resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} - dev: false - /has-symbols@1.0.3: + has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} - dev: false - /has-unicode@2.0.1: + has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: false - /hasown@2.0.2: + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - dependencies: - function-bind: 1.1.2 - dev: false - /himalaya@1.1.0: + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + himalaya@1.1.0: resolution: {integrity: sha512-LLase1dHCRMel68/HZTFft0N0wti0epHr3nNY7ynpLbyZpmrKMQ8YIpiOV77TM97cNpC8Wb2n6f66IRggwdWPw==} - dev: false - /http-cookie-agent@5.0.4(tough-cookie@4.1.4)(undici@5.28.4): - resolution: {integrity: sha512-OtvikW69RvfyP6Lsequ0fN5R49S+8QcS9zwd58k6VSr6r57T8G29BkPdyrBcSwLq6ExLs9V+rBlfxu7gDstJag==} - engines: {node: '>=14.18.0 <15.0.0 || >=16.0.0'} + http-cookie-agent@6.0.5: + resolution: {integrity: sha512-sfZ8fDgDP3B1YB+teqSnAK1aPgBu8reUUGxSsndP2XnYN6cM29EURXWXZqQQiaRdor3B4QjpkUNfv21syaO4DA==} + engines: {node: '>=18.0.0'} peerDependencies: - deasync: ^0.1.26 tough-cookie: ^4.0.0 - undici: ^5.11.0 + undici: ^5.11.0 || ^6.0.0 peerDependenciesMeta: - deasync: - optional: true undici: optional: true - dependencies: - agent-base: 7.1.1 - tough-cookie: 4.1.4 - undici: 5.28.4 - transitivePeerDependencies: - - supports-color - dev: false - /https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - dev: false - /i18next-fs-backend@2.3.1: + i18next-fs-backend@2.3.1: resolution: {integrity: sha512-tvfXskmG/9o+TJ5Fxu54sSO5OkY6d+uMn+K6JiUGLJrwxAVfer+8V3nU8jq3ts9Pe5lXJv4b1N7foIjJ8Iy2Gg==} - dev: false - /i18next@22.5.1: + i18next@22.5.1: resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} - dependencies: - '@babel/runtime': 7.24.6 - dev: false - /ieee754@1.2.1: + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: false - /ignore@5.3.1: + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} - dev: true - /import-fresh@3.3.0: + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: true - /imurmurhash@0.1.4: + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - dev: true - /inflight@1.0.6: + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - /inherits@2.0.4: + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /ip-address@9.0.5: + ip-address@9.0.5: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 - dev: false - /is-extglob@2.1.1: + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true - /is-fullwidth-code-point@3.0.0: + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: false - /is-glob@4.0.3: + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: true - /is-number@7.0.0: + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true - /is-path-inside@3.0.3: + is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - dev: true - - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false - /isexe@2.0.0: + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - /js-yaml@4.1.0: + isomorphic-unfetch@4.0.2: + resolution: {integrity: sha512-1Yd+CF/7al18/N2BDbsLBcp6RO3tucSW+jcLq24dqdX5MNbCNTw1z4BsGsp4zNmjr/Izm2cs/cEqZPp4kvWSCA==} + + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - /jsbn@1.1.0: + jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - dev: false - /json-buffer@3.0.1: + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true - /json-schema-traverse@0.4.1: + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - /json-stable-stringify-without-jsonify@1.0.1: + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true - /kareem@2.5.1: + kareem@2.5.1: resolution: {integrity: sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==} engines: {node: '>=12.0.0'} - dev: false - /keyv@4.5.4: + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - dev: true - /levn@0.4.1: + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - /locate-path@6.0.0: + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: true - /lodash.merge@4.6.2: + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - /lodash.snakecase@4.1.1: + lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - dev: false - /lodash@4.17.21: + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: false - /m3u8stream@0.8.6: + m3u8stream@0.8.6: resolution: {integrity: sha512-LZj8kIVf9KCphiHmH7sbFQTVe4tOemb202fWwvJwR9W5ENW/1hxJN6ksAWGhQgSBSa3jyWhnjKU1Fw1GaOdbyA==} engines: {node: '>=12'} - dependencies: - miniget: 4.2.3 - sax: 1.3.0 - dev: false - /magic-bytes.js@1.10.0: + magic-bytes.js@1.10.0: resolution: {integrity: sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==} - dev: false - /make-dir@3.1.0: + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} - dependencies: - semver: 6.3.1 - dev: false - /memory-pager@1.5.0: + memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - requiresBuild: true - dev: false - optional: true - - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: false - /merge2@1.4.1: + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true - /methods@1.1.2: + methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - dev: false - /micromatch@4.0.7: + micromatch@4.0.7: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - dev: true - /mime-db@1.52.0: + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: false - /mime-types@2.1.35: + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: false - /mime@2.6.0: + mime@2.6.0: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} engines: {node: '>=4.0.0'} hasBin: true - dev: false - - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: false - /miniget@4.2.3: + miniget@4.2.3: resolution: {integrity: sha512-SjbDPDICJ1zT+ZvQwK0hUcRY4wxlhhNpHL9nJOB2MEAXRGagTljsO8MEDzQMTFf0Q8g4QNi8P9lEm/g7e+qgzA==} engines: {node: '>=12'} - dev: false - /minimatch@3.1.2: + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - /minipass@3.3.6: + minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 - dev: false - /minipass@5.0.0: + minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - dev: false - /minizlib@2.1.2: + minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - dev: false - /mkdirp@1.0.4: + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - dev: false - /mongodb-connection-string-url@2.6.0: + mongodb-connection-string-url@2.6.0: resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} - dependencies: - '@types/whatwg-url': 8.2.2 - whatwg-url: 11.0.0 - dev: false - /mongodb@5.9.2: + mongodb@5.9.2: resolution: {integrity: sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==} engines: {node: '>=14.20.1'} peerDependencies: @@ -1687,68 +1014,39 @@ packages: optional: true snappy: optional: true - dependencies: - bson: 5.5.1 - mongodb-connection-string-url: 2.6.0 - socks: 2.8.3 - optionalDependencies: - '@mongodb-js/saslprep': 1.1.7 - dev: false - /mongoose@7.6.12: - resolution: {integrity: sha512-bjnOELGT2wWV3qjPZC4hol+O/QP4i7LrWenrtaN76A0X69T+Y19lmAhp74kLTNY13Djp1Agz1hoOq3bthmGLTg==} + mongoose@7.7.0: + resolution: {integrity: sha512-+HcoN/hmkB5IjAqWYA2ZAQeExGD8FNMe6L/eTYB04gqp9S2ZEngVivGkdtGrA4BYRf0suH+3rMNFW2JPOqC4Mg==} engines: {node: '>=14.20.1'} - dependencies: - bson: 5.5.1 - kareem: 2.5.1 - mongodb: 5.9.2 - mpath: 0.9.0 - mquery: 5.0.0 - ms: 2.1.3 - sift: 16.0.1 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - kerberos - - mongodb-client-encryption - - snappy - - supports-color - dev: false - /mpath@0.9.0: + mpath@0.9.0: resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==} engines: {node: '>=4.0.0'} - dev: false - /mquery@5.0.0: + mquery@5.0.0: resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==} engines: {node: '>=14.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - /ms@2.1.2: + ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - /ms@2.1.3: + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: false - /natural-compare-lite@1.4.0: + natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true - /natural-compare@1.4.0: + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true - /node-addon-api@5.1.0: + node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - dev: false - /node-fetch@2.7.0: + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} peerDependencies: @@ -1756,145 +1054,101 @@ packages: peerDependenciesMeta: encoding: optional: true - dependencies: - whatwg-url: 5.0.0 - dev: false - /node-gyp-build@4.8.1: + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp-build@4.8.1: resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} hasBin: true - dev: false - /node-os-utils@1.3.7: + node-html-parser@6.1.13: + resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} + + node-os-utils@1.3.7: resolution: {integrity: sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==} - dev: false - /nopt@5.0.0: + nopt@5.0.0: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} engines: {node: '>=6'} hasBin: true - dependencies: - abbrev: 1.1.1 - dev: false - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: false - - /npmlog@5.0.1: + npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} deprecated: This package is no longer supported. - dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - dev: false - /object-assign@4.1.1: + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: false - /object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - dev: false + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} - /once@1.4.0: + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - dev: false - /optionator@0.9.4: + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - dev: true - /p-limit@3.1.0: + opusscript@0.1.1: + resolution: {integrity: sha512-mL0fZZOUnXdZ78woRXp18lApwpp0lF5tozJOD1Wut0dgrA9WuQTgSels/CSmFleaAZrJi/nci5KOVtbuxeWoQA==} + + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: true - /p-locate@5.0.0: + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: true - /parent-module@1.0.1: + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: true - /path-exists@4.0.0: + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true - /path-is-absolute@1.0.1: + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - /path-key@3.1.1: + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - /path-type@4.0.0: + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true - /peek-readable@5.0.0: - resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} + peek-readable@5.1.1: + resolution: {integrity: sha512-4hEOSH7KeEaZpMDF/xfm1W9fS5rT7Ett3BkXWHqAEzRLLwLaHkwOL+GvvpIEh9UrvX9BDhzfkvteslgraoH69w==} engines: {node: '>=14.16'} - dev: false - /picomatch@2.3.1: + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true - /prelude-ls@1.2.1: + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - dev: true - /prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.0: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - dependencies: - fast-diff: 1.3.0 - dev: true - /prettier@3.2.5: - resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} + prettier@3.3.2: + resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==} engines: {node: '>=14'} hasBin: true - dev: true - /prism-media@1.3.5(@discordjs/opus@0.9.0): + prism-media@1.3.5: resolution: {integrity: sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==} peerDependencies: '@discordjs/opus': '>=0.8.0 <1.0.0' @@ -1910,487 +1164,294 @@ packages: optional: true opusscript: optional: true - dependencies: - '@discordjs/opus': 0.9.0 - dev: false - /proxy-from-env@1.1.0: + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false - /psl@1.9.0: + psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: false - /punycode@2.3.1: + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - /qs@6.12.1: - resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} + qs@6.12.2: + resolution: {integrity: sha512-x+NLUpx9SYrcwXtX7ob1gnkSems4i/mGZX5SlYxwIau6RrUSODO89TR/XDGGpn5RPWSYIB+aSfuSlV5+CmbTBg==} engines: {node: '>=0.6'} - dependencies: - side-channel: 1.0.6 - dev: false - /querystringify@2.2.0: + querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - dev: false - /queue-microtask@1.2.3: + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true - /readable-stream@3.6.2: + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - dev: false - /readable-web-to-node-stream@3.0.2: + readable-web-to-node-stream@3.0.2: resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} engines: {node: '>=8'} - dependencies: - readable-stream: 3.6.2 - dev: false - /regenerator-runtime@0.14.1: + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: false - /requires-port@1.0.0: + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - dev: false - /resolve-from@4.0.0: + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - dev: true - /reusify@1.0.4: + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true - /rimraf@3.0.2: + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - dependencies: - glob: 7.2.3 - /run-parallel@1.2.0: + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - dev: true - /safe-buffer@5.2.1: + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - /sax@1.3.0: - resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} - dev: false + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - /semver@6.3.1: + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - dev: false - /semver@7.6.2: + semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} hasBin: true - /set-blocking@2.0.0: + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: false - /set-function-length@1.2.2: + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - dev: false - /shebang-command@2.0.0: + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - /shebang-regex@3.0.0: + shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - /side-channel@1.0.6: + side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 - dev: false - /sift@16.0.1: + sift@16.0.1: resolution: {integrity: sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==} - dev: false - /signal-exit@3.0.7: + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: false - /slash@3.0.0: + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - dev: true - /smart-buffer@4.2.0: + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: false - /socks@2.8.3: + socks@2.8.3: resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - dependencies: - ip-address: 9.0.5 - smart-buffer: 4.2.0 - dev: false - /sodium-native@4.1.1: + sodium-native@4.1.1: resolution: {integrity: sha512-LXkAfRd4FHtkQS4X6g+nRcVaN7mWVNepV06phIsC6+IZFvGh1voW5TNQiQp2twVaMf05gZqQjuS+uWLM6gHhNQ==} - requiresBuild: true - dependencies: - node-gyp-build: 4.8.1 - dev: false - /soundcloud.ts@0.5.3: + soundcloud.ts@0.5.3: resolution: {integrity: sha512-ZMH6gG5e7WqJrIYXTv14MNArPhx3WzfrL1Ij/2qBDW8mVbNJc8lxOQOc4kLvrfvDl5TkCdZa7zXOiwD6ESXq+g==} - dependencies: - undici: 6.18.1 - dev: false - /sparse-bitfield@3.0.3: + sparse-bitfield@3.0.3: resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} - requiresBuild: true - dependencies: - memory-pager: 1.5.0 - dev: false - optional: true - /spotify-uri@4.1.0: + spotify-uri@4.1.0: resolution: {integrity: sha512-SFpBt8pQqO7DOFBsdUjv3GxGZAKYP7UqcTflfE7h3YL1lynl/6Motq7NERoJJR8eF9kXQRSpcdMmV5ou84rbng==} engines: {node: '>= 16'} - dev: false - /spotify-url-info@3.2.15: - resolution: {integrity: sha512-NlolD2mqZjvt91hq0XVy3db55FCL2li1XBivkf/i+zUW1K9RP9nIOK5Bpg82DmBf96sjl4TYRI3zPOS94LPulQ==} + spotify-url-info@3.2.16: + resolution: {integrity: sha512-szXt1PLt8lqhaXsTNYH7zPd+EBj6Ha0xtqitqicCijGX6x/jYvn6wgGaK2F1OQfJzx8lxDNfZbtarn4DxzaZ2Q==} engines: {node: '>= 12'} - dependencies: - himalaya: 1.1.0 - spotify-uri: 4.1.0 - dev: false - /spotify-web-api-node@5.0.2: + spotify-web-api-node@5.0.2: resolution: {integrity: sha512-r82dRWU9PMimHvHEzL0DwEJrzFk+SMCVfq249SLt3I7EFez7R+jeoKQd+M1//QcnjqlXPs2am4DFsGk8/GCsrA==} - dependencies: - superagent: 6.1.0 - transitivePeerDependencies: - - supports-color - dev: false - /sprintf-js@1.1.3: + sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - dev: false - /string-width@4.2.3: + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: false - /string_decoder@1.3.0: + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - dependencies: - safe-buffer: 5.2.1 - dev: false - /strip-ansi@6.0.1: + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: false - /strip-json-comments@3.1.1: + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - dev: true - /strtok3@7.0.0: - resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} + strtok3@7.1.0: + resolution: {integrity: sha512-19dQEwG6Jd+VabjPRyBhymIF069vZiqWSZa2jJBoKJTsqGKnTxowGoQaLnz+yLARfDI041IUQekyPUMWElOgsQ==} engines: {node: '>=14.16'} - dependencies: - '@tokenizer/token': 0.3.0 - peek-readable: 5.0.0 - dev: false - /superagent@6.1.0: + superagent@6.1.0: resolution: {integrity: sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==} engines: {node: '>= 7.0.0'} deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net - dependencies: - component-emitter: 1.3.1 - cookiejar: 2.1.4 - debug: 4.3.4 - fast-safe-stringify: 2.1.1 - form-data: 3.0.1 - formidable: 1.2.6 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.12.1 - readable-stream: 3.6.2 - semver: 7.6.2 - transitivePeerDependencies: - - supports-color - dev: false - /supports-color@7.2.0: + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - dev: true - /synckit@0.8.8: + synckit@0.8.8: resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} engines: {node: ^14.18.0 || >=16.0.0} - dependencies: - '@pkgr/core': 0.1.1 - tslib: 2.6.2 - dev: true - /tar@6.2.1: + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - dev: false - /text-table@0.2.0: + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true - /tiny-typed-emitter@2.1.0: + tiny-typed-emitter@2.1.0: resolution: {integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==} - dev: false - /to-regex-range@5.0.1: + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - dev: true - /token-types@5.0.1: + token-types@5.0.1: resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} engines: {node: '>=14.16'} - dependencies: - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - dev: false - /tough-cookie@4.1.4: + tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} - dependencies: - psl: 1.9.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 - dev: false - /tr46@0.0.3: + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false - /tr46@3.0.0: + tr46@3.0.0: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} - dependencies: - punycode: 2.3.1 - dev: false - /ts-mixer@6.0.4: + ts-mixer@6.0.4: resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} - dev: false - /tslib@1.14.1: + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true - /tslib@2.6.2: + tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - /tsutils@3.21.0(typescript@5.4.5): + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tsutils@3.21.0: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 5.4.5 - dev: true - /type-check@0.4.0: + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: true - /type-fest@0.20.2: + type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - dev: true - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + typescript@5.5.3: + resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} engines: {node: '>=14.17'} hasBin: true - dev: true - /undici-types@5.26.5: + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - /undici@5.27.2: - resolution: {integrity: sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==} - engines: {node: '>=14.0'} - dependencies: - '@fastify/busboy': 2.1.1 - dev: false - - /undici@5.28.4: + undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} - dependencies: - '@fastify/busboy': 2.1.1 - dev: false - - /undici@6.0.1: - resolution: {integrity: sha512-eZFYQLeS9BiXpsU0cuFhCwfeda2MnC48EVmmOz/eCjsTgmyTdaHdVsPSC/kwC2GtW2e0uH0HIPbadf3/bRWSxw==} - engines: {node: '>=18.0'} - dependencies: - '@fastify/busboy': 2.1.1 - dev: false - /undici@6.13.0: + undici@6.13.0: resolution: {integrity: sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==} engines: {node: '>=18.0'} - dev: false - /undici@6.18.1: - resolution: {integrity: sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==} + undici@6.19.2: + resolution: {integrity: sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==} engines: {node: '>=18.17'} - dev: false - /universalify@0.2.0: + unfetch@5.0.0: + resolution: {integrity: sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==} + + universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} - dev: false - /uri-js@4.4.1: + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.1 - dev: true - /url-parse@1.5.10: + url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - dev: false - /util-deprecate@1.0.2: + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: false - /uuid@9.0.1: + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - dev: false - /webidl-conversions@3.0.1: + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false - /webidl-conversions@7.0.0: + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} - dev: false - /whatwg-url@11.0.0: + whatwg-url@11.0.0: resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} engines: {node: '>=12'} - dependencies: - tr46: 3.0.0 - webidl-conversions: 7.0.0 - dev: false - /whatwg-url@5.0.0: + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false - /which@2.0.2: + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - dependencies: - isexe: 2.0.0 - /wide-align@1.1.5: + wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - dependencies: - string-width: 4.2.3 - dev: false - /word-wrap@1.2.5: + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - dev: true - /wrappy@1.0.2: + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - /ws@8.14.2: - resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2400,37 +1461,1458 @@ packages: optional: true utf-8-validate: optional: true - dev: false - /ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} - /yallist@4.0.0: + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: false - /yandex-music-client@0.4.3: - resolution: {integrity: sha512-bsWMXxTozWEgtRtEja/qkeVPmF5rTU5HPsXgNrg9uoTOkjU6SfmoaaKL9kwBBtU/55wxnKvpHlKozPLsfGIMvQ==} - dependencies: - crypto: 1.0.1 - form-data: 4.0.0 - dev: false + ym-api-meowed@1.3.4: + resolution: {integrity: sha512-vHnfdGLgmOj8ST6fXmpKsK/YDO1JxdHJFcRuqrtEJeLFv727vtX2AVUjF46qNpjhVARD/GHqMDL3HE9ixjLdJg==} - /yocto-queue@0.1.0: + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - dev: true - /zod@3.23.8: + zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - dev: false + +snapshots: + + '@babel/runtime@7.24.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@discordjs/builders@1.8.2': + dependencies: + '@discordjs/formatters': 0.4.0 + '@discordjs/util': 1.1.0 + '@sapphire/shapeshift': 3.9.7 + discord-api-types: 0.37.83 + fast-deep-equal: 3.1.3 + ts-mixer: 6.0.4 + tslib: 2.6.2 + + '@discordjs/collection@1.5.3': {} + + '@discordjs/collection@2.1.0': {} + + '@discordjs/formatters@0.4.0': + dependencies: + discord-api-types: 0.37.83 + + '@discordjs/node-pre-gyp@0.4.5': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.6.2 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@discordjs/opus@0.9.0': + dependencies: + '@discordjs/node-pre-gyp': 0.4.5 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@discordjs/rest@1.7.1': + dependencies: + '@discordjs/collection': 1.5.3 + '@discordjs/util': 0.3.1 + '@sapphire/async-queue': 1.5.2 + '@sapphire/snowflake': 3.5.3 + discord-api-types: 0.37.92 + file-type: 18.7.0 + tslib: 2.6.3 + undici: 5.28.4 + + '@discordjs/rest@2.3.0': + dependencies: + '@discordjs/collection': 2.1.0 + '@discordjs/util': 1.1.0 + '@sapphire/async-queue': 1.5.2 + '@sapphire/snowflake': 3.5.3 + '@vladfrangu/async_event_emitter': 2.4.1 + discord-api-types: 0.37.83 + magic-bytes.js: 1.10.0 + tslib: 2.6.2 + undici: 6.13.0 + + '@discordjs/util@0.3.1': {} + + '@discordjs/util@1.1.0': {} + + '@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1)': + dependencies: + '@types/ws': 8.5.10 + discord-api-types: 0.37.61 + prism-media: 1.3.5(@discordjs/opus@0.9.0)(opusscript@0.1.1) + tslib: 2.6.3 + ws: 8.18.0 + transitivePeerDependencies: + - '@discordjs/opus' + - bufferutil + - ffmpeg-static + - node-opus + - opusscript + - utf-8-validate + + '@discordjs/ws@1.1.1': + dependencies: + '@discordjs/collection': 2.1.0 + '@discordjs/rest': 2.3.0 + '@discordjs/util': 1.1.0 + '@sapphire/async-queue': 1.5.2 + '@types/ws': 8.5.10 + '@vladfrangu/async_event_emitter': 2.4.1 + discord-api-types: 0.37.83 + tslib: 2.6.2 + ws: 8.18.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@distube/direct-link@1.0.1(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3))': + dependencies: + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + undici: 6.19.2 + + '@distube/file@1.0.1(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3))': + dependencies: + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + + '@distube/soundcloud@2.0.2(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3))': + dependencies: + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + soundcloud.ts: 0.5.3 + + '@distube/spotify@2.0.2(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3))': + dependencies: + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + spotify-uri: 4.1.0 + spotify-url-info: 3.2.16 + spotify-web-api-node: 5.0.2 + undici: 6.19.2 + transitivePeerDependencies: + - supports-color + + '@distube/youtube@1.0.2(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3))': + dependencies: + '@distube/ytdl-core': 4.13.4 + '@distube/ytpl': 1.2.1 + '@distube/ytsr': 2.0.4 + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + transitivePeerDependencies: + - supports-color + + '@distube/yt-dlp@2.0.1(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3))': + dependencies: + dargs: 7.0.0 + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + undici: 6.19.2 + + '@distube/ytdl-core@4.13.4': + dependencies: + http-cookie-agent: 6.0.5(tough-cookie@4.1.4)(undici@6.19.2) + m3u8stream: 0.8.6 + miniget: 4.2.3 + sax: 1.4.1 + tough-cookie: 4.1.4 + undici: 6.19.2 + transitivePeerDependencies: + - supports-color + + '@distube/ytpl@1.2.1': + dependencies: + undici: 5.28.4 + + '@distube/ytsr@2.0.4': + dependencies: + undici: 6.19.2 + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.5 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.0': {} + + '@fastify/busboy@2.1.1': {} + + '@humanwhocodes/config-array@0.11.14': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.5 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@mongodb-js/saslprep@1.1.7': + dependencies: + sparse-bitfield: 3.0.3 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgr/core@0.1.1': {} + + '@sapphire/async-queue@1.5.2': {} + + '@sapphire/shapeshift@3.9.7': + dependencies: + fast-deep-equal: 3.1.3 + lodash: 4.17.21 + + '@sapphire/snowflake@3.5.3': {} + + '@tokenizer/token@0.3.0': {} + + '@types/json-schema@7.0.15': {} + + '@types/node-os-utils@1.3.4': {} + + '@types/node@20.14.10': + dependencies: + undici-types: 5.26.5 + + '@types/semver@7.5.8': {} + + '@types/uuid@9.0.8': {} + + '@types/webidl-conversions@7.0.3': {} + + '@types/whatwg-url@8.2.2': + dependencies: + '@types/node': 20.14.10 + '@types/webidl-conversions': 7.0.3 + + '@types/ws@8.5.10': + dependencies: + '@types/node': 20.14.10 + + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.3) + debug: 4.3.5 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare-lite: 1.4.0 + semver: 7.6.2 + tsutils: 3.21.0(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.3) + debug: 4.3.5 + eslint: 8.57.0 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.5.3) + debug: 4.3.5 + eslint: 8.57.0 + tsutils: 3.21.0(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.3)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.5 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.6.2 + tsutils: 3.21.0(typescript@5.5.3) + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.5.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.3) + eslint: 8.57.0 + eslint-scope: 5.1.1 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + '@vladfrangu/async_event_emitter@2.4.1': {} + + abbrev@1.1.1: + optional: true + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + optional: true + + agent-base@7.1.1: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + aproba@2.0.0: + optional: true + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + asynckit@0.4.0: {} + + axios@1.7.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + boolbase@1.0.0: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + bson@5.5.1: {} + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chownr@2.0.0: + optional: true + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-support@1.1.3: + optional: true + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + component-emitter@1.3.1: {} + + concat-map@0.0.1: {} + + console-control-strings@1.1.0: + optional: true + + cookiejar@2.1.4: {} + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.3 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-what@6.1.0: {} + + dargs@7.0.0: {} + + data-uri-to-buffer@4.0.1: {} + + debug@4.3.5: + dependencies: + ms: 2.1.2 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: + optional: true + + detect-libc@2.0.3: + optional: true + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + discord-api-types@0.37.61: {} + + discord-api-types@0.37.83: {} + + discord-api-types@0.37.92: {} + + discord.js@14.15.3: + dependencies: + '@discordjs/builders': 1.8.2 + '@discordjs/collection': 1.5.3 + '@discordjs/formatters': 0.4.0 + '@discordjs/rest': 2.3.0 + '@discordjs/util': 1.1.0 + '@discordjs/ws': 1.1.1 + '@sapphire/snowflake': 3.5.3 + discord-api-types: 0.37.83 + fast-deep-equal: 3.1.3 + lodash.snakecase: 4.1.1 + tslib: 2.6.2 + undici: 6.13.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + distube-apple-music@0.1.0(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)): + dependencies: + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + isomorphic-unfetch: 4.0.2 + node-html-parser: 6.1.13 + + distube-yandex-music-plugin@1.0.4(distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3)): + dependencies: + distube: 5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3) + ym-api-meowed: 1.3.4 + transitivePeerDependencies: + - debug + + distube@5.0.2(@discordjs/voice@0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1))(discord.js@14.15.3): + dependencies: + '@discordjs/voice': 0.16.1(@discordjs/opus@0.9.0)(opusscript@0.1.1) + discord.js: 14.15.3 + tiny-typed-emitter: 2.1.0 + undici: 6.19.2 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv@16.4.5: {} + + emoji-regex@8.0.0: + optional: true + + entities@4.5.0: {} + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@9.1.0(eslint@8.57.0): + dependencies: + eslint: 8.57.0 + + eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.2): + dependencies: + eslint: 8.57.0 + prettier: 3.3.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.8 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@8.57.0) + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.11.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.5 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + + esquery@1.5.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-safe-stringify@2.1.1: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-type@18.7.0: + dependencies: + readable-web-to-node-stream: 3.0.2 + strtok3: 7.1.0 + token-types: 5.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + follow-redirects@1.15.6: {} + + form-data@3.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + formidable@1.2.6: {} + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + optional: true + + fs.realpath@1.0.0: {} + + function-bind@1.1.2: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-unicode@2.0.1: + optional: true + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + himalaya@1.1.0: {} + + http-cookie-agent@6.0.5(tough-cookie@4.1.4)(undici@6.19.2): + dependencies: + agent-base: 7.1.1 + tough-cookie: 4.1.4 + optionalDependencies: + undici: 6.19.2 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + optional: true + + i18next-fs-backend@2.3.1: {} + + i18next@22.5.1: + dependencies: + '@babel/runtime': 7.24.7 + + ieee754@1.2.1: {} + + ignore@5.3.1: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: + optional: true + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + isexe@2.0.0: {} + + isomorphic-unfetch@4.0.2: + dependencies: + node-fetch: 3.3.2 + unfetch: 5.0.0 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsbn@1.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + kareem@2.5.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash@4.17.21: {} + + m3u8stream@0.8.6: + dependencies: + miniget: 4.2.3 + sax: 1.4.1 + + magic-bytes.js@1.10.0: {} + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + optional: true + + memory-pager@1.5.0: + optional: true + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@2.6.0: {} + + miniget@4.2.3: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + optional: true + + minipass@5.0.0: + optional: true + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + optional: true + + mkdirp@1.0.4: + optional: true + + mongodb-connection-string-url@2.6.0: + dependencies: + '@types/whatwg-url': 8.2.2 + whatwg-url: 11.0.0 + + mongodb@5.9.2: + dependencies: + bson: 5.5.1 + mongodb-connection-string-url: 2.6.0 + socks: 2.8.3 + optionalDependencies: + '@mongodb-js/saslprep': 1.1.7 + + mongoose@7.7.0: + dependencies: + bson: 5.5.1 + kareem: 2.5.1 + mongodb: 5.9.2 + mpath: 0.9.0 + mquery: 5.0.0 + ms: 2.1.3 + sift: 16.0.1 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - kerberos + - mongodb-client-encryption + - snappy + - supports-color + + mpath@0.9.0: {} + + mquery@5.0.0: + dependencies: + debug: 4.3.5 + transitivePeerDependencies: + - supports-color + + ms@2.1.2: {} + + ms@2.1.3: {} + + natural-compare-lite@1.4.0: {} + + natural-compare@1.4.0: {} + + node-addon-api@5.1.0: + optional: true + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + optional: true + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-gyp-build@4.8.1: {} + + node-html-parser@6.1.13: + dependencies: + css-select: 5.1.0 + he: 1.2.0 + + node-os-utils@1.3.7: {} + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + optional: true + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + object-assign@4.1.1: + optional: true + + object-inspect@1.13.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + opusscript@0.1.1: {} + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-type@4.0.0: {} + + peek-readable@5.1.1: {} + + picomatch@2.3.1: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.3.2: {} + + prism-media@1.3.5(@discordjs/opus@0.9.0)(opusscript@0.1.1): + optionalDependencies: + '@discordjs/opus': 0.9.0 + opusscript: 0.1.1 + + proxy-from-env@1.1.0: {} + + psl@1.9.0: {} + + punycode@2.3.1: {} + + qs@6.12.2: + dependencies: + side-channel: 1.0.6 + + querystringify@2.2.0: {} + + queue-microtask@1.2.3: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-web-to-node-stream@3.0.2: + dependencies: + readable-stream: 3.6.2 + + regenerator-runtime@0.14.1: {} + + requires-port@1.0.0: {} + + resolve-from@4.0.0: {} + + reusify@1.0.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.2.1: {} + + sax@1.4.1: {} + + semver@6.3.1: + optional: true + + semver@7.6.2: {} + + set-blocking@2.0.0: + optional: true + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + + sift@16.0.1: {} + + signal-exit@3.0.7: + optional: true + + slash@3.0.0: {} + + smart-buffer@4.2.0: {} + + socks@2.8.3: + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + + sodium-native@4.1.1: + dependencies: + node-gyp-build: 4.8.1 + + soundcloud.ts@0.5.3: + dependencies: + undici: 6.19.2 + + sparse-bitfield@3.0.3: + dependencies: + memory-pager: 1.5.0 + optional: true + + spotify-uri@4.1.0: {} + + spotify-url-info@3.2.16: + dependencies: + himalaya: 1.1.0 + spotify-uri: 4.1.0 + + spotify-web-api-node@5.0.2: + dependencies: + superagent: 6.1.0 + transitivePeerDependencies: + - supports-color + + sprintf-js@1.1.3: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + optional: true + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + + strtok3@7.1.0: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 5.1.1 + + superagent@6.1.0: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.3.5 + fast-safe-stringify: 2.1.1 + form-data: 3.0.1 + formidable: 1.2.6 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.12.2 + readable-stream: 3.6.2 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + synckit@0.8.8: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.6.3 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + optional: true + + text-table@0.2.0: {} + + tiny-typed-emitter@2.1.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + token-types@5.0.1: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + tough-cookie@4.1.4: + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@0.0.3: + optional: true + + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + + ts-mixer@6.0.4: {} + + tslib@1.14.1: {} + + tslib@2.6.2: {} + + tslib@2.6.3: {} + + tsutils@3.21.0(typescript@5.5.3): + dependencies: + tslib: 1.14.1 + typescript: 5.5.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + typescript@5.5.3: {} + + undici-types@5.26.5: {} + + undici@5.28.4: + dependencies: + '@fastify/busboy': 2.1.1 + + undici@6.13.0: {} + + undici@6.19.2: {} + + unfetch@5.0.0: {} + + universalify@0.2.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + util-deprecate@1.0.2: {} + + uuid@9.0.1: {} + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: + optional: true + + webidl-conversions@7.0.0: {} + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + optional: true + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + optional: true + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + ws@8.18.0: {} + + xml2js@0.6.2: + dependencies: + sax: 1.4.1 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + + yallist@4.0.0: + optional: true + + ym-api-meowed@1.3.4: + dependencies: + axios: 1.7.2 + xml2js: 0.6.2 + transitivePeerDependencies: + - debug + + yocto-queue@0.1.0: {} + + zod@3.23.8: {} diff --git a/src/EnvironmentVariables.ts b/src/EnvironmentVariables.ts index 8b08893..3ebdc9f 100644 --- a/src/EnvironmentVariables.ts +++ b/src/EnvironmentVariables.ts @@ -40,7 +40,8 @@ const envVariables = z.object({ BOT_SPOTIFY_CLIENT_SECRET: z.string().optional(), BOT_SPOTIFY_CLIENT_ID: z.string().optional(), - BOT_YANDEXMUSIC_TOKEN: z.string().optional() + BOT_YANDEXMUSIC_TOKEN: z.string().optional(), + BOT_YANDEXMUSIC_UID: z.coerce.number().optional() }); export const ENV = envVariables.parse(process.env); @@ -54,7 +55,7 @@ try { loggerSend('Cookie file is loaded', loggerPrefixEnv); } catch (e) { loggerError( - 'Cookie file is not provided or cookie is wrong. Please, follow this instructions https://distube.js.org/#/docs/DisTube/main/general/cookie', + 'Cookie file is not provided or cookie is wrong. Please, follow instructions in README.md', loggerPrefixEnv ); } diff --git a/src/Types.ts b/src/Types.ts index 090c23b..48f97c1 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -1,11 +1,9 @@ -import { Client, Collection } from 'discord.js'; +import { Client, ClientEvents, Collection } from 'discord.js'; import { ICommand, ICommandGroup } from './CommandTypes.js'; -import { AudioPlayerCore } from './commands/audio/audioPlayer/AudioPlayerCore.js'; -import { DisTube } from 'distube'; +import { AudioPlayerCore } from './audioplayer/AudioPlayerCore.js'; declare module 'discord.js' { export interface Client { - distube: DisTube; commands: Collection; commandsGroups: Collection; audioPlayer: AudioPlayerCore; @@ -13,7 +11,7 @@ declare module 'discord.js' { } export interface BotEvent { - name: string; + name: keyof ClientEvents; once?: boolean | false; execute: (client: Client, ...args: any) => void; } diff --git a/src/commands/audio/audioPlayer/AudioPlayerCore.ts b/src/audioplayer/AudioPlayerCore.ts similarity index 54% rename from src/commands/audio/audioPlayer/AudioPlayerCore.ts rename to src/audioplayer/AudioPlayerCore.ts index 3a86acd..b16a8db 100644 --- a/src/commands/audio/audioPlayer/AudioPlayerCore.ts +++ b/src/audioplayer/AudioPlayerCore.ts @@ -1,3 +1,15 @@ +import { DisTube, PlayOptions, Queue, RepeatMode, Song, Events as DistubeEvents } from 'distube'; +import { AudioPlayersManager } from './AudioPlayersManager.js'; +import { pagination } from '../utilities/pagination/pagination.js'; +import { ButtonStyles, ButtonTypes } from '../utilities/pagination/paginationTypes.js'; +import { clamp } from '../utilities/clamp.js'; +import { generateErrorEmbed } from '../utilities/generateErrorEmbed.js'; +import i18next from 'i18next'; +import { loggerError, loggerSend } from '../utilities/logger.js'; +import { ENV } from '../EnvironmentVariables.js'; +import { LoadPlugins } from './LoadPlugins.js'; +import { generateAddedPlaylistMessage } from './util/generateAddedPlaylistMessage.js'; +import { generateAddedSongMessage } from './util/generateAddedSongMessage.js'; import { Client, CommandInteraction, @@ -8,91 +20,12 @@ import { TextChannel, VoiceBasedChannel } from 'discord.js'; -import { - CustomPlugin, - DisTube, - ExtractorPlugin, - Playlist, - PlayOptions, - Queue, - RepeatMode, - SearchResult, - Song -} from 'distube'; -import { AudioPlayersManager } from './AudioPlayersManager.js'; -import { SpotifyPlugin } from '@distube/spotify'; -import { YtDlpPlugin } from '@distube/yt-dlp'; -import { SoundCloudPlugin } from '@distube/soundcloud'; -import { pagination } from '../../../utilities/pagination/pagination.js'; -import { ButtonStyles, ButtonTypes } from '../../../utilities/pagination/paginationTypes.js'; -import { clamp } from '../../../utilities/clamp.js'; -import { generateErrorEmbed } from '../../../utilities/generateErrorEmbed.js'; import { joinVoiceChannel } from '@discordjs/voice'; -import i18next from 'i18next'; - -import { YandexMusicPlugin } from 'distube-yandex-music-plugin'; -import { loggerError, loggerWarn } from '../../../utilities/logger.js'; -import { BOT_YOUTUBE_COOKIE, ENV } from '../../../EnvironmentVariables.js'; - -const loggerPrefixAudioplayer = `Audioplayer`; - -function LoadPlugins(): Array { - const plugins: Array = []; - - if (!BOT_YOUTUBE_COOKIE) { - loggerWarn( - 'BOT_YOUTUBE_COOKIE is not provided, 18+ videos from Youtube is not available', - loggerPrefixAudioplayer - ); - } - - if (ENV.BOT_SPOTIFY_CLIENT_ID && ENV.BOT_SPOTIFY_CLIENT_SECRET) { - plugins.push( - new SpotifyPlugin({ - parallel: true, - emitEventsAfterFetching: true, - api: { - clientId: ENV.BOT_SPOTIFY_CLIENT_ID, - clientSecret: ENV.BOT_SPOTIFY_CLIENT_SECRET - } - }) - ); - } else { - loggerWarn( - 'Spotify plugin is disabled, because BOT_SPOTIFY_CLIENT_ID and BOT_SPOTIFY_CLIENT_SECRET are wrong or not provided', - loggerPrefixAudioplayer - ); - } - - if (ENV.BOT_YANDEXMUSIC_TOKEN) { - plugins.push( - new YandexMusicPlugin({ - oauthToken: ENV.BOT_YANDEXMUSIC_TOKEN - }) - ); - } else { - loggerWarn( - 'Yandex Music plugin is disabled, because BOT_YANDEXMUSIC_TOKEN are wrong or not provided', - loggerPrefixAudioplayer - ); - } +import { generateWarningEmbed } from '../utilities/generateWarningEmbed.js'; - plugins.push(new SoundCloudPlugin()); - if (!ENV.BOT_SOUNDCLOUD_CLIENT_ID || !ENV.BOT_SOUNDCLOUD_TOKEN) { - loggerWarn( - 'Some Soundcloud features is disabled, because BOT_SOUNDCLOUD_CLIENT_ID or BOT_SOUNDCLOUD_TOKEN are wrong or not provided', - loggerPrefixAudioplayer - ); - } - - plugins.push( - new YtDlpPlugin({ - update: true - }) - ); +export const queueSongsLimit = 500; - return plugins; -} +export const loggerPrefixAudioplayer = `Audioplayer`; export class AudioPlayerCore { client: Client; @@ -103,64 +36,90 @@ export class AudioPlayerCore { this.client.audioPlayer = this; this.playersManager = new AudioPlayersManager(this.client); this.distube = new DisTube(this.client, { - leaveOnEmpty: true, - emptyCooldown: ENV.NODE_ENV === 'production' ? 120 : 5, - leaveOnFinish: false, - leaveOnStop: true, - youtubeCookie: BOT_YOUTUBE_COOKIE ?? undefined, nsfw: true, emitAddListWhenCreatingQueue: true, emitAddSongWhenCreatingQueue: true, savePreviousSongs: true, + joinNewVoiceChannel: true, plugins: LoadPlugins() }); - this.client.distube = this.distube; - this.setupEvents(); } async play( voiceChannel: VoiceBasedChannel, textChannel: TextChannel, - song: string | Song | SearchResult, + song: string | Song, options?: PlayOptions ) { - await this.distube.voices.join(voiceChannel); - - joinVoiceChannel({ - channelId: voiceChannel.id, - guildId: voiceChannel.guildId, - adapterCreator: voiceChannel.guild.voiceAdapterCreator - }); - try { + // I am need manual connect user to a voice channel, because when I am using only Distube "play" + // method, getVoiceConnection in @discordjs/voice is not working + joinVoiceChannel({ + channelId: voiceChannel.id, + guildId: voiceChannel.guildId, + adapterCreator: voiceChannel.guild.voiceAdapterCreator + }); + await this.distube.play(voiceChannel, song, options); } catch (e) { if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); - await textChannel.send({ embeds: [generateErrorEmbed(i18next.t('audioplayer:play_error'))] }); + await textChannel.send({ + embeds: [generateErrorEmbed(e.message, i18next.t('audioplayer:play_error') as string)] + }); } } async stop(guild: Guild) { - if (this.distube.getQueue(guild)) { - await this.distube.stop(guild); + const queue = this.distube.getQueue(guild); + + if (queue) { + await queue.stop(); + queue.voice.leave(); } else { - await this.distube.voices.leave(guild); + this.distube.voices.leave(guild); } + await this.playersManager.remove(guild.id); } async pause(guild: Guild) { + const queue = this.distube.getQueue(guild); + if (!queue) return; + const player = this.playersManager.get(queue.id); + if (!player) return; + if (!queue.paused) { + this.distube.pause(guild); + await player.setState('pause'); + } + + await player.update(); + } + + async resume(guild: Guild) { + const queue = this.distube.getQueue(guild); + if (!queue) return; + const player = this.playersManager.get(queue.id); + if (!player) return; + if (queue.paused) { + this.distube.resume(guild); + await player.setState('playing'); + } + + await player.update(); + } + + async pauseResume(guild: Guild) { const queue = this.distube.getQueue(guild); if (!queue) return; const player = this.playersManager.get(queue.id); if (!player) return; if (queue.paused) { - await this.distube.resume(guild); + this.distube.resume(guild); await player.setState('playing'); } else { - await this.distube.pause(guild); + this.distube.pause(guild); await player.setState('pause'); } @@ -175,15 +134,15 @@ export class AudioPlayerCore { switch (queue.repeatMode) { case RepeatMode.DISABLED: - await queue.setRepeatMode(RepeatMode.SONG); + queue.setRepeatMode(RepeatMode.SONG); player.embedBuilder.setLoopMode('song'); break; case RepeatMode.SONG: - await queue.setRepeatMode(RepeatMode.QUEUE); + queue.setRepeatMode(RepeatMode.QUEUE); player.embedBuilder.setLoopMode('queue'); break; case RepeatMode.QUEUE: - await queue.setRepeatMode(RepeatMode.DISABLED); + queue.setRepeatMode(RepeatMode.DISABLED); player.embedBuilder.setLoopMode('disabled'); break; } @@ -195,7 +154,7 @@ export class AudioPlayerCore { try { const queue = this.distube.getQueue(guild); if (queue) { - await this.distube.skip(guild); + await this.distube.skip(guild.id); return queue.songs[0]; } } catch (e) { @@ -251,13 +210,14 @@ export class AudioPlayerCore { const player = this.playersManager.get(queue.id); if (!player) return false; if (time < 0) time = 0; - await this.distube.seek(guild, time); + this.distube.seek(guild, time); await player.setState('playing'); return true; } catch (e) { return false; } } + async showQueue(interaction: Interaction) { if (!interaction.guild) return; const queue = this.distube.getQueue(interaction.guild); @@ -280,13 +240,18 @@ export class AudioPlayerCore { `${i + 1}. ` + `[${song.name}](${song.url})` + ` - \`${song.formattedDuration}\`\n`; } - return new EmbedBuilder() + const page = new EmbedBuilder() .setAuthor({ name: `${i18next.t('audioplayer:show_queue_songs_in_queue')}: ` }) .setTitle(queue.songs[0].name!) - .setURL(queue.songs[0].url) .setDescription( `**${i18next.t('audioplayer:show_queue_title')}: **\n${queueList}`.slice(0, 4096) ); + + if (queue.songs[0].url) { + page.setURL(queue.songs[0].url); + } + + return page; } const arrayEmbeds: Array = []; @@ -330,12 +295,18 @@ export class AudioPlayerCore { } private setupEvents() { + if (ENV.BOT_VERBOSE_LOGGING) { + this.distube + .on(DistubeEvents.DEBUG, (message) => { + loggerSend(message, loggerPrefixAudioplayer); + }) + .on(DistubeEvents.FFMPEG_DEBUG, (message) => { + loggerSend(message, loggerPrefixAudioplayer); + }); + } + this.distube - .on('empty', async (queue) => { - await queue.textChannel?.send(i18next.t('audioplayer:event_empty') as string); - await this.playersManager.remove(queue.id); - }) - .on('initQueue', async (queue) => { + .on(DistubeEvents.INIT_QUEUE, async (queue) => { await this.playersManager.add(queue.id, queue.textChannel as TextChannel, queue); const player = this.playersManager.get(queue.id); @@ -343,16 +314,16 @@ export class AudioPlayerCore { await player.init(); } }) - .on('playSong', async (queue) => { + .on(DistubeEvents.PLAY_SONG, async (queue) => { const player = this.playersManager.get(queue.id); if (player) { await player.setState('playing'); } }) - .on('disconnect', async (queue) => { + .on(DistubeEvents.DISCONNECT, async (queue) => { await this.playersManager.remove(queue.id); }) - .on('addSong', async (queue, song) => { + .on(DistubeEvents.ADD_SONG, async (queue, song) => { if (queue.textChannel) { await queue.textChannel.send({ embeds: [generateAddedSongMessage(song)] }); } @@ -362,9 +333,21 @@ export class AudioPlayerCore { await player.update(); } }) - .on('addList', async (queue, playlist) => { - if (queue.textChannel) { - await queue.textChannel.send({ embeds: [generateAddedPlaylistMessage(playlist)] }); + .on(DistubeEvents.ADD_LIST, async (queue, playlist) => { + if (!queue.textChannel) return; + + await queue.textChannel.send({ embeds: [generateAddedPlaylistMessage(playlist)] }); + if (queue.songs.length >= queueSongsLimit) { + await queue.textChannel.send({ + embeds: [ + generateWarningEmbed( + i18next.t('audioplayer:event_add_list_limit', { + queueLimit: queueSongsLimit + }) as string + ) + ] + }); + queue.songs.length = queueSongsLimit; } const player = this.playersManager.get(queue.id); @@ -372,13 +355,13 @@ export class AudioPlayerCore { await player.update(); } }) - .on('finishSong', async (queue) => { + .on(DistubeEvents.FINISH_SONG, async (queue) => { if (!this.playersManager.has(queue.id)) return; if (queue._next || queue._prev || queue.stopped || queue.songs.length > 1) return; - this.playersManager.get(queue.id)?.setState('waiting'); + await this.playersManager.get(queue.id)?.setState('waiting'); }) - .on('error', async (channel, error) => { - channel?.send({ + .on(DistubeEvents.ERROR, async (error, queue) => { + queue.textChannel?.send({ embeds: [ generateErrorEmbed(`${error.name} + \n\n + ${error.message} \n\n + ${error.stack}`) ] @@ -386,53 +369,3 @@ export class AudioPlayerCore { }); } } - -function generateAddedSongMessage(song: Song) { - return new EmbedBuilder() - .setTitle(song.name ?? i18next.t('audioplayer:player_embed_unknown')) - .setURL(song.url ?? null) - .setAuthor({ name: `🎵${i18next.t('audioplayer:event_add_song')}🎵` }) - .setThumbnail(song.thumbnail ?? null) - .addFields( - { - name: `${i18next.t('audioplayer:player_embed_requester')}`, - value: `${song.member!.user.toString()}`, - inline: true - }, - { - name: `${i18next.t('audioplayer:event_add_song_length')}`, - value: `\`${song.formattedDuration}\``, - inline: true - }, - { - name: `${i18next.t('audioplayer:event_add_song_author')}`, - value: `\`${song.uploader.name ?? i18next.t('audioplayer:player_embed_unknown')}\``, - inline: true - } - ); -} - -function generateAddedPlaylistMessage(playlist: Playlist) { - return new EmbedBuilder() - .setTitle(playlist.name ?? i18next.t('audioplayer:player_embed_unknown')) - .setURL(playlist.url ?? null) - .setAuthor({ name: `🎵${i18next.t('audioplayer:event_add_list')}🎵` }) - .setThumbnail(playlist.thumbnail ?? null) - .addFields( - { - name: `${i18next.t('audioplayer:player_embed_requester')}`, - value: `${playlist.member!.user.toString()}`, - inline: true - }, - { - name: `${i18next.t('audioplayer:event_add_list_songs_count')}`, - value: `\`${playlist.songs.length}\``, - inline: true - }, - { - name: `${i18next.t('audioplayer:event_add_song_length')}`, - value: `\`${playlist.formattedDuration}\``, - inline: true - } - ); -} diff --git a/src/audioplayer/AudioPlayerTypes.ts b/src/audioplayer/AudioPlayerTypes.ts new file mode 100644 index 0000000..55581f3 --- /dev/null +++ b/src/audioplayer/AudioPlayerTypes.ts @@ -0,0 +1,23 @@ +export type AudioPlayerState = 'playing' | 'pause' | 'waiting' | 'loading' | 'destroying'; +export type AudioPlayerLoopMode = 'disabled' | 'song' | 'queue'; + +export enum AudioPlayerIcons { + stop = '<:stopwhite:1014551716043173989>', + play = '<:play:1257590184455835698>', + pause = '<:pausewhite:1014551696174764133>', + toogleLoopMode = '<:repeatmodewhite:1014551751858331731>', + previous = '<:previousbutton:1092107334542696512>', + skip = '<:skipbutton:1092107438234275900>', + shuffle = '<:shufflebutton:1092107651384614912>', + list = '<:songlistwhite:1014551771705782405>' +} + +export enum AudioSourceIcons { + other = '<:audiowaves:1257591924693536829>', + attachment = '<:attachfile:1257591906658156576>', + youtube = '<:youtube:1257591994750992435>', + applemusic = '<:applemusic:1257591803260174467>', + yandexmusic = '<:yandexmusic:1257591977105555506>', + spotify = '<:spotify:1257591960726933576>', + soundcloud = '<:soundcloud:1257591943010193500>' +} diff --git a/src/commands/audio/audioPlayer/AudioPlayersManager.ts b/src/audioplayer/AudioPlayersManager.ts similarity index 87% rename from src/commands/audio/audioPlayer/AudioPlayersManager.ts rename to src/audioplayer/AudioPlayersManager.ts index eea762c..b92d9f0 100644 --- a/src/commands/audio/audioPlayer/AudioPlayersManager.ts +++ b/src/audioplayer/AudioPlayersManager.ts @@ -1,4 +1,4 @@ -import { Client, Collection, TextChannel } from 'discord.js'; +import { Client, Collection, GuildTextBasedChannel } from 'discord.js'; import { Queue } from 'distube'; import { MessagePlayer } from './MessagePlayer.js'; @@ -10,10 +10,10 @@ export class AudioPlayersManager { } async add( guildId: string, - textChannel: TextChannel, + textChannel: GuildTextBasedChannel, queue: Queue ): Promise { - if (await this.client.guilds.cache.get(guildId)) { + if (this.client.guilds.cache.get(guildId)) { if (!this.collection.has(guildId)) { this.collection.set(guildId, new MessagePlayer(this.client, textChannel, queue)); } diff --git a/src/audioplayer/LoadPlugins.ts b/src/audioplayer/LoadPlugins.ts new file mode 100644 index 0000000..14ec0cb --- /dev/null +++ b/src/audioplayer/LoadPlugins.ts @@ -0,0 +1,93 @@ +import { ExtractorPlugin, InfoExtractorPlugin, PlayableExtractorPlugin } from 'distube'; +import { BOT_YOUTUBE_COOKIE, ENV } from '../EnvironmentVariables.js'; +import { loggerSend, loggerWarn } from '../utilities/logger.js'; +import { SpotifyPlugin } from '@distube/spotify'; +import { SoundCloudPlugin } from '@distube/soundcloud'; +import { YtDlpPlugin } from '@distube/yt-dlp'; +import { loggerPrefixAudioplayer } from './AudioPlayerCore.js'; +import { YouTubePlugin } from '@distube/youtube'; +import { DirectLinkPlugin } from '@distube/direct-link'; +import { FilePlugin } from '@distube/file'; +import { AppleMusicPlugin } from 'distube-apple-music'; +import { YandexMusicPlugin } from 'distube-yandex-music-plugin'; + +export type DistubePlugin = ExtractorPlugin | InfoExtractorPlugin | PlayableExtractorPlugin; + +export function LoadPlugins(): Array { + const plugins: Array = []; + + if (!BOT_YOUTUBE_COOKIE) { + loggerWarn( + 'BOT_YOUTUBE_COOKIE is not provided, 18+ videos from Youtube is not available', + loggerPrefixAudioplayer + ); + } + + plugins.push( + new YouTubePlugin({ + cookies: BOT_YOUTUBE_COOKIE + }) + ); + + if (!ENV.BOT_SPOTIFY_CLIENT_ID || !ENV.BOT_SPOTIFY_CLIENT_SECRET) { + loggerWarn( + 'Spotify plugin can work worse, because BOT_SPOTIFY_CLIENT_ID and BOT_SPOTIFY_CLIENT_SECRET are wrong or not provided', + loggerPrefixAudioplayer + ); + } + + plugins.push( + new SpotifyPlugin({ + api: { + clientId: ENV.BOT_SPOTIFY_CLIENT_ID, + clientSecret: ENV.BOT_SPOTIFY_CLIENT_SECRET + } + }) + ); + + if (ENV.BOT_YANDEXMUSIC_TOKEN && ENV.BOT_YANDEXMUSIC_UID) { + plugins.push( + new YandexMusicPlugin({ + oauthToken: ENV.BOT_YANDEXMUSIC_TOKEN, + uid: ENV.BOT_YANDEXMUSIC_UID + }) + ); + } else { + loggerWarn( + 'Yandex Music plugin is disabled, because BOT_YANDEXMUSIC_TOKEN and BOT_YANDEXMUSIC_UID are wrong or not provided', + loggerPrefixAudioplayer + ); + } + + if (!ENV.BOT_SOUNDCLOUD_CLIENT_ID || !ENV.BOT_SOUNDCLOUD_TOKEN) { + loggerWarn( + 'Some Soundcloud features is disabled, because BOT_SOUNDCLOUD_CLIENT_ID or BOT_SOUNDCLOUD_TOKEN are wrong or not provided', + loggerPrefixAudioplayer + ); + } + + plugins.push( + new SoundCloudPlugin({ + clientId: ENV.BOT_SOUNDCLOUD_CLIENT_ID, + oauthToken: ENV.BOT_SOUNDCLOUD_TOKEN + }) + ); + + plugins.push(new AppleMusicPlugin()); + plugins.push(new DirectLinkPlugin()); + plugins.push(new FilePlugin()); + plugins.push( + new YtDlpPlugin({ + update: true + }) + ); + + loggerSend( + `Loaded plugins: ${plugins.map((plugin: DistubePlugin) => { + return ' ' + plugin.constructor.name; + })}`, + loggerPrefixAudioplayer + ); + + return plugins; +} diff --git a/src/commands/audio/audioPlayer/MessagePlayer.ts b/src/audioplayer/MessagePlayer.ts similarity index 77% rename from src/commands/audio/audioPlayer/MessagePlayer.ts rename to src/audioplayer/MessagePlayer.ts index 89480af..b898fa4 100644 --- a/src/commands/audio/audioPlayer/MessagePlayer.ts +++ b/src/audioplayer/MessagePlayer.ts @@ -1,17 +1,18 @@ -import { Client, Message, TextChannel } from 'discord.js'; +import { Client, GuildTextBasedChannel, Message } from 'discord.js'; import { MessagePlayerEmbedBuilder } from './MessagePlayerEmbedBuilder.js'; import { Queue, Song } from 'distube'; import { MessagePlayerButtonsHandler } from './MessagePlayerButtonsHandler.js'; import { AudioPlayerState } from './AudioPlayerTypes.js'; -import { checkBotInVoice } from '../../../utilities/checkBotInVoice.js'; +import { checkBotInVoice } from '../utilities/checkBotInVoice.js'; import i18next from 'i18next'; -import { ENV } from '../../../EnvironmentVariables.js'; -import { loggerError } from '../../../utilities/logger.js'; +import { ENV } from '../EnvironmentVariables.js'; +import { loggerError } from '../utilities/logger.js'; +import { generateSimpleEmbed } from '../utilities/generateSimpleEmbed.js'; export class MessagePlayer { private readonly client: Client; // TextChannel where player was created - readonly textChannel: TextChannel; + readonly textChannel: GuildTextBasedChannel; // Player state private state: AudioPlayerState = 'loading'; // Player embed interface @@ -24,34 +25,63 @@ export class MessagePlayer { // Variable for "recreationPlayer" lastDeletedMessage: Message | undefined; // Delay for player recreation - private updateTime = 3500; + private updateTime = 3500; // in ms // Timer for "recreationPlayer" private recreationTimer: NodeJS.Timeout | undefined; // Time for "waiting" state - private finishTime = 20000; + private finishTime = 20000; // in ms // Timer object for "waiting" state private finishTimer: NodeJS.Timeout | undefined; - constructor(client: Client, txtChannel: TextChannel, queue: Queue) { + + // If no one in voice channel, start afk timer + private afkTime = 20000; // in ms + private afkTimer: NodeJS.Timeout | undefined; + + constructor(client: Client, txtChannel: GuildTextBasedChannel, queue: Queue) { this.client = client; this.textChannel = txtChannel; this.queue = queue; this.buttonsHandler = new MessagePlayerButtonsHandler(this.client, this.textChannel); } + async startAfkTimer() { + try { + this.afkTimer = setTimeout(async () => { + await this.client.audioPlayer.stop(this.textChannel.guild); + await this.textChannel.send({ + embeds: [generateSimpleEmbed(i18next.t('audioplayer:event_empty') as string)] + }); + await this.stopAfkTimer(); + await this.stopFinishTimer(); + }, this.afkTime); + } catch (e) { + if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); + } + } + + async stopAfkTimer() { + if (this.afkTimer) { + clearTimeout(this.afkTimer); + } + } + // If a player is in "waiting" state, they start finish timer. // It can be canceled by switching state to any other state async startFinishTimer() { try { - if (await checkBotInVoice(this.textChannel.guild)) { + if (checkBotInVoice(this.textChannel.guild)) { await this.stopFinishTimer(); this.finishTimer = setTimeout(async () => { - const queue = this.client.audioPlayer.distube.getQueue(this.textChannel.guild); + const queue = this.client.audioPlayer.distube.getQueue(this.textChannel.guild.id); // loggerSend('try to stop player on cooldown') if (queue) return; - if (await checkBotInVoice(this.textChannel.guild)) { + if (checkBotInVoice(this.textChannel.guild)) { await this.client.audioPlayer.stop(this.textChannel.guild); - await this.textChannel.send(i18next.t('audioplayer:event_finish_time') as string); + await this.textChannel.send({ + embeds: [generateSimpleEmbed(i18next.t('audioplayer:event_finish_time') as string)] + }); await this.stopFinishTimer(); + await this.stopAfkTimer(); } }, this.finishTime); } @@ -68,7 +98,7 @@ export class MessagePlayer { // Update embed interface to represent the current state of player, BUT THIS NOT PUSHES UPDATED EMBED TO MESSAGE private updateEmbedState() { const queue: Queue | undefined = this.client.audioPlayer.distube.getQueue( - this.textChannel.guild + this.textChannel.guild.id ); if (queue) { this.queue = queue; @@ -78,15 +108,16 @@ export class MessagePlayer { const currentSong: Song = this.queue.songs[0]; if (currentSong) { this.embedBuilder.setSongDuration(currentSong.duration, currentSong.isLive); + this.embedBuilder.setSongSource(currentSong); this.embedBuilder.setSongTitle( currentSong.name ?? i18next.t('audioplayer:player_embed_unknown'), - currentSong.url + currentSong.url! ); this.embedBuilder.setThumbnailURL(currentSong.thumbnail ?? null); this.embedBuilder.setUploader(currentSong.uploader.name); if (currentSong.user) { - this.embedBuilder.setRequester(currentSong.user); + this.embedBuilder.setRequester(currentSong.user!); } } this.embedBuilder.setNextSong(this.queue.songs[1]?.name); @@ -99,22 +130,11 @@ export class MessagePlayer { private async updateMessageState() { if (!this.messageWithPlayer) return; try { - switch (this.state) { - case 'playing': - case 'pause': - await this.messageWithPlayer.edit({ - embeds: [this.embedBuilder], - components: this.buttonsHandler.getComponents() - }); - break; - case 'waiting': - case 'loading': - await this.messageWithPlayer.edit({ - embeds: [this.embedBuilder], - components: this.buttonsHandler.getComponentsOnlyStop() - }); - break; - } + this.buttonsHandler.setComponentsState(this.state); + await this.messageWithPlayer.edit({ + embeds: [this.embedBuilder], + components: this.buttonsHandler.getComponents() + }); } catch (e) { if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); } @@ -198,12 +218,12 @@ export class MessagePlayer { try { await this.messageWithPlayer?.delete(); } catch (e) { - if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); + //if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); } }, 5000); } } catch (e) { - if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); + //if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); } } @@ -212,17 +232,15 @@ export class MessagePlayer { this.state = state; // When Distube is waiting the song, they remove their Queue object. // So when we try to play a new song, we need to receive a new Queue - const queue = this.client.audioPlayer.distube.getQueue(this.textChannel.guild); + const queue = this.client.audioPlayer.distube.getQueue(this.textChannel.guild.id); if (queue) { this.queue = queue; } if (state === 'waiting') { await this.startFinishTimer(); - } else { - if (this.finishTimer && queue) { - clearTimeout(this.finishTimer); - } + } else if (queue) { + await this.stopFinishTimer(); } await this.update(); diff --git a/src/audioplayer/MessagePlayerButtonsHandler.ts b/src/audioplayer/MessagePlayerButtonsHandler.ts new file mode 100644 index 0000000..3e17bae --- /dev/null +++ b/src/audioplayer/MessagePlayerButtonsHandler.ts @@ -0,0 +1,243 @@ +import { + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + InteractionCollector, + ComponentType, + Client, + GuildMember, + ButtonInteraction, + Guild, + GuildTextBasedChannel +} from 'discord.js'; +import { checkMemberInVoiceWithBot } from '../utilities/checkMemberInVoiceWithBot.js'; +import { generateErrorEmbed } from '../utilities/generateErrorEmbed.js'; +import { loggerError } from '../utilities/logger.js'; +import { generateSkipEmbed, generateSkipEmbedFailure } from '../commands/audio/skip.command.js'; +import { + generateEmbedAudioPlayerShuffle, + generateEmbedAudioPlayerShuffleFailure +} from '../commands/audio/shuffle.command.js'; +import { AudioPlayerIcons, AudioPlayerState } from './AudioPlayerTypes.js'; +import { generateEmbedAudioPlayerStop } from '../commands/audio/stop.command.js'; +import { + generateEmbedAudioPlayerPrevious, + generateEmbedAudioPlayerPreviousFailure +} from '../commands/audio/previous.command.js'; + +enum ButtonIDs { + stopMusic = 'stopMusic', + pauseMusic = 'pauseMusic', + toggleLoopMode = 'toggleLoopMode', + previousSong = 'previousSong', + skipSong = 'skipSong', + //downloadSong = 'downloadSong', + shuffle = 'shuffle', + showQueue = 'showQueue' +} + +const rowPrimary = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(ButtonIDs.stopMusic) + .setStyle(ButtonStyle.Danger) + .setEmoji(AudioPlayerIcons.stop), + new ButtonBuilder() + .setCustomId(ButtonIDs.pauseMusic) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.pause), + new ButtonBuilder() + .setCustomId(ButtonIDs.toggleLoopMode) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.toogleLoopMode), + new ButtonBuilder() + .setCustomId(ButtonIDs.previousSong) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.previous), + new ButtonBuilder() + .setCustomId(ButtonIDs.skipSong) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.skip) +); + +const rowPrimaryPaused = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(ButtonIDs.stopMusic) + .setStyle(ButtonStyle.Danger) + .setEmoji(AudioPlayerIcons.stop), + new ButtonBuilder() + .setCustomId(ButtonIDs.pauseMusic) + .setStyle(ButtonStyle.Success) + .setEmoji(AudioPlayerIcons.play), + new ButtonBuilder() + .setCustomId(ButtonIDs.toggleLoopMode) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.toogleLoopMode), + new ButtonBuilder() + .setCustomId(ButtonIDs.previousSong) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.previous), + new ButtonBuilder() + .setCustomId(ButtonIDs.skipSong) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.skip) +); + +const rowSecondary = new ActionRowBuilder().addComponents( + //new ButtonBuilder().setCustomId(ButtonIDs.downloadSong).setStyle(ButtonStyle.Success).setEmoji('<:downloadwhite:1014553027614617650>'), + new ButtonBuilder() + .setCustomId(ButtonIDs.shuffle) + .setStyle(ButtonStyle.Primary) + .setEmoji(AudioPlayerIcons.shuffle), + new ButtonBuilder() + .setCustomId(ButtonIDs.showQueue) + .setStyle(ButtonStyle.Secondary) + .setEmoji(AudioPlayerIcons.list) +); + +const rowWithOnlyStop = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId(ButtonIDs.stopMusic) + .setStyle(ButtonStyle.Danger) + .setEmoji(AudioPlayerIcons.stop) +); + +export class MessagePlayerButtonsHandler { + private collector: InteractionCollector; + private client: Client; + private components: Array>; + + constructor(client: Client, textChannel: GuildTextBasedChannel) { + this.components = [rowPrimary, rowSecondary]; + this.client = client; + + this.collector = textChannel.createMessageComponentCollector({ + componentType: ComponentType.Button + }); + + this.collector.on('collect', async (ButtonInteraction: ButtonInteraction) => { + try { + const checkObj = await checkMemberInVoiceWithBot(ButtonInteraction.member as GuildMember); + if (!checkObj.channelTheSame) { + await ButtonInteraction.reply({ + embeds: [generateErrorEmbed(checkObj.errorMessage)], + ephemeral: true + }); + return; + } + + switch (ButtonInteraction.customId) { + case ButtonIDs.stopMusic: { + const guild = ButtonInteraction.guild as Guild; + + const player = this.client.audioPlayer.playersManager.get(guild.id); + + if (player) { + await player.textChannel.send({ + embeds: [generateEmbedAudioPlayerStop(ButtonInteraction.member as GuildMember)] + }); + } + + await this.client.audioPlayer.stop(guild); + + await ButtonInteraction.deferUpdate(); + break; + } + case ButtonIDs.pauseMusic: + await this.client.audioPlayer.pauseResume(ButtonInteraction.guild as Guild); + await ButtonInteraction.deferUpdate(); + break; + + case ButtonIDs.previousSong: { + const song = await this.client.audioPlayer.previous(ButtonInteraction.guild as Guild); + if (song) { + await ButtonInteraction.reply({ + embeds: [ + generateEmbedAudioPlayerPrevious(ButtonInteraction.member as GuildMember, song) + ] + }); + } else { + await ButtonInteraction.reply({ + embeds: [generateEmbedAudioPlayerPreviousFailure()], + ephemeral: true + }); + } + break; + } + + case ButtonIDs.skipSong: { + const song = await this.client.audioPlayer.skip(ButtonInteraction.guild as Guild); + + if (song) { + await ButtonInteraction.reply({ + embeds: [generateSkipEmbed(song, ButtonInteraction.member as GuildMember)] + }); + } else { + await ButtonInteraction.reply({ + embeds: [generateSkipEmbedFailure()], + ephemeral: true + }); + } + break; + } + + case ButtonIDs.toggleLoopMode: + await this.client.audioPlayer.changeLoopMode(ButtonInteraction.guild as Guild); + await ButtonInteraction.deferUpdate(); + break; + + // case ButtonIDs.downloadSong: { + // const song = this.client.audioplayer.distube.getQueue(ButtonInteraction.guild as Guild)?.songs[0] + // + // if (!song) { + // await ButtonInteraction.reply({embeds: [generateErrorEmbed(i18next.t("audioplayer:download_song_error"))]}) + // break + // } + // await ButtonInteraction.reply({ephemeral: true, embeds: [generateDownloadSongEmbed(song.streamURL ?? song.url)]}) + // break + // } + + case ButtonIDs.showQueue: + await this.client.audioPlayer.showQueue(ButtonInteraction); + break; + + case ButtonIDs.shuffle: { + if (await this.client.audioPlayer.shuffle(ButtonInteraction.guild as Guild)) { + await ButtonInteraction.reply({ + embeds: [generateEmbedAudioPlayerShuffle(ButtonInteraction.member as GuildMember)] + }); + } else { + await ButtonInteraction.reply({ embeds: [generateEmbedAudioPlayerShuffleFailure()] }); + } + break; + } + } + } catch (e) { + loggerError(e); + } + }); + } + + setComponentsState(state: AudioPlayerState) { + switch (state) { + case 'playing': + this.components = [rowPrimary, rowSecondary]; + break; + case 'pause': + this.components = [rowPrimaryPaused, rowSecondary]; + break; + case 'loading': + case 'waiting': + case 'destroying': + default: + this.components = [rowWithOnlyStop]; + } + } + + getComponents(): Array> { + return this.components; + } + + destroy() { + this.collector.stop(); + } +} diff --git a/src/commands/audio/audioPlayer/MessagePlayerEmbedBuilder.ts b/src/audioplayer/MessagePlayerEmbedBuilder.ts similarity index 89% rename from src/commands/audio/audioPlayer/MessagePlayerEmbedBuilder.ts rename to src/audioplayer/MessagePlayerEmbedBuilder.ts index c9ee04c..04a02ae 100644 --- a/src/commands/audio/audioPlayer/MessagePlayerEmbedBuilder.ts +++ b/src/audioplayer/MessagePlayerEmbedBuilder.ts @@ -1,8 +1,10 @@ import { EmbedBuilder, User } from 'discord.js'; -import { AudioPlayerLoopMode, AudioPlayerState } from './AudioPlayerTypes.js'; -import { getNoun } from '../../../utilities/getNoun.js'; -import { formatSecondsToTime } from '../../../utilities/formatSecondsToTime.js'; +import { AudioPlayerLoopMode, AudioPlayerState, AudioSourceIcons } from './AudioPlayerTypes.js'; +import { getNoun } from '../utilities/getNoun.js'; +import { formatSecondsToTime } from '../utilities/formatSecondsToTime.js'; import i18next from 'i18next'; +import { Playlist, Song } from 'distube'; +import { getIconFromSource } from './util/getIconFromSource.js'; export class MessagePlayerEmbedBuilder extends EmbedBuilder { private playerState: AudioPlayerState = 'loading'; @@ -16,11 +18,13 @@ export class MessagePlayerEmbedBuilder extends EmbedBuilder { private titleUrl: string | null = null; private thumbnailURL: string | null = null; private formattedDuration = '00:00'; + private sourceIcon: AudioSourceIcons; constructor() { super(); this.setPlayerState('loading'); this.setNextSong(undefined); + this.sourceIcon = AudioSourceIcons.other; } update() { @@ -35,7 +39,7 @@ export class MessagePlayerEmbedBuilder extends EmbedBuilder { }); } this.setThumbnail(this.thumbnailURL); - this.setTitle(this.title); + this.setTitle(this.sourceIcon + " " + this.title); this.setURL(this.titleUrl); this.addFields({ name: i18next.t('audioplayer:player_embed_author'), @@ -75,6 +79,10 @@ export class MessagePlayerEmbedBuilder extends EmbedBuilder { return this; } + setSongSource(playable: Song | Playlist) { + this.sourceIcon = getIconFromSource(playable.source); + } + setSongTitle(name: string, url: string) { this.title = name; this.titleUrl = url; diff --git a/src/audioplayer/eventsHandlers/AudioPlayerEventChannelDelete.ts b/src/audioplayer/eventsHandlers/AudioPlayerEventChannelDelete.ts new file mode 100644 index 0000000..a7af156 --- /dev/null +++ b/src/audioplayer/eventsHandlers/AudioPlayerEventChannelDelete.ts @@ -0,0 +1,8 @@ +import { Client, TextChannel } from 'discord.js'; + +export async function AudioPlayerEventChannelDelete(client: Client, channel: TextChannel) { + const player = client.audioPlayer.playersManager.get(channel.guild.id); + if (player?.textChannel.id === channel.id) { + await client.audioPlayer.stop(channel.guild); + } +} diff --git a/src/events/messageHandlers/playerMessageHandler.ts b/src/audioplayer/eventsHandlers/AudioPlayerEventMessageCreate.ts similarity index 84% rename from src/events/messageHandlers/playerMessageHandler.ts rename to src/audioplayer/eventsHandlers/AudioPlayerEventMessageCreate.ts index 6b09dfe..27f3d13 100644 --- a/src/events/messageHandlers/playerMessageHandler.ts +++ b/src/audioplayer/eventsHandlers/AudioPlayerEventMessageCreate.ts @@ -2,7 +2,7 @@ import { TextChannel } from 'discord.js'; import { ENV } from '../../EnvironmentVariables.js'; import { loggerError } from '../../utilities/logger.js'; -export async function playerMessageHandler(textChannel: TextChannel) { +export async function AudioPlayerEventMessageCreate(textChannel: TextChannel) { try { const player = textChannel.client.audioPlayer.playersManager.get(textChannel.guild.id); if (player) { diff --git a/src/audioplayer/eventsHandlers/AudioPlayerEventMessageDeleted.ts b/src/audioplayer/eventsHandlers/AudioPlayerEventMessageDeleted.ts new file mode 100644 index 0000000..ecbf243 --- /dev/null +++ b/src/audioplayer/eventsHandlers/AudioPlayerEventMessageDeleted.ts @@ -0,0 +1,19 @@ +import { Client, Message, TextChannel } from 'discord.js'; +import { ENV } from '../../EnvironmentVariables.js'; +import { loggerError } from '../../utilities/logger.js'; + +export async function AudioPlayerEventMessageDeleted(client: Client, message: Message){ + if (!message.guild) return; + + const textChannel = message.channel as TextChannel; + const player = client.audioPlayer.playersManager.get(textChannel.guild.id); + if (player) { + if (player.textChannel.id !== textChannel.id) return; + if (player.lastDeletedMessage?.id === message.id) return; // If player deleted message them self, then don`t "trigger" event + try { + await player.recreatePlayer(); + } catch (e) { + if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); + } + } +} diff --git a/src/audioplayer/eventsHandlers/AudioPlayerEventVoiceChannelUpdate.ts b/src/audioplayer/eventsHandlers/AudioPlayerEventVoiceChannelUpdate.ts new file mode 100644 index 0000000..b44c628 --- /dev/null +++ b/src/audioplayer/eventsHandlers/AudioPlayerEventVoiceChannelUpdate.ts @@ -0,0 +1,19 @@ +import { Client, VoiceState } from 'discord.js'; +import { isVoiceChannelEmpty } from 'distube'; + +export async function AudioPlayerEventVoiceChannelUpdate( + client: Client, + oldState: VoiceState, + newState: VoiceState +) { + const messagePlayer = client.audioPlayer.playersManager.get(oldState.guild.id); + if (!messagePlayer) return; + + if (isVoiceChannelEmpty(oldState)) { + await messagePlayer.startAfkTimer(); + await client.audioPlayer.pause(oldState.guild); + } else if (!isVoiceChannelEmpty(newState) && messagePlayer.getState() === 'pause') { + await messagePlayer.stopAfkTimer(); + await client.audioPlayer.resume(oldState.guild); + } +} diff --git a/src/commands/audio/util/AudioCommandWrappers.ts b/src/audioplayer/util/AudioCommandWrappers.ts similarity index 94% rename from src/commands/audio/util/AudioCommandWrappers.ts rename to src/audioplayer/util/AudioCommandWrappers.ts index fddd686..c7b9633 100644 --- a/src/commands/audio/util/AudioCommandWrappers.ts +++ b/src/audioplayer/util/AudioCommandWrappers.ts @@ -1,4 +1,4 @@ -import { generateErrorEmbed } from '../../../utilities/generateErrorEmbed.js'; +import { generateErrorEmbed } from '../../utilities/generateErrorEmbed.js'; import { ChatInputCommandInteraction, Message } from 'discord.js'; import i18next from 'i18next'; diff --git a/src/commands/audio/util/downloadSong.ts b/src/audioplayer/util/downloadSong.ts similarity index 67% rename from src/commands/audio/util/downloadSong.ts rename to src/audioplayer/util/downloadSong.ts index 8b25a44..3f40636 100644 --- a/src/commands/audio/util/downloadSong.ts +++ b/src/audioplayer/util/downloadSong.ts @@ -4,11 +4,13 @@ import fs, { createReadStream, ReadStream } from 'fs'; import { pipeline } from 'stream/promises'; import { v4 as uuidv4 } from 'uuid'; import { unlink } from 'fs/promises'; -import { isURL } from 'distube'; +import { isURL, Playlist, Song } from 'distube'; import i18next from 'i18next'; -fs.rmSync('./downloads', { recursive: true, force: true }); -fs.mkdirSync('./downloads'); +const downloadFolderPath = process.cwd() + '/downloads'; + +fs.rmSync(downloadFolderPath, { recursive: true, force: true }); +fs.mkdirSync(downloadFolderPath); class DownloadSongError extends Error { constructor(message: DownloadSongMessage) { @@ -17,34 +19,37 @@ class DownloadSongError extends Error { } } -type DownloadSongMessage = 'is_not_url' | 'not_found' | 'song_is_too_large' | 'failed_loading'; -const maxDownloadSize = 10000000; //bytes +type DownloadSongMessage = + | 'is_not_url' + | 'not_found' + | 'song_is_too_large' + | 'failed_loading' + | 'this_is_playlist'; + +const maxDownloadSize = 2.5e7; //bytes +const maxDownloadSizeMB = maxDownloadSize / 1000000; export async function downloadSong( client: Client, request: string ): Promise { - let streamUrl = ''; + let streamUrl: string | undefined = ''; if (!isURL(request)) { throw new DownloadSongError('is_not_url'); } - for (const plugin of client.audioPlayer.distube.customPlugins) { - if (await plugin.validate(request)) { - streamUrl = await plugin.getStreamURL(request); - break; - } + const song: Song | Playlist = await client.audioPlayer.distube.handler.resolve(request); + if (song instanceof Playlist) { + throw new DownloadSongError('this_is_playlist'); } - for (const plugin of client.audioPlayer.distube.extractorPlugins) { - if (await plugin.validate(request)) { - streamUrl = await plugin.getStreamURL(request); - break; - } - } + await client.audioPlayer.distube.handler.attachStreamInfo(song); + + // @ts-expect-error Url property exists, I know it + streamUrl = song.stream.playFromSource ? song.stream.url : song.stream.song?.stream.url; - if (streamUrl === '') { + if (streamUrl === '' || streamUrl === undefined) { throw new DownloadSongError('not_found'); } @@ -68,6 +73,7 @@ async function convertWebmToMp3(webmStream: ReadableStream) { const file = fs.createWriteStream(file_name); const duplex = prism.FFmpeg.from(webmStream); + // @ts-expect-error Duplex can be provided to pipeline function await pipeline(duplex, file); return createReadStream(file_name); } @@ -88,7 +94,7 @@ export async function deleteMP3file(fileName: string) { export function DownloadSongErrorGetLocale(errorMessage: DownloadSongMessage) { if (errorMessage === 'song_is_too_large') { return i18next.t(`commands:download_song_error_${errorMessage}`, { - maxDownloadSize: maxDownloadSize / 1000000 + maxDownloadSize: maxDownloadSizeMB }); } return i18next.t(`commands:download_song_error_${errorMessage}`); diff --git a/src/audioplayer/util/generateAddedPlaylistMessage.ts b/src/audioplayer/util/generateAddedPlaylistMessage.ts new file mode 100644 index 0000000..e7cf060 --- /dev/null +++ b/src/audioplayer/util/generateAddedPlaylistMessage.ts @@ -0,0 +1,31 @@ +import { Playlist } from 'distube'; +import { EmbedBuilder } from 'discord.js'; +import i18next from 'i18next'; +import { getIconFromSource } from './getIconFromSource.js'; + +export function generateAddedPlaylistMessage(playlist: Playlist) { + const serviceIcon = getIconFromSource(playlist.source); + + return new EmbedBuilder() + .setTitle(`${serviceIcon} ${playlist.name}` ?? i18next.t('audioplayer:player_embed_unknown')) + .setURL(playlist.url ?? null) + .setAuthor({ name: `${i18next.t('audioplayer:event_add_list')}` }) + .setThumbnail(playlist.thumbnail ?? null) + .addFields( + { + name: `${i18next.t('audioplayer:player_embed_requester')}`, + value: `${playlist.member!.user.toString()}`, + inline: true + }, + { + name: `${i18next.t('audioplayer:event_add_list_songs_count')}`, + value: `\`${playlist.songs.length}\``, + inline: true + }, + { + name: `${i18next.t('audioplayer:event_add_song_length')}`, + value: `\`${playlist.formattedDuration}\``, + inline: true + } + ); +} diff --git a/src/audioplayer/util/generateAddedSongMessage.ts b/src/audioplayer/util/generateAddedSongMessage.ts new file mode 100644 index 0000000..ff9d23e --- /dev/null +++ b/src/audioplayer/util/generateAddedSongMessage.ts @@ -0,0 +1,31 @@ +import { Song } from 'distube'; +import { EmbedBuilder } from 'discord.js'; +import i18next from 'i18next'; +import { getIconFromSource } from './getIconFromSource.js'; + +export function generateAddedSongMessage(song: Song) { + const serviceIcon = getIconFromSource(song.source); + + return new EmbedBuilder() + .setTitle(`${serviceIcon} ${song.name}` ?? i18next.t('audioplayer:player_embed_unknown')) + .setURL(song.url ?? null) + .setAuthor({ name: `${i18next.t('audioplayer:event_add_song')}` }) + .setThumbnail(song.thumbnail ?? null) + .addFields( + { + name: `${i18next.t('audioplayer:player_embed_requester')}`, + value: `${song.member!.user.toString()}`, + inline: true + }, + { + name: `${i18next.t('audioplayer:event_add_song_length')}`, + value: `\`${song.formattedDuration}\``, + inline: true + }, + { + name: `${i18next.t('audioplayer:event_add_song_author')}`, + value: `\`${song.uploader.name ?? i18next.t('audioplayer:player_embed_unknown')}\``, + inline: true + } + ); +} diff --git a/src/audioplayer/util/getIconFromSource.ts b/src/audioplayer/util/getIconFromSource.ts new file mode 100644 index 0000000..584a765 --- /dev/null +++ b/src/audioplayer/util/getIconFromSource.ts @@ -0,0 +1,20 @@ +import { AudioSourceIcons } from '../AudioPlayerTypes.js'; + +export function getIconFromSource(source: string): AudioSourceIcons { + switch (source) { + case 'applemusic': + return AudioSourceIcons.applemusic; + case 'spotify': + return AudioSourceIcons.spotify; + case 'youtube': + return AudioSourceIcons.youtube; + case 'file': + return AudioSourceIcons.attachment; + case 'soundcloud': + return AudioSourceIcons.soundcloud; + case 'yandexmusic': + return AudioSourceIcons.yandexmusic; + default: + return AudioSourceIcons.other; + } +} diff --git a/src/commands/audio/util/isAudioFile.ts b/src/audioplayer/util/isAudioFile.ts similarity index 100% rename from src/commands/audio/util/isAudioFile.ts rename to src/audioplayer/util/isAudioFile.ts diff --git a/src/commands/admin/setPrefix.command.ts b/src/commands/admin/setPrefix.command.ts index 3016f1f..4578798 100644 --- a/src/commands/admin/setPrefix.command.ts +++ b/src/commands/admin/setPrefix.command.ts @@ -9,7 +9,7 @@ export default function (): ICommand { text_data: { name: 'setprefix', description: i18next.t('commands:set_prefix_desc'), - arguments: [new CommandArgument('символ', true)], + arguments: [new CommandArgument('newprefix', true)], execute: async (message: Message, args: string[]): Promise => { const prefix: string = args[0]; if (!prefix) return; @@ -50,7 +50,7 @@ export default function (): ICommand { async function changePrefixTo(guild: Guild, prefix: string): Promise { if (prefix === '/' || prefix === '@' || prefix === '#') return i18next.t('commands:set_prefix_restrict_prefixes', { prefixes: '/ @ #' }) as string; - if (prefix.length > 1) return i18next.t('commands:set_prefix_length_error') as string; + if (prefix.length > 2) return i18next.t('commands:set_prefix_length_error') as string; await setGuildOption(guild, 'prefix', prefix); return i18next.t('commands:set_prefix_success_change', { prefix: prefix }) as string; } diff --git a/src/commands/audio/audioPlayer/AudioPlayerTypes.ts b/src/commands/audio/audioPlayer/AudioPlayerTypes.ts deleted file mode 100644 index 53e09ea..0000000 --- a/src/commands/audio/audioPlayer/AudioPlayerTypes.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type AudioPlayerState = 'playing' | 'pause' | 'waiting' | 'loading' | 'destroying'; -export type AudioPlayerLoopMode = 'disabled' | 'song' | 'queue'; diff --git a/src/commands/audio/audioPlayer/MessagePlayerButtonsHandler.ts b/src/commands/audio/audioPlayer/MessagePlayerButtonsHandler.ts deleted file mode 100644 index 8a8c515..0000000 --- a/src/commands/audio/audioPlayer/MessagePlayerButtonsHandler.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { - ActionRowBuilder, - ButtonBuilder, - ButtonStyle, - InteractionCollector, - TextChannel, - ComponentType, - Client, - GuildMember, - ButtonInteraction, - Guild -} from 'discord.js'; -import { checkMemberInVoiceWithBot } from '../../../utilities/checkMemberInVoiceWithBot.js'; -import { generateErrorEmbed } from '../../../utilities/generateErrorEmbed.js'; -import { loggerError } from '../../../utilities/logger.js'; -import { generateSkipMessage, generateSkipMessageFailure } from '../skip.command.js'; -import { generateMessageAudioPlayerStop } from '../stop.command.js'; -import { - generateMessageAudioPlayerPrevious, - generateMessageAudioPlayerPreviousFailure -} from '../previous.command.js'; -import { - generateMessageAudioPlayerShuffle, - generateMessageAudioPlayerShuffleFailure -} from '../shuffle.command.js'; - -enum ButtonIDs { - stopMusic = 'stopMusic', - pauseMusic = 'pauseMusic', - toggleLoopMode = 'toggleLoopMode', - previousSong = 'previousSong', - skipSong = 'skipSong', - downloadSong = 'downloadSong', - shuffle = 'shuffle', - showQueue = 'showQueue' -} -export class MessagePlayerButtonsHandler { - rowPrimary = new ActionRowBuilder(); - rowSecondary = new ActionRowBuilder(); - rowWithOnlyStop = new ActionRowBuilder(); - collector: InteractionCollector; - client: Client; - constructor(client: Client, textChannel: TextChannel) { - this.client = client; - - this.rowPrimary.addComponents( - new ButtonBuilder() - .setCustomId(ButtonIDs.stopMusic) - .setStyle(ButtonStyle.Danger) - .setEmoji('<:stopwhite:1014551716043173989>'), - new ButtonBuilder() - .setCustomId(ButtonIDs.pauseMusic) - .setStyle(ButtonStyle.Primary) - .setEmoji('<:pausewhite:1014551696174764133>'), - new ButtonBuilder() - .setCustomId(ButtonIDs.toggleLoopMode) - .setStyle(ButtonStyle.Primary) - .setEmoji('<:repeatmodewhite:1014551751858331731>'), - new ButtonBuilder() - .setCustomId(ButtonIDs.previousSong) - .setStyle(ButtonStyle.Primary) - .setEmoji('<:previousbutton:1092107334542696512>'), - new ButtonBuilder() - .setCustomId(ButtonIDs.skipSong) - .setStyle(ButtonStyle.Primary) - .setEmoji('<:skipbutton:1092107438234275900>') - ); - - this.rowSecondary.addComponents( - //new ButtonBuilder().setCustomId(ButtonIDs.downloadSong).setStyle(ButtonStyle.Success).setEmoji('<:downloadwhite:1014553027614617650>'), - new ButtonBuilder() - .setCustomId(ButtonIDs.shuffle) - .setStyle(ButtonStyle.Primary) - .setEmoji('<:shufflebutton:1092107651384614912>'), - new ButtonBuilder() - .setCustomId(ButtonIDs.showQueue) - .setStyle(ButtonStyle.Secondary) - .setEmoji('<:songlistwhite:1014551771705782405>') - ); - - this.rowWithOnlyStop.addComponents( - new ButtonBuilder() - .setCustomId(ButtonIDs.stopMusic) - .setStyle(ButtonStyle.Danger) - .setEmoji('<:stopwhite:1014551716043173989>') - ); - - this.collector = textChannel.createMessageComponentCollector({ - componentType: ComponentType.Button - }); - - this.collector.on('collect', async (ButtonInteraction: ButtonInteraction) => { - try { - const checkObj = await checkMemberInVoiceWithBot(ButtonInteraction.member as GuildMember); - if (!checkObj.channelTheSame) { - await ButtonInteraction.reply({ - embeds: [generateErrorEmbed(checkObj.errorMessage)], - ephemeral: true - }); - return; - } - - switch (ButtonInteraction.customId) { - case ButtonIDs.stopMusic: - await this.client.audioPlayer.stop(ButtonInteraction.guild as Guild); - await ButtonInteraction.reply({ - content: generateMessageAudioPlayerStop(ButtonInteraction.member as GuildMember) - }); - break; - - case ButtonIDs.pauseMusic: - await this.client.audioPlayer.pause(ButtonInteraction.guild as Guild); - await ButtonInteraction.deferUpdate(); - break; - - case ButtonIDs.previousSong: { - const song = await this.client.audioPlayer.previous(ButtonInteraction.guild as Guild); - if (song) { - await ButtonInteraction.reply({ - content: generateMessageAudioPlayerPrevious( - ButtonInteraction.member as GuildMember, - song - ) - }); - } else { - await ButtonInteraction.reply({ - content: generateMessageAudioPlayerPreviousFailure(), - ephemeral: true - }); - } - break; - } - - case ButtonIDs.skipSong: { - const song = await this.client.audioPlayer.skip(ButtonInteraction.guild as Guild); - - if (song) { - await ButtonInteraction.reply({ - content: generateSkipMessage(song, ButtonInteraction.member as GuildMember) - }); - } else { - await ButtonInteraction.reply({ - content: generateSkipMessageFailure(), - ephemeral: true - }); - } - break; - } - - case ButtonIDs.toggleLoopMode: - await this.client.audioPlayer.changeLoopMode(ButtonInteraction.guild as Guild); - await ButtonInteraction.deferUpdate(); - break; - - // case ButtonIDs.downloadSong: { - // const song = this.client.audioPlayer.distube.getQueue(ButtonInteraction.guild as Guild)?.songs[0] - // - // if (!song) { - // await ButtonInteraction.reply({embeds: [generateErrorEmbed(i18next.t("audioplayer:download_song_error"))]}) - // break - // } - // await ButtonInteraction.reply({ephemeral: true, embeds: [generateDownloadSongEmbed(song.streamURL ?? song.url)]}) - // break - // } - - case ButtonIDs.showQueue: - await this.client.audioPlayer.showQueue(ButtonInteraction); - break; - - case ButtonIDs.shuffle: { - if (await this.client.audioPlayer.shuffle(ButtonInteraction.guild as Guild)) { - await ButtonInteraction.reply({ - content: generateMessageAudioPlayerShuffle(ButtonInteraction.member as GuildMember) - }); - } else { - await ButtonInteraction.reply(generateMessageAudioPlayerShuffleFailure()); - } - break; - } - } - } catch (e) { - loggerError(e); - } - }); - } - - getComponents(): Array> { - return [this.rowPrimary, this.rowSecondary]; - } - - getComponentsOnlyStop(): Array> { - return [this.rowWithOnlyStop]; - } - - destroy() { - this.collector.stop(); - } -} diff --git a/src/commands/audio/audiodebug.command.ts b/src/commands/audio/audiodebug.command.ts index 178938a..fcaa8be 100644 --- a/src/commands/audio/audiodebug.command.ts +++ b/src/commands/audio/audiodebug.command.ts @@ -1,6 +1,7 @@ import { ICommand } from '../../CommandTypes.js'; import { PermissionsBitField } from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; +import { isOverpoweredUser } from '../../utilities/isOverpoweredUser.js'; export default function (): ICommand { return { @@ -8,6 +9,8 @@ export default function (): ICommand { name: 'audiodebug', description: 'Debug info about audioplayers', execute: async (message) => { + if (!isOverpoweredUser(message.author.id)) return; + await message.reply({ content: message.client.audioPlayer.playersManager.debug(), allowedMentions: { users: [] } diff --git a/src/commands/audio/download.command.ts b/src/commands/audio/download.command.ts index 79da861..d6720e8 100644 --- a/src/commands/audio/download.command.ts +++ b/src/commands/audio/download.command.ts @@ -6,7 +6,7 @@ import { deleteMP3file, DownloadSongErrorGetLocale, getSongFileAttachment -} from './util/downloadSong.js'; +} from '../../audioplayer/util/downloadSong.js'; import i18next from 'i18next'; import { generateErrorEmbed } from '../../utilities/generateErrorEmbed.js'; import { ReadStream } from 'fs'; diff --git a/src/commands/audio/jump.command.ts b/src/commands/audio/jump.command.ts index d3db5d9..d4e341b 100644 --- a/src/commands/audio/jump.command.ts +++ b/src/commands/audio/jump.command.ts @@ -1,13 +1,14 @@ import { CommandArgument, ICommand } from '../../CommandTypes.js'; -import { GuildMember, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; +import { EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import { generateErrorEmbed } from '../../utilities/generateErrorEmbed.js'; import { Song } from 'distube'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { @@ -32,9 +33,9 @@ export default function (): ICommand { await AudioCommandWrapperText(message, async () => { const song = await message.client.audioPlayer.jump(message.guild!, pos!); if (song) { - await message.reply(generateMessageAudioPlayerJump(message.member!, song)); + await message.reply({ embeds: [generateEmbedAudioPlayerJump(message.member!, song)] }); } else { - await message.reply(generateMessageAudioPlayerJumpFailure()); + await message.reply({ embeds: [generateEmbedAudioPlayerJumpFailure()] }); } }); } @@ -59,11 +60,11 @@ export default function (): ICommand { await AudioCommandWrapperInteraction(interaction, async () => { const song = await interaction.client.audioPlayer.jump(interaction.guild!, pos!); if (song) { - await interaction.reply( - generateMessageAudioPlayerJump(interaction.member as GuildMember, song) - ); + await interaction.reply({ + embeds: [generateEmbedAudioPlayerJump(interaction.member as GuildMember, song)] + }); } else { - await interaction.reply(generateMessageAudioPlayerJumpFailure()); + await interaction.reply({ embeds: [generateEmbedAudioPlayerJumpFailure()] }); } }); } @@ -78,10 +79,12 @@ export default function (): ICommand { }; } -function generateMessageAudioPlayerJump(member: GuildMember, song: Song) { - return `:fast_forward: ${member} ${i18next.t('commands:jump_success')} ${song.name} :fast_forward:`; +function generateEmbedAudioPlayerJump(member: GuildMember, song: Song): EmbedBuilder { + return generateSimpleEmbed( + `:fast_forward: ${member} ${i18next.t('commands:jump_success')} ${song.name} :fast_forward:` + ); } -function generateMessageAudioPlayerJumpFailure() { - return i18next.t('commands:jump_failure'); +function generateEmbedAudioPlayerJumpFailure(): EmbedBuilder { + return generateSimpleEmbed(i18next.t('commands:jump_failure')); } diff --git a/src/commands/audio/play.command.ts b/src/commands/audio/play.command.ts index a143911..9629c7e 100644 --- a/src/commands/audio/play.command.ts +++ b/src/commands/audio/play.command.ts @@ -1,18 +1,24 @@ import { CommandArgument, ICommand } from '../../CommandTypes.js'; import { + ApplicationCommandOptionChoiceData, AutocompleteInteraction, + Client, + Guild, GuildMember, Message, PermissionsBitField, SlashCommandBuilder, TextChannel, + VoiceBasedChannel, VoiceChannel } from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; -import { isValidURL } from '../../utilities/isValidURL.js'; -import { SearchResultType, SearchResultVideo } from 'distube'; import { truncateString } from '../../utilities/truncateString.js'; import i18next from 'i18next'; +import { SearchResultType } from '@distube/youtube'; +import ytsr from '@distube/ytsr'; +import { queueSongsLimit } from '../../audioplayer/AudioPlayerCore.js'; +import { generateWarningEmbed } from '../../utilities/generateWarningEmbed.js'; export const services = 'Youtube, Spotify, Soundcloud, Yandex Music, HTTP-stream'; export default function (): ICommand { @@ -27,13 +33,28 @@ export default function (): ICommand { const songQuery = args.join(' '); const member = message.member as GuildMember; + const channel = message.channel as TextChannel; + + if (queueSongsIsFull(message.client, message.guild as Guild)) { + await message.reply({ + embeds: [ + generateWarningEmbed( + i18next.t('commands:play_error_songs_limit', { + queueLimit: queueSongsLimit + }) as string + ) + ] + }); + return; + } + await message.client.audioPlayer.play( - member.voice.channel as VoiceChannel, - message.channel as TextChannel, + member.voice.channel as VoiceBasedChannel, + channel, songQuery, { - member: message.member as GuildMember, - textChannel: message.channel as TextChannel + member: member, + textChannel: channel } ); @@ -55,12 +76,27 @@ export default function (): ICommand { execute: async (interaction) => { const songQuery = interaction.options.getString('request'); + if (queueSongsIsFull(interaction.client, interaction.guild as Guild)) { + await interaction.reply({ + embeds: [ + generateWarningEmbed( + i18next.t('commands:play_error_songs_limit', { + queueLimit: queueSongsLimit + }) as string + ) + ], + ephemeral: true + }); + return; + } + await interaction.reply({ content: i18next.t('general:thinking') as string }); await interaction.deleteReply(); const member = interaction.member as GuildMember; + if (songQuery) { await interaction.client.audioPlayer.play( member.voice.channel as VoiceChannel, @@ -90,29 +126,39 @@ export default function (): ICommand { }; } +const liveText = i18next.t('commands:play_stream'); + export async function songSearchAutocomplete(interaction: AutocompleteInteraction) { const focusedValue = interaction.options.getFocused(false); - if (focusedValue && !isValidURL(focusedValue)) { - const choices = await interaction.client.audioPlayer.distube.search(focusedValue, { + if (focusedValue) { + const choices = await ytsr(focusedValue, { + safeSearch: true, limit: 10, - type: SearchResultType.VIDEO, - safeSearch: false + type: SearchResultType.VIDEO }); - const finalResult = choices.map((choice: SearchResultVideo) => { - const duration = choice.isLive ? i18next.t('commands:play_stream') : choice.formattedDuration; - let choiceString = `${duration} | ${truncateString(choice.uploader.name ?? '', 20)} | `; - choiceString += truncateString(choice.name, 100 - choiceString.length); + const finalResult = choices.items.map((video: ytsr.Video) => { + const duration = video.isLive ? liveText : video.duration; + let choiceString = `${duration} | ${truncateString(video.author?.name ?? ' ', 20)} | `; + choiceString += truncateString(video.name, 100 - choiceString.length); return { name: choiceString, - value: choice.url + value: video.url }; }); - await interaction.respond(finalResult); + await interaction.respond(finalResult as Array); return; } await interaction.respond([]); } + +function queueSongsIsFull(client: Client, guild: Guild): boolean { + const queue = client.audioPlayer.distube.getQueue(guild); + + if (!queue) return false; + + return queue.songs.length >= queueSongsLimit; +} diff --git a/src/commands/audio/playfile.command.ts b/src/commands/audio/playfile.command.ts index d15bf51..5327293 100644 --- a/src/commands/audio/playfile.command.ts +++ b/src/commands/audio/playfile.command.ts @@ -8,7 +8,7 @@ import { VoiceChannel } from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; -import { isAudioFile } from './util/isAudioFile.js'; +import { isAudioFile } from '../../audioplayer/util/isAudioFile.js'; import { generateErrorEmbed } from '../../utilities/generateErrorEmbed.js'; import i18next from 'i18next'; diff --git a/src/commands/audio/playing.command.ts b/src/commands/audio/playing.command.ts index 49d3441..db06604 100644 --- a/src/commands/audio/playing.command.ts +++ b/src/commands/audio/playing.command.ts @@ -4,7 +4,7 @@ import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import { splitBar } from '../../utilities/splitBar.js'; import { Queue } from 'distube'; import i18next from 'i18next'; @@ -50,7 +50,7 @@ export function generatePlayingMessage(guild: Guild): EmbedBuilder { if (queue) { const song = queue.songs[0]; embed.setTitle(song.name!); - embed.setURL(song.url); + embed.setURL(song.url!); embed.setAuthor({ name: `${i18next.t('commands:playing_now_playing')}:` }); embed.addFields({ name: i18next.t('commands:playing_song_length'), diff --git a/src/commands/audio/previous.command.ts b/src/commands/audio/previous.command.ts index 354624b..0254766 100644 --- a/src/commands/audio/previous.command.ts +++ b/src/commands/audio/previous.command.ts @@ -1,12 +1,13 @@ import { ICommand } from '../../CommandTypes.js'; -import { GuildMember, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; +import { EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import { Song } from 'distube'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { @@ -18,10 +19,10 @@ export default function (): ICommand { const song = await message.client.audioPlayer.previous(message.guild!); if (song) { await message.reply({ - content: generateMessageAudioPlayerPrevious(message.member as GuildMember, song) + embeds: [generateEmbedAudioPlayerPrevious(message.member as GuildMember, song)] }); } else { - await message.reply({ content: generateMessageAudioPlayerPreviousFailure() }); + await message.reply({ embeds: [generateEmbedAudioPlayerPreviousFailure()] }); } }); } @@ -35,11 +36,11 @@ export default function (): ICommand { const song = await interaction.client.audioPlayer.previous(interaction.guild!); if (song) { await interaction.reply({ - content: generateMessageAudioPlayerPrevious(interaction.member as GuildMember, song) + embeds: [generateEmbedAudioPlayerPrevious(interaction.member as GuildMember, song)] }); } else { await interaction.reply({ - content: generateMessageAudioPlayerPreviousFailure(), + embeds: [generateEmbedAudioPlayerPreviousFailure()], ephemeral: true }); } @@ -56,10 +57,12 @@ export default function (): ICommand { }; } -export function generateMessageAudioPlayerPrevious(member: GuildMember, song: Song) { - return `:rewind: ${member} ${i18next.t('previous_success')} ${song.name} :rewind:`; +export function generateEmbedAudioPlayerPrevious(member: GuildMember, song: Song): EmbedBuilder { + return generateSimpleEmbed( + `:rewind: ${member} ${i18next.t('commands:previous_success')} ${song.name} - ${song.uploader.name} :rewind:` + ); } -export function generateMessageAudioPlayerPreviousFailure() { - return i18next.t('previous_error_song_not_exists'); +export function generateEmbedAudioPlayerPreviousFailure() { + return generateSimpleEmbed(i18next.t('commands:previous_error_song_not_exists')); } diff --git a/src/commands/audio/rewind.command.ts b/src/commands/audio/rewind.command.ts index f0f76e5..3641e4a 100644 --- a/src/commands/audio/rewind.command.ts +++ b/src/commands/audio/rewind.command.ts @@ -1,12 +1,13 @@ import { CommandArgument, ICommand } from '../../CommandTypes.js'; -import { GuildMember, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; +import { EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import { formatSecondsToTime } from '../../utilities/formatSecondsToTime.js'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { @@ -21,11 +22,11 @@ export default function (): ICommand { if (time) { if (await message.client.audioPlayer.rewind(message.guild!, time)) { await message.reply({ - content: generateMessageAudioPlayerRewind(message.member!, time) + embeds: [generateEmbedAudioPlayerRewind(message.member!, time)] }); } } else { - await message.reply({ content: generateMessageAudioPlayerRewindFailure() }); + await message.reply({ embeds: [generateEmbedAudioPlayerRewindFailure()] }); } }); } @@ -50,12 +51,12 @@ export default function (): ICommand { if (time) { if (await interaction.client.audioPlayer.rewind(interaction.guild!, time)) { await interaction.reply({ - content: generateMessageAudioPlayerRewind(interaction.member as GuildMember, time) + embeds: [generateEmbedAudioPlayerRewind(interaction.member as GuildMember, time)] }); } } else { await interaction.reply({ - content: generateMessageAudioPlayerRewindFailure(), + embeds: [generateEmbedAudioPlayerRewindFailure()], ephemeral: true }); } @@ -93,10 +94,12 @@ function hmsToSeconds(str: string): number | undefined { return s; } -export function generateMessageAudioPlayerRewind(member: GuildMember, time: number) { - return `${member} ${i18next.t('commands:rewind_success')} ${formatSecondsToTime(time)}`; +export function generateEmbedAudioPlayerRewind(member: GuildMember, time: number): EmbedBuilder { + return generateSimpleEmbed( + `${member} ${i18next.t('commands:rewind_success')} ${formatSecondsToTime(time)}` + ); } -export function generateMessageAudioPlayerRewindFailure() { - return i18next.t('commands:rewind_failure'); +export function generateEmbedAudioPlayerRewindFailure(): EmbedBuilder { + return generateSimpleEmbed(i18next.t('commands:rewind_failure')); } diff --git a/src/commands/audio/shuffle.command.ts b/src/commands/audio/shuffle.command.ts index f70f3da..2728029 100644 --- a/src/commands/audio/shuffle.command.ts +++ b/src/commands/audio/shuffle.command.ts @@ -1,11 +1,18 @@ import { ICommand } from '../../CommandTypes.js'; -import { GuildMember, Message, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; +import { + EmbedBuilder, + GuildMember, + Message, + PermissionsBitField, + SlashCommandBuilder +} from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { text_data: { @@ -14,9 +21,9 @@ export default function (): ICommand { execute: async (message: Message): Promise => { await AudioCommandWrapperText(message, async (): Promise => { if (await message.client.audioPlayer.shuffle(message.guild!)) { - await message.reply({ content: generateMessageAudioPlayerShuffle(message.member!) }); + await message.reply({ embeds: [generateEmbedAudioPlayerShuffle(message.member!)] }); } else { - await message.reply(generateMessageAudioPlayerShuffleFailure()); + await message.reply({ embeds: [generateEmbedAudioPlayerShuffleFailure()] }); } }); } @@ -29,10 +36,10 @@ export default function (): ICommand { await AudioCommandWrapperInteraction(interaction, async (): Promise => { if (await interaction.client.audioPlayer.shuffle(interaction.guild!)) { await interaction.reply({ - content: generateMessageAudioPlayerShuffle(interaction.member as GuildMember) + embeds: [generateEmbedAudioPlayerShuffle(interaction.member as GuildMember)] }); } else { - await interaction.reply(generateMessageAudioPlayerShuffleFailure()); + await interaction.reply({ embeds: [generateEmbedAudioPlayerShuffleFailure()] }); } }); } @@ -47,10 +54,10 @@ export default function (): ICommand { }; } -export function generateMessageAudioPlayerShuffle(member: GuildMember): string { - return `${member} ${i18next.t('commands:shuffle_success')}`; +export function generateEmbedAudioPlayerShuffle(member: GuildMember): EmbedBuilder { + return generateSimpleEmbed(`${member} ${i18next.t('commands:shuffle_success')}`); } -export function generateMessageAudioPlayerShuffleFailure(): string { - return i18next.t('commands:shuffle_failure'); +export function generateEmbedAudioPlayerShuffleFailure(): EmbedBuilder { + return generateSimpleEmbed(i18next.t('commands:shuffle_failure')); } diff --git a/src/commands/audio/skip.command.ts b/src/commands/audio/skip.command.ts index e8181f6..b43b856 100644 --- a/src/commands/audio/skip.command.ts +++ b/src/commands/audio/skip.command.ts @@ -1,12 +1,19 @@ import { ICommand } from '../../CommandTypes.js'; -import { GuildMember, Message, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; +import { + EmbedBuilder, + GuildMember, + Message, + PermissionsBitField, + SlashCommandBuilder +} from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import { Song } from 'distube'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { @@ -17,9 +24,9 @@ export default function (): ICommand { await AudioCommandWrapperText(message, async () => { const song = await message.client.audioPlayer.skip(message.guild!); if (song) { - await message.reply({ content: generateSkipMessage(song, message.member!) }); + await message.reply({ embeds: [generateSkipEmbed(song, message.member!)] }); } else { - await message.reply({ content: generateSkipMessageFailure() }); + await message.reply({ embeds: [generateSkipEmbedFailure()] }); } }); } @@ -33,10 +40,10 @@ export default function (): ICommand { const song = await interaction.client.audioPlayer.skip(interaction.guild!); if (song) { await interaction.reply({ - content: generateSkipMessage(song, interaction.member as GuildMember) + embeds: [generateSkipEmbed(song, interaction.member as GuildMember)] }); } else { - await interaction.reply({ content: generateSkipMessageFailure(), ephemeral: true }); + await interaction.reply({ embeds: [generateSkipEmbedFailure()], ephemeral: true }); } }); } @@ -51,10 +58,12 @@ export default function (): ICommand { }; } -export function generateSkipMessage(song: Song, member: GuildMember): string { - return `:fast_forward: ${member} ${i18next.t('commands:skip_success')} ${song.name} - ${song.uploader.name} :fast_forward:`; +export function generateSkipEmbed(song: Song, member: GuildMember): EmbedBuilder { + return generateSimpleEmbed( + `:fast_forward: ${member} ${i18next.t('commands:skip_success')} ${song.name} - ${song.uploader.name} :fast_forward:` + ); } -export function generateSkipMessageFailure(): string { - return i18next.t('commands:skip_failure'); +export function generateSkipEmbedFailure(): EmbedBuilder { + return generateSimpleEmbed(i18next.t('commands:skip_failure')); } diff --git a/src/commands/audio/stop.command.ts b/src/commands/audio/stop.command.ts index 6ad3a12..1695468 100644 --- a/src/commands/audio/stop.command.ts +++ b/src/commands/audio/stop.command.ts @@ -1,11 +1,18 @@ import { ICommand } from '../../CommandTypes.js'; -import { GuildMember, Message, PermissionsBitField, SlashCommandBuilder } from 'discord.js'; +import { + EmbedBuilder, + GuildMember, + Message, + PermissionsBitField, + SlashCommandBuilder +} from 'discord.js'; import { GroupAudio } from './AudioTypes.js'; import { AudioCommandWrapperInteraction, AudioCommandWrapperText -} from './util/AudioCommandWrappers.js'; +} from '../../audioplayer/util/AudioCommandWrappers.js'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { @@ -15,7 +22,7 @@ export default function (): ICommand { execute: async (message: Message) => { await AudioCommandWrapperText(message, async () => { await message.client.audioPlayer.stop(message.guild!); - await message.reply({ content: generateMessageAudioPlayerStop(message.member!) }); + await message.reply({ embeds: [generateEmbedAudioPlayerStop(message.member!)] }); }); } }, @@ -27,7 +34,7 @@ export default function (): ICommand { await AudioCommandWrapperInteraction(interaction, async () => { await interaction.client.audioPlayer.stop(interaction.guild!); await interaction.reply({ - content: generateMessageAudioPlayerStop(interaction.member as GuildMember) + embeds: [generateEmbedAudioPlayerStop(interaction.member as GuildMember)] }); }); } @@ -42,6 +49,6 @@ export default function (): ICommand { }; } -export function generateMessageAudioPlayerStop(member: GuildMember): string { - return `${member} ${i18next.t('commands:stop_success')}`; +export function generateEmbedAudioPlayerStop(member: GuildMember): EmbedBuilder { + return generateSimpleEmbed(`${member} ${i18next.t('commands:stop_success')}`); } diff --git a/src/commands/info/report.command.ts b/src/commands/info/report.command.ts index 858e725..ee33878 100644 --- a/src/commands/info/report.command.ts +++ b/src/commands/info/report.command.ts @@ -1,17 +1,13 @@ import { ICommand } from '../../CommandTypes.js'; import { - ActionRowBuilder, ChatInputCommandInteraction, Message, - ModalActionRowComponentBuilder, - ModalBuilder, PermissionsBitField, - SlashCommandBuilder, - TextInputBuilder, - TextInputStyle + SlashCommandBuilder } from 'discord.js'; import { GroupInfo } from './InfoTypes.js'; import i18next from 'i18next'; +import { generateSimpleEmbed } from '../../utilities/generateSimpleEmbed.js'; export default function (): ICommand { return { @@ -19,7 +15,7 @@ export default function (): ICommand { name: 'report', description: i18next.t('commands:report_desc'), execute: async (message: Message) => { - await message.reply(i18next.t('commands:report_text_error') as string); + await message.reply({ embeds: [generateReportEmbed()] }); } }, slash_data: { @@ -27,7 +23,7 @@ export default function (): ICommand { .setName('report') .setDescription(i18next.t('commands:report_desc')), execute: async (interaction: ChatInputCommandInteraction) => { - await interaction.showModal(generateModalWindow()); + await interaction.reply({ embeds: [generateReportEmbed()], ephemeral: true }); } }, group: GroupInfo, @@ -35,24 +31,12 @@ export default function (): ICommand { }; } -function generateModalWindow() { - const modal = new ModalBuilder() - .setCustomId('reportModal') - .setTitle(i18next.t('commands:report_modal_title')); - - const reportInput = new TextInputBuilder() - .setCustomId('reportInput') - .setLabel(i18next.t('commands:report_modal_text_label')) - .setStyle(TextInputStyle.Paragraph) - .setMinLength(20) - .setPlaceholder(i18next.t('commands:report_modal_text_placeholder')) - .setRequired(true); - - const firstActionRow = new ActionRowBuilder().addComponents( - reportInput +function generateReportEmbed() { + return generateSimpleEmbed( + i18next.t('commands:report_message', { + issueLink: 'https://github.com/AlexInCube/AlCoTest/issues/new/choose', + discussionLink: 'https://github.com/AlexInCube/AlCoTest/discussions/new?category=q-a', + interpolation: { escapeValue: false } + }) ); - - modal.addComponents(firstActionRow); - - return modal; } diff --git a/src/commands/info/status.command.ts b/src/commands/info/status.command.ts index e7ab0b5..a79afa1 100644 --- a/src/commands/info/status.command.ts +++ b/src/commands/info/status.command.ts @@ -8,8 +8,6 @@ import { SlashCommandBuilder } from 'discord.js'; import { GroupInfo } from './InfoTypes.js'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import node_os_pkg from 'node-os-utils'; import i18next from 'i18next'; diff --git a/src/events/interactionCreate.event.ts b/src/events/interactionCreate.event.ts index 87cccd3..bc73dcb 100644 --- a/src/events/interactionCreate.event.ts +++ b/src/events/interactionCreate.event.ts @@ -1,7 +1,6 @@ import { BotEvent } from '../Types.js'; import { slashCommandHandler } from './interactionHandlers/slashCommandHandler.js'; import { autocompleteHandler } from './interactionHandlers/autocompleteHandler.js'; -import { modalsHandler } from './interactionHandlers/modalsHandler.js'; import { Client, Events, Interaction } from 'discord.js'; const event: BotEvent = { @@ -9,7 +8,6 @@ const event: BotEvent = { execute: async (client: Client, interaction: Interaction) => { await slashCommandHandler(interaction); await autocompleteHandler(interaction); - await modalsHandler(interaction); } }; diff --git a/src/events/interactionHandlers/modalsHandler.ts b/src/events/interactionHandlers/modalsHandler.ts deleted file mode 100644 index cb4a041..0000000 --- a/src/events/interactionHandlers/modalsHandler.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { submitReport } from '../../handlers/MongoSchemas/SchemaReport.js'; -import { Interaction } from 'discord.js'; -import i18next from 'i18next'; -import { ENV } from '../../EnvironmentVariables.js'; - -export async function modalsHandler(interaction: Interaction) { - if (!interaction.isModalSubmit()) return; - if (interaction.customId === 'reportModal') { - await interaction.reply({ - content: i18next.t('commands:report_modal_feedback') as string, - ephemeral: true - }); - - const reportInput = interaction.fields.getTextInputValue('reportInput'); - const overpoweredHuman = interaction.client.users.cache.get(ENV.BOT_DISCORD_OVERPOWERED_ID); - if (overpoweredHuman) { - await overpoweredHuman.send( - `New report from ${interaction.user.username} with ID: ${interaction.user.id}\n\n${reportInput}` - ); - } - await submitReport(interaction.user.id, reportInput); - } -} diff --git a/src/events/interactionHandlers/slashCommandHandler.ts b/src/events/interactionHandlers/slashCommandHandler.ts index 00b18a0..f7da309 100644 --- a/src/events/interactionHandlers/slashCommandHandler.ts +++ b/src/events/interactionHandlers/slashCommandHandler.ts @@ -72,9 +72,6 @@ export async function slashCommandHandler(interaction: Interaction) { await command.slash_data.execute(interaction); } catch (e) { if (ENV.BOT_VERBOSE_LOGGING) - loggerError( - `${i18next.t('commandshandlers:slash_command_error')}: ${e}`, - loggerPrefixCommandHandler - ); + loggerError(`Error when executing slash command: ${e}`, loggerPrefixCommandHandler); } } diff --git a/src/events/messageCreate.event.ts b/src/events/messageCreate.event.ts index 4360b0f..91b7490 100644 --- a/src/events/messageCreate.event.ts +++ b/src/events/messageCreate.event.ts @@ -1,7 +1,7 @@ import { BotEvent } from '../Types.js'; import { Client, Events, Message, TextChannel } from 'discord.js'; import { textCommandsHandler } from './messageHandlers/textCommandsHandler.js'; -import { playerMessageHandler } from './messageHandlers/playerMessageHandler.js'; +import { AudioPlayerEventMessageCreate } from '../audioplayer/eventsHandlers/AudioPlayerEventMessageCreate.js'; const event: BotEvent = { name: Events.MessageCreate, @@ -9,7 +9,7 @@ const event: BotEvent = { await textCommandsHandler(client, message); if (!message.guild) return; - await playerMessageHandler(message.channel as TextChannel); + await AudioPlayerEventMessageCreate(message.channel as TextChannel); } }; diff --git a/src/events/messageDeleted.event.ts b/src/events/messageDeleted.event.ts index 7fc9bf3..601f433 100644 --- a/src/events/messageDeleted.event.ts +++ b/src/events/messageDeleted.event.ts @@ -1,24 +1,13 @@ import { BotEvent } from '../Types.js'; -import { Client, Events, Message, TextChannel } from 'discord.js'; -import { ENV } from '../EnvironmentVariables.js'; -import { loggerError } from '../utilities/logger.js'; +import { Client, Events, Message } from 'discord.js'; +import { + AudioPlayerEventMessageDeleted +} from '../audioplayer/eventsHandlers/AudioPlayerEventMessageDeleted.js'; const event: BotEvent = { name: Events.MessageDelete, execute: async (client: Client, message: Message) => { - if (!message.guild) return; - - const textChannel = message.channel as TextChannel; - const player = client.audioPlayer.playersManager.get(textChannel.guild.id); - if (player) { - if (player.textChannel.id !== textChannel.id) return; - if (player.lastDeletedMessage?.id === message.id) return; // If player deleted message them self, then don`t "trigger" event - try { - await player.recreatePlayer(); - } catch (e) { - if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); - } - } + await AudioPlayerEventMessageDeleted(client, message); } }; diff --git a/src/events/messageHandlers/textCommandsHandler.ts b/src/events/messageHandlers/textCommandsHandler.ts index ffa3f42..3f86b73 100644 --- a/src/events/messageHandlers/textCommandsHandler.ts +++ b/src/events/messageHandlers/textCommandsHandler.ts @@ -123,9 +123,6 @@ export async function textCommandsHandler(client: Client, message: Message) { await command.text_data.execute(message, args); } catch (e) { if (ENV.BOT_VERBOSE_LOGGING) - loggerError( - `${i18next.t('commandshandlers:text_command_error')}: ${e}`, - loggerPrefixCommandHandler - ); + loggerError(`commandshandlers:text_command_error: ${e}`, loggerPrefixCommandHandler); } } diff --git a/src/events/onChannelDelete.event.ts b/src/events/onChannelDelete.event.ts index 4787de5..8767974 100644 --- a/src/events/onChannelDelete.event.ts +++ b/src/events/onChannelDelete.event.ts @@ -1,13 +1,13 @@ import { BotEvent } from '../Types.js'; import { Client, Events, TextChannel } from 'discord.js'; +import { + AudioPlayerEventChannelDelete +} from '../audioplayer/eventsHandlers/AudioPlayerEventChannelDelete.js'; const event: BotEvent = { name: Events.ChannelDelete, execute: async (client: Client, channel: TextChannel) => { - const player = client.audioPlayer.playersManager.get(channel.guild.id); - if (player?.textChannel.id === channel.id) { - await client.audioPlayer.stop(channel.guild); - } + await AudioPlayerEventChannelDelete(client, channel) } }; diff --git a/src/events/onReady.event.ts b/src/events/onReady.event.ts index 2b5e5ac..cbb9c29 100644 --- a/src/events/onReady.event.ts +++ b/src/events/onReady.event.ts @@ -8,9 +8,7 @@ const event: BotEvent = { execute: (client) => { if (!client.user) return; - loggerSend( - `Bot ${client.user.username} is running, on version ${process.env.npm_package_version}` - ); + loggerSend(`Bot ${client.user.username} is successfully started!`); client.user.setActivity('/help'); } }; diff --git a/src/events/voiceChannelUpdate.event.ts b/src/events/voiceChannelUpdate.event.ts new file mode 100644 index 0000000..299bff5 --- /dev/null +++ b/src/events/voiceChannelUpdate.event.ts @@ -0,0 +1,15 @@ +import { BotEvent } from '../Types'; +import { Client, Events, VoiceState } from 'discord.js'; +import { + AudioPlayerEventVoiceChannelUpdate +} from '../audioplayer/eventsHandlers/AudioPlayerEventVoiceChannelUpdate.js'; + +export const event: BotEvent = { + name: Events.VoiceStateUpdate, + execute: async (client: Client, oldState: VoiceState, newState: VoiceState) => { + //ENV.NODE_ENV === 'production' ? 120 : 5 + await AudioPlayerEventVoiceChannelUpdate(client, oldState, newState) + } +}; + +export default event; diff --git a/src/handlers/Command.handler.ts b/src/handlers/Command.handler.ts index f78efd1..19e160c 100644 --- a/src/handlers/Command.handler.ts +++ b/src/handlers/Command.handler.ts @@ -19,7 +19,7 @@ const handler = async (client: Client) => { const commandsDir = path.join(getDirName(import.meta.url), '../commands'); const scanResult: string[] = getAllCommandFilesInDir(commandsDir); // Recursion for scan "commands" folder for files end with ".command.js" - const buildersArray: SlashBuilder[] = []; // Prepare builders array for send into Discord REST API + const buildersArray: SlashBuilder[] = []; // Prepare a builder array for sending into Discord REST API for (const filePath of scanResult) { const importPath = `file:///${filePath}`; diff --git a/src/locales/en/audioplayer.json b/src/locales/en/audioplayer.json index 46a4a0d..771800f 100644 --- a/src/locales/en/audioplayer.json +++ b/src/locales/en/audioplayer.json @@ -1,7 +1,7 @@ { "audio_commands_wrapper_song_processing": "Song is processing, please wait", "audio_commands_wrapper_player_not_exist": "Player is not exists", - "play_error": "With this link is something wrong", + "play_error": "With this song is something wrong", "download_song_error": "Something went wrong, when retrieving link to audio", "show_queue_title": "Now playing", "show_queue_songs_in_queue": "Songs in queue", @@ -12,6 +12,7 @@ "event_add_song_author": "Author", "event_add_list": "Playlist added", "event_add_list_songs_count": "Songs count", + "event_add_list_limit": "Some songs are added, but its trimmed to {{queueLimit}}", "player_embed_unknown": "Unknown", "player_embed_requester": "Requester", "player_embed_author": "Author", diff --git a/src/locales/en/commands.json b/src/locales/en/commands.json index dba8c7d..763f7f0 100644 --- a/src/locales/en/commands.json +++ b/src/locales/en/commands.json @@ -2,7 +2,7 @@ "set_prefix_desc": "Change prefix for TEXT commands and only for current server", "set_prefix_arg_newprefix_desc": "Don't forget to inform the other server members about the changed prefix", "set_prefix_restrict_prefixes": "You can't specify characters {{prefixes}} as a prefix", - "set_prefix_length_error": "The prefix cannot be longer than one character", + "set_prefix_length_error": "The prefix cannot be longer than two characters", "set_prefix_success_change": "Prefix successful changed on this server to {{prefix}}", "help_desc": "Commands list", "help_arg_command": "command name", @@ -17,6 +17,7 @@ "play_desc": "Playing requested song", "play_arg_link": "Links from {{services}} or any text", "play_stream": "Live", + "play_error_songs_limit": "You cant add new songs, because queue hit the limit in {{queueLimit}} songs", "download_desc": "Download song from the link", "download_arg_request": "Links from {{services}}", "download_please_wait": "Please wait, it can take a long time", @@ -55,12 +56,8 @@ "stop_success": "turned off the audioplayer", "alcotest_desc": "Writes the percentage of beer in your blood", "alcotest_success": "You are consisting of beer on the", - "report_desc": "Opens a modal window for sending a message to the developer", - "report_text_error": "Unfortunately, this command only works if it is called with /. So write /report", - "report_modal_title": "Creating a wish/complaint", - "report_modal_text_label": "What functionality to add or what to fix", - "report_modal_text_placeholder": "Describe clearly and precisely", - "report_modal_feedback": "Maybe I'll listen to you.", + "report_desc": "Return links to Github Issues or GitHub Discussions", + "report_message": "Create [Issue]({{issueLink}}) or [Discussion]({{discussionLink}})", "status_desc": "View bot status", "status_embed_title": "Bot status", "status_embed_bot_version": "Bot version", diff --git a/src/locales/en/general.json b/src/locales/en/general.json index 563c890..36dec00 100644 --- a/src/locales/en/general.json +++ b/src/locales/en/general.json @@ -1,5 +1,6 @@ { "error": "Error", + "warning": "Warning", "thinking": "I am thinking", "page": "Page" } diff --git a/src/locales/ru/audioplayer.json b/src/locales/ru/audioplayer.json index c5b8d14..16da67b 100644 --- a/src/locales/ru/audioplayer.json +++ b/src/locales/ru/audioplayer.json @@ -1,7 +1,8 @@ { "audio_commands_wrapper_song_processing": "Песни всё ещё обрабатываются, подожди", "audio_commands_wrapper_player_not_exist": "Плеера не существует", - "play_error": "Попробуйте другую ссылку", + "play_error": "Попробуйте другую песню, с этой какая-то ерунда происходит.", + "download_song_error": "Что-то пошло не так при получении ссылки на скачивание", "show_queue_title": "Сейчас играет", "show_queue_songs_in_queue": "Песни в очереди", "event_empty": "Все ушли от меня, значит я тоже ухожу", @@ -11,6 +12,7 @@ "event_add_song_author": "Автор", "event_add_list": "Добавлен плейлист", "event_add_list_songs_count": "Количество песен", + "event_add_list_limit": "Некоторые песни добавлены, но их количество было ограничено до {{queueLimit}}", "player_embed_unknown": "Неизвестно", "player_embed_requester": "Запросил", "player_embed_author": "Автор", diff --git a/src/locales/ru/commands.json b/src/locales/ru/commands.json index 9092f2c..7e64d87 100644 --- a/src/locales/ru/commands.json +++ b/src/locales/ru/commands.json @@ -2,7 +2,7 @@ "set_prefix_desc": "Меняет префикс для ТЕКСТОВЫХ команд и только для текущего сервера", "set_prefix_arg_newprefix_desc": "Не забудьте сообщить остальным участникам сервера об изменённом префиксе", "set_prefix_restrict_prefixes": "Нельзя указывать символы {{prefixes}} в качестве префикса", - "set_prefix_length_error": "Префикс не может быть длиннее одного символа", + "set_prefix_length_error": "Префикс не может быть длиннее двух символов", "set_prefix_success_change": "Префикс на этом сервере успешно изменён на {{prefix}}", "help_desc": "Список команд", "help_arg_command": "название команды", @@ -17,6 +17,7 @@ "play_desc": "Проигрывает музыку указанную пользователем", "play_arg_link": "Ссылка с {{services}} или любой текст", "play_stream": "Стрим", + "play_error_songs_limit": "Вы не можете добавлять новые песни, потому-что достигнут лимит очереди в {{queueLimit}} песен", "download_desc": "Скачивает песню по ссылке", "download_arg_request": "Ссылка с {{services}}", "download_please_wait": "Пожалуйста, подождите. Это может занять много времени", @@ -55,12 +56,8 @@ "stop_success": "выключил(-а) плеер", "alcotest_desc": "Пишет процент пива в твоей крови", "alcotest_success": "Вы состоите из пива на", - "report_desc": "Открывает окно для отправки сообщения разработчику", - "report_text_error": "К сожалению эта команда работает только если она вызвана через /. Так что напишите /report", - "report_modal_title": "Создание пожелания/жалобы", - "report_modal_text_label": "Какой функционал добавить или что исправить", - "report_modal_text_placeholder": "Описывайте ясно и чётко", - "report_modal_feedback": "Возможно я к тебе прислушаюсь", + "report_desc": "Возвращает ссылки на Github Issues или Github Discussions", + "report_message": "Создайте [Issue]({{issueLink}}) или [Discussion]({{discussionLink}})", "status_desc": "Просмотр состояния бота", "status_embed_title": "Состояние бота", "status_embed_bot_version": "Версия бота", diff --git a/src/locales/ru/commandshandlers.json b/src/locales/ru/commandshandlers.json index bb1121d..a8d0bdc 100644 --- a/src/locales/ru/commandshandlers.json +++ b/src/locales/ru/commandshandlers.json @@ -1,6 +1,4 @@ { - "text_command_error": "Ошибка исполнения текстовой команды", - "slash_command_error": "Ошибка при исполнении \"/\" команды", "bot_not_enough_permissions_1": "У БОТА недостаточно прав на этом канале или сервере", "bot_not_enough_permissions_2": "Напишите /help (название команды), чтобы увидеть недостающие права", "bot_not_enough_permissions_3": "А также попросите администрацию сервера их выдать боту", diff --git a/src/locales/ru/general.json b/src/locales/ru/general.json index dd13a3c..67012d1 100644 --- a/src/locales/ru/general.json +++ b/src/locales/ru/general.json @@ -1,5 +1,6 @@ { "error": "Ошибка", + "warning": "Предупреждение", "thinking": "Я думаю", "page": "Страница" } diff --git a/src/main.ts b/src/main.ts index 22423ea..85db499 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,16 @@ +loggerSend(`Starting bot on version ${process.env.npm_package_version}`); + import { Client, GatewayIntentBits, Partials } from 'discord.js'; -import { loggerError } from './utilities/logger.js'; +import { loggerError, loggerSend } from './utilities/logger.js'; import { loginBot } from './utilities/loginBot.js'; -import { AudioPlayerCore } from './commands/audio/audioPlayer/AudioPlayerCore.js'; +import { AudioPlayerCore } from './audioplayer/AudioPlayerCore.js'; import loadLocale from './locales/Locale.js'; await loadLocale(); import { handlersLoad } from './handlers/handlersLoad.js'; -const client = new Client({ +const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildPresences, diff --git a/src/utilities/generateErrorEmbed.ts b/src/utilities/generateErrorEmbed.ts index 79e889e..38da149 100644 --- a/src/utilities/generateErrorEmbed.ts +++ b/src/utilities/generateErrorEmbed.ts @@ -1,9 +1,12 @@ import { EmbedBuilder } from 'discord.js'; import i18next from 'i18next'; -export function generateErrorEmbed(errorMessage: string): EmbedBuilder { +export function generateErrorEmbed( + errorMessage: string, + errorName = i18next.t('general:error') +): EmbedBuilder { return new EmbedBuilder() - .setTitle(`⚠️ ${i18next.t('general:error')}`) + .setTitle(`<:error:1257892426790731786> ${errorName}`) .setColor('Red') .setDescription(errorMessage.slice(0, 2048)); } diff --git a/src/utilities/generateSimpleEmbed.ts b/src/utilities/generateSimpleEmbed.ts new file mode 100644 index 0000000..3017c76 --- /dev/null +++ b/src/utilities/generateSimpleEmbed.ts @@ -0,0 +1,5 @@ +import { EmbedBuilder } from 'discord.js'; + +export function generateSimpleEmbed(message: string): EmbedBuilder { + return new EmbedBuilder().setColor('Grey').setDescription(message.slice(0, 2048)); +} diff --git a/src/utilities/generateWarningEmbed.ts b/src/utilities/generateWarningEmbed.ts new file mode 100644 index 0000000..bcd09f2 --- /dev/null +++ b/src/utilities/generateWarningEmbed.ts @@ -0,0 +1,9 @@ +import { EmbedBuilder } from 'discord.js'; +import i18next from 'i18next'; + +export function generateWarningEmbed(warningMessage: string): EmbedBuilder { + return new EmbedBuilder() + .setTitle(`<:warning:1257892727014817865> ${i18next.t('general:warning')}`) + .setColor('Yellow') + .setDescription(warningMessage.slice(0, 2048)); +} diff --git a/src/utilities/isOverpoweredUser.ts b/src/utilities/isOverpoweredUser.ts new file mode 100644 index 0000000..72f35ee --- /dev/null +++ b/src/utilities/isOverpoweredUser.ts @@ -0,0 +1,5 @@ +import { ENV } from '../EnvironmentVariables.js'; + +export function isOverpoweredUser(userId: string): boolean { + return userId === ENV.BOT_DISCORD_OVERPOWERED_ID; +} diff --git a/src/utilities/logger.ts b/src/utilities/logger.ts index e6acdcc..d186e05 100644 --- a/src/utilities/logger.ts +++ b/src/utilities/logger.ts @@ -45,7 +45,7 @@ export function loggerSend( if (prefix) { console.log(`[ ${getCurrentTimestamp()} ] [ ${prefix} ] ${finalOutput}`); } else { - console.log(`[ ${getCurrentTimestamp()} ] [ UNKNOWN ] ${finalOutput}`); + console.log(`[ ${getCurrentTimestamp()} ] ${finalOutput}`); } } diff --git a/tsconfig.json b/tsconfig.json index 4ca1534..a0823dc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { - "module": "NodeNext", - "target": "es2021", + "baseUrl": "./", + "module": "preserve", + "target": "es2022", "sourceMap": true, "rootDir": "./src", "outDir": "./build", @@ -12,7 +13,8 @@ "resolveJsonModule": true, "forceConsistentCasingInFileNames": false, "allowSyntheticDefaultImports": true, - "skipLibCheck": true + "preserveConstEnums": false, + "skipLibCheck": true, }, "compileOnSave": true, "include": [ @@ -22,5 +24,5 @@ "exclude": [ "node_modules", "build" - ], + ] } diff --git a/wiki/API-Configure.md b/wiki/API-Configure.md new file mode 100644 index 0000000..4c13f4c --- /dev/null +++ b/wiki/API-Configure.md @@ -0,0 +1,78 @@ +> [!CAUTION] +> Don't share this API data with anyone because you can get compromised. +> If this happens, reset the tokens and get them again. + +# Discord Developer Portal (required) + +1. Open the [Discord Developer Portal](https://discord.com/developers/applications) and log into your account. +2. Click on the "New Application" button. +3. Enter a name and confirm the pop-up window by clicking the "Create" button. + +You should see a page like this: + +![discord-dev-selected-app](images/api-configure/discord-dev-selected-app.png) + +You can edit your application's name, description, and avatar here. Once you've done that, then congratulations—you're now the proud owner of a shiny new Discord bot! You're not entirely done, though. + +4. On "General Information" page copies the Application ID, this is your Client ID. +5. On the sidebar select section "Bot" +6. Press "Reset token" button and copy token. +7. Also enable all "Privileged Gateway Intents" + +![discord-dev-enable-intents](images/api-configure/discord-dev-enable-intents.png) + +# YouTube Cookie (optional) +Preferable to provide cookies for YouTube. +This will allow you to play 18+ videos and bypass YouTube rate limiting error (429 Error). +I highly recommend that you create a new Google account from which you can get the cookie. + +> [!WARNING] +> Time to time, cookies are resets by YouTube, so you need to retrieve it again. +> It's so annoying, but hasn't figured out a way to automate the cookie retrieval process. +> If you know workaround, please create the [issue](https://github.com/AlexInCube/AlCoTest/issues/new/choose) + +1. Install an extension for extracting cookie, [EditThisCookie](https://www.editthiscookie.com/blog/2014/03/install-editthiscookie/) +2. Go to [YouTube](https://www.youtube.com/) +3. Log in to your account. (You should use a new account for this purpose) +4. Click on the extension icon and click "Export" button. +5. Create file yt-cookies.json and paste cookie in this file + +# Yandex Music (optional) +If you do not provide token and UID, Yandex Music will not work at all. + +> [!WARNING] +> If your bot is outside Russia VDS, you must have a Yandex Plus subscription to play songs. + +## Token +1. Login into [Yandex](https://passport.yandex.ru/auth) account. +2. Download [browser extension](https://chromewebstore.google.com/detail/yandex-music-token/lcbjeookjibfhjjopieifgjnhlegmkib) +This must look like this ![yandex-extension](images/api-configure/yandex-music-extension.png) +3. Click "Скопировать токен" button. + +## UID +1. Login into [Yandex](https://passport.yandex.ru/auth) account. +2. You can retrieve uid by opening [Yandex Mail](https://mail.yandex.ru) and copy uid from the url in the address bar. +![yandex-uid](images/api-configure/yandex-music-uid.png) + +# Spotify (optional) +Spotify Module can work without provided data, but for more stability better provide custom application data. + +> [!WARNING] +> If you want to fetch playlist with more than 100 songs, this API data is required! + +1. Login in [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) +2. Press "Create app" button![Spotify Dev Main](images/api-configure/spotify-dev-main.png) +3. Fill the fields like this![Spotify Dev Create App](images/api-configure/spotify-dev-create-app.png) +4. Press "Save" +5. On the redirected page, press "Settings" +6. On "Basic Information" section copy Client ID. +7. Under the Client ID field, press "View Client Secret" and copy Client Secret. + +# Soundcloud (optional) + +1. Go to [SoundCloud](https://soundcloud.com) and login. +2. Open up the dev tools (Right-click -> inspect) and go to the Network tab. +3. Refresh the page with opened dev tools. +4. Find the request that has the name session (you can filter by typing session in the filter box) and click on it +5. Go to the Payload tab +6. You should see your client id in the Query String Parameters section, and your oauth token (access_token) in the Request Payload section diff --git a/wiki/Commands.md b/wiki/Commands.md new file mode 100644 index 0000000..cd1769b --- /dev/null +++ b/wiki/Commands.md @@ -0,0 +1,131 @@ +# About command system +Bot supports slash and text command systems. + +## Slash commands +Nothing special, start writing / and select command from the list + +## Text commands +If a bot owner does not change the default prefix in .env.production file, the prefix is // + +> [!NOTE] +> If bot prefix conflicting with another bot on server, +> administrator can set the second text prefix for server using [/setPrefix](#setprefix) command + +# Commands list +## Admin +It Can be used only by server administrators (people who have a role with "Administrator" permission) + +### setPrefix +Changes bot prefix for the server + +Example: /newPrefix $$ + +Prefix symbols cannot be symbols: / @ #, because these symbols reserved for Discord purpose. +Prefix length cannot be longer than two symbols. + +## Audio + +The bot philosophy built around audioplayer to reduce commands using. + +Use [/play](#play) or [/playfile](#playfile) to spawn audioplayer + +![play-audioplayer](images/commands/play-audioplayer.png) + +Most of the time of using bot, you need only add songs by [/play](#play) or [/playfile](#playfile) + +When any message created in chat where audioplayer is spawned, +bot will recreate the player so that the player is always at the bottom of the chat room. + +> [!NOTE] +> Highly recommended to create a text channel for the bot + +> [!WARNING] +> Audio commands which change audioplayer state require audioplayer and be with bot in the same voice channel to exist. +> Also this kind of commands needs to be written in the same channel where audioplayer was spawned. + +### play + +Example: /play https://open.spotify.com/track/46gSk82duJtX3TTA182ruG?si=c668ab77755f4d88 + +Spawn audioplayer in a text channel if not exists. +Accept songs/playlist from links. +Support searching on YouTube +when you write something like this ![play-autocomplete](images/commands/play-autocomplete.png) + +### playfile + +Do the same things as play command, but accept MP3/WAV/OGG files instead of text/links + +![play-file](images/commands/play-file.png) + +### playing + +Return the current playback time of the song + +![playing](images/commands/playing.png) + +### download + +Accept links you songs and return mp3 file in a text channel where the command is called. + +Example: /download https://www.youtube.com/watch?v=60ItHLz5WEA + +### jump + +If your queue has a large count of songs, you can jump to the desired song. +To get the number of songs you want, press the button in audioplayer to get a queue songs list. + +Example: /jump 4 + +### previous + +Returns to previous played/skipped song in queue + +### rewind + +Allow you to change the current playback time. +Accept time in HH:MM:SS or MM:SS or SS format. + +Example: 1:02:32 + +### shuffle + +Shuffle the next songs in the queue + +### skip + +Skip current playing song + +### stop + +Kill the audioplayer + +### audiodebug + +Give the current count of spawned audioplayers + +> [!NOTE] +> This command supported only by text command system +> Also you must be overpowered user. + +## Fun + +### alcotest +Generate random number for 0 to 100 + +## Info + +### help +Return the command list or certain command description + +Command list example: /help +Certain command example: /help play + +### inviteLink +Return the link for inviting the bot. Give the link to the administrator on another server. + +### status +Return bot status data, about: OS, Ram Usage, Cpu Model, Cpu Usage, AICoTest Version, Servers Count. + +### report +Return links to GitHub Issues or GitHub Discussions. diff --git a/wiki/Frequently-Asked-Questions.md b/wiki/Frequently-Asked-Questions.md new file mode 100644 index 0000000..432c4f2 --- /dev/null +++ b/wiki/Frequently-Asked-Questions.md @@ -0,0 +1,4 @@ +## Spotify/Apple Music songs some times are wrong + +Bot can't get audio-stream from these services. So we can fetch only info about songs from these services. +Then bot trying to find a song on YouTube and play it. diff --git a/wiki/Home.md b/wiki/Home.md new file mode 100644 index 0000000..fbf4f21 --- /dev/null +++ b/wiki/Home.md @@ -0,0 +1,10 @@ +# Welcome to AICoTest wiki + +Here you can find information on how to use or setup bot. + +## Getting started with AICoTest + +- You can explore features of the bot in [[Commands]] +- If you want to set up bot on your own host, go to [[Setup]] +- Prior to seeking assistance, please review the [[Frequently Asked Questions]] section for potential resolutions to your issue. +- If you still require assistance or find a bug, create [issue](https://github.com/AlexInCube/AlCoTest/issues/new/choose) or [discussion](https://github.com/AlexInCube/AlCoTest/discussions/new?category=q-a). diff --git a/wiki/Setup.md b/wiki/Setup.md new file mode 100644 index 0000000..351960c --- /dev/null +++ b/wiki/Setup.md @@ -0,0 +1,77 @@ +# ⚙️ Configure .env + +You can use Docker Compose or install all dependencies and source code locally. +But in both cases, you need to configure .env file. + +Also you need retrieve token, client id and enable intents on Discord Developer Portal. + +- Create file .env.production +- Fill all fields in .env.production. If the field is marked as (Optional), you can skip it. +- (Required) To get Discord Token and enable intents, follow the [Discord Developer Portal](https://github.com/AlexInCube/AlCoTest/wiki/API-Configure#discord-developer-portal-required) section. +- (Optional) To get Spotify Secret and ID, follow the [Spotify](https://github.com/AlexInCube/AlCoTest/wiki/API-Configure#spotify-optional) section. +- (Optional) To get Yandex Music token, follow the [Yandex Music](https://github.com/AlexInCube/AlCoTest/wiki/API-Configure#yandex-music-optional) section. +- (Optional) To get SoundCloud token, follow the [Soundcloud](https://github.com/AlexInCube/AlCoTest/wiki/API-Configure#soundcloud-optional) section. + +| Name | Example | Description | Required? | +|------------------------------|-----------------------|---------------------------------------------------------------------------|-----------| +| `BOT_VERBOSE_LOGGING` | false | The bot will give more information to the console, useful for debugging | ❌ | +| `BOT_COMMAND_PREFIX` | // | Used only for text commands | ✔️ | +| `BOT_LANGUAGE` | en | Supported values: en ru | ❌ | +| `MONGO_URI` | mongodb://mongo:27017 | The public key for sending notifications | ✔️ | +| `MONGO_DATABASE_NAME` | aicbot | Database name in MongoDB | ✔️ | +| `BOT_DISCORD_TOKEN` | ODEzNzUwMTY1N... | Token from Discord Developer Portal | ✔️ | +| `BOT_DISCORD_CLIENT_ID` | 813750165783... | Application ID from Discord Developer Portal | ✔️ | +| `BOT_DISCORD_OVERPOWERED_ID` | 29016845994426.... | Discord bot owner user ID, required for having more bot control for owner | ✔️ | +| `BOT_SPOTIFY_CLIENT_SECRET` | | Used when the Spotify module cannot get the credentials automatically | ❌ | +| `BOT_SPOTIFY_CLIENT_ID` | | Used when the Spotify module get the credentials automatically | ❌ | +| `BOT_YANDEXMUSIC_TOKEN` | | Provide to enable Yandex Music module | ❌ | +| `BOT_YANDEXMUSIC_UID` | | Provide to enable Yandex Music module | ❌ | +| `BOT_SOUNDCLOUD_CLIENT_ID` | | Provide to fetch more data with SoundCloud Go+ account | ❌ | +| `BOT_SOUNDCLOUD_TOKEN` | | Provide to fetch more data with SoundCloud Go+ account | ❌ | + +# 🐋 Run in Docker (recommended) + +> [!NOTE] +> Using Docker provides the auto-update feature + +- Install [Docker](https://www.docker.com/get-started/) +- Copy docker-compose.yml, Dockerfile in empty folder +- Follow the [Configure .env](#-configure-env) section and copy .env.production in folder with docker-compose.yml etc. +- (Optional) Follow the [YouTube Cookie](https://github.com/AlexInCube/AlCoTest/wiki/API-Configure#-youtube-cookie-optional) section and copy yt-cookies.json in the folder with docker-compose.yml etc. +- Your file structure must be like this + +``` +AICoTest/ + ├─ .env.production + ├─ docker-compose.yml + ├─ yt-cookies.yml +``` + +- Run command `docker-compose up --detach --force-recreate` from folder with files + +# 🖥️ Run locally (if you are not a developer, this way is no sense) + +- Install [Node.js 22](https://nodejs.org/en/download/prebuilt-installer) or higher +- Install [Python 3.12](https://www.python.org/downloads/) +- Install C++ compiler. Follow this [guide](https://github.com/nodejs/node-gyp#on-windows) +- Install FFMpeg. Follow this [guide](https://www.wikihow.com/Install-FFmpeg-on-Windows) +- Clone repository to your computer +- Follow the [Configure .env](#-configure-env) section and copy .env.production in folder with repository. +- (Optional) Follow the [YouTube Cookie](https://github.com/AlexInCube/AlCoTest/wiki/API-Configure#-youtube-cookie-optional) and copy yt-cookies.json in the folder with repository. +- Install Node.js packages in the folder with repository + +```npm +npm install +``` + +- Compile bot + +``` +npm run build +``` + +- Run the bot + +``` +npm run production +``` diff --git a/wiki/images/api-configure/discord-dev-enable-intents.png b/wiki/images/api-configure/discord-dev-enable-intents.png new file mode 100644 index 0000000..ebdeffe Binary files /dev/null and b/wiki/images/api-configure/discord-dev-enable-intents.png differ diff --git a/wiki/images/api-configure/discord-dev-selected-app.png b/wiki/images/api-configure/discord-dev-selected-app.png new file mode 100644 index 0000000..0d8e3ae Binary files /dev/null and b/wiki/images/api-configure/discord-dev-selected-app.png differ diff --git a/wiki/images/api-configure/spotify-dev-create-app.png b/wiki/images/api-configure/spotify-dev-create-app.png new file mode 100644 index 0000000..92727af Binary files /dev/null and b/wiki/images/api-configure/spotify-dev-create-app.png differ diff --git a/wiki/images/api-configure/spotify-dev-main.png b/wiki/images/api-configure/spotify-dev-main.png new file mode 100644 index 0000000..aad0be3 Binary files /dev/null and b/wiki/images/api-configure/spotify-dev-main.png differ diff --git a/wiki/images/api-configure/yandex-music-extension.png b/wiki/images/api-configure/yandex-music-extension.png new file mode 100644 index 0000000..de67842 Binary files /dev/null and b/wiki/images/api-configure/yandex-music-extension.png differ diff --git a/wiki/images/api-configure/yandex-music-uid.png b/wiki/images/api-configure/yandex-music-uid.png new file mode 100644 index 0000000..4b89766 Binary files /dev/null and b/wiki/images/api-configure/yandex-music-uid.png differ diff --git a/wiki/images/commands/play-audioplayer.png b/wiki/images/commands/play-audioplayer.png new file mode 100644 index 0000000..ad93649 Binary files /dev/null and b/wiki/images/commands/play-audioplayer.png differ diff --git a/wiki/images/commands/play-autocomplete.png b/wiki/images/commands/play-autocomplete.png new file mode 100644 index 0000000..a5d9d84 Binary files /dev/null and b/wiki/images/commands/play-autocomplete.png differ diff --git a/wiki/images/commands/play-file.png b/wiki/images/commands/play-file.png new file mode 100644 index 0000000..aff3396 Binary files /dev/null and b/wiki/images/commands/play-file.png differ diff --git a/wiki/images/commands/playing.png b/wiki/images/commands/playing.png new file mode 100644 index 0000000..ffebf5c Binary files /dev/null and b/wiki/images/commands/playing.png differ