├── .husky ├── .gitignore └── pre-push ├── .gitattributes ├── .gitpod.yml ├── src ├── bot │ ├── events │ │ ├── index.ts │ │ ├── voiceJoin.ts │ │ └── speech.ts │ ├── voiceMessage │ │ ├── index.ts │ │ ├── createVoiceMessage.ts │ │ └── VoiceMessage.ts │ ├── speechOptions.ts │ └── addSpeechEvent.ts ├── index.ts ├── utils │ └── audio.ts ├── events.ts └── speechRecognition │ ├── witai.ts │ └── googleV2.ts ├── tests ├── utils │ ├── index.ts │ ├── audio.ts │ └── TestManager.ts ├── sampleData.ts ├── config.ts ├── witai.test.ts ├── googleSpeech.test.ts ├── voiceMessage.test.ts ├── speechOptions.test.ts └── addSpeechEvent.test.ts ├── .gitignore ├── .gitpod.Dockerfile ├── typedoc.json ├── .env.example ├── examples └── simpleBot │ ├── package.json │ ├── README.md │ ├── index.js │ └── package-lock.json ├── .nycrc ├── .github └── workflows │ ├── test.yml │ ├── npm-publish.yml │ └── codeql-analysis.yml ├── .eslintrc.js ├── docs └── README.md ├── LICENSE ├── README.md ├── package.json ├── tsconfig.json └── tsconfig.build.json /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint 5 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | tasks: 4 | - name: Install dependencies 5 | init: npm i 6 | -------------------------------------------------------------------------------- /src/bot/events/index.ts: -------------------------------------------------------------------------------- 1 | export { default as setupSpeechEvent } from "./speech"; 2 | export { default as setupVoiceJoinEvent } from "./voiceJoin"; 3 | -------------------------------------------------------------------------------- /src/bot/voiceMessage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as createVoiceMessage } from "./createVoiceMessage"; 2 | export { default } from "./VoiceMessage"; 3 | -------------------------------------------------------------------------------- /tests/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { readFileToAudioBuffer, wavUrlToBuffer } from "./audio"; 2 | export { default as TestManager } from "./TestManager"; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | generated_docs 4 | config.json 5 | coverage 6 | .vscode 7 | .nyc_output 8 | recordings 9 | tsconfig.tsbuildinfo 10 | .env 11 | Dockerfile 12 | .dockerignore -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full:latest 2 | 3 | RUN bash -c ". .nvm/nvm.sh && nvm install 16 && nvm use 16 && nvm alias default 16" 4 | 5 | RUN echo "nvm use default &>/dev/null" >> ~/.bashrc.d/51-nvm-fix -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["src/index.ts"], 3 | "out": "generated_docs", 4 | "name": "Discord Speech Recognition", 5 | "excludePrivate": true, 6 | "includes": "./examples", 7 | "readme": "docs/README.md" 8 | } 9 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | BOT_TOKEN="Put here token of bot, that you want to test" 2 | TESTBOT_TOKEN="Here goes token of bot that will test the other bot" 3 | GUILD_ID="Id of the guild that two of the bots are in" 4 | WITAI_KEY="Server api key for wit.ai" -------------------------------------------------------------------------------- /tests/sampleData.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | normal: { 3 | url: "https://cdn.discordapp.com/attachments/838767598778843149/841360475631779861/Hello_my_name_is_John.wav", 4 | text: "my name is", 5 | }, 6 | vulgarism: { 7 | url: "https://cdn.discordapp.com/attachments/838767598778843149/891340727878029342/What_the_fuck_are_you_doing_right_now.wav", 8 | text: "what the fuck are you doing right now", 9 | }, 10 | } as Record; 11 | -------------------------------------------------------------------------------- /examples/simpleBot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simplebot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@discordjs/opus": "^0.8.0", 14 | "@discordjs/voice": "^0.11.0", 15 | "discord-speech-recognition": "latest", 16 | "discord.js": "^14.3.0", 17 | "sodium": "^3.0.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "cache": false, 3 | "check-coverage": true, 4 | "extension": [ 5 | ".ts" 6 | ], 7 | "include": [ 8 | "**/*.ts" 9 | ], 10 | "exclude": [ 11 | "coverage/**", 12 | "node_modules/**", 13 | "**/*.d.ts", 14 | "tests/**", 15 | ".eslintrc.js", 16 | "docs/**", 17 | "examples/**" 18 | ], 19 | "sourceMap": true, 20 | "reporter": [ 21 | "html", 22 | "text", 23 | "text-summary" 24 | ], 25 | "all": true, 26 | "instrument": true 27 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { addSpeechEvent } from "./bot/addSpeechEvent"; 2 | export { 3 | SpeechOptions, 4 | SpeechRecognition as speechRecognition, 5 | CommonSpeechOptions, 6 | } from "./bot/speechOptions"; 7 | export { default as VoiceMessage } from "./bot/voiceMessage"; 8 | export { SpeechEvents } from "./events"; 9 | export { 10 | GoogleSpeechV2Options, 11 | resolveSpeechWithGoogleSpeechV2, 12 | } from "./speechRecognition/googleV2"; 13 | export { 14 | resolveSpeechWithWitai, 15 | WitaiOptions, 16 | } from "./speechRecognition/witai"; 17 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: "16.x" 19 | - run: npm ci 20 | - run: npm run lint 21 | - run: npm run coverage 22 | env: 23 | BOT_TOKEN: ${{secrets.BOT_TOKEN}} 24 | TESTBOT_TOKEN: ${{secrets.TESTBOT_TOKEN}} 25 | GUILD_ID: ${{secrets.GUILD_ID}} 26 | WITAI_KEY: ${{secrets.WITAI_KEY}} 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | commonjs: true, 4 | es2021: true, 5 | }, 6 | parser: "@typescript-eslint/parser", 7 | extends: ["airbnb-typescript/base", "prettier"], 8 | plugins: ["@typescript-eslint", "prettier", "import"], 9 | parserOptions: { 10 | ecmaVersion: 12, 11 | project: "./tsconfig.json", 12 | }, 13 | rules: { 14 | "require-jsdoc": 0, 15 | "valid-jsdoc": 0, 16 | "prettier/prettier": "error", 17 | semi: 2, 18 | "max-len": 0, 19 | "no-prototype-builtins": 0, 20 | "@typescript-eslint/no-unused-expressions": 0, 21 | "class-methods-use-this": 0, 22 | "no-non-null-assertion": 0, 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/utils/audio.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert stereo audio buffer to mono 3 | * @param input Buffer of stereo audio 4 | * @returns 5 | */ 6 | export function convertStereoToMono(input: Buffer): Buffer { 7 | const stereoData = new Int16Array(input); 8 | const monoData = new Int16Array(stereoData.length / 2); 9 | for (let i = 0, j = 0; i < stereoData.length; i += 4) { 10 | monoData[j] = stereoData[i]; 11 | j += 1; 12 | monoData[j] = stereoData[i + 1]; 13 | j += 1; 14 | } 15 | return Buffer.from(monoData); 16 | } 17 | 18 | export function getDurationFromMonoBuffer(buffer: Buffer): number { 19 | const duration = buffer.length / 48000 / 2; 20 | return duration; 21 | } 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Discord Speech Recognition Extension 2 | 3 | This is an extension for [discord.js](https://discord.js.org) library that makes creating discord speech recognition bots as easy as common text bots. 4 | 5 | ## Installation 6 | 7 | **Discord.js v14**: 8 | 9 | ``` 10 | npm i discord-speech-recognition 11 | ``` 12 | 13 | Checkout simpleBot example in examples directory for ready-to-use bot. 14 | 15 | **Discord.js v13**: 16 | 17 | ``` 18 | npm i discord-speech-recognition@2 19 | ``` 20 | 21 | **Discord.js v12**: 22 | 23 | ``` 24 | npm i discord-speech-recognition@1 25 | ``` 26 | 27 | ## Example usage for discord.js v14 28 | 29 | ```javascript 30 | [[include:simpleBot/index.js]] 31 | ``` 32 | -------------------------------------------------------------------------------- /tests/config.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | 3 | dotenv.config(); 4 | 5 | class EnvError extends Error { 6 | constructor(envVariableName: string) { 7 | super(`${envVariableName} wasn't specified in environmental variables.`); 8 | } 9 | } 10 | 11 | const getConfigObject = (variables: Array) => { 12 | const config: Record = {}; 13 | variables.forEach((variable) => { 14 | const value = process.env[variable]; 15 | if (!value) throw new EnvError(variable); 16 | config[variable] = value; 17 | }); 18 | return config as Record; 19 | }; 20 | 21 | export default getConfigObject([ 22 | "BOT_TOKEN", 23 | "TESTBOT_TOKEN", 24 | "GUILD_ID", 25 | "WITAI_KEY", 26 | ]); 27 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | publish-npm: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: "16.x" 19 | registry-url: "https://registry.npmjs.org" 20 | - run: npm ci 21 | - run: npm publish 22 | env: 23 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 24 | -------------------------------------------------------------------------------- /src/events.ts: -------------------------------------------------------------------------------- 1 | export enum SpeechEvents { 2 | /** 3 | * Emitted when someone ends talking in channel 4 | *
5 | * 6 | * Example usage: 7 | * ```javascript 8 | * client.on("speech", (msg) => { 9 | * msg.author.send(msg.content); 10 | * }); 11 | * ``` 12 | * @event 13 | */ 14 | speech = "speech", 15 | /** 16 | * Emitted when error occurs during processing audio stream. Usually when someone tries to talk using web version of discord. See https://github.com/discordjs/opus/issues/49 17 | * @param error 18 | * @event 19 | */ 20 | audioStreamError = "audioStreamError", 21 | /** 22 | * Emitted when bot joins a voice channel and has not started speech recognition in it 23 | * @event 24 | */ 25 | voiceJoin = "voiceJoin", 26 | } 27 | -------------------------------------------------------------------------------- /examples/simpleBot/README.md: -------------------------------------------------------------------------------- 1 | # Simple bot with speech recognition 2 | 3 | It's the simplest example of how to use discord-speech-recognition library. 4 | 5 | ## Installation 6 | 7 | ```sh 8 | git clone https://github.com/Rei-x/discord-speech-recognition 9 | cd discord-speech-recognition/examples/simpleBot 10 | npm install 11 | ``` 12 | 13 | Replace `token` in index.js file with your bot's token. 14 | 15 | ## How to use it? 16 | 17 | Run: 18 | 19 | ```sh 20 | npm start 21 | ``` 22 | 23 | If you did everything correctly you should see "Ready!" in console. 24 | 25 | Join a voice channel and send a message in guild text channel. Bot will join and start speech recognition. Try to say something in english and it will send you a DM with recognized speech! 26 | 27 | If something doesn't work for you, please open an issue on github and I will try my best to help you! 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Bartosz Gotowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/simpleBot/index.js: -------------------------------------------------------------------------------- 1 | const { Client, GatewayIntentBits, Events } = require("discord.js"); 2 | const { joinVoiceChannel } = require("@discordjs/voice"); 3 | const { addSpeechEvent, SpeechEvents } = require("discord-speech-recognition"); 4 | 5 | const client = new Client({ 6 | intents: [ 7 | GatewayIntentBits.GuildVoiceStates, 8 | GatewayIntentBits.GuildMessages, 9 | GatewayIntentBits.Guilds, 10 | GatewayIntentBits.MessageContent, 11 | ], 12 | }); 13 | addSpeechEvent(client); 14 | 15 | client.on(Events.MessageCreate, (msg) => { 16 | const voiceChannel = msg.member?.voice.channel; 17 | if (voiceChannel) { 18 | joinVoiceChannel({ 19 | channelId: voiceChannel.id, 20 | guildId: voiceChannel.guild.id, 21 | adapterCreator: voiceChannel.guild.voiceAdapterCreator, 22 | selfDeaf: false, 23 | }); 24 | } 25 | }); 26 | 27 | client.on(SpeechEvents.speech, (msg) => { 28 | // If bot didn't recognize speech, content will be empty 29 | if (!msg.content) return; 30 | 31 | msg.author.send(msg.content); 32 | }); 33 | 34 | client.on(Events.ClientReady, () => { 35 | console.log("Ready!"); 36 | }); 37 | 38 | client.login("token"); 39 | -------------------------------------------------------------------------------- /src/bot/speechOptions.ts: -------------------------------------------------------------------------------- 1 | import { User } from "discord.js"; 2 | import { resolveSpeechWithGoogleSpeechV2 } from "../speechRecognition/googleV2"; 3 | 4 | /** 5 | * Speech recognition function, you can create your own and specify it in addSpeechEvent 6 | * 7 | * All options that you pass to addSpeechEvent function, will be later passed to this function. 8 | */ 9 | export interface SpeechRecognition { 10 | (audioBuffer: Buffer, options?: Record): Promise; 11 | } 12 | 13 | export interface CommonSpeechOptions { 14 | /** 15 | * Group identifier 16 | * https://discordjs.github.io/voice/interfaces/joinvoicechanneloptions.html#group 17 | */ 18 | group?: string; 19 | 20 | /** 21 | * Custom handler to decide whether to recognize speech 22 | * @param user The user who spoke 23 | * @returns 24 | */ 25 | shouldProcessSpeech?: (user: User) => boolean; 26 | 27 | /** 28 | * Defaults to true 29 | */ 30 | ignoreBots?: boolean; 31 | 32 | /** 33 | * Minimal length of voice message that will be processed 34 | */ 35 | minimalVoiceMessageDuration?: number; 36 | } 37 | 38 | /** 39 | * Options that will be passed to SpeechRecognition function 40 | * 41 | * Usage: `SpeechOptions` 42 | */ 43 | export type SpeechOptions< 44 | T extends SpeechRecognition = typeof resolveSpeechWithGoogleSpeechV2 45 | > = CommonSpeechOptions & { 46 | speechRecognition?: T; 47 | } & Parameters[1]; 48 | -------------------------------------------------------------------------------- /tests/witai.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-invalid-this */ 2 | import chai from "chai"; 3 | import chaiAsPRosmied from "chai-as-promised"; 4 | import { resolveSpeechWithWitai } from "../src/index"; 5 | import config from "./config"; 6 | import sampleData from "./sampleData"; 7 | import { wavUrlToBuffer } from "./utils"; 8 | 9 | chai.use(chaiAsPRosmied); 10 | const { expect } = chai; 11 | 12 | describe("wit.ai test", function witAiTest() { 13 | this.timeout(16000); 14 | 15 | it("Speech recognition", async () => { 16 | const audioBuffer = await wavUrlToBuffer(sampleData.normal.url); 17 | const response = await resolveSpeechWithWitai(audioBuffer, { 18 | key: config.WITAI_KEY, 19 | }); 20 | expect(response.toLowerCase()).to.contain(sampleData.normal.text); 21 | }); 22 | 23 | it("Empty request body throws error", () => { 24 | const emptyBuffer = Buffer.from(""); 25 | return expect( 26 | resolveSpeechWithWitai(emptyBuffer, { 27 | key: config.WITAI_KEY, 28 | }) 29 | ).to.be.rejectedWith("Api error, code: 400"); 30 | }); 31 | 32 | it("Empty token throws error", async () => { 33 | const audioBuffer = await wavUrlToBuffer(sampleData.normal.url); 34 | return expect( 35 | resolveSpeechWithWitai(audioBuffer, { 36 | key: "", 37 | }) 38 | ).to.be.rejectedWith("wit.ai API key wasn't specified."); 39 | }); 40 | 41 | it("Bad token throws error", async () => { 42 | const audioBuffer = await wavUrlToBuffer(sampleData.normal.url); 43 | return expect( 44 | resolveSpeechWithWitai(audioBuffer, { 45 | key: "d", 46 | }) 47 | ).to.be.rejectedWith("Api error, code: 400"); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /tests/googleSpeech.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-invalid-this */ 2 | import chai, { expect } from "chai"; 3 | import chaiAsPRomised from "chai-as-promised"; 4 | import { resolveSpeechWithGoogleSpeechV2 } from "../src/index"; 5 | import sampleData from "./sampleData"; 6 | import { wavUrlToBuffer } from "./utils"; 7 | 8 | chai.use(chaiAsPRomised); 9 | 10 | describe("Google Speech V2 test", () => { 11 | it("Speech recognition", async function speechRecognition() { 12 | this.timeout(16000); 13 | 14 | const audioBuffer = await wavUrlToBuffer(sampleData.normal.url); 15 | const response = await resolveSpeechWithGoogleSpeechV2(audioBuffer); 16 | expect(response.toLowerCase()).to.contain(sampleData.normal.text); 17 | }); 18 | 19 | it("Censor bad words", async function () { 20 | this.timeout(16000); 21 | 22 | const audioBuffer = await wavUrlToBuffer(sampleData.vulgarism.url); 23 | const response = await resolveSpeechWithGoogleSpeechV2(audioBuffer, { 24 | profanityFilter: true, 25 | }); 26 | const censoredText = sampleData.vulgarism.text.replace("fuck", "f***"); 27 | expect(response.toLowerCase()).to.contain(censoredText); 28 | }); 29 | 30 | it("Disabling profanity filter ", async function () { 31 | this.timeout(16000); 32 | 33 | const audioBuffer = await wavUrlToBuffer(sampleData.vulgarism.url); 34 | const response = await resolveSpeechWithGoogleSpeechV2(audioBuffer, { 35 | profanityFilter: false, 36 | }); 37 | expect(response.toLowerCase()).to.contain(sampleData.vulgarism.text); 38 | }); 39 | 40 | it("Bad request data throws error", () => { 41 | const badAudioBuffer = Buffer.from("test"); 42 | return expect(resolveSpeechWithGoogleSpeechV2(badAudioBuffer)).to.be 43 | .rejected; 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/bot/voiceMessage/createVoiceMessage.ts: -------------------------------------------------------------------------------- 1 | import { VoiceConnection } from "@discordjs/voice"; 2 | import { Client, User } from "discord.js"; 3 | import { 4 | convertStereoToMono, 5 | getDurationFromMonoBuffer, 6 | } from "../../utils/audio"; 7 | import { SpeechOptions, SpeechRecognition } from "../speechOptions"; 8 | import VoiceMessage from "../voiceMessage"; 9 | 10 | export default async ({ 11 | client, 12 | bufferData, 13 | user, 14 | connection, 15 | speechOptions, 16 | }: { 17 | client: Client; 18 | bufferData: Uint8Array[]; 19 | user: User; 20 | connection: VoiceConnection; 21 | speechOptions: SpeechOptions; 22 | }): Promise => { 23 | if (!connection.joinConfig.channelId) return undefined; 24 | 25 | const stereoBuffer = Buffer.concat(bufferData); 26 | const monoBuffer = convertStereoToMono(stereoBuffer); 27 | const duration = getDurationFromMonoBuffer(monoBuffer); 28 | 29 | const minimalDuration = speechOptions.minimalVoiceMessageDuration ?? 1; 30 | 31 | if (duration < minimalDuration || duration > 19) return undefined; 32 | 33 | let content: string | undefined; 34 | let error: Error | undefined; 35 | try { 36 | content = await speechOptions.speechRecognition?.( 37 | monoBuffer, 38 | speechOptions 39 | ); 40 | } catch (e) { 41 | error = e as Error; 42 | } 43 | 44 | const channel = client.channels.cache.get(connection.joinConfig.channelId); 45 | if (!channel || !channel.isVoiceBased()) return undefined; 46 | 47 | return new VoiceMessage({ 48 | client, 49 | data: { 50 | author: user, 51 | duration, 52 | audioBuffer: stereoBuffer, 53 | content, 54 | error, 55 | connection, 56 | }, 57 | channel, 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /src/bot/addSpeechEvent.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "discord.js"; 2 | import { resolveSpeechWithGoogleSpeechV2 } from "../speechRecognition/googleV2"; 3 | import { setupSpeechEvent, setupVoiceJoinEvent } from "./events"; 4 | import { SpeechOptions, SpeechRecognition } from "./speechOptions"; 5 | 6 | /** 7 | * Main function, use this to add new events to present [discord.Client](https://discord.js.org/#/docs/main/stable/class/Client) 8 | * 9 | * Defaults uses `en-US` language and google speech v2 api with generic key, that should be used for personal or testing purposes only, as it may be revoked by Google at any time.\ 10 | * You can obtain your own API key here .\ 11 | * See [python speech recognition package](https://github.com/Uberi/speech_recognition/blob/c89856088ad81d81d38be314e3db50905481c5fe/speech_recognition/__init__.py#L850) for more details. 12 | *
13 | * 14 | * Example usage: 15 | * ```javascript 16 | * const client = new Client({ 17 | * intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_VOICE_STATES], 18 | * }); 19 | * // You can modify speechOptions object to change how things work 20 | * const speechOptions = addSpeechEvent(client, { lang: "pl-PL" }); 21 | * ``` 22 | */ 23 | export const addSpeechEvent = < 24 | T extends SpeechRecognition = typeof resolveSpeechWithGoogleSpeechV2 25 | >( 26 | client: Client, 27 | options?: SpeechOptions 28 | ) => { 29 | const defaultOptions: SpeechOptions = 30 | { 31 | lang: "en-US", 32 | speechRecognition: resolveSpeechWithGoogleSpeechV2, 33 | ignoreBots: true, 34 | minimalVoiceMessageDuration: 1, 35 | }; 36 | const speechOptions = { ...defaultOptions, ...options }; 37 | 38 | setupVoiceJoinEvent(client, speechOptions); 39 | setupSpeechEvent(client, speechOptions); 40 | 41 | return speechOptions; 42 | }; 43 | -------------------------------------------------------------------------------- /src/speechRecognition/witai.ts: -------------------------------------------------------------------------------- 1 | import fetch from "node-fetch"; 2 | import { MessageResponse } from "node-wit"; 3 | 4 | export interface WitaiOptions { 5 | key?: string; 6 | } 7 | 8 | /** 9 | * There is an issue with wit.ai response from /speech endpoint, which returns multiple root objects. You can check the docs here: https://wit.ai/docs/http/20210928/#post__speech_link 10 | * This function converts response text to valid json by wrapping it in array and fixing commas. 11 | * @param text 12 | * @returns 13 | */ 14 | const formatWitaiResponse = (text: string): Array => { 15 | const fixedCommas = text.replace(/\n}\r\n/g, "},"); 16 | const wrappedInArray = `[${fixedCommas}]`; 17 | return JSON.parse(wrappedInArray); 18 | }; 19 | 20 | async function extractSpeechText( 21 | key: string, 22 | audioBuffer: Buffer, 23 | contenttype: string 24 | ): Promise { 25 | const response = await fetch("https://api.wit.ai/speech", { 26 | method: "post", 27 | body: audioBuffer, 28 | headers: { 29 | Authorization: `Bearer ${key}`, 30 | "Content-type": contenttype, 31 | }, 32 | }); 33 | if (response.status !== 200) 34 | throw new Error(`Api error, code: ${response.status}`); 35 | 36 | const data = formatWitaiResponse(await response.text()); 37 | 38 | const latestMessage = data.at(-1); 39 | if (!latestMessage) throw new Error(`Invalid API response`); 40 | 41 | return latestMessage; 42 | } 43 | 44 | export async function resolveSpeechWithWitai( 45 | audioBuffer: Buffer, 46 | options?: WitaiOptions 47 | ): Promise { 48 | const key = options?.key; 49 | if (!key) throw new Error("wit.ai API key wasn't specified."); 50 | 51 | const contentType = 52 | "audio/raw;encoding=signed-integer;bits=16;rate=48k;endian=little"; 53 | const output = await extractSpeechText(key, audioBuffer, contentType); 54 | 55 | return output.text; 56 | } 57 | -------------------------------------------------------------------------------- /tests/utils/audio.ts: -------------------------------------------------------------------------------- 1 | import { Writable } from "stream"; 2 | import fs from "fs"; 3 | import wav from "wav"; 4 | import axios from "axios"; 5 | 6 | const createWritable = (buffs: Uint8Array[]): Writable => 7 | new Writable({ 8 | write(chunk, _encoding, callback) { 9 | buffs.push(chunk); 10 | callback(); 11 | }, 12 | }); 13 | 14 | const writeAudioDataToWavStream = ( 15 | stream: fs.ReadStream, 16 | streamReader: Writable 17 | ): void => { 18 | const reader = new wav.Reader(); 19 | reader.on("format", () => { 20 | reader.pipe(streamReader); 21 | }); 22 | stream.pipe(reader); 23 | }; 24 | 25 | export const readFileToAudioBuffer = async ( 26 | filename: fs.PathLike 27 | ): Promise => { 28 | const file = fs.createReadStream(filename); 29 | 30 | const buffs: Uint8Array[] = []; 31 | const pcmDataStream = createWritable(buffs); 32 | 33 | writeAudioDataToWavStream(file, pcmDataStream); 34 | 35 | return new Promise((resolve) => { 36 | pcmDataStream.on("finish", () => { 37 | const audioBuffer = Buffer.concat(buffs); 38 | resolve(audioBuffer); 39 | }); 40 | }); 41 | }; 42 | 43 | export async function wavUrlToBuffer(url: string): Promise { 44 | const response = await axios({ 45 | url, 46 | method: "GET", 47 | responseType: "stream", 48 | }); 49 | const buffs: Uint8Array[] = []; 50 | const pcmDataStream = new Writable({ 51 | write(chunk, _encoding, callback) { 52 | buffs.push(chunk); 53 | callback(); 54 | }, 55 | emitClose: true, 56 | }); 57 | 58 | const reader = new wav.Reader(); 59 | reader.on("format", () => { 60 | reader.pipe(pcmDataStream); 61 | }); 62 | response.data.pipe(reader); 63 | 64 | return new Promise((resolve) => { 65 | pcmDataStream.on("finish", () => { 66 | const audioBuffer = Buffer.concat(buffs); 67 | resolve(audioBuffer); 68 | }); 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '36 15 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'typescript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v2 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord Speech Recognition Extension 2 | 3 | This is an extension for [discord.js](https://discord.js.org) library that makes creating discord speech recognition bots as easy as common text bots. 4 | 5 | ## Installation 6 | 7 | **Discord.js v14**: 8 | 9 | ``` 10 | npm i discord-speech-recognition 11 | ``` 12 | 13 | Checkout simpleBot example in examples directory for ready-to-use bot. 14 | 15 | **Discord.js v13**: 16 | 17 | ``` 18 | npm i discord-speech-recognition@2 19 | ``` 20 | 21 | **Discord.js v12**: 22 | 23 | ``` 24 | npm i discord-speech-recognition@1 25 | ``` 26 | 27 | ## Docs 28 | 29 | 30 | 31 | ## Example usage for discord.js v14 32 | 33 | ```javascript 34 | const { Client, GatewayIntentBits, Events } = require("discord.js"); 35 | const { joinVoiceChannel } = require("@discordjs/voice"); 36 | const { addSpeechEvent, SpeechEvents } = require("discord-speech-recognition"); 37 | 38 | const client = new Client({ 39 | intents: [ 40 | GatewayIntentBits.GuildVoiceStates, 41 | GatewayIntentBits.GuildMessages, 42 | GatewayIntentBits.Guilds, 43 | GatewayIntentBits.MessageContent, 44 | ], 45 | }); 46 | addSpeechEvent(client); 47 | 48 | client.on(Events.MessageCreate, (msg) => { 49 | const voiceChannel = msg.member?.voice.channel; 50 | if (voiceChannel) { 51 | joinVoiceChannel({ 52 | channelId: voiceChannel.id, 53 | guildId: voiceChannel.guild.id, 54 | adapterCreator: voiceChannel.guild.voiceAdapterCreator, 55 | selfDeaf: false, 56 | }); 57 | } 58 | }); 59 | 60 | client.on(SpeechEvents.speech, (msg) => { 61 | // If bot didn't recognize speech, content will be empty 62 | if (!msg.content) return; 63 | 64 | msg.author.send(msg.content); 65 | }); 66 | 67 | client.on(Events.ClientReady, () => { 68 | console.log("Ready!"); 69 | }); 70 | 71 | client.login("token"); 72 | ``` 73 | 74 | You need to enable message content for this example, so it can react to messages in chat. 75 | 76 | ![](https://i.imgur.com/06doHXE.png) 77 | -------------------------------------------------------------------------------- /tests/voiceMessage.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-invalid-this */ 2 | import { expect } from "chai"; 3 | import fs from "fs"; 4 | import { VoiceMessage } from "../src"; 5 | import { getDurationFromMonoBuffer } from "../src/utils/audio"; 6 | import sampleData from "./sampleData"; 7 | import wav from "wav"; 8 | import { readFileToAudioBuffer, wavUrlToBuffer } from "./utils"; 9 | 10 | describe("Voice message", () => { 11 | const mockData = undefined as never; 12 | 13 | const filename = "./test.wav"; 14 | 15 | let audioBuffer: Buffer; 16 | let voiceMessage: VoiceMessage; 17 | 18 | before(async function before() { 19 | this.timeout(6000); 20 | 21 | audioBuffer = await wavUrlToBuffer(sampleData.normal.url); 22 | voiceMessage = new VoiceMessage({ 23 | client: mockData, 24 | data: { 25 | audioBuffer, 26 | author: mockData, 27 | duration: getDurationFromMonoBuffer(audioBuffer), 28 | connection: mockData, 29 | }, 30 | channel: mockData, 31 | }); 32 | }); 33 | 34 | it("Save to .wav file", async function saveToWav() { 35 | this.timeout(4000); 36 | 37 | voiceMessage.saveToFile(filename); 38 | expect(fs.existsSync(filename)); 39 | const audioBufferFromFile = await readFileToAudioBuffer(filename); 40 | expect(audioBuffer.toString()).to.be.equal(audioBufferFromFile.toString()); 41 | fs.unlinkSync(filename); 42 | }); 43 | 44 | it("Duration", () => { 45 | expect(voiceMessage.duration.toPrecision(3)).to.be.equal("2.09"); 46 | }); 47 | 48 | it("Get base64 audio", async () => { 49 | const base64Audio = voiceMessage.getWavEncodedToBase64Audio(); 50 | 51 | const decodedWavAudio = Buffer.from(base64Audio, "base64"); 52 | 53 | const reader = new wav.Reader(); 54 | 55 | const checkFormat = new Promise((resolve) => { 56 | reader.on("format", (format) => { 57 | expect(format.sampleRate).to.be.equal(48000); 58 | expect(format.channels).to.be.equal(1); 59 | resolve(); 60 | }); 61 | }); 62 | reader.write(decodedWavAudio); 63 | 64 | await checkFormat; 65 | 66 | const pcmAudio = reader.read(reader.readableLength); 67 | 68 | const duration = getDurationFromMonoBuffer(pcmAudio); 69 | expect(voiceMessage.duration).to.be.equal(duration); 70 | 71 | expect(pcmAudio.toString()).to.be.equal(audioBuffer.toString()); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/speechRecognition/googleV2.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig } from "axios"; 2 | 3 | /** 4 | * You can obtain API key here [http://www.chromium.org/developers/how-tos/api-keys](http://www.chromium.org/developers/how-tos/api-keys) 5 | */ 6 | export interface GoogleSpeechV2Options { 7 | key?: string; 8 | lang?: string; 9 | profanityFilter?: boolean; 10 | } 11 | 12 | /** 13 | * If API key is not specified uses generic key that works out of the box. 14 | * If language is not specified uses ``"en-US"`` 15 | * See [python speech recognition package](https://github.com/Uberi/speech_recognition/blob/c89856088ad81d81d38be314e3db50905481c5fe/speech_recognition/__init__.py#L850) for more details. 16 | * @param options 17 | * @returns Request config for {@link resolveSpeechWithGoogleSpeechV2} 18 | */ 19 | function getGoogleRequestOptions( 20 | options?: GoogleSpeechV2Options 21 | ): AxiosRequestConfig { 22 | let key = "AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw"; 23 | let lang = "en-US"; 24 | let profanityFilter = "1"; // Google's default 25 | 26 | if (options) { 27 | if (options.key) key = options.key; 28 | if (options.lang) lang = options.lang; 29 | if (options.profanityFilter !== undefined) 30 | profanityFilter = options.profanityFilter ? "1" : "0"; 31 | } 32 | 33 | const googleRequestOptions: AxiosRequestConfig = { 34 | url: `https://www.google.com/speech-api/v2/recognize?output=json&lang=${lang}&key=${key}&pFilter=${profanityFilter}`, 35 | headers: { 36 | "Content-Type": "audio/l16; rate=48000;", 37 | }, 38 | method: "POST", 39 | transformResponse: [ 40 | (data) => { 41 | const fixedData = data.replace('{"result":[]}', ""); 42 | try { 43 | return JSON.parse(fixedData); 44 | } catch (e) { 45 | return { error: e }; 46 | } 47 | }, 48 | ], 49 | }; 50 | return googleRequestOptions; 51 | } 52 | 53 | /** 54 | * Performs speech recognition using the Google Speech Recognition API V2 55 | * @param audioBuffer PCM mono audio with 48kHz 56 | * @param options 57 | * @returns Recognized text from speech 58 | */ 59 | export async function resolveSpeechWithGoogleSpeechV2( 60 | audioBuffer: Buffer, 61 | options: GoogleSpeechV2Options = { lang: "en-US" } 62 | ): Promise { 63 | const requestOptions = getGoogleRequestOptions(options); 64 | requestOptions.data = audioBuffer; 65 | const response = await axios(requestOptions); 66 | if (response.data.error) 67 | throw new Error(`Google speech api error: ${response.data}`); 68 | return response.data.result[0].alternative[0].transcript; 69 | } 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-speech-recognition", 3 | "version": "3.4.1", 4 | "description": "Extension for making discord speech recognition bots.", 5 | "main": "./dist/index.js", 6 | "types": "./dist/index.d.ts", 7 | "scripts": { 8 | "dev": "nodemon dist/index.js", 9 | "watch": "tsc -w", 10 | "doc": "typedoc", 11 | "build": "tsc --project tsconfig.build.json", 12 | "test": "ts-mocha --exit --require source-map-support/register tests/**/*.test.ts ", 13 | "coverage": "nyc npm run test", 14 | "lint": "eslint src/ && tsc --noEmit", 15 | "prepare": "husky install", 16 | "prepublishOnly": "npm run build" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/Rei-x/discord-speech-recognition.git" 21 | }, 22 | "keywords": [ 23 | "discord.js", 24 | "discord", 25 | "speechrecognition" 26 | ], 27 | "author": "Rei-x", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/Rei-x/discord-speech-recognition/issues" 31 | }, 32 | "homepage": "https://discordsr.netlify.app/", 33 | "engines": { 34 | "node": ">=16.6.0" 35 | }, 36 | "peerDependencies": { 37 | "@discordjs/opus": "^0.x", 38 | "@discordjs/voice": "^0.x", 39 | "discord.js": "14.x" 40 | }, 41 | "dependencies": { 42 | "axios": "^0.21.1", 43 | "node-fetch": "^2.6.5", 44 | "wav": "^1.0.2" 45 | }, 46 | "devDependencies": { 47 | "@istanbuljs/nyc-config-typescript": "^1.0.1", 48 | "@types/chai": "^4.2.18", 49 | "@types/chai-as-promised": "^7.1.4", 50 | "@types/chai-spies": "^1.0.3", 51 | "@types/expect": "^24.3.0", 52 | "@types/mocha": "^9.1.1", 53 | "@types/node": "^16.10.2", 54 | "@types/node-fetch": "^2.5.12", 55 | "@types/node-wit": "^6.0.1", 56 | "@types/wav": "^1.0.0", 57 | "@typescript-eslint/eslint-plugin": "^5.33.0", 58 | "@typescript-eslint/parser": "^5.33.0", 59 | "chai": "^4.3.4", 60 | "chai-as-promised": "^7.1.1", 61 | "chai-spies": "^1.0.0", 62 | "dotenv": "^10.0.0", 63 | "eslint": "^8.21.0", 64 | "eslint-config-airbnb-base": "^15.0.0", 65 | "eslint-config-airbnb-typescript": "^17.0.0", 66 | "eslint-config-prettier": "^8.3.0", 67 | "eslint-plugin-import": "^2.26.0", 68 | "eslint-plugin-prettier": "^4.2.1", 69 | "ffmpeg-static": "^4.4.0", 70 | "husky": "^7.0.2", 71 | "nyc": "^15.1.0", 72 | "prettier": "^2.4.1", 73 | "source-map-support": "^0.5.20", 74 | "ts-mocha": "^9.0.2", 75 | "tweetnacl": "^1.0.3", 76 | "typedoc": "^0.23.10", 77 | "typescript": "^4.7.4" 78 | }, 79 | "files": [ 80 | "dist/" 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /src/bot/events/voiceJoin.ts: -------------------------------------------------------------------------------- 1 | import { getVoiceConnection, VoiceConnection } from "@discordjs/voice"; 2 | import { Client } from "discord.js"; 3 | import { SpeechOptions, SpeechRecognition } from "../speechOptions"; 4 | import { SpeechEvents } from "../../events"; 5 | 6 | declare module "discord.js" { 7 | interface ClientEvents { 8 | /** 9 | * This event is emitted when speech recognition is attached to voice connection 10 | */ 11 | voiceJoin: [connection: VoiceConnection | undefined]; 12 | } 13 | } 14 | 15 | /** 16 | * It's a bit hacky solution to check if speech handler has been already attached to connection receiver 17 | * It does it by checking if in the listeners of speaking map exists function with the same name as function 18 | * which handles speech event 19 | * @param connection 20 | * @returns 21 | */ 22 | const isSpeechHandlerAttachedToConnection = ( 23 | connection: VoiceConnection 24 | ): boolean => { 25 | return Boolean( 26 | connection.receiver.speaking 27 | .listeners("start") 28 | .find((func) => func.name === "handleSpeechEventOnConnectionReceiver") 29 | ); 30 | }; 31 | 32 | /** 33 | * https://github.com/discordjs/discord.js/issues/9185 34 | * 35 | * Workaround from this comment: https://github.com/discordjs/discord.js/issues/9185#issuecomment-1459083216 36 | * @param connection 37 | */ 38 | const fixStoppingAfterOneMinute = (connection: VoiceConnection) => { 39 | const networkStateChangeHandler = ( 40 | oldNetworkState: any, 41 | newNetworkState: any 42 | ) => { 43 | const newUdp = Reflect.get(newNetworkState, "udp"); 44 | clearInterval(newUdp?.keepAliveInterval); 45 | }; 46 | 47 | connection.on("stateChange", (oldState, newState) => { 48 | Reflect.get(oldState, "networking")?.off( 49 | "stateChange", 50 | networkStateChangeHandler 51 | ); 52 | Reflect.get(newState, "networking")?.on( 53 | "stateChange", 54 | networkStateChangeHandler 55 | ); 56 | }); 57 | }; 58 | 59 | export default ( 60 | client: Client, 61 | speechOptions: SpeechOptions 62 | ): void => { 63 | client.on("voiceStateUpdate", (_old, newVoiceState) => { 64 | if (!newVoiceState.channel) return; 65 | 66 | const connection = getVoiceConnection( 67 | newVoiceState.channel.guild.id, 68 | speechOptions.group 69 | ); 70 | if (connection && !isSpeechHandlerAttachedToConnection(connection)) { 71 | fixStoppingAfterOneMinute(connection); 72 | 73 | client.emit( 74 | SpeechEvents.voiceJoin, 75 | getVoiceConnection(newVoiceState.channel.guild.id, speechOptions.group) 76 | ); 77 | } 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /src/bot/voiceMessage/VoiceMessage.ts: -------------------------------------------------------------------------------- 1 | import { VoiceConnection } from "@discordjs/voice"; 2 | import { 3 | Client, 4 | Guild, 5 | GuildMember, 6 | StageChannel, 7 | User, 8 | VoiceChannel, 9 | } from "discord.js"; 10 | import wav from "wav"; 11 | 12 | export interface VoiceMessageData { 13 | duration: number; 14 | audioBuffer: Buffer; 15 | content?: string; 16 | error?: Error; 17 | connection: VoiceConnection; 18 | author: User; 19 | } 20 | 21 | export default class VoiceMessage { 22 | channel: VoiceChannel | StageChannel; 23 | 24 | /** 25 | * Speech to text translation 26 | */ 27 | content?: string; 28 | 29 | author: User; 30 | 31 | /** 32 | * Duration in seconds 33 | */ 34 | duration: number; 35 | 36 | private static audioBufferFormat = { 37 | sampleRate: 48000, 38 | channels: 1, 39 | }; 40 | 41 | /** 42 | * PCM mono 48k audio data 43 | */ 44 | audioBuffer: Buffer; 45 | 46 | client: Client; 47 | 48 | /** 49 | * If there was any error during handling speech event, this will be set 50 | */ 51 | error?: Error; 52 | 53 | connection: VoiceConnection; 54 | 55 | /** 56 | * Voice message, it is emited `speech` event 57 | * @param client 58 | * @param data 59 | * @param channel 60 | * @private 61 | */ 62 | constructor({ 63 | client, 64 | data, 65 | channel, 66 | }: { 67 | client: Client; 68 | data: VoiceMessageData; 69 | channel: VoiceChannel | StageChannel; 70 | }) { 71 | this.client = client; 72 | this.channel = channel; 73 | this.author = data.author; 74 | this.audioBuffer = data.audioBuffer; 75 | this.connection = data.connection; 76 | this.duration = data.duration; 77 | this.content = data?.content; 78 | this.error = data?.error; 79 | } 80 | 81 | /** 82 | * Saves audio to .wav file 83 | * @param filename File directory, for example: `./test.wav` 84 | */ 85 | saveToFile(filename: string): void { 86 | const outputFile = new wav.FileWriter( 87 | filename, 88 | VoiceMessage.audioBufferFormat 89 | ); 90 | outputFile.write(this.audioBuffer); 91 | outputFile.end(); 92 | } 93 | 94 | /** 95 | * Useful if you want to send audio to some API or play it in browser 96 | * @returns Wav audio encoded to base64 97 | */ 98 | getWavEncodedToBase64Audio() { 99 | const writer = new wav.Writer(VoiceMessage.audioBufferFormat); 100 | 101 | writer.write(this.audioBuffer); 102 | writer.end(); 103 | 104 | return writer.read(writer.readableLength).toString("base64"); 105 | } 106 | 107 | get member(): GuildMember | undefined { 108 | return this.guild.members.cache.get(this.author.id); 109 | } 110 | 111 | get guild(): Guild { 112 | return this.channel.guild; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tests/speechOptions.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createAudioPlayer, 3 | createAudioResource, 4 | StreamType, 5 | } from "@discordjs/voice"; 6 | import chai, { expect } from "chai"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | import chaiSpies from "chai-spies"; 9 | import { User } from "discord.js"; 10 | import { once } from "events"; 11 | import { addSpeechEvent, SpeechEvents } from "../src"; 12 | import config from "./config"; 13 | import sampleData from "./sampleData"; 14 | import { TestManager } from "./utils"; 15 | 16 | chai.use(chaiAsPromised); 17 | chai.use(chaiSpies); 18 | 19 | describe("Speech options", () => { 20 | let tm: TestManager; 21 | 22 | beforeEach(async function before() { 23 | this.timeout(6000); 24 | tm = new TestManager(config.BOT_TOKEN, config.TESTBOT_TOKEN); 25 | await tm.loginClients(); 26 | await tm.setTestVoiceChannel(config.GUILD_ID); 27 | }); 28 | 29 | it("Ignore bots", async function ignoreBots() { 30 | this.timeout(7000); 31 | 32 | addSpeechEvent(tm.client, { 33 | group: "client", 34 | ignoreBots: true, 35 | }); 36 | 37 | const player = createAudioPlayer(); 38 | const resource = createAudioResource(sampleData.normal.url, { 39 | inputType: StreamType.Arbitrary, 40 | }); 41 | const connection = await tm.connectToVoiceChannel("testClient"); 42 | connection.subscribe(player); 43 | 44 | await tm.connectToVoiceChannel("client"); 45 | 46 | player.play(resource); 47 | 48 | return new Promise((resolve, reject) => { 49 | once(tm.client, SpeechEvents.speech).then(() => 50 | reject("Bot recognized speech with ignoreBots set to true") 51 | ); 52 | setTimeout(() => resolve(), 4000); 53 | }); 54 | }); 55 | 56 | it("shouldProcessSpeech option", async function processSpeech() { 57 | this.timeout(7000); 58 | 59 | const shouldProcessSpeech = (user: User) => { 60 | expect(user.username).to.equal(tm.testClient.user?.username); 61 | return true; 62 | }; 63 | const spiedShouldProcessSpeech = chai.spy(shouldProcessSpeech); 64 | 65 | const speechOptions = addSpeechEvent(tm.client, { 66 | group: "client", 67 | ignoreBots: false, 68 | }); 69 | 70 | speechOptions.shouldProcessSpeech = spiedShouldProcessSpeech; 71 | 72 | const player = createAudioPlayer(); 73 | const resource = createAudioResource(sampleData.normal.url, { 74 | inputType: StreamType.Arbitrary, 75 | }); 76 | const connection = await tm.connectToVoiceChannel("testClient"); 77 | connection.subscribe(player); 78 | 79 | await tm.connectToVoiceChannel("client"); 80 | 81 | player.play(resource); 82 | 83 | return new Promise((resolve, reject) => { 84 | tm.client.once(SpeechEvents.speech, () => { 85 | try { 86 | chai.expect(spiedShouldProcessSpeech).to.have.been.called(); 87 | } catch (e) { 88 | reject(e); 89 | } 90 | resolve(); 91 | }); 92 | }); 93 | }); 94 | 95 | afterEach(() => { 96 | tm.disconnectFromVoiceChannel(config.GUILD_ID, "client"); 97 | tm.disconnectFromVoiceChannel(config.GUILD_ID, "testClient"); 98 | tm.resetClients(); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /src/bot/events/speech.ts: -------------------------------------------------------------------------------- 1 | import { OpusEncoder } from "@discordjs/opus"; 2 | import { 3 | EndBehaviorType, 4 | entersState, 5 | VoiceConnection, 6 | VoiceConnectionStatus, 7 | } from "@discordjs/voice"; 8 | import { Client } from "discord.js"; 9 | import { Transform } from "stream"; 10 | import { SpeechOptions, SpeechRecognition } from "../speechOptions"; 11 | import VoiceMessage, { createVoiceMessage } from "../voiceMessage"; 12 | import { SpeechEvents } from "../../events"; 13 | 14 | declare module "discord.js" { 15 | interface ClientEvents { 16 | [SpeechEvents.speech]: [voiceMessage: VoiceMessage]; 17 | } 18 | } 19 | 20 | class OpusDecodingStream extends Transform { 21 | encoder: OpusEncoder; 22 | 23 | constructor() { 24 | super(); 25 | this.encoder = new OpusEncoder(48000, 2); 26 | } 27 | 28 | _transform(data: any, encoding: any, callback: () => void) { 29 | this.push(this.encoder.decode(data)); 30 | callback(); 31 | } 32 | } 33 | 34 | /** 35 | * Starts listening on connection and emits `speech` event when someone stops speaking 36 | * @param connection Connection to listen 37 | */ 38 | const handleSpeakingEvent = ({ 39 | client, 40 | connection, 41 | speechOptions, 42 | }: { 43 | client: Client; 44 | connection: VoiceConnection; 45 | speechOptions: SpeechOptions; 46 | }) => { 47 | connection.receiver.speaking.on( 48 | "start", 49 | function handleSpeechEventOnConnectionReceiver(userId) { 50 | const user = client.users.cache.get(userId); 51 | 52 | // Shouldn't proceed if user is undefined, some checks will fail even if they shouldn't 53 | if (!user) return; 54 | 55 | const { shouldProcessSpeech } = speechOptions; 56 | if (shouldProcessSpeech && !shouldProcessSpeech(user)) { 57 | return; 58 | } 59 | 60 | if (speechOptions.ignoreBots && user?.bot) { 61 | return; 62 | } 63 | 64 | const { receiver } = connection; 65 | const opusStream = receiver.subscribe(userId, { 66 | end: { 67 | behavior: EndBehaviorType.AfterSilence, 68 | duration: 300, 69 | }, 70 | }); 71 | 72 | const bufferData: Uint8Array[] = []; 73 | opusStream 74 | .pipe(new OpusDecodingStream()) 75 | .on("data", (data: Uint8Array) => { 76 | bufferData.push(data); 77 | }); 78 | 79 | opusStream.on("end", async () => { 80 | const voiceMessage = await createVoiceMessage({ 81 | client, 82 | bufferData, 83 | user, 84 | connection, 85 | speechOptions, 86 | }); 87 | 88 | if (voiceMessage) client.emit(SpeechEvents.speech, voiceMessage); 89 | }); 90 | } 91 | ); 92 | }; 93 | 94 | /** 95 | * Enables `speech` event on Client, which is called whenever someone stops speaking 96 | */ 97 | export default ( 98 | client: Client, 99 | speechOptions: SpeechOptions 100 | ): void => { 101 | client.on(SpeechEvents.voiceJoin, async (connection) => { 102 | if (!connection) { 103 | return; 104 | } 105 | 106 | await entersState(connection, VoiceConnectionStatus.Ready, 20e3); 107 | handleSpeakingEvent({ client, speechOptions, connection }); 108 | }); 109 | }; 110 | -------------------------------------------------------------------------------- /tests/addSpeechEvent.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createAudioPlayer, 3 | createAudioResource, 4 | StreamType, 5 | } from "@discordjs/voice"; 6 | import { expect } from "chai"; 7 | import { Guild } from "discord.js"; 8 | import { once } from "events"; 9 | import { 10 | addSpeechEvent, 11 | resolveSpeechWithGoogleSpeechV2, 12 | resolveSpeechWithWitai, 13 | SpeechEvents, 14 | SpeechOptions, 15 | VoiceMessage, 16 | } from "../src/index"; 17 | import config from "./config"; 18 | import sampleData from "./sampleData"; 19 | import { TestManager } from "./utils"; 20 | 21 | describe("Test bot", () => { 22 | let tm: TestManager; 23 | 24 | before(async function before() { 25 | this.timeout(4000); 26 | tm = new TestManager(config.BOT_TOKEN, config.TESTBOT_TOKEN); 27 | await tm.loginClients(); 28 | }); 29 | 30 | it("Test config", () => { 31 | expect(config.TESTBOT_TOKEN, "Test bot token not specified").to.be.string; 32 | expect(config.BOT_TOKEN, "Main bot token not specified").to.be.string; 33 | expect(config.GUILD_ID, "Guild ID not specified").to.be.string; 34 | }); 35 | 36 | it("Check if test bot and main bot are in the same guild", async () => { 37 | const testClientGuild = tm.testClient.guilds.cache.get(config.GUILD_ID); 38 | const clientGuild = tm.client.guilds.cache.get(config.GUILD_ID); 39 | 40 | expect(testClientGuild, "Test bot not in guild").to.be.an.instanceOf(Guild); 41 | expect(clientGuild, "Main bot not in guild").to.be.an.instanceOf(Guild); 42 | expect(testClientGuild?.id).to.be.equal(clientGuild?.id); 43 | }); 44 | 45 | const speechOptions: Array = [ 46 | { speechRecognition: resolveSpeechWithGoogleSpeechV2 }, 47 | { speechRecognition: resolveSpeechWithWitai, key: config.WITAI_KEY }, 48 | ]; 49 | speechOptions.forEach((speechOption) => { 50 | describe(`Events with ${ 51 | speechOption.speechRecognition?.name || "default" 52 | }`, () => { 53 | before(async function before() { 54 | this.timeout(16000); 55 | 56 | tm.resetClients(); 57 | await tm.loginClients(); 58 | 59 | addSpeechEvent(tm.client, { 60 | group: "client", 61 | ignoreBots: false, 62 | ...speechOptions, 63 | }); 64 | 65 | return tm.setTestVoiceChannel(config.GUILD_ID); 66 | }); 67 | 68 | it("Voice join event", async () => { 69 | const voiceConnection = tm.connectToVoiceChannel("client"); 70 | await once(tm.client, SpeechEvents.voiceJoin); 71 | (await voiceConnection).destroy(); 72 | }); 73 | 74 | it("Default speech recognition", async function testSpeechRecognition() { 75 | this.timeout(12000); 76 | 77 | const player = createAudioPlayer(); 78 | const resource = createAudioResource(sampleData.normal.url, { 79 | inputType: StreamType.Arbitrary, 80 | }); 81 | 82 | const testConnection = await tm.connectToVoiceChannel("testClient"); 83 | await tm.connectToVoiceChannel("client"); 84 | 85 | player.play(resource); 86 | testConnection.subscribe(player); 87 | 88 | return new Promise((resolve, reject) => { 89 | tm.client.on(SpeechEvents.speech, (msg: VoiceMessage) => { 90 | try { 91 | expect(msg.content?.toLowerCase()).to.contain( 92 | sampleData.normal.text 93 | ); 94 | resolve(); 95 | } catch (error) { 96 | reject(error); 97 | } 98 | }); 99 | }); 100 | }); 101 | 102 | after(() => { 103 | tm.disconnectFromVoiceChannel(config.GUILD_ID, "client"); 104 | tm.disconnectFromVoiceChannel(config.GUILD_ID, "testClient"); 105 | }); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /tests/utils/TestManager.ts: -------------------------------------------------------------------------------- 1 | import { 2 | entersState, 3 | getVoiceConnection, 4 | joinVoiceChannel, 5 | VoiceConnection, 6 | VoiceConnectionStatus, 7 | } from "@discordjs/voice"; 8 | import { 9 | Client, 10 | ClientOptions, 11 | Guild, 12 | VoiceChannel, 13 | GatewayIntentBits, 14 | ChannelType, 15 | } from "discord.js"; 16 | 17 | export default class TestManager { 18 | testClient: Client; 19 | 20 | client: Client; 21 | 22 | testVoiceChannel: VoiceChannel | undefined; 23 | 24 | clientVoiceChannel: VoiceChannel | undefined; 25 | 26 | clientToken: string; 27 | 28 | testToken: string; 29 | 30 | constructor(mainToken: string, testToken: string) { 31 | this.clientToken = mainToken; 32 | this.testToken = testToken; 33 | 34 | this.testClient = new Client(TestManager.clientOptions); 35 | this.client = new Client(TestManager.clientOptions); 36 | } 37 | 38 | static get clientOptions(): ClientOptions { 39 | return { 40 | intents: [ 41 | GatewayIntentBits.GuildVoiceStates, 42 | GatewayIntentBits.GuildMessages, 43 | GatewayIntentBits.Guilds, 44 | ], 45 | }; 46 | } 47 | 48 | resetClients() { 49 | this.client = new Client(TestManager.clientOptions); 50 | this.testClient = new Client(TestManager.clientOptions); 51 | } 52 | 53 | async loginClients() { 54 | this.client.login(this.clientToken); 55 | this.testClient.login(this.testToken); 56 | const isClientReady = this.waitForClientToBeReady(this.client); 57 | const isTestClientReady = this.waitForClientToBeReady(this.testClient); 58 | return Promise.all([isClientReady, isTestClientReady]); 59 | } 60 | 61 | async setTestVoiceChannel(guildID: string): Promise { 62 | const guild = this.getGuildFromID(guildID); 63 | if (!guild) return; 64 | await this.setOrCreateTestVoiceChannels(guild); 65 | } 66 | 67 | async disconnectFromVoiceChannel( 68 | guildID: string, 69 | type: "client" | "testClient" 70 | ) { 71 | getVoiceConnection(guildID, type)?.destroy(); 72 | } 73 | 74 | async connectToVoiceChannel( 75 | type: "client" | "testClient" 76 | ): Promise { 77 | let channel; 78 | if (type === "client") channel = this.clientVoiceChannel; 79 | else if (type === "testClient") channel = this.testVoiceChannel; 80 | 81 | if (!channel) throw new Error("Voice channel doesn't exist"); 82 | 83 | const connection = joinVoiceChannel({ 84 | group: type, 85 | channelId: channel.id, 86 | guildId: channel.guild.id, 87 | adapterCreator: channel.guild.voiceAdapterCreator, 88 | selfDeaf: false, 89 | }); 90 | 91 | try { 92 | await entersState(connection, VoiceConnectionStatus.Ready, 30e3); 93 | return connection; 94 | } catch (error) { 95 | connection.destroy(); 96 | throw error; 97 | } 98 | } 99 | 100 | private async waitForClientToBeReady(client: Client) { 101 | return new Promise((resolve) => 102 | client.once("ready", () => resolve()) 103 | ); 104 | } 105 | 106 | private getGuildFromID(guildID: string) { 107 | return this.testClient.guilds.cache.get(guildID); 108 | } 109 | 110 | private async setOrCreateTestVoiceChannels(guild: Guild): Promise { 111 | const voiceChannel = this.getTestChannel(guild); 112 | if (voiceChannel) { 113 | this.testVoiceChannel = voiceChannel; 114 | } else { 115 | this.testVoiceChannel = await this.createTestChannel(guild); 116 | } 117 | this.clientVoiceChannel = (await this.client.channels.fetch( 118 | this.testVoiceChannel.id 119 | )) as VoiceChannel; 120 | } 121 | 122 | private getTestChannel(guild: Guild): VoiceChannel | undefined { 123 | return guild.channels.cache.find( 124 | (channel) => 125 | channel.type === ChannelType.GuildVoice && channel.name === "test" 126 | ) as VoiceChannel; 127 | } 128 | 129 | private async createTestChannel(guild: Guild): Promise { 130 | return guild.channels.create({ 131 | name: "test", 132 | type: ChannelType.GuildVoice, 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | { 3 | "compilerOptions": { 4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | "lib": [ 10 | "ESNext.String" /* ESNext.String is used for replaceAll method in string */ 11 | ], 12 | // "allowJs": true, /* Allow javascript files to be compiled. *//* Report errors in .js files. */ 13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 14 | "declaration": true /* Generates corresponding '.d.ts' file. */, 15 | //"declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 16 | "sourceMap": true /* Generates corresponding '.map' file. */, 17 | // "outFile": "./", /* Concatenate and emit output to single file. */ 18 | "outDir": "./dist" /* Redirect output structure to the directory. */, 19 | //"rootDir": "./src/", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 20 | //"composite": true, /* Enable project compilation */ 21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 22 | // "removeComments": true, /* Do not emit comments to output. */ 23 | // "noEmit": true, /* Do not emit outputs. */ 24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 30 | // "strictNullChecks": true, /* Enable strict null checks. */ 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | /* Additional Checks */ 37 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 41 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 42 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 43 | /* Module Resolution Options */ 44 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 45 | /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 46 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 47 | // "typeRoots": [], /* List of folders to include type definitions from. */ 48 | // "types": [], /* Type declaration files to be included in compilation. */ 49 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 50 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 51 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 52 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | /* Experimental Options */ 59 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 60 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 61 | /* Advanced Options */ 62 | "skipLibCheck": true /* Skip type checking of declaration files. */, 63 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 64 | "resolveJsonModule": true 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 6 | /* Basic Options */ 7 | // "incremental": true, /* Enable incremental compilation */ 8 | "target": "ES2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 9 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 10 | // "lib": [], /* Specify library files to be included in the compilation. */ 11 | // "allowJs": true, /* Allow javascript files to be compiled. *//* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 13 | "declaration": true /* Generates corresponding '.d.ts' file. */, 14 | //"declarationMap": false, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | "sourceMap": true /* Generates corresponding '.map' file. */, 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./dist" /* Redirect output structure to the directory. */, 18 | //"rootDir": "./src/", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | //"composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | /* Strict Type-Checking Options */ 27 | "strict": true /* Enable all strict type-checking options. */, 28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 29 | // "strictNullChecks": true, /* Enable strict null checks. */ 30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 41 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 42 | /* Module Resolution Options */ 43 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 44 | /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | /* Source Map Options */ 53 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 54 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 55 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 56 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 57 | /* Experimental Options */ 58 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 59 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 60 | /* Advanced Options */ 61 | "skipLibCheck": true /* Skip type checking of declaration files. */, 62 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 63 | "resolveJsonModule": false 64 | }, 65 | "exclude": ["./tests/**/*", "./examples/**/*"], 66 | "include": ["src"] 67 | } 68 | -------------------------------------------------------------------------------- /examples/simpleBot/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simplebot", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "simplebot", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@discordjs/opus": "^0.8.0", 13 | "@discordjs/voice": "^0.11.0", 14 | "discord-speech-recognition": "latest", 15 | "discord.js": "^14.3.0", 16 | "sodium": "^3.0.2" 17 | } 18 | }, 19 | "node_modules/@discordjs/builders": { 20 | "version": "1.2.0", 21 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.2.0.tgz", 22 | "integrity": "sha512-ARy4BUTMU+S0ZI6605NDqfWO+qZqV2d/xfY32z3hVSsd9IaAKJBZ1ILTZLy87oIjW8+gUpQmk9Kt0ZP9bmmd8Q==", 23 | "dependencies": { 24 | "@sapphire/shapeshift": "^3.5.1", 25 | "discord-api-types": "^0.37.3", 26 | "fast-deep-equal": "^3.1.3", 27 | "ts-mixer": "^6.0.1", 28 | "tslib": "^2.4.0" 29 | }, 30 | "engines": { 31 | "node": ">=16.9.0" 32 | } 33 | }, 34 | "node_modules/@discordjs/builders/node_modules/discord-api-types": { 35 | "version": "0.37.5", 36 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", 37 | "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" 38 | }, 39 | "node_modules/@discordjs/collection": { 40 | "version": "1.1.0", 41 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.1.0.tgz", 42 | "integrity": "sha512-PQ2Bv6pnT7aGPCKWbvvNRww5tYCGpggIQVgpuF9TdDPeR6n6vQYxezXiLVOS9z2B62Dp4c+qepQ15SgJbLYtCQ==", 43 | "engines": { 44 | "node": ">=16.9.0" 45 | } 46 | }, 47 | "node_modules/@discordjs/node-pre-gyp": { 48 | "version": "0.4.4", 49 | "resolved": "https://registry.npmjs.org/@discordjs/node-pre-gyp/-/node-pre-gyp-0.4.4.tgz", 50 | "integrity": "sha512-x569MMtdk6jdGo2S58iiZoyv4p/N2Ju8Nh6vvzZb1wyouV7IE3VuU0hg2kqUmTfD0z6r4uD6acvMTuc+iA3f8g==", 51 | "dependencies": { 52 | "detect-libc": "^2.0.0", 53 | "https-proxy-agent": "^5.0.0", 54 | "make-dir": "^3.1.0", 55 | "node-fetch": "^2.6.7", 56 | "nopt": "^5.0.0", 57 | "npmlog": "^5.0.1", 58 | "rimraf": "^3.0.2", 59 | "semver": "^7.3.5", 60 | "tar": "^6.1.11" 61 | }, 62 | "bin": { 63 | "node-pre-gyp": "bin/node-pre-gyp" 64 | } 65 | }, 66 | "node_modules/@discordjs/opus": { 67 | "version": "0.8.0", 68 | "resolved": "https://registry.npmjs.org/@discordjs/opus/-/opus-0.8.0.tgz", 69 | "integrity": "sha512-uHE7OmHEmP8YM0yvsH3iSdacdeghO0qTkF0CIkV07Tg0qdyOLUVkoZHj5Zcpge9rC4qb/JvTS2xRgttSZLM43Q==", 70 | "hasInstallScript": true, 71 | "dependencies": { 72 | "@discordjs/node-pre-gyp": "^0.4.4", 73 | "node-addon-api": "^5.0.0" 74 | }, 75 | "engines": { 76 | "node": ">=12.0.0" 77 | } 78 | }, 79 | "node_modules/@discordjs/rest": { 80 | "version": "1.1.0", 81 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.1.0.tgz", 82 | "integrity": "sha512-yCrthRTQeUyNThQEpCk7bvQJlwQmz6kU0tf3dcWBv2WX3Bncl41x7Wc+v5b5OsIxfNYq38PvVtWircu9jtYZug==", 83 | "dependencies": { 84 | "@discordjs/collection": "^1.0.1", 85 | "@sapphire/async-queue": "^1.5.0", 86 | "@sapphire/snowflake": "^3.2.2", 87 | "discord-api-types": "^0.37.3", 88 | "file-type": "^17.1.6", 89 | "tslib": "^2.4.0", 90 | "undici": "^5.9.1" 91 | }, 92 | "engines": { 93 | "node": ">=16.9.0" 94 | } 95 | }, 96 | "node_modules/@discordjs/rest/node_modules/discord-api-types": { 97 | "version": "0.37.5", 98 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", 99 | "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" 100 | }, 101 | "node_modules/@discordjs/voice": { 102 | "version": "0.11.0", 103 | "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.11.0.tgz", 104 | "integrity": "sha512-6+9cj1dxzBJm7WJ9qyG2XZZQ8rcLl6x2caW0C0OxuTtMLAaEDntpb6lqMTFiBg/rDc4Rd59g1w0gJmib33CuHw==", 105 | "dependencies": { 106 | "@types/ws": "^8.5.3", 107 | "discord-api-types": "^0.36.2", 108 | "prism-media": "^1.3.4", 109 | "tslib": "^2.4.0", 110 | "ws": "^8.8.1" 111 | }, 112 | "engines": { 113 | "node": ">=16.9.0" 114 | } 115 | }, 116 | "node_modules/@sapphire/async-queue": { 117 | "version": "1.5.0", 118 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", 119 | "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", 120 | "engines": { 121 | "node": ">=v14.0.0", 122 | "npm": ">=7.0.0" 123 | } 124 | }, 125 | "node_modules/@sapphire/shapeshift": { 126 | "version": "3.6.0", 127 | "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.6.0.tgz", 128 | "integrity": "sha512-tu2WLRdo5wotHRvsCkspg3qMiP6ETC3Q1dns1Q5V6zKUki+1itq6AbhMwohF9ZcLoYqg+Y8LkgRRtVxxTQVTBQ==", 129 | "dependencies": { 130 | "fast-deep-equal": "^3.1.3", 131 | "lodash.uniqwith": "^4.5.0" 132 | }, 133 | "engines": { 134 | "node": ">=v14.0.0", 135 | "npm": ">=7.0.0" 136 | } 137 | }, 138 | "node_modules/@sapphire/snowflake": { 139 | "version": "3.2.2", 140 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz", 141 | "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==", 142 | "engines": { 143 | "node": ">=v14.0.0", 144 | "npm": ">=7.0.0" 145 | } 146 | }, 147 | "node_modules/@tokenizer/token": { 148 | "version": "0.3.0", 149 | "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", 150 | "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" 151 | }, 152 | "node_modules/@types/node": { 153 | "version": "16.10.3", 154 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", 155 | "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==" 156 | }, 157 | "node_modules/@types/ws": { 158 | "version": "8.5.3", 159 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", 160 | "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", 161 | "dependencies": { 162 | "@types/node": "*" 163 | } 164 | }, 165 | "node_modules/abbrev": { 166 | "version": "1.1.1", 167 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 168 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 169 | }, 170 | "node_modules/agent-base": { 171 | "version": "6.0.2", 172 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 173 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 174 | "dependencies": { 175 | "debug": "4" 176 | }, 177 | "engines": { 178 | "node": ">= 6.0.0" 179 | } 180 | }, 181 | "node_modules/ansi-regex": { 182 | "version": "5.0.1", 183 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 184 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 185 | "engines": { 186 | "node": ">=8" 187 | } 188 | }, 189 | "node_modules/aproba": { 190 | "version": "2.0.0", 191 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", 192 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" 193 | }, 194 | "node_modules/are-we-there-yet": { 195 | "version": "2.0.0", 196 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", 197 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", 198 | "dependencies": { 199 | "delegates": "^1.0.0", 200 | "readable-stream": "^3.6.0" 201 | }, 202 | "engines": { 203 | "node": ">=10" 204 | } 205 | }, 206 | "node_modules/axios": { 207 | "version": "0.21.4", 208 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", 209 | "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", 210 | "dependencies": { 211 | "follow-redirects": "^1.14.0" 212 | } 213 | }, 214 | "node_modules/balanced-match": { 215 | "version": "1.0.2", 216 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 217 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 218 | }, 219 | "node_modules/brace-expansion": { 220 | "version": "1.1.11", 221 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 222 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 223 | "dependencies": { 224 | "balanced-match": "^1.0.0", 225 | "concat-map": "0.0.1" 226 | } 227 | }, 228 | "node_modules/buffer-alloc": { 229 | "version": "1.2.0", 230 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 231 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 232 | "dependencies": { 233 | "buffer-alloc-unsafe": "^1.1.0", 234 | "buffer-fill": "^1.0.0" 235 | } 236 | }, 237 | "node_modules/buffer-alloc-unsafe": { 238 | "version": "1.1.0", 239 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 240 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 241 | }, 242 | "node_modules/buffer-fill": { 243 | "version": "1.0.0", 244 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 245 | "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" 246 | }, 247 | "node_modules/buffer-from": { 248 | "version": "1.1.2", 249 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 250 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 251 | }, 252 | "node_modules/chownr": { 253 | "version": "2.0.0", 254 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 255 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", 256 | "engines": { 257 | "node": ">=10" 258 | } 259 | }, 260 | "node_modules/color-support": { 261 | "version": "1.1.3", 262 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", 263 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", 264 | "bin": { 265 | "color-support": "bin.js" 266 | } 267 | }, 268 | "node_modules/concat-map": { 269 | "version": "0.0.1", 270 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 271 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 272 | }, 273 | "node_modules/console-control-strings": { 274 | "version": "1.1.0", 275 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 276 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" 277 | }, 278 | "node_modules/core-util-is": { 279 | "version": "1.0.3", 280 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 281 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 282 | }, 283 | "node_modules/debug": { 284 | "version": "4.3.4", 285 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 286 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 287 | "dependencies": { 288 | "ms": "2.1.2" 289 | }, 290 | "engines": { 291 | "node": ">=6.0" 292 | }, 293 | "peerDependenciesMeta": { 294 | "supports-color": { 295 | "optional": true 296 | } 297 | } 298 | }, 299 | "node_modules/delegates": { 300 | "version": "1.0.0", 301 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 302 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" 303 | }, 304 | "node_modules/detect-libc": { 305 | "version": "2.0.1", 306 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", 307 | "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", 308 | "engines": { 309 | "node": ">=8" 310 | } 311 | }, 312 | "node_modules/discord-api-types": { 313 | "version": "0.36.3", 314 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", 315 | "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" 316 | }, 317 | "node_modules/discord-speech-recognition": { 318 | "version": "3.0.1", 319 | "resolved": "https://registry.npmjs.org/discord-speech-recognition/-/discord-speech-recognition-3.0.1.tgz", 320 | "integrity": "sha512-VgS2FxHe4g3rfINDvOxr/ThvcOownrg+xLBxn3PWYsKJtQ6woV02oLROf9TllZuSyIenjU+4/jDB3wgjsoScQQ==", 321 | "dependencies": { 322 | "axios": "^0.21.1", 323 | "node-fetch": "^2.6.5", 324 | "wav": "^1.0.2" 325 | }, 326 | "engines": { 327 | "node": ">=16.6.0" 328 | }, 329 | "peerDependencies": { 330 | "@discordjs/opus": "^0.x", 331 | "@discordjs/voice": "^0.11.x", 332 | "discord.js": "14.x" 333 | } 334 | }, 335 | "node_modules/discord.js": { 336 | "version": "14.3.0", 337 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.3.0.tgz", 338 | "integrity": "sha512-CpIwoAAuELiHSgVKRMzsCADS6ZlJwAZ9RlvcJYdEgS00aW36dSvXyBgE+S3pigkc7G+jU6BEalMUWIJFveqrBQ==", 339 | "dependencies": { 340 | "@discordjs/builders": "^1.2.0", 341 | "@discordjs/collection": "^1.1.0", 342 | "@discordjs/rest": "^1.1.0", 343 | "@sapphire/snowflake": "^3.2.2", 344 | "@types/ws": "^8.5.3", 345 | "discord-api-types": "^0.37.3", 346 | "fast-deep-equal": "^3.1.3", 347 | "lodash.snakecase": "^4.1.1", 348 | "tslib": "^2.4.0", 349 | "undici": "^5.9.1", 350 | "ws": "^8.8.1" 351 | }, 352 | "engines": { 353 | "node": ">=16.9.0" 354 | } 355 | }, 356 | "node_modules/discord.js/node_modules/discord-api-types": { 357 | "version": "0.37.5", 358 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", 359 | "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" 360 | }, 361 | "node_modules/emoji-regex": { 362 | "version": "8.0.0", 363 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 364 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 365 | }, 366 | "node_modules/fast-deep-equal": { 367 | "version": "3.1.3", 368 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 369 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 370 | }, 371 | "node_modules/file-type": { 372 | "version": "17.1.6", 373 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", 374 | "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", 375 | "dependencies": { 376 | "readable-web-to-node-stream": "^3.0.2", 377 | "strtok3": "^7.0.0-alpha.9", 378 | "token-types": "^5.0.0-alpha.2" 379 | }, 380 | "engines": { 381 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 382 | }, 383 | "funding": { 384 | "url": "https://github.com/sindresorhus/file-type?sponsor=1" 385 | } 386 | }, 387 | "node_modules/follow-redirects": { 388 | "version": "1.15.1", 389 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", 390 | "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", 391 | "funding": [ 392 | { 393 | "type": "individual", 394 | "url": "https://github.com/sponsors/RubenVerborgh" 395 | } 396 | ], 397 | "engines": { 398 | "node": ">=4.0" 399 | }, 400 | "peerDependenciesMeta": { 401 | "debug": { 402 | "optional": true 403 | } 404 | } 405 | }, 406 | "node_modules/fs-minipass": { 407 | "version": "2.1.0", 408 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 409 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 410 | "dependencies": { 411 | "minipass": "^3.0.0" 412 | }, 413 | "engines": { 414 | "node": ">= 8" 415 | } 416 | }, 417 | "node_modules/fs.realpath": { 418 | "version": "1.0.0", 419 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 420 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 421 | }, 422 | "node_modules/gauge": { 423 | "version": "3.0.2", 424 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", 425 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", 426 | "dependencies": { 427 | "aproba": "^1.0.3 || ^2.0.0", 428 | "color-support": "^1.1.2", 429 | "console-control-strings": "^1.0.0", 430 | "has-unicode": "^2.0.1", 431 | "object-assign": "^4.1.1", 432 | "signal-exit": "^3.0.0", 433 | "string-width": "^4.2.3", 434 | "strip-ansi": "^6.0.1", 435 | "wide-align": "^1.1.2" 436 | }, 437 | "engines": { 438 | "node": ">=10" 439 | } 440 | }, 441 | "node_modules/glob": { 442 | "version": "7.2.3", 443 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 444 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 445 | "dependencies": { 446 | "fs.realpath": "^1.0.0", 447 | "inflight": "^1.0.4", 448 | "inherits": "2", 449 | "minimatch": "^3.1.1", 450 | "once": "^1.3.0", 451 | "path-is-absolute": "^1.0.0" 452 | }, 453 | "engines": { 454 | "node": "*" 455 | }, 456 | "funding": { 457 | "url": "https://github.com/sponsors/isaacs" 458 | } 459 | }, 460 | "node_modules/has-unicode": { 461 | "version": "2.0.1", 462 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 463 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" 464 | }, 465 | "node_modules/https-proxy-agent": { 466 | "version": "5.0.1", 467 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 468 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 469 | "dependencies": { 470 | "agent-base": "6", 471 | "debug": "4" 472 | }, 473 | "engines": { 474 | "node": ">= 6" 475 | } 476 | }, 477 | "node_modules/ieee754": { 478 | "version": "1.2.1", 479 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 480 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 481 | "funding": [ 482 | { 483 | "type": "github", 484 | "url": "https://github.com/sponsors/feross" 485 | }, 486 | { 487 | "type": "patreon", 488 | "url": "https://www.patreon.com/feross" 489 | }, 490 | { 491 | "type": "consulting", 492 | "url": "https://feross.org/support" 493 | } 494 | ] 495 | }, 496 | "node_modules/inflight": { 497 | "version": "1.0.6", 498 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 499 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 500 | "dependencies": { 501 | "once": "^1.3.0", 502 | "wrappy": "1" 503 | } 504 | }, 505 | "node_modules/inherits": { 506 | "version": "2.0.4", 507 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 508 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 509 | }, 510 | "node_modules/is-fullwidth-code-point": { 511 | "version": "3.0.0", 512 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 513 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 514 | "engines": { 515 | "node": ">=8" 516 | } 517 | }, 518 | "node_modules/isarray": { 519 | "version": "0.0.1", 520 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 521 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" 522 | }, 523 | "node_modules/lodash.snakecase": { 524 | "version": "4.1.1", 525 | "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", 526 | "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" 527 | }, 528 | "node_modules/lodash.uniqwith": { 529 | "version": "4.5.0", 530 | "resolved": "https://registry.npmjs.org/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz", 531 | "integrity": "sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==" 532 | }, 533 | "node_modules/lru-cache": { 534 | "version": "6.0.0", 535 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 536 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 537 | "dependencies": { 538 | "yallist": "^4.0.0" 539 | }, 540 | "engines": { 541 | "node": ">=10" 542 | } 543 | }, 544 | "node_modules/make-dir": { 545 | "version": "3.1.0", 546 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 547 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 548 | "dependencies": { 549 | "semver": "^6.0.0" 550 | }, 551 | "engines": { 552 | "node": ">=8" 553 | }, 554 | "funding": { 555 | "url": "https://github.com/sponsors/sindresorhus" 556 | } 557 | }, 558 | "node_modules/make-dir/node_modules/semver": { 559 | "version": "6.3.0", 560 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 561 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 562 | "bin": { 563 | "semver": "bin/semver.js" 564 | } 565 | }, 566 | "node_modules/minimatch": { 567 | "version": "3.1.2", 568 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 569 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 570 | "dependencies": { 571 | "brace-expansion": "^1.1.7" 572 | }, 573 | "engines": { 574 | "node": "*" 575 | } 576 | }, 577 | "node_modules/minipass": { 578 | "version": "3.3.4", 579 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", 580 | "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", 581 | "dependencies": { 582 | "yallist": "^4.0.0" 583 | }, 584 | "engines": { 585 | "node": ">=8" 586 | } 587 | }, 588 | "node_modules/minizlib": { 589 | "version": "2.1.2", 590 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 591 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 592 | "dependencies": { 593 | "minipass": "^3.0.0", 594 | "yallist": "^4.0.0" 595 | }, 596 | "engines": { 597 | "node": ">= 8" 598 | } 599 | }, 600 | "node_modules/mkdirp": { 601 | "version": "1.0.4", 602 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 603 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 604 | "bin": { 605 | "mkdirp": "bin/cmd.js" 606 | }, 607 | "engines": { 608 | "node": ">=10" 609 | } 610 | }, 611 | "node_modules/ms": { 612 | "version": "2.1.2", 613 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 614 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 615 | }, 616 | "node_modules/node-addon-api": { 617 | "version": "5.0.0", 618 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", 619 | "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" 620 | }, 621 | "node_modules/node-fetch": { 622 | "version": "2.6.7", 623 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 624 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 625 | "dependencies": { 626 | "whatwg-url": "^5.0.0" 627 | }, 628 | "engines": { 629 | "node": "4.x || >=6.0.0" 630 | }, 631 | "peerDependencies": { 632 | "encoding": "^0.1.0" 633 | }, 634 | "peerDependenciesMeta": { 635 | "encoding": { 636 | "optional": true 637 | } 638 | } 639 | }, 640 | "node_modules/nopt": { 641 | "version": "5.0.0", 642 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", 643 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", 644 | "dependencies": { 645 | "abbrev": "1" 646 | }, 647 | "bin": { 648 | "nopt": "bin/nopt.js" 649 | }, 650 | "engines": { 651 | "node": ">=6" 652 | } 653 | }, 654 | "node_modules/npmlog": { 655 | "version": "5.0.1", 656 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", 657 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", 658 | "dependencies": { 659 | "are-we-there-yet": "^2.0.0", 660 | "console-control-strings": "^1.1.0", 661 | "gauge": "^3.0.0", 662 | "set-blocking": "^2.0.0" 663 | } 664 | }, 665 | "node_modules/object-assign": { 666 | "version": "4.1.1", 667 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 668 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 669 | "engines": { 670 | "node": ">=0.10.0" 671 | } 672 | }, 673 | "node_modules/once": { 674 | "version": "1.4.0", 675 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 676 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 677 | "dependencies": { 678 | "wrappy": "1" 679 | } 680 | }, 681 | "node_modules/path-is-absolute": { 682 | "version": "1.0.1", 683 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 684 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 685 | "engines": { 686 | "node": ">=0.10.0" 687 | } 688 | }, 689 | "node_modules/peek-readable": { 690 | "version": "5.0.0", 691 | "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", 692 | "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", 693 | "engines": { 694 | "node": ">=14.16" 695 | }, 696 | "funding": { 697 | "type": "github", 698 | "url": "https://github.com/sponsors/Borewit" 699 | } 700 | }, 701 | "node_modules/prism-media": { 702 | "version": "1.3.4", 703 | "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.4.tgz", 704 | "integrity": "sha512-eW7LXORkTCQznZs+eqe9VjGOrLBxcBPXgNyHXMTSRVhphvd/RrxgIR7WaWt4fkLuhshcdT5KHL88LAfcvS3f5g==", 705 | "peerDependencies": { 706 | "@discordjs/opus": "^0.8.0", 707 | "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", 708 | "node-opus": "^0.3.3", 709 | "opusscript": "^0.0.8" 710 | }, 711 | "peerDependenciesMeta": { 712 | "@discordjs/opus": { 713 | "optional": true 714 | }, 715 | "ffmpeg-static": { 716 | "optional": true 717 | }, 718 | "node-opus": { 719 | "optional": true 720 | }, 721 | "opusscript": { 722 | "optional": true 723 | } 724 | } 725 | }, 726 | "node_modules/readable-stream": { 727 | "version": "3.6.0", 728 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 729 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 730 | "dependencies": { 731 | "inherits": "^2.0.3", 732 | "string_decoder": "^1.1.1", 733 | "util-deprecate": "^1.0.1" 734 | }, 735 | "engines": { 736 | "node": ">= 6" 737 | } 738 | }, 739 | "node_modules/readable-web-to-node-stream": { 740 | "version": "3.0.2", 741 | "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", 742 | "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", 743 | "dependencies": { 744 | "readable-stream": "^3.6.0" 745 | }, 746 | "engines": { 747 | "node": ">=8" 748 | }, 749 | "funding": { 750 | "type": "github", 751 | "url": "https://github.com/sponsors/Borewit" 752 | } 753 | }, 754 | "node_modules/rimraf": { 755 | "version": "3.0.2", 756 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 757 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 758 | "dependencies": { 759 | "glob": "^7.1.3" 760 | }, 761 | "bin": { 762 | "rimraf": "bin.js" 763 | }, 764 | "funding": { 765 | "url": "https://github.com/sponsors/isaacs" 766 | } 767 | }, 768 | "node_modules/safe-buffer": { 769 | "version": "5.2.1", 770 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 771 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 772 | "funding": [ 773 | { 774 | "type": "github", 775 | "url": "https://github.com/sponsors/feross" 776 | }, 777 | { 778 | "type": "patreon", 779 | "url": "https://www.patreon.com/feross" 780 | }, 781 | { 782 | "type": "consulting", 783 | "url": "https://feross.org/support" 784 | } 785 | ] 786 | }, 787 | "node_modules/semver": { 788 | "version": "7.3.7", 789 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", 790 | "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", 791 | "dependencies": { 792 | "lru-cache": "^6.0.0" 793 | }, 794 | "bin": { 795 | "semver": "bin/semver.js" 796 | }, 797 | "engines": { 798 | "node": ">=10" 799 | } 800 | }, 801 | "node_modules/set-blocking": { 802 | "version": "2.0.0", 803 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 804 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 805 | }, 806 | "node_modules/signal-exit": { 807 | "version": "3.0.7", 808 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 809 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 810 | }, 811 | "node_modules/sodium": { 812 | "version": "3.0.2", 813 | "resolved": "https://registry.npmjs.org/sodium/-/sodium-3.0.2.tgz", 814 | "integrity": "sha512-IsTwTJeoNBU97km3XkrbCGC/n/9aUQejgD3QPr2YY2gtbSPru3TI6nhCqgoez9Mv88frF9oVZS/jrXFbd6WXyA==", 815 | "hasInstallScript": true, 816 | "dependencies": { 817 | "node-addon-api": "*" 818 | } 819 | }, 820 | "node_modules/stream-parser": { 821 | "version": "0.3.1", 822 | "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", 823 | "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==", 824 | "dependencies": { 825 | "debug": "2" 826 | } 827 | }, 828 | "node_modules/stream-parser/node_modules/debug": { 829 | "version": "2.6.9", 830 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 831 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 832 | "dependencies": { 833 | "ms": "2.0.0" 834 | } 835 | }, 836 | "node_modules/stream-parser/node_modules/ms": { 837 | "version": "2.0.0", 838 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 839 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 840 | }, 841 | "node_modules/string_decoder": { 842 | "version": "1.3.0", 843 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 844 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 845 | "dependencies": { 846 | "safe-buffer": "~5.2.0" 847 | } 848 | }, 849 | "node_modules/string-width": { 850 | "version": "4.2.3", 851 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 852 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 853 | "dependencies": { 854 | "emoji-regex": "^8.0.0", 855 | "is-fullwidth-code-point": "^3.0.0", 856 | "strip-ansi": "^6.0.1" 857 | }, 858 | "engines": { 859 | "node": ">=8" 860 | } 861 | }, 862 | "node_modules/strip-ansi": { 863 | "version": "6.0.1", 864 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 865 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 866 | "dependencies": { 867 | "ansi-regex": "^5.0.1" 868 | }, 869 | "engines": { 870 | "node": ">=8" 871 | } 872 | }, 873 | "node_modules/strtok3": { 874 | "version": "7.0.0", 875 | "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", 876 | "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", 877 | "dependencies": { 878 | "@tokenizer/token": "^0.3.0", 879 | "peek-readable": "^5.0.0" 880 | }, 881 | "engines": { 882 | "node": ">=14.16" 883 | }, 884 | "funding": { 885 | "type": "github", 886 | "url": "https://github.com/sponsors/Borewit" 887 | } 888 | }, 889 | "node_modules/tar": { 890 | "version": "6.1.11", 891 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", 892 | "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", 893 | "dependencies": { 894 | "chownr": "^2.0.0", 895 | "fs-minipass": "^2.0.0", 896 | "minipass": "^3.0.0", 897 | "minizlib": "^2.1.1", 898 | "mkdirp": "^1.0.3", 899 | "yallist": "^4.0.0" 900 | }, 901 | "engines": { 902 | "node": ">= 10" 903 | } 904 | }, 905 | "node_modules/token-types": { 906 | "version": "5.0.1", 907 | "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", 908 | "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", 909 | "dependencies": { 910 | "@tokenizer/token": "^0.3.0", 911 | "ieee754": "^1.2.1" 912 | }, 913 | "engines": { 914 | "node": ">=14.16" 915 | }, 916 | "funding": { 917 | "type": "github", 918 | "url": "https://github.com/sponsors/Borewit" 919 | } 920 | }, 921 | "node_modules/tr46": { 922 | "version": "0.0.3", 923 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 924 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" 925 | }, 926 | "node_modules/ts-mixer": { 927 | "version": "6.0.1", 928 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", 929 | "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" 930 | }, 931 | "node_modules/tslib": { 932 | "version": "2.4.0", 933 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 934 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 935 | }, 936 | "node_modules/undici": { 937 | "version": "5.10.0", 938 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", 939 | "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==", 940 | "engines": { 941 | "node": ">=12.18" 942 | } 943 | }, 944 | "node_modules/util-deprecate": { 945 | "version": "1.0.2", 946 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 947 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 948 | }, 949 | "node_modules/wav": { 950 | "version": "1.0.2", 951 | "resolved": "https://registry.npmjs.org/wav/-/wav-1.0.2.tgz", 952 | "integrity": "sha512-viHtz3cDd/Tcr/HbNqzQCofKdF6kWUymH9LGDdskfWFoIy/HJ+RTihgjEcHfnsy1PO4e9B+y4HwgTwMrByquhg==", 953 | "dependencies": { 954 | "buffer-alloc": "^1.1.0", 955 | "buffer-from": "^1.0.0", 956 | "debug": "^2.2.0", 957 | "readable-stream": "^1.1.14", 958 | "stream-parser": "^0.3.1" 959 | } 960 | }, 961 | "node_modules/wav/node_modules/debug": { 962 | "version": "2.6.9", 963 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 964 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 965 | "dependencies": { 966 | "ms": "2.0.0" 967 | } 968 | }, 969 | "node_modules/wav/node_modules/ms": { 970 | "version": "2.0.0", 971 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 972 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 973 | }, 974 | "node_modules/wav/node_modules/readable-stream": { 975 | "version": "1.1.14", 976 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 977 | "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", 978 | "dependencies": { 979 | "core-util-is": "~1.0.0", 980 | "inherits": "~2.0.1", 981 | "isarray": "0.0.1", 982 | "string_decoder": "~0.10.x" 983 | } 984 | }, 985 | "node_modules/wav/node_modules/string_decoder": { 986 | "version": "0.10.31", 987 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 988 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" 989 | }, 990 | "node_modules/webidl-conversions": { 991 | "version": "3.0.1", 992 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 993 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" 994 | }, 995 | "node_modules/whatwg-url": { 996 | "version": "5.0.0", 997 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 998 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", 999 | "dependencies": { 1000 | "tr46": "~0.0.3", 1001 | "webidl-conversions": "^3.0.0" 1002 | } 1003 | }, 1004 | "node_modules/wide-align": { 1005 | "version": "1.1.5", 1006 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 1007 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 1008 | "dependencies": { 1009 | "string-width": "^1.0.2 || 2 || 3 || 4" 1010 | } 1011 | }, 1012 | "node_modules/wrappy": { 1013 | "version": "1.0.2", 1014 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1015 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1016 | }, 1017 | "node_modules/ws": { 1018 | "version": "8.8.1", 1019 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", 1020 | "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", 1021 | "engines": { 1022 | "node": ">=10.0.0" 1023 | }, 1024 | "peerDependencies": { 1025 | "bufferutil": "^4.0.1", 1026 | "utf-8-validate": "^5.0.2" 1027 | }, 1028 | "peerDependenciesMeta": { 1029 | "bufferutil": { 1030 | "optional": true 1031 | }, 1032 | "utf-8-validate": { 1033 | "optional": true 1034 | } 1035 | } 1036 | }, 1037 | "node_modules/yallist": { 1038 | "version": "4.0.0", 1039 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1040 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1041 | } 1042 | }, 1043 | "dependencies": { 1044 | "@discordjs/builders": { 1045 | "version": "1.2.0", 1046 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.2.0.tgz", 1047 | "integrity": "sha512-ARy4BUTMU+S0ZI6605NDqfWO+qZqV2d/xfY32z3hVSsd9IaAKJBZ1ILTZLy87oIjW8+gUpQmk9Kt0ZP9bmmd8Q==", 1048 | "requires": { 1049 | "@sapphire/shapeshift": "^3.5.1", 1050 | "discord-api-types": "^0.37.3", 1051 | "fast-deep-equal": "^3.1.3", 1052 | "ts-mixer": "^6.0.1", 1053 | "tslib": "^2.4.0" 1054 | }, 1055 | "dependencies": { 1056 | "discord-api-types": { 1057 | "version": "0.37.5", 1058 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", 1059 | "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" 1060 | } 1061 | } 1062 | }, 1063 | "@discordjs/collection": { 1064 | "version": "1.1.0", 1065 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.1.0.tgz", 1066 | "integrity": "sha512-PQ2Bv6pnT7aGPCKWbvvNRww5tYCGpggIQVgpuF9TdDPeR6n6vQYxezXiLVOS9z2B62Dp4c+qepQ15SgJbLYtCQ==" 1067 | }, 1068 | "@discordjs/node-pre-gyp": { 1069 | "version": "0.4.4", 1070 | "resolved": "https://registry.npmjs.org/@discordjs/node-pre-gyp/-/node-pre-gyp-0.4.4.tgz", 1071 | "integrity": "sha512-x569MMtdk6jdGo2S58iiZoyv4p/N2Ju8Nh6vvzZb1wyouV7IE3VuU0hg2kqUmTfD0z6r4uD6acvMTuc+iA3f8g==", 1072 | "requires": { 1073 | "detect-libc": "^2.0.0", 1074 | "https-proxy-agent": "^5.0.0", 1075 | "make-dir": "^3.1.0", 1076 | "node-fetch": "^2.6.7", 1077 | "nopt": "^5.0.0", 1078 | "npmlog": "^5.0.1", 1079 | "rimraf": "^3.0.2", 1080 | "semver": "^7.3.5", 1081 | "tar": "^6.1.11" 1082 | } 1083 | }, 1084 | "@discordjs/opus": { 1085 | "version": "0.8.0", 1086 | "resolved": "https://registry.npmjs.org/@discordjs/opus/-/opus-0.8.0.tgz", 1087 | "integrity": "sha512-uHE7OmHEmP8YM0yvsH3iSdacdeghO0qTkF0CIkV07Tg0qdyOLUVkoZHj5Zcpge9rC4qb/JvTS2xRgttSZLM43Q==", 1088 | "requires": { 1089 | "@discordjs/node-pre-gyp": "^0.4.4", 1090 | "node-addon-api": "^5.0.0" 1091 | } 1092 | }, 1093 | "@discordjs/rest": { 1094 | "version": "1.1.0", 1095 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.1.0.tgz", 1096 | "integrity": "sha512-yCrthRTQeUyNThQEpCk7bvQJlwQmz6kU0tf3dcWBv2WX3Bncl41x7Wc+v5b5OsIxfNYq38PvVtWircu9jtYZug==", 1097 | "requires": { 1098 | "@discordjs/collection": "^1.0.1", 1099 | "@sapphire/async-queue": "^1.5.0", 1100 | "@sapphire/snowflake": "^3.2.2", 1101 | "discord-api-types": "^0.37.3", 1102 | "file-type": "^17.1.6", 1103 | "tslib": "^2.4.0", 1104 | "undici": "^5.9.1" 1105 | }, 1106 | "dependencies": { 1107 | "discord-api-types": { 1108 | "version": "0.37.5", 1109 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", 1110 | "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" 1111 | } 1112 | } 1113 | }, 1114 | "@discordjs/voice": { 1115 | "version": "0.11.0", 1116 | "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.11.0.tgz", 1117 | "integrity": "sha512-6+9cj1dxzBJm7WJ9qyG2XZZQ8rcLl6x2caW0C0OxuTtMLAaEDntpb6lqMTFiBg/rDc4Rd59g1w0gJmib33CuHw==", 1118 | "requires": { 1119 | "@types/ws": "^8.5.3", 1120 | "discord-api-types": "^0.36.2", 1121 | "prism-media": "^1.3.4", 1122 | "tslib": "^2.4.0", 1123 | "ws": "^8.8.1" 1124 | } 1125 | }, 1126 | "@sapphire/async-queue": { 1127 | "version": "1.5.0", 1128 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", 1129 | "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==" 1130 | }, 1131 | "@sapphire/shapeshift": { 1132 | "version": "3.6.0", 1133 | "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.6.0.tgz", 1134 | "integrity": "sha512-tu2WLRdo5wotHRvsCkspg3qMiP6ETC3Q1dns1Q5V6zKUki+1itq6AbhMwohF9ZcLoYqg+Y8LkgRRtVxxTQVTBQ==", 1135 | "requires": { 1136 | "fast-deep-equal": "^3.1.3", 1137 | "lodash.uniqwith": "^4.5.0" 1138 | } 1139 | }, 1140 | "@sapphire/snowflake": { 1141 | "version": "3.2.2", 1142 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz", 1143 | "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==" 1144 | }, 1145 | "@tokenizer/token": { 1146 | "version": "0.3.0", 1147 | "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", 1148 | "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" 1149 | }, 1150 | "@types/node": { 1151 | "version": "16.10.3", 1152 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", 1153 | "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==" 1154 | }, 1155 | "@types/ws": { 1156 | "version": "8.5.3", 1157 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", 1158 | "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", 1159 | "requires": { 1160 | "@types/node": "*" 1161 | } 1162 | }, 1163 | "abbrev": { 1164 | "version": "1.1.1", 1165 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 1166 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 1167 | }, 1168 | "agent-base": { 1169 | "version": "6.0.2", 1170 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 1171 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 1172 | "requires": { 1173 | "debug": "4" 1174 | } 1175 | }, 1176 | "ansi-regex": { 1177 | "version": "5.0.1", 1178 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1179 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" 1180 | }, 1181 | "aproba": { 1182 | "version": "2.0.0", 1183 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", 1184 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" 1185 | }, 1186 | "are-we-there-yet": { 1187 | "version": "2.0.0", 1188 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", 1189 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", 1190 | "requires": { 1191 | "delegates": "^1.0.0", 1192 | "readable-stream": "^3.6.0" 1193 | } 1194 | }, 1195 | "axios": { 1196 | "version": "0.21.4", 1197 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", 1198 | "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", 1199 | "requires": { 1200 | "follow-redirects": "^1.14.0" 1201 | } 1202 | }, 1203 | "balanced-match": { 1204 | "version": "1.0.2", 1205 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1206 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 1207 | }, 1208 | "brace-expansion": { 1209 | "version": "1.1.11", 1210 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1211 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1212 | "requires": { 1213 | "balanced-match": "^1.0.0", 1214 | "concat-map": "0.0.1" 1215 | } 1216 | }, 1217 | "buffer-alloc": { 1218 | "version": "1.2.0", 1219 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 1220 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 1221 | "requires": { 1222 | "buffer-alloc-unsafe": "^1.1.0", 1223 | "buffer-fill": "^1.0.0" 1224 | } 1225 | }, 1226 | "buffer-alloc-unsafe": { 1227 | "version": "1.1.0", 1228 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 1229 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 1230 | }, 1231 | "buffer-fill": { 1232 | "version": "1.0.0", 1233 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 1234 | "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" 1235 | }, 1236 | "buffer-from": { 1237 | "version": "1.1.2", 1238 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 1239 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 1240 | }, 1241 | "chownr": { 1242 | "version": "2.0.0", 1243 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 1244 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" 1245 | }, 1246 | "color-support": { 1247 | "version": "1.1.3", 1248 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", 1249 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" 1250 | }, 1251 | "concat-map": { 1252 | "version": "0.0.1", 1253 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1254 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 1255 | }, 1256 | "console-control-strings": { 1257 | "version": "1.1.0", 1258 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 1259 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" 1260 | }, 1261 | "core-util-is": { 1262 | "version": "1.0.3", 1263 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 1264 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 1265 | }, 1266 | "debug": { 1267 | "version": "4.3.4", 1268 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1269 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1270 | "requires": { 1271 | "ms": "2.1.2" 1272 | } 1273 | }, 1274 | "delegates": { 1275 | "version": "1.0.0", 1276 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 1277 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" 1278 | }, 1279 | "detect-libc": { 1280 | "version": "2.0.1", 1281 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", 1282 | "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" 1283 | }, 1284 | "discord-api-types": { 1285 | "version": "0.36.3", 1286 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", 1287 | "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" 1288 | }, 1289 | "discord-speech-recognition": { 1290 | "version": "3.0.1", 1291 | "resolved": "https://registry.npmjs.org/discord-speech-recognition/-/discord-speech-recognition-3.0.1.tgz", 1292 | "integrity": "sha512-VgS2FxHe4g3rfINDvOxr/ThvcOownrg+xLBxn3PWYsKJtQ6woV02oLROf9TllZuSyIenjU+4/jDB3wgjsoScQQ==", 1293 | "requires": { 1294 | "axios": "^0.21.1", 1295 | "node-fetch": "^2.6.5", 1296 | "wav": "^1.0.2" 1297 | } 1298 | }, 1299 | "discord.js": { 1300 | "version": "14.3.0", 1301 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.3.0.tgz", 1302 | "integrity": "sha512-CpIwoAAuELiHSgVKRMzsCADS6ZlJwAZ9RlvcJYdEgS00aW36dSvXyBgE+S3pigkc7G+jU6BEalMUWIJFveqrBQ==", 1303 | "requires": { 1304 | "@discordjs/builders": "^1.2.0", 1305 | "@discordjs/collection": "^1.1.0", 1306 | "@discordjs/rest": "^1.1.0", 1307 | "@sapphire/snowflake": "^3.2.2", 1308 | "@types/ws": "^8.5.3", 1309 | "discord-api-types": "^0.37.3", 1310 | "fast-deep-equal": "^3.1.3", 1311 | "lodash.snakecase": "^4.1.1", 1312 | "tslib": "^2.4.0", 1313 | "undici": "^5.9.1", 1314 | "ws": "^8.8.1" 1315 | }, 1316 | "dependencies": { 1317 | "discord-api-types": { 1318 | "version": "0.37.5", 1319 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.5.tgz", 1320 | "integrity": "sha512-RhzoX02jw2M+n/AU5K74KTM4J8Sn3ZImUJvoA4lh+SDcrqi1ddSjrafciF4bECj4rPc2vHwoyyTNgbUwE8vbpA==" 1321 | } 1322 | } 1323 | }, 1324 | "emoji-regex": { 1325 | "version": "8.0.0", 1326 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1327 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 1328 | }, 1329 | "fast-deep-equal": { 1330 | "version": "3.1.3", 1331 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1332 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 1333 | }, 1334 | "file-type": { 1335 | "version": "17.1.6", 1336 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", 1337 | "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", 1338 | "requires": { 1339 | "readable-web-to-node-stream": "^3.0.2", 1340 | "strtok3": "^7.0.0-alpha.9", 1341 | "token-types": "^5.0.0-alpha.2" 1342 | } 1343 | }, 1344 | "follow-redirects": { 1345 | "version": "1.15.1", 1346 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", 1347 | "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" 1348 | }, 1349 | "fs-minipass": { 1350 | "version": "2.1.0", 1351 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 1352 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 1353 | "requires": { 1354 | "minipass": "^3.0.0" 1355 | } 1356 | }, 1357 | "fs.realpath": { 1358 | "version": "1.0.0", 1359 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1360 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 1361 | }, 1362 | "gauge": { 1363 | "version": "3.0.2", 1364 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", 1365 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", 1366 | "requires": { 1367 | "aproba": "^1.0.3 || ^2.0.0", 1368 | "color-support": "^1.1.2", 1369 | "console-control-strings": "^1.0.0", 1370 | "has-unicode": "^2.0.1", 1371 | "object-assign": "^4.1.1", 1372 | "signal-exit": "^3.0.0", 1373 | "string-width": "^4.2.3", 1374 | "strip-ansi": "^6.0.1", 1375 | "wide-align": "^1.1.2" 1376 | } 1377 | }, 1378 | "glob": { 1379 | "version": "7.2.3", 1380 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1381 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1382 | "requires": { 1383 | "fs.realpath": "^1.0.0", 1384 | "inflight": "^1.0.4", 1385 | "inherits": "2", 1386 | "minimatch": "^3.1.1", 1387 | "once": "^1.3.0", 1388 | "path-is-absolute": "^1.0.0" 1389 | } 1390 | }, 1391 | "has-unicode": { 1392 | "version": "2.0.1", 1393 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 1394 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" 1395 | }, 1396 | "https-proxy-agent": { 1397 | "version": "5.0.1", 1398 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 1399 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 1400 | "requires": { 1401 | "agent-base": "6", 1402 | "debug": "4" 1403 | } 1404 | }, 1405 | "ieee754": { 1406 | "version": "1.2.1", 1407 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1408 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1409 | }, 1410 | "inflight": { 1411 | "version": "1.0.6", 1412 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1413 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1414 | "requires": { 1415 | "once": "^1.3.0", 1416 | "wrappy": "1" 1417 | } 1418 | }, 1419 | "inherits": { 1420 | "version": "2.0.4", 1421 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1422 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1423 | }, 1424 | "is-fullwidth-code-point": { 1425 | "version": "3.0.0", 1426 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1427 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 1428 | }, 1429 | "isarray": { 1430 | "version": "0.0.1", 1431 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1432 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" 1433 | }, 1434 | "lodash.snakecase": { 1435 | "version": "4.1.1", 1436 | "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", 1437 | "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" 1438 | }, 1439 | "lodash.uniqwith": { 1440 | "version": "4.5.0", 1441 | "resolved": "https://registry.npmjs.org/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz", 1442 | "integrity": "sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==" 1443 | }, 1444 | "lru-cache": { 1445 | "version": "6.0.0", 1446 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1447 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1448 | "requires": { 1449 | "yallist": "^4.0.0" 1450 | } 1451 | }, 1452 | "make-dir": { 1453 | "version": "3.1.0", 1454 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 1455 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 1456 | "requires": { 1457 | "semver": "^6.0.0" 1458 | }, 1459 | "dependencies": { 1460 | "semver": { 1461 | "version": "6.3.0", 1462 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1463 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 1464 | } 1465 | } 1466 | }, 1467 | "minimatch": { 1468 | "version": "3.1.2", 1469 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1470 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1471 | "requires": { 1472 | "brace-expansion": "^1.1.7" 1473 | } 1474 | }, 1475 | "minipass": { 1476 | "version": "3.3.4", 1477 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", 1478 | "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", 1479 | "requires": { 1480 | "yallist": "^4.0.0" 1481 | } 1482 | }, 1483 | "minizlib": { 1484 | "version": "2.1.2", 1485 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 1486 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 1487 | "requires": { 1488 | "minipass": "^3.0.0", 1489 | "yallist": "^4.0.0" 1490 | } 1491 | }, 1492 | "mkdirp": { 1493 | "version": "1.0.4", 1494 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 1495 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" 1496 | }, 1497 | "ms": { 1498 | "version": "2.1.2", 1499 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1500 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1501 | }, 1502 | "node-addon-api": { 1503 | "version": "5.0.0", 1504 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", 1505 | "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" 1506 | }, 1507 | "node-fetch": { 1508 | "version": "2.6.7", 1509 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", 1510 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", 1511 | "requires": { 1512 | "whatwg-url": "^5.0.0" 1513 | } 1514 | }, 1515 | "nopt": { 1516 | "version": "5.0.0", 1517 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", 1518 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", 1519 | "requires": { 1520 | "abbrev": "1" 1521 | } 1522 | }, 1523 | "npmlog": { 1524 | "version": "5.0.1", 1525 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", 1526 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", 1527 | "requires": { 1528 | "are-we-there-yet": "^2.0.0", 1529 | "console-control-strings": "^1.1.0", 1530 | "gauge": "^3.0.0", 1531 | "set-blocking": "^2.0.0" 1532 | } 1533 | }, 1534 | "object-assign": { 1535 | "version": "4.1.1", 1536 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1537 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1538 | }, 1539 | "once": { 1540 | "version": "1.4.0", 1541 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1542 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1543 | "requires": { 1544 | "wrappy": "1" 1545 | } 1546 | }, 1547 | "path-is-absolute": { 1548 | "version": "1.0.1", 1549 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1550 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 1551 | }, 1552 | "peek-readable": { 1553 | "version": "5.0.0", 1554 | "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", 1555 | "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==" 1556 | }, 1557 | "prism-media": { 1558 | "version": "1.3.4", 1559 | "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.4.tgz", 1560 | "integrity": "sha512-eW7LXORkTCQznZs+eqe9VjGOrLBxcBPXgNyHXMTSRVhphvd/RrxgIR7WaWt4fkLuhshcdT5KHL88LAfcvS3f5g==", 1561 | "requires": {} 1562 | }, 1563 | "readable-stream": { 1564 | "version": "3.6.0", 1565 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1566 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1567 | "requires": { 1568 | "inherits": "^2.0.3", 1569 | "string_decoder": "^1.1.1", 1570 | "util-deprecate": "^1.0.1" 1571 | } 1572 | }, 1573 | "readable-web-to-node-stream": { 1574 | "version": "3.0.2", 1575 | "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", 1576 | "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", 1577 | "requires": { 1578 | "readable-stream": "^3.6.0" 1579 | } 1580 | }, 1581 | "rimraf": { 1582 | "version": "3.0.2", 1583 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1584 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1585 | "requires": { 1586 | "glob": "^7.1.3" 1587 | } 1588 | }, 1589 | "safe-buffer": { 1590 | "version": "5.2.1", 1591 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1592 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1593 | }, 1594 | "semver": { 1595 | "version": "7.3.7", 1596 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", 1597 | "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", 1598 | "requires": { 1599 | "lru-cache": "^6.0.0" 1600 | } 1601 | }, 1602 | "set-blocking": { 1603 | "version": "2.0.0", 1604 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1605 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 1606 | }, 1607 | "signal-exit": { 1608 | "version": "3.0.7", 1609 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1610 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 1611 | }, 1612 | "sodium": { 1613 | "version": "3.0.2", 1614 | "resolved": "https://registry.npmjs.org/sodium/-/sodium-3.0.2.tgz", 1615 | "integrity": "sha512-IsTwTJeoNBU97km3XkrbCGC/n/9aUQejgD3QPr2YY2gtbSPru3TI6nhCqgoez9Mv88frF9oVZS/jrXFbd6WXyA==", 1616 | "requires": { 1617 | "node-addon-api": "*" 1618 | } 1619 | }, 1620 | "stream-parser": { 1621 | "version": "0.3.1", 1622 | "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", 1623 | "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==", 1624 | "requires": { 1625 | "debug": "2" 1626 | }, 1627 | "dependencies": { 1628 | "debug": { 1629 | "version": "2.6.9", 1630 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1631 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1632 | "requires": { 1633 | "ms": "2.0.0" 1634 | } 1635 | }, 1636 | "ms": { 1637 | "version": "2.0.0", 1638 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1639 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1640 | } 1641 | } 1642 | }, 1643 | "string_decoder": { 1644 | "version": "1.3.0", 1645 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1646 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1647 | "requires": { 1648 | "safe-buffer": "~5.2.0" 1649 | } 1650 | }, 1651 | "string-width": { 1652 | "version": "4.2.3", 1653 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1654 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1655 | "requires": { 1656 | "emoji-regex": "^8.0.0", 1657 | "is-fullwidth-code-point": "^3.0.0", 1658 | "strip-ansi": "^6.0.1" 1659 | } 1660 | }, 1661 | "strip-ansi": { 1662 | "version": "6.0.1", 1663 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1664 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1665 | "requires": { 1666 | "ansi-regex": "^5.0.1" 1667 | } 1668 | }, 1669 | "strtok3": { 1670 | "version": "7.0.0", 1671 | "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", 1672 | "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", 1673 | "requires": { 1674 | "@tokenizer/token": "^0.3.0", 1675 | "peek-readable": "^5.0.0" 1676 | } 1677 | }, 1678 | "tar": { 1679 | "version": "6.1.11", 1680 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", 1681 | "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", 1682 | "requires": { 1683 | "chownr": "^2.0.0", 1684 | "fs-minipass": "^2.0.0", 1685 | "minipass": "^3.0.0", 1686 | "minizlib": "^2.1.1", 1687 | "mkdirp": "^1.0.3", 1688 | "yallist": "^4.0.0" 1689 | } 1690 | }, 1691 | "token-types": { 1692 | "version": "5.0.1", 1693 | "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", 1694 | "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", 1695 | "requires": { 1696 | "@tokenizer/token": "^0.3.0", 1697 | "ieee754": "^1.2.1" 1698 | } 1699 | }, 1700 | "tr46": { 1701 | "version": "0.0.3", 1702 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1703 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" 1704 | }, 1705 | "ts-mixer": { 1706 | "version": "6.0.1", 1707 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", 1708 | "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" 1709 | }, 1710 | "tslib": { 1711 | "version": "2.4.0", 1712 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 1713 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 1714 | }, 1715 | "undici": { 1716 | "version": "5.10.0", 1717 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.10.0.tgz", 1718 | "integrity": "sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==" 1719 | }, 1720 | "util-deprecate": { 1721 | "version": "1.0.2", 1722 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1723 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1724 | }, 1725 | "wav": { 1726 | "version": "1.0.2", 1727 | "resolved": "https://registry.npmjs.org/wav/-/wav-1.0.2.tgz", 1728 | "integrity": "sha512-viHtz3cDd/Tcr/HbNqzQCofKdF6kWUymH9LGDdskfWFoIy/HJ+RTihgjEcHfnsy1PO4e9B+y4HwgTwMrByquhg==", 1729 | "requires": { 1730 | "buffer-alloc": "^1.1.0", 1731 | "buffer-from": "^1.0.0", 1732 | "debug": "^2.2.0", 1733 | "readable-stream": "^1.1.14", 1734 | "stream-parser": "^0.3.1" 1735 | }, 1736 | "dependencies": { 1737 | "debug": { 1738 | "version": "2.6.9", 1739 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1740 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1741 | "requires": { 1742 | "ms": "2.0.0" 1743 | } 1744 | }, 1745 | "ms": { 1746 | "version": "2.0.0", 1747 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1748 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1749 | }, 1750 | "readable-stream": { 1751 | "version": "1.1.14", 1752 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1753 | "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", 1754 | "requires": { 1755 | "core-util-is": "~1.0.0", 1756 | "inherits": "~2.0.1", 1757 | "isarray": "0.0.1", 1758 | "string_decoder": "~0.10.x" 1759 | } 1760 | }, 1761 | "string_decoder": { 1762 | "version": "0.10.31", 1763 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1764 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" 1765 | } 1766 | } 1767 | }, 1768 | "webidl-conversions": { 1769 | "version": "3.0.1", 1770 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1771 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" 1772 | }, 1773 | "whatwg-url": { 1774 | "version": "5.0.0", 1775 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1776 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", 1777 | "requires": { 1778 | "tr46": "~0.0.3", 1779 | "webidl-conversions": "^3.0.0" 1780 | } 1781 | }, 1782 | "wide-align": { 1783 | "version": "1.1.5", 1784 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 1785 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 1786 | "requires": { 1787 | "string-width": "^1.0.2 || 2 || 3 || 4" 1788 | } 1789 | }, 1790 | "wrappy": { 1791 | "version": "1.0.2", 1792 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1793 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1794 | }, 1795 | "ws": { 1796 | "version": "8.8.1", 1797 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", 1798 | "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", 1799 | "requires": {} 1800 | }, 1801 | "yallist": { 1802 | "version": "4.0.0", 1803 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1804 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1805 | } 1806 | } 1807 | } 1808 | --------------------------------------------------------------------------------