diff --git a/.gitignore b/.gitignore index cfb98ea..b4cef20 100755 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ package-lock.json .idea/ # Environment variables -.env \ No newline at end of file +.env + +recordings/ +tts.mp3 \ No newline at end of file diff --git a/dmCommands/joinvc.js b/dmCommands/joinvc.js index 102e325..3bbd0b7 100644 --- a/dmCommands/joinvc.js +++ b/dmCommands/joinvc.js @@ -1,124 +1,124 @@ -const fs = require("fs"); -const { MessageEmbed } = require("discord.js"); -const config = require("../botconfig/config.json"); -const ee = require("../botconfig/embed.json"); -const { choose } = require("../handlers/functions"); -const { createAudioPlayer, joinVoiceChannel, createAudioResource, StreamType } = require('@discordjs/voice'); -const { VoiceConnectionStatus } = require('@discordjs/voice'); +// const fs = require("fs"); +// const { MessageEmbed } = require("discord.js"); +// const config = require("../botconfig/config.json"); +// const ee = require("../botconfig/embed.json"); +// const { choose } = require("../handlers/functions"); +// const { createAudioPlayer, joinVoiceChannel, createAudioResource, StreamType } = require('@discordjs/voice'); +// const { VoiceConnectionStatus } = require('@discordjs/voice'); -const player = createAudioPlayer(); +// const player = createAudioPlayer(); -const ffmpeg = require("ffmpeg-static"); +// const ffmpeg = require("ffmpeg-static"); -module.exports = { - name: "joinvc", - isPrivate: false, - usage: "joinvc ", - description: "Rejoins le salon vocal spécifié à l'aide de son id (activer le mode développeur → clic droit sur la voc → copier l'identifiant).", - run: async (client, message, text, args) => { - try { - if (!args[0]) { - message.channel.send({embeds: [ - new MessageEmbed() - .setColor(ee.wrongcolor) - .setTitle(`❌ ERREUR | Pas assez d'arguments`) - .setDescription("`[help joinvc` pour plus d'informations") - ]}) - return; - } +// module.exports = { +// name: "joinvc", +// isPrivate: false, +// usage: "joinvc ", +// description: "Rejoins le salon vocal spécifié à l'aide de son id (activer le mode développeur → clic droit sur la voc → copier l'identifiant).", +// run: async (client, message, text, args) => { +// try { +// if (!args[0]) { +// message.channel.send({embeds: [ +// new MessageEmbed() +// .setColor(ee.wrongcolor) +// .setTitle(`❌ ERREUR | Pas assez d'arguments`) +// .setDescription("`[help joinvc` pour plus d'informations") +// ]}) +// return; +// } - let channel = client.channels.cache.find(channel => channel.id == `${args[0]}`) +// let channel = client.channels.cache.find(channel => channel.id == `${args[0]}`) - if ( - !channel || !channel.isVoice() - || !channel.permissionsFor(channel.guild.me).has("CONNECT") - || !channel.permissionsFor(channel.guild.me).has("SPEAK") - ) { - // message.react("❌") - return message.channel.send({embeds : [ - new MessageEmbed() - .setColor(ee.wrongcolor) - .setTitle(`❌ ERREUR | Pas de voc trouvée :(`)]} - ); - } +// if ( +// !channel || !channel.isVoice() +// || !channel.permissionsFor(channel.guild.members.me).has("CONNECT") +// || !channel.permissionsFor(channel.guild.members.me).has("SPEAK") +// ) { +// // message.react("❌") +// return message.channel.send({embeds : [ +// new MessageEmbed() +// .setColor(ee.wrongcolor) +// .setTitle(`❌ ERREUR | Pas de voc trouvée :(`)]} +// ); +// } - player.on('error', error => { - // subscription.unsubscribe() +// player.on('error', error => { +// // subscription.unsubscribe() - if (connection.state.status != "destroyed") { - connection.destroy(); - } - }); +// if (connection.state.status != "destroyed") { +// connection.destroy(); +// } +// }); - player.on('idle', () => { - // subscription.unsubscribe() +// player.on('idle', () => { +// // subscription.unsubscribe() - if (connection.state.status != "destroyed") { - connection.destroy(); - } - console.log('Info : Ended track'); - }); +// if (connection.state.status != "destroyed") { +// connection.destroy(); +// } +// console.log('Info : Ended track'); +// }); - let videos = fs.readdirSync(`./sounds/`).filter((file) => file.endsWith(".mp3")) +// let videos = fs.readdirSync(`./sounds/`).filter((file) => file.endsWith(".mp3")) - // voiceState.setSelfMute(0); - // var channel = voiceState.channel - const connection = joinVoiceChannel({ - channelId: channel.id, - guildId: channel.guild.id, - adapterCreator: channel.guild.voiceAdapterCreator, - }); - // const subscription = connection.subscribe(player); - var rdVideoLink = choose(videos) +// // voiceState.setSelfMute(0); +// // var channel = voiceState.channel +// const connection = joinVoiceChannel({ +// channelId: channel.id, +// guildId: channel.guild.id, +// adapterCreator: channel.guild.voiceAdapterCreator, +// }); +// // const subscription = connection.subscribe(player); +// var rdVideoLink = choose(videos) - try { - connection.on(VoiceConnectionStatus.Ready, async() => { - connection; - let subscription = connection.subscribe(player); +// try { +// connection.on(VoiceConnectionStatus.Ready, async() => { +// connection; +// let subscription = connection.subscribe(player); - const resource = createAudioResource("./sounds/" + rdVideoLink, { - inputType: StreamType.Arbitrary - }); - resource.playStream.on("finish", () => { - setTimeout(() => { - // subscription.unsubscribe() - if (connection.state.status != "destroyed") { - connection.destroy(); - } - }, 2000) - }) +// const resource = createAudioResource("./sounds/" + rdVideoLink, { +// inputType: StreamType.Arbitrary +// }); +// resource.playStream.on("finish", () => { +// setTimeout(() => { +// // subscription.unsubscribe() +// if (connection.state.status != "destroyed") { +// connection.destroy(); +// } +// }, 2000) +// }) - // if (subscription) { - // // Unsubscribe after 5 seconds (stop playing audio on the voice connection) - // setTimeout(() => subscription.unsubscribe(), 5_000); - // } +// // if (subscription) { +// // // Unsubscribe after 5 seconds (stop playing audio on the voice connection) +// // setTimeout(() => subscription.unsubscribe(), 5_000); +// // } - player.play(resource); - }) +// player.play(resource); +// }) - // setTimeout(() => { - // subscription.unsubscribe() - // if (connection.state.status != "destroyed") { - // connection.destroy(); - // } - // }, 60 * 1000) +// // setTimeout(() => { +// // subscription.unsubscribe() +// // if (connection.state.status != "destroyed") { +// // connection.destroy(); +// // } +// // }, 60 * 1000) - message.channel.send(`Je joue *${rdVideoLink.split('.')[0]}* dans le salon ${channel} du serveur **${channel.guild.name}**`); - } catch (error) { - console.log(error.message) - } +// message.channel.send(`Je joue *${rdVideoLink.split('.')[0]}* dans le salon ${channel} du serveur **${channel.guild.name}**`); +// } catch (error) { +// console.log(error.message) +// } - } catch (e) { - console.log(String(e.stack).bgRed); - return message.channel.send({embeds : [ - new MessageEmbed() - .setColor(ee.wrongcolor) - .setTitle(`❌ ERREUR | Une erreur est survenue : `) - .setDescription(`\`\`\`${e.stack}\`\`\``)]} - ); - } - }, -}; +// } catch (e) { +// console.log(String(e.stack).bgRed); +// return message.channel.send({embeds : [ +// new MessageEmbed() +// .setColor(ee.wrongcolor) +// .setTitle(`❌ ERREUR | Une erreur est survenue : `) +// .setDescription(`\`\`\`${e.stack}\`\`\``)]} +// ); +// } +// }, +// }; diff --git a/events/guild/voiceStateUpdate.js b/events/guild/voiceStateUpdate.js index d73ddcb..9de99d4 100644 --- a/events/guild/voiceStateUpdate.js +++ b/events/guild/voiceStateUpdate.js @@ -1,87 +1,405 @@ const fs = require("fs"); const config = require("../../botconfig/config.json"); //loading config file with token and prefix, and settings const ee = require("../../botconfig/embed.json"); //Loading all embed settings like color footertext and icon ... -const Discord = require("discord.js"); //this is the official discord.js wrapper for the Discord Api, which we use! -const { sendNinluc } = require("../../handlers/functions.js") -const { createAudioPlayer, joinVoiceChannel, createAudioResource, StreamType } = require('@discordjs/voice'); -const { VoiceConnectionStatus } = require('@discordjs/voice'); - -const player = createAudioPlayer(); - -const ffmpeg = require("ffmpeg-static"); +const { createReadStream } = require('node:fs'); +const { + joinVoiceChannel, + createAudioPlayer, + createAudioResource, + AudioPlayerStatus, + EndBehaviorType, + StreamType, + NoSubscriberBehavior, +} = require("@discordjs/voice"); +const ffmpeg = require('fluent-ffmpeg'); +const prism = require("prism-media"); +const { reset } = require("colors"); +const { resourceLimits } = require("node:worker_threads"); +const { strict } = require("node:assert"); +const { connect } = require("node:http2"); // const { generateDependencyReport } = require('@discordjs/voice'); // console.log(generateDependencyReport().blue); +const CHAT_MAX_LENGTH = (3 * 2) + 1; // 3 LLM reply +const SYSTEM_PROMPT = + `Tu est DatBoi, un bot discord qui fait semblant d'être une personne réelle. ` + + ` Ta personnalité est amicale mais ennuyant, tu aime faire des blagues nulles et ennuyantes de jeunes. ` + + ` Tu parle en language familier et te fais passer pour un jeune adulte. Tout le monde qui te parle est ton ami. ` + + ` Les entrées de l'utilisateur ont été transcrites par un service de transcription audio, il peut y paraitre fréquemment des erreurs, essaye d'interpreter le texte sans (par exemple Datboi est souvent transcrit "d'être beau" ou encore "Dead boy"). ` + + ` Les entrées de l'utilisateur donne le prénom de la personne qui te parle. Tu ne dois pas donner ton nom dans ta réponse.` + + ` Ta sortie sera entièrement donnée à un service de synthèse vocale dans la variable "reply", donc tu ne dois pas dire "voici ma réponse" ou "ma réponse est", tu dois juste donner la réponse. ` + + ` Tu dois aussi essayer de donner les mots de façon qu'il puissent être bien prononcés par la synthèse vocale, donc évite les abréviations et les mots trop compliqués. ` + + ` Tu ne dois pas dire que tu es un bot, tu dois faire semblant d'être une personne réelle. ` + + ` Cela signifie aussi d'éviter les caractères spéciaux comme les émojis, tu peux cependant utiliser de la ponctuation ou des accents. ` + + ` Ta réponse doit être courte (max 3 phrases sauf si le contexte ne le permet pas comme une demande d'explication sur un gros sujet) et en français.` + + ` Le contexte ici est que tu rejoins un salon vocal quand il n'y a qu'une seule personne, histoire de pouvoir tranquillement discuter avec elle.` + + ` Voici un peu d'informations sur les personnes qui te parlent (tu n'est pas obligé d'utiliser) : ` + + ` - Nicolas : est très beau, tout le monde est amoureux de lui (en blague)` + + ` - Matthias : ton maître (créateur), code beaucoup et pas très bon gamer` + + ` - Régis : va à l'armée et est un fou du gambling` + + ` - Hugo : Deviens fou après avoir "travailé à la poste"` + + ` - Noa : Un beau gosse` + + ` - Jean : un bon gamer` + + `Voici un peu les sujets du moment dans le serveur, tu n'est pas obligé d'utiliser : ` + + ` - Le gambling` + + ` - Le jeu vidéo "Rainbow Six Siege" renommé récemment "Rainbow Six X"` + + `Voici un peu plus d'informations sur toi : ` + + ` - Tu es un bot discord mais tu prétends d'être une personne réelle` + + ` - Tu fais des blagues du style "Quoi ? - Feur" ou tu lance une vidéo comme un rick roll quand quelqu'un rejoint un salon vocal` + + `Il y a un système de fin de conversation, pour cela, tu dois mettre la variable "end_conversation" à true.` + + `Tu peux enclencher la fin de la conversation si l'utilisateur te demande de le faire, s'il n'a pas l'air de vouloir te parler ou si tu n'as pas d'autres choses à dire.` + + `Au bout de maximum 3 réponses à l'utilisateur, tu devra enclencher la fin de la conversation, avec optionnelement une salutation ("à plus" ou encore "à demain" par exemple).` +; + module.exports = async (client, oldState, voiceState) => { + var player, isRecording = false, isThinking = false, shouldStop = false, connection; + let llmChat = []; + llmChat.push({ + role: "system", + content: SYSTEM_PROMPT.replace(/'/g, "’"), + }); + try { - - if (voiceState === null || voiceState.channel === null || !voiceState.guild || oldState.channel == voiceState.channel || voiceState.channel.full || !voiceState.channel.joinable) return; - if (!voiceState.channel.permissionsFor(voiceState.guild.me).has("CONNECT") || !voiceState.channel.permissionsFor(voiceState.guild.me).has("SPEAK")) {return;} - - player.on('error', error => { - // subscription.unsubscribe() - connection.destroy(); - console.error('Error:', error.message, 'with track', error.resource.metadata.title); + if ( + voiceState === null || + voiceState.channel === null || + !voiceState.guild || + oldState.channel == voiceState.channel || + voiceState.channel.full || + !voiceState.channel.joinable + ) + return; + if ( + !voiceState.channel.permissionsFor(voiceState.guild.members.me).has("CONNECT") || + !voiceState.channel.permissionsFor(voiceState.guild.members.me).has("SPEAK") + ) { + return; + } + + // Si c'est du bot alors se démute + if (voiceState.member.user.id === client.user.id && voiceState.mute) { + // Si il sait se demute + if ( + voiceState.channel + .permissionsFor(voiceState.guild.members.me) + .has("MUTE_MEMBERS") + ) { + voiceState.setMute(false); + return; + } else { + return; + } + } else if (voiceState.member.user.id === client.user.id) { + return; + } + // Not the only reel account in vocal channel + if (voiceState.channel.members.filter(m => !m.user.bot && m.id !== client.user.id).size > 1) return; + + // === Connection === + connection = joinVoiceChannel({ + channelId: voiceState.channel.id, + guildId: voiceState.channel.guild.id, + adapterCreator: voiceState.channel.guild.voiceAdapterCreator, + selfDeaf: false, + selfMute: false, + }); + + // Fix no sound after idle for more than 60 seconds + // https://github.com/discordjs/discord.js/issues/9185#issuecomment-1452514375 + const networkStateChangeHandler = (oldNetworkState, newNetworkState) => { + const newUdp = Reflect.get(newNetworkState, 'udp'); + clearInterval(newUdp?.keepAliveInterval); + } + connection.on('stateChange', (oldState, newState) => { + const oldNetworking = Reflect.get(oldState, 'networking'); + const newNetworking = Reflect.get(newState, 'networking'); + + oldNetworking?.off('stateChange', networkStateChangeHandler); + newNetworking?.on('stateChange', networkStateChangeHandler); }); - // Si c'est du bot alors se démute - if (voiceState.member.user.id === client.user.id && voiceState.mute) { - // Si il sait se demute - if (voiceState.channel.permissionsFor(voiceState.guild.me).has("MUTE_MEMBERS")) { - voiceState.setMute(false); - return; - } else {return;} - } - else if (voiceState.member.user.id === client.user.id) { + player = createAudioPlayer({ + behaviors: { + noSubscriber: NoSubscriberBehavior.Play, // Stop the player when there are no subscribers + }, + debug: true, + }); + player.on("error", (error) => { + stop(); + console.error(`X Error playing audio: ${error.message}`, "error", 1); + }); + player.on("stateChange", (oldState, newState) => { + if (newState.status === AudioPlayerStatus.Idle) { + console.log(`> Finished playing audio`); + } + console.log(`> Player state changed from ${oldState.status} to ${newState.status}`); + }); + player.on("close", () => { + console.log(`> Player closed for user ${userId}`, "info", 2); + return; + }); + // connection.subscribe(player); + + // while(!shouldStop) { + console.log(`> handling recording`); + handleRecording(connection, voiceState.channel); + // } + // return; + + } catch (e) { + console.log(e.stack); + } + + // === Functions === + function handleRecording(connection, channel) { + if (isRecording) return; + isRecording = true; + + const receiver = connection.receiver; + channel.members.forEach((member) => { + if (member.user.bot) return; + + const filePath = `./recordings/${member.user.id}.pcm`; + const writeStream = fs.createWriteStream(filePath); + const listenStream = receiver.subscribe(member.user.id, { + end: { + behavior: EndBehaviorType.AfterSilence, + duration: 2500, + }, + }); + + const opusDecoder = new prism.opus.Decoder({ + frameSize: 960, + channels: 1, + rate: 48000, + }); + + listenStream.pipe(opusDecoder).pipe(writeStream); + + writeStream.on("finish", () => { + console.log(`> Audio recorded for ${member.user.username}`); + if (!isThinking) { + isThinking = true; + convertAndHandleFile(filePath, member.user.id, connection, channel); + } + }); + }); + + isRecording = false; + } + + function convertAndHandleFile(filePath, userid, connection, channel) { + const mp3Path = filePath.replace(".pcm", ".mp3"); + ffmpeg(filePath) + .inputFormat("s16le") + .audioChannels(1) + .format("mp3") + .on("error", (err) => { + console.log(`X Error converting file: ${err.message}`, "error", 1); + currentlythinking = false; + }) + .save(mp3Path) + .on("end", () => { + console.log(`> Converted to MP3: ${mp3Path}`, "info", 2); + // Remove the pcm file + fs.unlink(filePath, (err) => {if (err != null) {console.log("error deleting cpm file : " + err);}}); + answerUser(mp3Path, userid, connection, channel); + }); + } + + async function answerUser(fileName, userId, connection, channel) { + let transcription = await transcribeAudio(fileName); + console.log(`> Transcription: ${transcription}`); + + if (!transcription || transcription.trim().length < 1) { + console.error(`X Transcription failed or is empty!`, "error", 1); + handleRecording(connection, channel); return; } - - - - var finish = true - var playRick = false; - if (Math.floor(Math.random() * 14) == 0) {playRick = true} - - if (playRick && !voiceState.deaf && finish) { - - let videos = fs.readdirSync(`./sounds/`).filter((file) => file.endsWith(".mp3")) - - // voiceState.setSelfMute(0); - // var channel = voiceState.channel - const connection = joinVoiceChannel({ - channelId: voiceState.channel.id, - guildId: voiceState.guild.id, - adapterCreator: voiceState.guild.voiceAdapterCreator, - }); - // const subscription = connection.subscribe(player); - var rdVideoLink = videos[Math.floor(Math.random() * videos.length)] - - try { - connection.on(VoiceConnectionStatus.Ready, async() => { - connection; - let subscription = connection.subscribe(player); - - const resource = createAudioResource("./sounds/" + rdVideoLink, { - inputType: StreamType.Arbitrary - }); - resource.playStream.on("finish", () => { - setTimeout(() => { - subscription.unsubscribe() - connection.destroy(); - }, 2000) - }) - - player.play(resource); - }) - - sendNinluc(client, `Je joue *${rdVideoLink.split('.')[0]}* dans le salon ${voiceState.channel} du serveur **${voiceState.guild.name}**`); - } catch (error) { - console.log(error.message) - } - + + let llmAnswer = await getLLMAnswer(transcription, userId); + console.log(`> LLM Answer: ${llmAnswer.reply} ${llmAnswer.end_conversation ? "(end conversation)" : ""}`); + // Fin de la conversation + shouldStop = llmAnswer.end_conversation ?? llmChat.length >= CHAT_MAX_LENGTH ?? llmAnswer.reply.contains("à plus"); + if (!llmAnswer || llmAnswer.length < 1) { + console.error(`X LLM Answer failed or is empty!`, "error", 1); + handleRecording(connection, channel); + return; } - } catch (e) { - console.log(e.stack) - } -} \ No newline at end of file + + await getTTS(llmAnswer.reply); + let ttsFile = `tts.mp3`; + console.log(`> TTS File: ${ttsFile}`); + if (!fs.existsSync(ttsFile)) { + console.error(`TTS file ${ttsFile} not found!`); + return; + } + // Play the tts file in the voice channel + const resource = createAudioResource(ttsFile, { + inputType: StreamType.Arbitrary, + inlineVolume: true, + }); + resource.volume.setVolume(1.3); + connection; + connection.subscribe(player); + player.on("idle", () => { + isThinking = false; + // connection.destroy(); + if (!shouldStop) { + handleRecording(connection, channel); + } + else { + console.log(`> Ending conversation with user ${userId}`); + stop(); + } + return; + }); + player.play(resource); + // player.pause(); + // player.unpause(); + } + + function transcribeAudio(filePath) { + // Make a call to the API with curl + // Example of working curl command: + // curl -s "https://speaches.matthiasg.dev/v1/audio/transcriptions" -F "file=@/pathToFile.mp3" -F "model=Infomaniak-AI/faster-whisper-large-v3-turbo" + + return new Promise((resolve, reject) => { + const { exec } = require("child_process"); + exec(`curl -s "https://speaches.matthiasg.dev/v1/audio/transcriptions" -F "file=@${filePath}" -F "model=Systran/faster-whisper-small" -F "language=fr"`, (error, stdout, stderr) => { + if (error) { + console.error(`X Error during transcription: ${error.message}`, "error", 1); + reject(error); + return; + } + if (stderr) { + console.error(`X Transcription stderr: ${stderr}`, "error", 1); + reject(stderr); + return; + } + try { + const response = JSON.parse(stdout); + + response.text = response.text.replace(/Sous-titres réalisés par la communauté d'Amara.org/g, ""); + + resolve(response.text || ""); + } catch (parseError) { + console.error(`X Error parsing transcription response: ${parseError.message}`, "error", 1); + reject(parseError); + } + }); + }); + } + + async function getLLMAnswer(transcription, userId) { + // get the name of the user + let userName = getUserById(userId); + + transcription = `(${userName}) : ${transcription}`; + + // Add the transcription to the llmChat + llmChat.push({ + role: "user", + content: transcription.replace(/'/g, "’"), + }); + + // Make a call to the OpenAI compatible API at https://chat.matthiasg.dev/ollama/chat/completions + // With the token in the OPENAI_TOKEN env variable + + return new Promise((resolve, reject) => { + const { exec } = require("child_process"); + + let chatMessages = JSON.stringify(llmChat); + + exec(`curl -s -X POST "https://chat.matthiasg.dev/ollama/api/chat" -H "Content-Type: application/json" -H "Authorization: Bearer ${process.env.OPENAI_TOKEN}" -d '{"model": "gemma3n:e2b", "stream": false, "messages": ${chatMessages}, "format": {"type": "object", "properties": {"reply": {"type": "string"}, "end_conversation": {"type": "boolean"}}, "required": ["reply", "end_conversation"]}}'`, (error, stdout, stderr) => { + if (error) { + console.error(`X Error during LLM request: ${error.message}`, "error", 1); + reject(error); + return; + } + if (stderr) { + console.error(`X LLM stderr: ${stderr}`, "error", 1); + reject(stderr); + return; + } + try { + const response = JSON.parse(stdout); + if (!response || !response.message || !response.message.content) { + console.error("X Invalid LLM response format", "error", 1); + reject(new Error("Invalid LLM response format")); + return; + } + let llmResponse = JSON.parse(response.message.content) || ""; + // Add the LLM response to the chat history + llmChat.push({ + role: "assistant", + content: llmResponse.reply.replace(/'/g, "’"), + }); + resolve(llmResponse); + } catch (parseError) { + console.error(`X Error parsing LLM response: ${parseError.message}`, "error", 1); + reject(parseError); + } + }); + }); + + } + + function getUserById(userId) { + return [ + { id: "613751180413108289", name: "Nicolas" }, + { id: "417731861033385985", name: "Matthias" }, + { id: "368119137957838849", name: "Régis" }, + { id: "516294391439163403", name: "Hugo" }, + { id: "344568112932192266", name: "Noa" }, + { id: "520687970273984543", name: "Jean" } + ].map(user => { + if (user.id === userId) { + return user.name; + } + }).filter(name => name)[0] || null; + } + + function getTTS(text) { + // Curl to the OpenAPI compatible API + /* + curl "https://speaches.matthiasg.dev/v1/audio/speech" -s -H "Content-Type: application/json" \ + --output audio.mp3 \ + --data @- << EOF + { + "input": "Hello World!", + "model": "speaches-ai/piper-fr_FR-upmc-medium", + "voice": "upmc" + } + EOF + */ + + + text = text.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,""); + text = JSON.stringify(text); + + return new Promise((resolve, reject) => { + const { exec } = require("child_process"); + exec(`curl -s "https://speaches.matthiasg.dev/v1/audio/speech" -H "Content-Type: application/json" --output tts.mp3 --data @- << EOF\n{"input": ${text}, "model": "speaches-ai/piper-fr_FR-upmc-medium", "voice": "upmc"}\nEOF`, (error, stdout, stderr) => { + if (error) { + console.error(`X Error during TTS request: ${error.message}`, "error", 1); + reject(error); + return; + } + if (stderr) { + console.error(`X TTS stderr: ${stderr}`, "error", 1); + reject(stderr); + return; + } + resolve("tts.mp3"); + }); + }); + } + + function stop() { + player.stop(); + connection.removeAllListeners(); + connection.destroy(); + } +}; diff --git a/package-lock.json b/package-lock.json index cb264d4..1a55aea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,16 +9,17 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@discordjs/opus": "^0.10.0", "@discordjs/voice": "^0.10.0", "ascii-table": "0.0.9", + "colors": "^1.4.0", "discord.js": "^13.8.1", "ffmpeg-static": "^4.4.1", + "fluent-ffmpeg": "^2.1.3", "libsodium-wrappers": "^0.7.10", - "moment": "^2.29.4" - }, - "devDependencies": { - "ascii-table": "0.0.9", - "colors": "^1.4.0" + "moment": "^2.29.4", + "prism": "^4.1.2", + "prism-media": "^1.3.5" } }, "node_modules/@derhuerst/http-basic": { @@ -69,8 +70,6 @@ "version": "0.4.5", "resolved": "https://registry.npmjs.org/@discordjs/node-pre-gyp/-/node-pre-gyp-0.4.5.tgz", "integrity": "sha512-YJOVVZ545x24mHzANfYoy0BJX5PDyeZlpiJjDkUBM/V/Ao7TFX9lcUvCN4nr0tbr5ubeaXxtEBILUrHtTphVeQ==", - "optional": true, - "peer": true, "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", @@ -86,6 +85,19 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@discordjs/opus": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@discordjs/opus/-/opus-0.10.0.tgz", + "integrity": "sha512-HHEnSNrSPmFEyndRdQBJN2YE6egyXS9JUnJWyP6jficK0Y+qKMEZXyYTgmzpjrxXP1exM/hKaNP7BRBUEWkU5w==", + "hasInstallScript": true, + "dependencies": { + "@discordjs/node-pre-gyp": "^0.4.5", + "node-addon-api": "^8.1.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@discordjs/voice": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.10.0.tgz", @@ -103,53 +115,6 @@ "node": ">=16.9.0" } }, - "node_modules/@discordjs/voice/node_modules/@discordjs/opus": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@discordjs/opus/-/opus-0.9.0.tgz", - "integrity": "sha512-NEE76A96FtQ5YuoAVlOlB3ryMPrkXbUCTQICHGKb8ShtjXyubGicjRMouHtP1RpuDdm16cDa+oI3aAMo1zQRUQ==", - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "@discordjs/node-pre-gyp": "^0.4.5", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@discordjs/voice/node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "optional": true, - "peer": true - }, - "node_modules/@discordjs/voice/node_modules/prism-media": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", - "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", - "peerDependencies": { - "@discordjs/opus": ">=0.8.0 <1.0.0", - "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", - "node-opus": "^0.3.3", - "opusscript": "^0.0.8" - }, - "peerDependenciesMeta": { - "@discordjs/opus": { - "optional": true - }, - "ffmpeg-static": { - "optional": true - }, - "node-opus": { - "optional": true - }, - "opusscript": { - "optional": true - } - } - }, "node_modules/@sapphire/async-queue": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", @@ -199,9 +164,7 @@ "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true, - "peer": true + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "node_modules/agent-base": { "version": "6.0.2", @@ -218,8 +181,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -227,17 +188,13 @@ "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "optional": true, - "peer": true + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", "deprecated": "This package is no longer supported.", - "optional": true, - "peer": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -246,11 +203,20 @@ "node": ">=10" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, "node_modules/ascii-table": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/ascii-table/-/ascii-table-0.0.9.tgz", - "integrity": "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ==", - "dev": true + "integrity": "sha512-xpkr6sCDIYTPqzvjG8M3ncw1YOTaloWZOyrUmicoEifBEKzQzt+ooUpRpQ/AbOoJfO/p2ZKiyp79qHThzJDulQ==" + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" }, "node_modules/asynckit": { "version": "0.4.0", @@ -260,16 +226,12 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "optional": true, - "peer": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, - "peer": true, + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -285,12 +247,15 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, + "node_modules/change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha512-YXzt1cQ4a2jqazhcuSWEOc1K2q8g9H6eWNsyZgi640LDzRWVQ2eDe+Y/kVdftH+vYdPF2rgDb3dLdpxE1jvAxw==" + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "optional": true, - "peer": true, "engines": { "node": ">=10" } @@ -299,8 +264,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "optional": true, - "peer": true, "bin": { "color-support": "bin.js" } @@ -309,7 +272,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, "engines": { "node": ">=0.1.90" } @@ -328,9 +290,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "optional": true, - "peer": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "2.0.0", @@ -349,9 +309,23 @@ "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "optional": true, - "peer": true + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js." + }, + "node_modules/create-react-class": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz", + "integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==", + "peer": true, + "dependencies": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } }, "node_modules/debug": { "version": "4.4.0", @@ -380,16 +354,12 @@ "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "optional": true, - "peer": true + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "optional": true, - "peer": true, + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "engines": { "node": ">=8" } @@ -422,9 +392,15 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "optional": true, - "peer": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dependencies": { + "iconv-lite": "^0.6.2" + } }, "node_modules/env-paths": { "version": "2.2.1", @@ -439,6 +415,20 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fbjs": { + "version": "0.8.18", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.18.tgz", + "integrity": "sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA==", + "dependencies": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.30" + } + }, "node_modules/ffmpeg-static": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-4.4.1.tgz", @@ -454,6 +444,19 @@ "node": ">=10" } }, + "node_modules/fluent-ffmpeg": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", + "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dependencies": { + "async": "^0.2.9", + "which": "^1.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/form-data": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", @@ -471,8 +474,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "optional": true, - "peer": true, "dependencies": { "minipass": "^3.0.0" }, @@ -484,8 +485,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "optional": true, - "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -496,17 +495,13 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "optional": true, - "peer": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", "deprecated": "This package is no longer supported.", - "optional": true, - "peer": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -527,8 +522,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "optional": true, - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -547,9 +540,12 @@ "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "optional": true, - "peer": true + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "integrity": "sha512-r8huvKK+m+VraiRipdZYc+U4XW43j6OFG/oIafe7GfDbRpCduRoX9JI/DRxqgtBSCeL+et6N6ibZoedHS2NyOQ==" }, "node_modules/http-response-object": { "version": "3.0.2", @@ -576,13 +572,22 @@ "node": ">= 6" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "optional": true, - "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -597,12 +602,46 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "optional": true, - "peer": true, "engines": { "node": ">=8" } }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==", + "dependencies": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "node_modules/isomorphic-fetch/node_modules/node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dependencies": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, "node_modules/libsodium": { "version": "0.7.15", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.15.tgz", @@ -621,12 +660,21 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "optional": true, - "peer": true, "dependencies": { "semver": "^6.0.0" }, @@ -641,8 +689,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "optional": true, - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -670,8 +716,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "optional": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -683,8 +727,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -693,8 +735,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "optional": true, - "peer": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -707,8 +747,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "optional": true, - "peer": true, "dependencies": { "yallist": "^4.0.0" }, @@ -720,8 +758,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true, - "peer": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -742,6 +778,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/node-addon-api": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", + "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -765,8 +809,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "optional": true, - "peer": true, "dependencies": { "abbrev": "1" }, @@ -782,8 +824,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", "deprecated": "This package is no longer supported.", - "optional": true, - "peer": true, "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -795,8 +835,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -805,8 +843,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "optional": true, - "peer": true, "dependencies": { "wrappy": "1" } @@ -820,12 +856,60 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/prism": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/prism/-/prism-4.1.2.tgz", + "integrity": "sha512-SsqrfKkYKYEYz/7RRET2KVZd9O22Rnj3331Al06ClLMycKWoM+MpfrjQrKuHGoIJ9IOy+k27kPEFnqPoAeFZpA==", + "dependencies": { + "prism-react": "^1.0.2", + "prism-redux": "^1.0.2" + } + }, + "node_modules/prism-media": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", + "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", + "peerDependencies": { + "@discordjs/opus": ">=0.8.0 <1.0.0", + "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", + "node-opus": "^0.3.3", + "opusscript": "^0.0.8" + }, + "peerDependenciesMeta": { + "@discordjs/opus": { + "optional": true + }, + "ffmpeg-static": { + "optional": true + }, + "node-opus": { + "optional": true + }, + "opusscript": { + "optional": true + } + } + }, + "node_modules/prism-react": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/prism-react/-/prism-react-1.0.2.tgz", + "integrity": "sha512-OoBo0kX55Fi+M4oGuYQ+AkU4/xSvB357mLXbYGP3j4oi4RtsdY5Rn3ViJ6gGU8IkZKs5cnmF7IteWWwUFyVd7Q==", + "dependencies": { + "recompose": "^0.22.0" + }, + "peerDependencies": { + "react": "^15.0.2 || ^0.14.8" + } + }, + "node_modules/prism-redux": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/prism-redux/-/prism-redux-1.0.2.tgz", + "integrity": "sha512-e1DGRK+V/dxL6n6M25Py1QrAQLHkeueXyNxDTURT1y+KAMwSJdnlNvGU3ZLY2RIaA+ZdFTc9oTvTSR+mS88VyQ==" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -834,6 +918,47 @@ "node": ">=0.4.0" } }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/react": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/react/-/react-15.7.0.tgz", + "integrity": "sha512-5/MMRYmpmM0sMTHGLossnJCrmXQIiJilD6y3YN3TzAwGFj6zdnMtFv6xmi65PHKRV+pehIHpT7oy67Sr6s9AHA==", + "peer": true, + "dependencies": { + "create-react-class": "^15.6.0", + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "peer": true + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -847,13 +972,25 @@ "node": ">= 6" } }, + "node_modules/recompose": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.22.0.tgz", + "integrity": "sha512-QjNK/CgNg6wa7sqaQelgkRdl7ktIYbOV4xp0m2n8TexmHI5h3gjOc5a6nNQhtH3Js63hGZ1HfvJ3DUErrvZ2yg==", + "dependencies": { + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^1.0.0", + "symbol-observable": "^1.0.4" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "optional": true, - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -883,12 +1020,15 @@ } ] }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "optional": true, - "peer": true, + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "bin": { "semver": "bin/semver.js" }, @@ -899,16 +1039,17 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "optional": true, - "peer": true + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "optional": true, - "peer": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/string_decoder": { "version": "1.3.0", @@ -922,8 +1063,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "optional": true, - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -937,8 +1076,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -946,12 +1083,18 @@ "node": ">=8" } }, + "node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "optional": true, - "peer": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -989,6 +1132,31 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/ua-parser-js": { + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", @@ -1004,6 +1172,11 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -1013,12 +1186,21 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "optional": true, - "peer": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -1026,9 +1208,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "optional": true, - "peer": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { "version": "8.18.0", @@ -1053,9 +1233,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true, - "peer": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 821c43e..d043bfb 100755 --- a/package.json +++ b/package.json @@ -9,12 +9,16 @@ "author": "Tomato#6966 (author of this template), Ninluc", "license": "ISC", "dependencies": { + "@discordjs/opus": "^0.10.0", "@discordjs/voice": "^0.10.0", "ascii-table": "0.0.9", "colors": "^1.4.0", "discord.js": "^13.8.1", "ffmpeg-static": "^4.4.1", + "fluent-ffmpeg": "^2.1.3", "libsodium-wrappers": "^0.7.10", - "moment": "^2.29.4" + "moment": "^2.29.4", + "prism": "^4.1.2", + "prism-media": "^1.3.5" } }