├── src ├── extensions │ ├── index.js │ └── Guild.js ├── bot.js ├── commands │ ├── source.js │ ├── ping.js │ ├── help.js │ ├── nowplaying.js │ ├── pause.js │ ├── resume.js │ ├── stop.js │ ├── clearqueue.js │ ├── shuffle.js │ ├── previous.js │ ├── queue.js │ ├── eval.js │ ├── skip.js │ ├── removedupes.js │ ├── stats.js │ ├── remove.js │ ├── volume.js │ ├── move.js │ ├── loop.js │ ├── seek.js │ ├── lyrics.js │ ├── play.js │ └── search.js ├── listeners │ ├── ready.js │ ├── voiceStateUpdate.js │ └── message.js ├── structures │ ├── Rest.js │ ├── MusicClient.js │ └── MusicHandler.js └── util.js ├── index.js ├── .env.example ├── .eslintrc ├── .github └── dependabot.yml ├── README.md ├── package.json ├── LICENSE ├── .gitignore └── yarn.lock /src/extensions/index.js: -------------------------------------------------------------------------------- 1 | require("./Guild"); 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require("dotenv/config"); 2 | require("./src/bot"); -------------------------------------------------------------------------------- /src/bot.js: -------------------------------------------------------------------------------- 1 | const MusicClient = require("./structures/MusicClient"); 2 | 3 | const client = new MusicClient({ 4 | disableMentions: "everyone" 5 | }); 6 | 7 | client.build(); -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TOKEN=000000000000000000 2 | PREFIX=, 3 | OWNER_ID=190190190190190190 4 | LAVA_HOST=localhost 5 | LAVA_PORT=2333 6 | LAVA_PASS=youshallnotpass 7 | ENABLE_SPOTIFY=false 8 | # you will need these if you enabled spotify 9 | SPOTIFY_ID=00000A0000 10 | SPOTIFY_SECRET=AAAAAA0AAAA 11 | SPOTIFY_PLAYLIST_PAGE_LIMIT=2 12 | -------------------------------------------------------------------------------- /src/commands/source.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "source", 5 | aliases: ["src"], 6 | exec: (msg) => { 7 | msg.channel.send(util.embed().setDescription("✅ | [Here](https://github.com/Allvaa/lavalink-musicbot) is the open source repository this bot uses.")); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": 10 10 | }, 11 | "rules": { 12 | "indent": ["warn", 4], 13 | "semi": "error", 14 | "quotes": ["warn", "double"] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/extensions/Guild.js: -------------------------------------------------------------------------------- 1 | const { Structures } = require("discord.js"); 2 | const MusicHandler = require("../structures/MusicHandler"); 3 | 4 | const Guild = Structures.get("Guild"); 5 | 6 | class MusicGuild extends Guild { 7 | constructor(client, data) { 8 | super(client, data); 9 | this.music = new MusicHandler(this); 10 | } 11 | } 12 | 13 | Structures.extend("Guild", () => MusicGuild); 14 | -------------------------------------------------------------------------------- /src/commands/ping.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | module.exports = { 3 | name: "ping", 4 | exec: (msg) => { 5 | const responseTime = Date.now() - msg.createdTimestamp; 6 | const apiLatency = msg.client.ws.ping; 7 | msg.channel.send( 8 | util.embed() 9 | .setDescription(`**Response Time**: ${responseTime}ms\n**API Latency**: ${apiLatency}ms`) 10 | ); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "22:00" 8 | open-pull-requests-limit: 10 9 | versioning-strategy: increase 10 | ignore: 11 | - dependency-name: discord.js 12 | versions: 13 | - 12.5.2 14 | - dependency-name: eslint 15 | versions: 16 | - 7.18.0 17 | - 7.20.0 18 | - 7.21.0 19 | - dependency-name: lava-spotify 20 | versions: 21 | - 2.1.0 22 | -------------------------------------------------------------------------------- /src/listeners/ready.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "ready", 3 | exec: async (client) => { 4 | console.log(`Logged in as ${client.user.tag}`); 5 | 6 | if (client.spotify) await client.spotify.requestToken(); 7 | 8 | const nodes = [...client.manager.nodes.values()]; 9 | for (const node of nodes) { 10 | try { 11 | await node.connect(); 12 | } catch (e) { 13 | client.manager.emit("error", e, node); 14 | } 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lavalink-MusicBot 2 | Music bot that uses Lavalink for its audio player. 3 | 4 | ## Prerequisites 5 | - [Lavalink server](https://github.com/freyacodes/Lavalink#server-configuration). 6 | - Node.js v12 or above. 7 | 8 | ## Usage 9 | - Rename `.env.example` to `.env` and replace those values with yours. 10 | - Install dependencies `npm i --production` or with yarn `yarn --prod`. 11 | - Running the bot `npm start`. 12 | 13 | ## Author 14 | - GitHub [@Allvaa](https://github.com/Allvaa) 15 | - Discord [allvzx#2840](https://discord.com/users/740075062190669884) 16 | -------------------------------------------------------------------------------- /src/structures/Rest.js: -------------------------------------------------------------------------------- 1 | module.exports = class Rest extends require("lavacord").Rest { 2 | /** 3 | * @param {import("lavacord").LavalinkNode} node 4 | * @param {string} query 5 | * @param {import("lavasfy").LavasfyClient} lsClient 6 | * @returns {Promise} 7 | */ 8 | static async load(node, query, lsClient) { 9 | const spotify = lsClient ? lsClient.nodes.get(node.id) : undefined; 10 | return lsClient && lsClient.isValidURL(query) ? await spotify.load(query) : await super.load(node, query); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/listeners/voiceStateUpdate.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "voiceStateUpdate", 3 | exec: async (client, oldState, newState) => { 4 | // if the member was not cached 5 | if (!newState.member) await newState.guild.members.fetch(newState.id); 6 | 7 | const { guild: { music } } = newState; 8 | if (newState.member.user.equals(client.user) && !newState.channel && music.player) { 9 | if (music.player.playing) await music.stop(); 10 | if (music.player) await client.manager.leave(music.guild.id); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/commands/help.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | const unlisted = ["eval", "source"]; 4 | 5 | module.exports = { 6 | name: "help", 7 | aliases: ["commands", "?"], 8 | exec: (msg) => { 9 | const commands = msg.client.commands 10 | .filter(c => !unlisted.includes(c.name)) 11 | .map(c => `\`${c.name}\``); 12 | 13 | const embed = util.embed() 14 | .setAuthor("Command List", msg.client.user.displayAvatarURL()) 15 | .setDescription(commands.join(", ")) 16 | .setTimestamp(); 17 | 18 | msg.channel.send(embed); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/commands/nowplaying.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "nowplaying", 5 | aliases: ["np", "nowplay"], 6 | exec: (msg) => { 7 | const { music } = msg.guild; 8 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 9 | const progress = util.progress(music.player.state.position, music.current.info.length); 10 | msg.channel.send(util.embed().setDescription(`🎶 | Now playing ${music.current.info.isStream ? "[**◉ LIVE**]" : ""}\n**${music.current.info.title}**.${music.current.info.isStream ? "" : `\n\n${util.millisToDuration(music.player.state.position)} ${progress.bar} ${util.millisToDuration(music.current.info.length)}`}`)); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lavalink-musicbot", 3 | "version": "1.0.0", 4 | "description": "Music bot that uses Lavalink for its audio player.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node ." 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Allvaa/lavalink-musicbot.git" 12 | }, 13 | "author": "Allvaa", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/Allvaa/lavalink-musicbot/issues" 17 | }, 18 | "homepage": "https://github.com/Allvaa/lavalink-musicbot#readme", 19 | "dependencies": { 20 | "@lavacord/discord.js": "0.0.7", 21 | "discord.js": "^12.5.3", 22 | "dotenv": "^10.0.0", 23 | "lavasfy": "2.2.1", 24 | "pretty-ms": "^7.0.1" 25 | }, 26 | "devDependencies": { 27 | "eslint": "^7.27.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/listeners/message.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "message", 3 | exec: async (client, msg) => { 4 | if (!msg.guild) return; 5 | if (msg.author.bot) return; 6 | 7 | const prefix = msg.content.toLowerCase().startsWith(client.prefix) ? client.prefix : `<@!${client.user.id}>`; 8 | if (!msg.content.toLowerCase().startsWith(prefix)) return; 9 | 10 | const args = msg.content.slice(prefix.length).trim().split(/ +/g); 11 | const commandName = args.shift().toLowerCase(); 12 | const command = client.commands.get(commandName) || client.commands.find(c => c.aliases && c.aliases.includes(commandName)); 13 | if (command) { 14 | try { 15 | await command.exec(msg, args); 16 | } catch (e) { 17 | console.error(e); 18 | } 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/commands/pause.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "pause", 5 | exec: async (msg) => { 6 | const { music } = msg.guild; 7 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 8 | if (!msg.member.voice.channel) 9 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 10 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 11 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 12 | 13 | try { 14 | await music.pause(); 15 | msg.react("⏸️").catch(e => e); 16 | } catch (e) { 17 | msg.channel.send(`An error occured: ${e.message}.`); 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/commands/resume.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "resume", 5 | exec: async (msg) => { 6 | const { music } = msg.guild; 7 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌| Currently not playing anything.")); 8 | if (!msg.member.voice.channel) 9 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 10 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 11 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 12 | 13 | try { 14 | await music.resume(); 15 | msg.react("▶️").catch(e => e); 16 | } catch (e) { 17 | msg.channel.send(`An error occured: ${e.message}.`); 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/commands/stop.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "stop", 5 | aliases: ["leave", "dc"], 6 | exec: async (msg) => { 7 | const { music } = msg.guild; 8 | if (!music.player) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 9 | if (!msg.member.voice.channel) 10 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 11 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 12 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 13 | 14 | try { 15 | await music.stop(); 16 | msg.react("⏹️").catch(e => e); 17 | } catch (e) { 18 | msg.channel.send(`An error occured: ${e.message}.`); 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/commands/clearqueue.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "clearqueue", 5 | description:"Clean up the queue.", 6 | aliases: ["clr", "clear"], 7 | exec: (msg) => { 8 | const { music } = msg.guild; 9 | if (!music.player) return msg.channel.send(util.embed().setDescription("❌| Currently not playing anything.")); 10 | if (!music.queue.length) return msg.channel.send(util.embed().setDescription("❌ | Queue is empty.")); 11 | 12 | if (!msg.member.voice.channel) 13 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 14 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 15 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 16 | 17 | music.queue.splice(0, 1); 18 | msg.channel.send(util.embed().setDescription("✅ | Cleared the queue.")).catch(e => e); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/commands/shuffle.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "shuffle", 5 | aliases: ["sf"], 6 | exec: async (msg) => { 7 | const { music } = msg.guild; 8 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 9 | if (!music.queue.length) return msg.channel.send(util.embed().setDescription("❌ | Queue is empty.")); 10 | if (!msg.member.voice.channel) 11 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 12 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 13 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 14 | 15 | music.queue = util.shuffleArray(music.queue); 16 | 17 | msg.channel.send(util.embed().setDescription(`✅ | Queue shuffled! Type \`${msg.client.prefix}queue\` to see changes.`)); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/commands/previous.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "previous", 5 | aliases: ["prev"], 6 | exec: async (msg) => { 7 | const { music } = msg.guild; 8 | if (!music.player) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 9 | if (!music.previous) return msg.channel.send(util.embed().setDescription("❌ | No previous track.")); 10 | 11 | if (!msg.member.voice.channel) 12 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 13 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 14 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 15 | 16 | try { 17 | music.queue.unshift(music.previous); 18 | await music.skip(); 19 | msg.react("⏮️").catch(e => e); 20 | } catch (e) { 21 | msg.channel.send(`An error occured: ${e.message}.`); 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 - 2021 Allvaa 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 | -------------------------------------------------------------------------------- /src/commands/queue.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "queue", 5 | aliases: ["q"], 6 | exec: async (msg) => { 7 | const { music } = msg.guild; 8 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 9 | if (!music.queue.length) return msg.channel.send(util.embed().setDescription("❌ | Queue is empty.")); 10 | 11 | const queue = music.queue.map((t, i) => `\`${++i}.\` **${t.info.title}** ${t.requester}`); 12 | const chunked = util.chunk(queue, 10).map(x => x.join("\n")); 13 | 14 | const embed = util.embed() 15 | .setAuthor(`${msg.guild.name} Music Queue`, msg.guild.iconURL({ dynamic: true })) 16 | .setDescription(chunked[0]) 17 | .setFooter(`Page 1 of ${chunked.length}.`); 18 | 19 | try { 20 | const queueMsg = await msg.channel.send(embed); 21 | if (chunked.length > 1) await util.pagination(queueMsg, msg.author, chunked); 22 | } catch (e) { 23 | msg.channel.send(`An error occured: ${e.message}.`); 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/commands/eval.js: -------------------------------------------------------------------------------- 1 | const { inspect } = require("util"); 2 | const { isValidURL } = require("../util"); 3 | 4 | module.exports = { 5 | name: "eval", 6 | aliases: ["e"], 7 | exec: async (msg, args) => { 8 | if (msg.author.id !== process.env.OWNER_ID) return; 9 | const isAsync = args.includes("--async"); 10 | const isSilent = args.includes("--silent"); 11 | const code = args.filter(e => !/^--(async|silent)$/.test(e)).join(" "); 12 | try { 13 | let result = eval(isAsync ? `(async()=>{${code}})()` : code); 14 | let isResultPromise = false; 15 | if (result instanceof Promise) { 16 | result = await result; 17 | isResultPromise = true; 18 | } 19 | if (isSilent) return; 20 | let inspectedResult = inspect(result, { depth: 0 }); 21 | if (isResultPromise) inspectedResult = `Promise<${inspectedResult}>`; 22 | await msg.channel.send(`${isValidURL(inspectedResult) ? inspectedResult : `\`\`\`js\n${inspectedResult}\`\`\``}`); 23 | } catch (e) { 24 | msg.channel.send(`\`\`\`js\n${e}\`\`\``); 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/commands/skip.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "skip", 5 | aliases: ["skipto"], 6 | exec: async (msg, args) => { 7 | const { music } = msg.guild; 8 | const skipTo = args[0] ? parseInt(args[0], 10) : null; 9 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 10 | 11 | if (!msg.member.voice.channel) 12 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 13 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 14 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 15 | 16 | if (skipTo !== null && (isNaN(skipTo) || skipTo < 1 || skipTo > music.queue.length)) 17 | return msg.channel.send(util.embed().setDescription("❌ | Invalid number to skip.")); 18 | 19 | try { 20 | await music.skip(skipTo); 21 | msg.react("⏭️").catch(e => e); 22 | } catch (e) { 23 | msg.channel.send(`An error occured: ${e.message}.`); 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/commands/removedupes.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "removedupes", 5 | aliases: ["rdp"], 6 | description: "Removes duplicated tracks from the queue.", 7 | exec: (msg) => { 8 | const { music } = msg.guild; 9 | const seen = {}; 10 | 11 | if (!music.player) return msg.channel.send(util.embed().setDescription("❌| Currently not playing anything.")); 12 | if (!music.queue.length) return msg.channel.send(util.embed().setDescription("❌ | Queue is empty.")); 13 | 14 | if (!msg.member.voice.channel) 15 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 16 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 17 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 18 | 19 | for (const song of music.queue) { 20 | if (seen[song.info.indentifier] === undefined) seen[song.info.indentifier] = song; 21 | } 22 | music.queue = Object.values(seen); 23 | 24 | msg.channel.send(util.embed().setDescription("✅ | Removed all Dupes")).catch(e => e); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/commands/stats.js: -------------------------------------------------------------------------------- 1 | const prettyMs = require("pretty-ms"); 2 | const util = require("../util"); 3 | 4 | module.exports = { 5 | name: "stats", 6 | exec: (msg) => { 7 | /** @type {import("lavacord").LavalinkNode[]} */ 8 | const nodes = [...msg.client.manager.nodes.values()]; 9 | 10 | msg.channel.send(util.embed() 11 | .setAuthor("Lavalink Node(s) Stats", msg.client.user.displayAvatarURL()) 12 | .setTitle("Source Code") 13 | .setURL("https://github.com/Allvaa/lavalink-musicbot") 14 | .setDescription( 15 | nodes.map(node => { 16 | const cpuLoad = (node.stats.cpu.lavalinkLoad * 100).toFixed(2); 17 | const memUsage = (node.stats.memory.used / 1024 / 1024).toFixed(2); 18 | const uptime = prettyMs(node.stats.uptime, { verbose: true, secondsDecimalDigits: 0 }); 19 | 20 | return `\`\`\`asciidoc 21 | ID :: ${node.id} 22 | Status :: ${node.connected ? "Connected" : "Disconnected"} 23 | ${node.connected ? ` 24 | CPU Load :: ${cpuLoad}% 25 | Mem Usage :: ${memUsage} MB 26 | Uptime :: ${uptime} 27 | Players :: ${node.stats.playingPlayers} of ${node.stats.players} playing` : ""}\`\`\``; 28 | }) 29 | ) 30 | .setTimestamp() 31 | ); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/commands/remove.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "remove", 5 | aliases: ["rm"], 6 | exec: async (msg, args) => { 7 | const { music } = msg.guild; 8 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌| Currently not playing anything.")); 9 | if (!music.queue.length) return msg.channel.send(util.embed().setDescription("❌ | Queue is empty.")); 10 | 11 | if (!msg.member.voice.channel) 12 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 13 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 14 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 15 | 16 | if (!args[0]) return msg.channel.send(util.embed().setDescription("❌ | Missing args.")); 17 | 18 | let iToRemove = parseInt(args[0], 10); 19 | if (isNaN(iToRemove) || iToRemove < 1 || iToRemove > music.queue.length) 20 | return msg.channel.send(util.embed().setDescription("❌ | Invalid number to remove.")); 21 | 22 | const removed = music.queue.splice(--iToRemove, 1)[0]; 23 | msg.channel.send(util.embed().setDescription(`✅ | Removed **${removed.info.title}** from the queue.`)); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/commands/volume.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "volume", 5 | aliases: ["vol"], 6 | exec: async (msg, args) => { 7 | const { music } = msg.guild; 8 | const newVolume = parseInt(args[0], 10); 9 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 10 | try { 11 | if (isNaN(newVolume)) { 12 | msg.channel.send(util.embed().setDescription(`🔉 | Current volume \`${music.volume}\`.`)); 13 | } else { 14 | if (!msg.member.voice.channel) 15 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 16 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 17 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 18 | 19 | if (newVolume < 0 || newVolume > 150) 20 | return msg.channel.send(util.embed().setDescription("❌ | You can only set the volume from 0 to 150.")); 21 | 22 | await music.setVolume(newVolume); 23 | msg.channel.send(util.embed().setDescription(`🔉 | Volume set to \`${music.volume}\`.`)); 24 | } 25 | } catch (e) { 26 | msg.channel.send(`An error occured: ${e.message}.`); 27 | } 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/commands/move.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "move", 5 | aliases: ["mv"], 6 | exec: async (msg, args) => { 7 | const { music } = msg.guild; 8 | const from = args[0] ? parseInt(args[0], 10) : null; 9 | const to = args[1] ? parseInt(args[1], 10) : null; 10 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 11 | if (!music.queue.length) return msg.channel.send(util.embed().setDescription("❌ | Queue is empty.")); 12 | 13 | if (!msg.member.voice.channel) 14 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 15 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 16 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 17 | 18 | if (from === null || to === null) 19 | return msg.channel.send(util.embed().setDescription(`❌ | Missing args. Example usage e.g. \`${msg.client.prefix}move 2 1\``)); 20 | 21 | if (from === to || (isNaN(from) || from < 1 || from > music.queue.length) || (isNaN(to) || to < 1 || to > music.queue.length)) 22 | return msg.channel.send(util.embed().setDescription("❌ | Number is invalid or exceeds queue length.")); 23 | 24 | const moved = music.queue[from - 1]; 25 | 26 | util.moveArrayElement(music.queue, from - 1, to - 1); 27 | 28 | msg.channel.send(util.embed().setDescription(`✅ | Moved **${moved.info.title}** to \`${to}\`.`)); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/commands/loop.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | const modes = ["none", "track", "queue"]; 4 | const aliases = { 5 | single: "track", 6 | track: "track", 7 | song: "track", 8 | this: "track", 9 | current: "track", 10 | all: "queue", 11 | every: "queue", 12 | queue: "queue", 13 | off: "none", 14 | none: "none", 15 | nothing: "none" 16 | }; 17 | 18 | module.exports = { 19 | name: "loop", 20 | aliases: ["repeat"], 21 | exec: (msg, args) => { 22 | const { music } = msg.guild; 23 | if (!music.player) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 24 | if (args[0]) { 25 | if (!msg.member.voice.channel) 26 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 27 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 28 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 29 | 30 | const loopMode = aliases[args[0].toLowerCase()]; 31 | if (loopMode && modes.includes(loopMode)) { 32 | music.loop = modes.indexOf(loopMode); 33 | msg.channel.send(util.embed().setDescription(music.loop === 0 ? "✅ | Loop disabled." : `✅ | Set loop to ${modes[music.loop]}.`)); 34 | } else { 35 | msg.channel.send( 36 | util.embed() 37 | .setDescription("❌ | Invalid loop mode. Try single/all/off.") 38 | ); 39 | } 40 | } else { 41 | msg.channel.send(util.embed().setDescription(`✅ | Current loop mode: ${modes[music.loop]}`)); 42 | } 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/commands/seek.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | const durationPattern = /^[0-5]?[0-9](:[0-5][0-9]){1,2}$/; 4 | 5 | module.exports = { 6 | name: "seek", 7 | exec: async (msg, args) => { 8 | const { music } = msg.guild; 9 | if (!music.player || !music.player.playing) return msg.channel.send(util.embed().setDescription("❌ | Currently not playing anything.")); 10 | if (!msg.member.voice.channel) 11 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 12 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 13 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 14 | 15 | if (!music.current.info.isSeekable) 16 | return msg.channel.send(util.embed().setDescription("❌ | Current track isn't seekable.")); 17 | 18 | const duration = args[0]; 19 | if (!duration) 20 | return msg.channel.send(util.embed().setDescription("❌ | You must provide duration to seek. Valid duration e.g. `1:34`.")); 21 | if (!durationPattern.test(duration)) 22 | return msg.channel.send(util.embed().setDescription("❌ | You provided an invalid duration. Valid duration e.g. `1:34`.")); 23 | 24 | const durationMs = util.durationToMillis(duration); 25 | if (durationMs > music.current.info.length) 26 | return msg.channel.send(util.embed().setDescription("❌ | The duration you provide exceeds the duration of the current track.")); 27 | 28 | try { 29 | await music.player.seek(durationMs); 30 | msg.channel.send(util.embed().setDescription(`✅ | Seeked to ${util.millisToDuration(durationMs)}.`)); 31 | } catch (e) { 32 | msg.channel.send(`An error occured: ${e.message}.`); 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/commands/lyrics.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | const util = require("../util"); 3 | 4 | const getLyrics = async (query) => { 5 | const body = await (await fetch(`https://some-random-api.ml/lyrics?title=${encodeURIComponent(query)}`)).json(); 6 | if (body.error) throw Error(body.error); 7 | return body; 8 | }; 9 | 10 | module.exports = { 11 | name: "lyrics", 12 | aliases: ["ly"], 13 | exec: async (msg, args) => { 14 | let query; 15 | if (args.length) { 16 | query = args.join(" "); 17 | } else if (msg.guild.music.current) { 18 | const separatedArtistAndTitle = /(.+) - (.+)/.test(msg.guild.music.current.info.title); 19 | query = `${separatedArtistAndTitle ? msg.guild.music.current.info.title : msg.guild.music.current.info.author.replace(" - Topic", "")} - ${msg.guild.music.current.info.title}`; 20 | } else { 21 | return msg.channel.send(util.embed().setDescription("❌ | Missing args.")); 22 | } 23 | 24 | try { 25 | const res = await getLyrics(query); 26 | const splittedLyrics = util.chunk(res.lyrics, 1024); 27 | 28 | const embed = util.embed() 29 | .setAuthor(res.author) 30 | .setTitle(res.title) 31 | .setURL(res.links.genius) 32 | .setThumbnail(res.thumbnail.genius) 33 | .setDescription(splittedLyrics[0]) 34 | .setFooter(`Page 1 of ${splittedLyrics.length}.`); 35 | 36 | const lyricsMsg = await msg.channel.send(embed); 37 | if (splittedLyrics.length > 1) await util.pagination(lyricsMsg, msg.author, splittedLyrics); 38 | } catch (e) { 39 | if (e.message === "Sorry I couldn't find that song's lyrics") msg.channel.send(util.embed().setDescription(`❌ | ${e.message}`)); 40 | else msg.channel.send(`An error occured: ${e.message}.`); 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /src/structures/MusicClient.js: -------------------------------------------------------------------------------- 1 | const { Manager } = require("@lavacord/discord.js"); 2 | const { Client, Collection } = require("discord.js"); 3 | const { promises: { readdir } } = require("fs"); 4 | const { join } = require("path"); 5 | const { LavasfyClient } = require("lavasfy"); 6 | 7 | require("../extensions"); 8 | 9 | module.exports = class MusicClient extends Client { 10 | /** @param {import("discord.js").ClientOptions} [opt] */ 11 | constructor(opt) { 12 | super(opt); 13 | this.commands = new Collection(); 14 | this.manager = new Manager(this, [ 15 | { 16 | id: "main", 17 | host: process.env.LAVA_HOST, 18 | port: process.env.LAVA_PORT, 19 | password: process.env.LAVA_PASS 20 | } 21 | ]); 22 | this.spotify = process.env.ENABLE_SPOTIFY === "true" 23 | ? new LavasfyClient({ 24 | clientID: process.env.SPOTIFY_ID, 25 | clientSecret: process.env.SPOTIFY_SECRET, 26 | playlistLoadLimit: process.env.SPOTIFY_PLAYLIST_PAGE_LIMIT, 27 | audioOnlyResults: true, 28 | useSpotifyMetadata: true 29 | }, [...[...this.manager.nodes.values()]]) 30 | : null; 31 | 32 | this.prefix = process.env.PREFIX.toLowerCase(); 33 | } 34 | 35 | build() { 36 | this.loadCommands(); 37 | this.loadEventListeners(); 38 | this.login(process.env.TOKEN); 39 | 40 | this.manager 41 | .on("ready", node => console.log(`Node ${node.id} is ready!`)) 42 | .on("disconnect", (ws, node) => console.log(`Node ${node.id} disconnected.`)) 43 | .on("reconnecting", (node) => console.log(`Node ${node.id} tries to reconnect.`)) 44 | .on("error", (error, node) => console.log(`Node ${node.id} got an error: ${error.message}`)); 45 | } 46 | 47 | /** @private */ 48 | async loadCommands() { 49 | const commands = await readdir(join(__dirname, "..", "commands")); 50 | for (const commandFile of commands) { 51 | const command = require(`../commands/${commandFile}`); 52 | this.commands.set(command.name, command); 53 | } 54 | } 55 | 56 | /** @private */ 57 | async loadEventListeners() { 58 | const listeners = await readdir(join(__dirname, "..", "listeners")); 59 | for (const listenerFile of listeners) { 60 | const listener = require(`../listeners/${listenerFile}`); 61 | this.on(listener.name, (...args) => listener.exec(this, ...args)); 62 | } 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/commands/play.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | const getAttachmentURL = (msg) => (msg.attachments.first() || {}).url; 4 | 5 | module.exports = { 6 | name: "play", 7 | aliases: ["p"], 8 | exec: async (msg, args) => { 9 | const { music } = msg.guild; 10 | if (!msg.member.voice.channel) 11 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 12 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 13 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 14 | 15 | const missingPerms = util.missingPerms(msg.guild.me.permissionsIn(msg.member.voice.channel), ["CONNECT", "SPEAK"]); 16 | if ((!music.player || !music.player.playing) && missingPerms.length) 17 | return msg.channel.send(util.embed().setDescription(`❌ | I need ${missingPerms.length > 1 ? "these" : "this"} permission${missingPerms.length > 1 ? "s" : ""} on your voice channel: ${missingPerms.map(x => `\`${x}\``).join(", ")}.`)); 18 | 19 | if (!music.node || !music.node.connected) 20 | return msg.channel.send(util.embed().setDescription("❌ | Lavalink node not connected.")); 21 | 22 | const query = args.join(" ") || getAttachmentURL(msg); 23 | if (!query) return msg.channel.send(util.embed().setDescription("❌ | Missing args.")); 24 | 25 | try { 26 | const { loadType, playlistInfo: { name }, tracks } = await music.load(util.isValidURL(query) ? query : `ytsearch:${query}`); 27 | if (!tracks.length) return msg.channel.send(util.embed().setDescription("❌ | Couldn't find any results.")); 28 | 29 | if (loadType === "PLAYLIST_LOADED") { 30 | for (const track of tracks) { 31 | track.requester = msg.author; 32 | music.queue.push(track); 33 | } 34 | msg.channel.send(util.embed().setDescription(`✅ | Loaded \`${tracks.length}\` tracks from **${name}**.`)); 35 | } else { 36 | const track = tracks[0]; 37 | track.requester = msg.author; 38 | music.queue.push(track); 39 | if (music.player && music.player.playing) 40 | msg.channel.send(util.embed().setDescription(`✅ | **${track.info.title}** added to the queue.`)); 41 | } 42 | 43 | if (!music.player) await music.join(msg.member.voice.channel); 44 | if (!music.player.playing) await music.start(); 45 | 46 | music.setTextCh(msg.channel); 47 | } catch (e) { 48 | msg.channel.send(`An error occured: ${e.message}.`); 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/commands/search.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | 3 | module.exports = { 4 | name: "search", 5 | exec: async (msg, args) => { 6 | const { music } = msg.guild; 7 | if (!msg.member.voice.channel) 8 | return msg.channel.send(util.embed().setDescription("❌ | You must be on a voice channel.")); 9 | if (msg.guild.me.voice.channel && !msg.guild.me.voice.channel.equals(msg.member.voice.channel)) 10 | return msg.channel.send(util.embed().setDescription(`❌ | You must be on ${msg.guild.me.voice.channel} to use this command.`)); 11 | 12 | const missingPerms = util.missingPerms(msg.guild.me.permissionsIn(msg.member.voice.channel), ["CONNECT", "SPEAK"]); 13 | if ((!music.player || !music.player.playing) && missingPerms.length) 14 | return msg.channel.send(util.embed().setDescription(`❌ | I need ${missingPerms.length > 1 ? "these" : "this"} permission${missingPerms.length > 1 ? "s" : ""} on your voice channel: ${missingPerms.map(x => `\`${x}\``).join(", ")}.`)); 15 | 16 | if (!music.node || !music.node.connected) 17 | return msg.channel.send(util.embed().setDescription("❌ | Lavalink node not connected.")); 18 | 19 | const query = args.join(" "); 20 | if (!query) return msg.channel.send(util.embed().setDescription("❌ | Missing args.")); 21 | 22 | try { 23 | let { tracks } = await music.load(`ytsearch:${query}`); 24 | if (!tracks.length) return msg.channel.send(util.embed().setDescription("❌ | Couldn't find any results.")); 25 | 26 | tracks = tracks.slice(0, 10); 27 | 28 | const resultMessage = await msg.channel.send(util.embed() 29 | .setAuthor("Search Result", msg.client.user.displayAvatarURL()) 30 | .setDescription(tracks.map((x, i) => `\`${++i}.\` **${x.info.title}**`)) 31 | .setFooter("Select from 1 to 10 or type \"cancel\" to cancel the command.")); 32 | 33 | const collector = await awaitMessages(); 34 | if (!collector) return resultMessage.edit(util.embed().setDescription("❌ | Time is up!")); 35 | const response = collector.first(); 36 | 37 | if (response.deletable) response.delete(); 38 | 39 | if (/^cancel$/i.exec(response.content)) 40 | return resultMessage.edit(util.embed().setDescription("✅ | Cancelled.")); 41 | 42 | const track = tracks[response.content - 1]; 43 | track.requester = msg.author; 44 | music.queue.push(track); 45 | 46 | if (music.player && music.player.playing) { 47 | resultMessage.edit(util.embed().setDescription(`✅ | **${track.info.title}** added to the queue.`)); 48 | } else { 49 | resultMessage.delete(); 50 | } 51 | 52 | if (!music.player) await music.join(msg.member.voice.channel); 53 | if (!music.player.playing) await music.start(); 54 | 55 | music.setTextCh(msg.channel); 56 | } catch (e) { 57 | msg.channel.send(`An error occured: ${e.message}.`); 58 | } 59 | 60 | async function awaitMessages() { 61 | try { 62 | const collector = await msg.channel.awaitMessages( 63 | m => m.author.equals(msg.author) && (/^cancel$/i.exec(m.content) || (!isNaN(parseInt(m.content, 10)) && (m.content >= 1 && m.content <= 10))), 64 | { 65 | time: 10000, 66 | max: 1, 67 | errors: ["time"] 68 | } 69 | ); 70 | return collector; 71 | } catch { 72 | return null; 73 | } 74 | } 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed, Permissions } = require("discord.js"); 2 | const prettyMilliseconds = require("pretty-ms"); 3 | 4 | module.exports = class Util { 5 | static embed() { 6 | return new MessageEmbed() 7 | .setColor("#99AAB5"); 8 | } 9 | 10 | static durationToMillis(dur) { 11 | return dur.split(":").map(Number).reduce((acc, curr) => curr + acc * 60) * 1000; 12 | } 13 | 14 | static millisToDuration(ms) { 15 | return prettyMilliseconds(ms, { colonNotation: true, secondsDecimalDigits: 0 }); 16 | } 17 | 18 | static chunk(arr, size) { 19 | const temp = []; 20 | for (let i = 0; i < arr.length; i += size) { 21 | temp.push(arr.slice(i, i + size)); 22 | } 23 | return temp; 24 | } 25 | 26 | static isValidURL(url) { 27 | return /^https?:\/\/((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3}))(:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(#[-a-z\d_]*)?$/i 28 | .test(url); 29 | } 30 | 31 | static shuffleArray(arr) { 32 | for (let i = arr.length - 1; i > 0; i--) { 33 | const j = Math.floor(Math.random() * (i + 1)); 34 | [arr[i], arr[j]] = [arr[j], arr[i]]; 35 | } 36 | return arr; 37 | } 38 | 39 | static get paginationEmojis() { 40 | return ["◀", "⛔", "▶"]; 41 | } 42 | 43 | static async pagination(msg, author, contents, init = true, currPage = 0) { 44 | if (init) for (const emoji of this.paginationEmojis) await msg.react(emoji); 45 | 46 | const collector = msg.createReactionCollector((reaction, user) => { 47 | return this.paginationEmojis.includes(reaction.emoji.name) && user.id === author.id; 48 | }, { 49 | max: 1, 50 | time: 30000 51 | }); 52 | 53 | collector 54 | .on("collect", (reaction) => { 55 | reaction.users.remove(author); 56 | 57 | const emoji = reaction.emoji.name; 58 | if (emoji === this.paginationEmojis[0]) currPage--; 59 | if (emoji === this.paginationEmojis[1]) return collector.stop(); 60 | if (emoji === this.paginationEmojis[2]) currPage++; 61 | currPage = ((currPage % contents.length) + contents.length) % contents.length; 62 | 63 | const embed = msg.embeds[0] 64 | .setDescription(contents[currPage]) 65 | .setFooter(`Page ${currPage + 1} of ${contents.length}.`); 66 | 67 | msg.edit(embed); 68 | 69 | this.pagination(msg, author, contents, false, currPage); 70 | }) 71 | .on("end", (_, reason) => { 72 | if (["time", "user"].includes(reason)) msg.reactions.removeAll(); 73 | }); 74 | } 75 | 76 | /** 77 | * @param {import("discord.js").PermissionResolvable} memberPerms 78 | * @param {import("discord.js").PermissionResolvable} requiredPerms 79 | * @returns {import("discord.js").PermissionString[]} 80 | */ 81 | static missingPerms(memberPerms, requiredPerms) { 82 | return new Permissions(memberPerms).missing(new Permissions(requiredPerms)); 83 | } 84 | 85 | static moveArrayElement(arr, fromIndex, toIndex) { 86 | arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]); 87 | return arr; 88 | } 89 | 90 | static progress(current, total, size = 16) { 91 | const percent = current / total * size; 92 | const progbar = new Array(size).fill("▬"); 93 | progbar[Math.round(percent)] = "🔘"; 94 | return { 95 | bar: progbar.join(""), 96 | percent 97 | }; 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /src/structures/MusicHandler.js: -------------------------------------------------------------------------------- 1 | const Rest = require("./Rest"); 2 | const util = require("../util"); 3 | 4 | module.exports = class MusicHandler { 5 | /** @param {import("discord.js").Guild} guild */ 6 | constructor(guild) { 7 | this.guild = guild; 8 | this.volume = 100; 9 | this.loop = 0; // 0 = none; 1 = track; 2 = queue; 10 | this.previous = null; 11 | this.current = null; 12 | this.queue = []; 13 | /** @type {import("discord.js").TextChannel|null} */ 14 | this.textChannel = null; 15 | this.shouldSkipCurrent = false; 16 | } 17 | 18 | get voiceChannel() { 19 | return this.guild.me.voice.channel; 20 | } 21 | 22 | /** @returns {import("../structures/MusicClient")} */ 23 | get client() { 24 | return this.guild.client; 25 | } 26 | 27 | get player() { 28 | return this.client.manager.players.get(this.guild.id) || null; 29 | } 30 | 31 | get node() { 32 | return this.client.manager.nodes.get("main"); 33 | } 34 | 35 | reset() { 36 | this.loop = 0; 37 | this.volume = 100; 38 | this.previous = null; 39 | this.current = null; 40 | this.queue = []; 41 | this.textChannel = null; 42 | } 43 | 44 | /** @param {import("discord.js").VoiceChannel} voice */ 45 | async join(voice) { 46 | if (this.player) return; 47 | await this.client.manager.join({ 48 | channel: voice.id, 49 | guild: this.guild.id, 50 | node: this.node.id 51 | }, { selfdeaf: true }); 52 | 53 | this.player 54 | .on("start", () => { 55 | this.current = this.queue.shift(); 56 | if (this.textChannel) this.textChannel.send(util.embed().setDescription(`🎶 | Now playing **${this.current.info.title}**.`)); 57 | }) 58 | .on("end", (data) => { 59 | if (data.reason === "REPLACED") return; 60 | this.previous = this.current; 61 | this.current = null; 62 | 63 | if (this.loop === 1 && !this.shouldSkipCurrent) this.queue.unshift(this.previous); 64 | else if (this.loop === 2) this.queue.push(this.previous); 65 | 66 | if (this.shouldSkipCurrent) this.shouldSkipCurrent = false; 67 | 68 | if (!this.queue.length) { 69 | this.client.manager.leave(this.guild.id); 70 | if (this.textChannel) this.textChannel.send(util.embed().setDescription("✅ | Queue is empty. Leaving voice channel..")); 71 | this.reset(); 72 | return; 73 | } 74 | this.start(); 75 | }) 76 | .on("error", console.error); 77 | } 78 | 79 | /** @param {import("discord.js").TextChannel} text */ 80 | setTextCh(text) { 81 | this.textChannel = text; 82 | } 83 | 84 | async load(query) { 85 | const res = await Rest.load(this.node, query, this.client.spotify); 86 | return res; 87 | } 88 | 89 | async start() { 90 | if (!this.player) return; 91 | await this.player.play(this.queue[0].track); 92 | } 93 | 94 | async pause() { 95 | if (!this.player) return; 96 | if (!this.player.paused) await this.player.pause(true); 97 | } 98 | 99 | async resume() { 100 | if (!this.player) return; 101 | if (this.player.paused) await this.player.pause(false); 102 | } 103 | 104 | async skip(to = 1) { 105 | if (!this.player) return; 106 | if (to > 1) { 107 | this.queue.unshift(this.queue[to - 1]); 108 | this.queue.splice(to, 1); 109 | } 110 | if (this.loop === 1 && this.queue[0]) this.shouldSkipCurrent = true; 111 | await this.player.stop(); 112 | } 113 | 114 | async stop() { 115 | if (!this.player) return; 116 | this.loop = 0; 117 | this.queue = []; 118 | await this.skip(); 119 | } 120 | 121 | async setVolume(newVol) { 122 | if (!this.player) return; 123 | const parsed = parseInt(newVol, 10); 124 | if (isNaN(parsed)) return; 125 | await this.player.volume(parsed); 126 | this.volume = newVol; 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@7.12.11": 6 | version "7.12.11" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" 8 | integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== 9 | dependencies: 10 | "@babel/highlight" "^7.10.4" 11 | 12 | "@babel/helper-validator-identifier@^7.10.4": 13 | version "7.12.11" 14 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" 15 | integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== 16 | 17 | "@babel/highlight@^7.10.4": 18 | version "7.10.4" 19 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" 20 | integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== 21 | dependencies: 22 | "@babel/helper-validator-identifier" "^7.10.4" 23 | chalk "^2.0.0" 24 | js-tokens "^4.0.0" 25 | 26 | "@discordjs/collection@^0.1.6": 27 | version "0.1.6" 28 | resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.6.tgz#9e9a7637f4e4e0688fd8b2b5c63133c91607682c" 29 | integrity sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ== 30 | 31 | "@discordjs/form-data@^3.0.1": 32 | version "3.0.1" 33 | resolved "https://registry.yarnpkg.com/@discordjs/form-data/-/form-data-3.0.1.tgz#5c9e6be992e2e57d0dfa0e39979a850225fb4697" 34 | integrity sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg== 35 | dependencies: 36 | asynckit "^0.4.0" 37 | combined-stream "^1.0.8" 38 | mime-types "^2.1.12" 39 | 40 | "@eslint/eslintrc@^0.4.1": 41 | version "0.4.1" 42 | resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" 43 | integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== 44 | dependencies: 45 | ajv "^6.12.4" 46 | debug "^4.1.1" 47 | espree "^7.3.0" 48 | globals "^12.1.0" 49 | ignore "^4.0.6" 50 | import-fresh "^3.2.1" 51 | js-yaml "^3.13.1" 52 | minimatch "^3.0.4" 53 | strip-json-comments "^3.1.1" 54 | 55 | "@lavacord/discord.js@0.0.7": 56 | version "0.0.7" 57 | resolved "https://registry.yarnpkg.com/@lavacord/discord.js/-/discord.js-0.0.7.tgz#8ba1e944cb5853693a72274ead66798a79e1c07a" 58 | integrity sha512-EJ3Jz7Eeepbb+fTirLmVGRshtACuzkhDFccR+6hrTRAmEV9P3dFsfbCtsZfLhRa2j2geJ73VqpjfiJmml0AMuQ== 59 | dependencies: 60 | lavacord "1.1.9" 61 | 62 | abort-controller@^3.0.0: 63 | version "3.0.0" 64 | resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" 65 | integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== 66 | dependencies: 67 | event-target-shim "^5.0.0" 68 | 69 | acorn-jsx@^5.3.1: 70 | version "5.3.1" 71 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" 72 | integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== 73 | 74 | acorn@^7.4.0: 75 | version "7.4.1" 76 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" 77 | integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== 78 | 79 | ajv@^6.10.0, ajv@^6.12.4: 80 | version "6.12.6" 81 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" 82 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 83 | dependencies: 84 | fast-deep-equal "^3.1.1" 85 | fast-json-stable-stringify "^2.0.0" 86 | json-schema-traverse "^0.4.1" 87 | uri-js "^4.2.2" 88 | 89 | ajv@^8.0.1: 90 | version "8.5.0" 91 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" 92 | integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== 93 | dependencies: 94 | fast-deep-equal "^3.1.1" 95 | json-schema-traverse "^1.0.0" 96 | require-from-string "^2.0.2" 97 | uri-js "^4.2.2" 98 | 99 | ansi-colors@^4.1.1: 100 | version "4.1.1" 101 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" 102 | integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== 103 | 104 | ansi-regex@^5.0.0: 105 | version "5.0.0" 106 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 107 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 108 | 109 | ansi-styles@^3.2.1: 110 | version "3.2.1" 111 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 112 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 113 | dependencies: 114 | color-convert "^1.9.0" 115 | 116 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 117 | version "4.3.0" 118 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 119 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 120 | dependencies: 121 | color-convert "^2.0.1" 122 | 123 | argparse@^1.0.7: 124 | version "1.0.10" 125 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 126 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 127 | dependencies: 128 | sprintf-js "~1.0.2" 129 | 130 | astral-regex@^2.0.0: 131 | version "2.0.0" 132 | resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" 133 | integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== 134 | 135 | asynckit@^0.4.0: 136 | version "0.4.0" 137 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 138 | integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= 139 | 140 | balanced-match@^1.0.0: 141 | version "1.0.0" 142 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 143 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 144 | 145 | brace-expansion@^1.1.7: 146 | version "1.1.11" 147 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 148 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 149 | dependencies: 150 | balanced-match "^1.0.0" 151 | concat-map "0.0.1" 152 | 153 | callsites@^3.0.0: 154 | version "3.1.0" 155 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 156 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 157 | 158 | chalk@^2.0.0: 159 | version "2.4.2" 160 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 161 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 162 | dependencies: 163 | ansi-styles "^3.2.1" 164 | escape-string-regexp "^1.0.5" 165 | supports-color "^5.3.0" 166 | 167 | chalk@^4.0.0: 168 | version "4.1.0" 169 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" 170 | integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== 171 | dependencies: 172 | ansi-styles "^4.1.0" 173 | supports-color "^7.1.0" 174 | 175 | color-convert@^1.9.0: 176 | version "1.9.3" 177 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 178 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 179 | dependencies: 180 | color-name "1.1.3" 181 | 182 | color-convert@^2.0.1: 183 | version "2.0.1" 184 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 185 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 186 | dependencies: 187 | color-name "~1.1.4" 188 | 189 | color-name@1.1.3: 190 | version "1.1.3" 191 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 192 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 193 | 194 | color-name@~1.1.4: 195 | version "1.1.4" 196 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 197 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 198 | 199 | combined-stream@^1.0.8: 200 | version "1.0.8" 201 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 202 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 203 | dependencies: 204 | delayed-stream "~1.0.0" 205 | 206 | concat-map@0.0.1: 207 | version "0.0.1" 208 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 209 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 210 | 211 | cross-spawn@^7.0.2: 212 | version "7.0.3" 213 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 214 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 215 | dependencies: 216 | path-key "^3.1.0" 217 | shebang-command "^2.0.0" 218 | which "^2.0.1" 219 | 220 | debug@^4.0.1, debug@^4.1.1: 221 | version "4.3.1" 222 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" 223 | integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== 224 | dependencies: 225 | ms "2.1.2" 226 | 227 | deep-is@^0.1.3: 228 | version "0.1.3" 229 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 230 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 231 | 232 | delayed-stream@~1.0.0: 233 | version "1.0.0" 234 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 235 | integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= 236 | 237 | discord.js@^12.5.3: 238 | version "12.5.3" 239 | resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-12.5.3.tgz#56820d473c24320871df9ea0bbc6b462f21cf85c" 240 | integrity sha512-D3nkOa/pCkNyn6jLZnAiJApw2N9XrIsXUAdThf01i7yrEuqUmDGc7/CexVWwEcgbQR97XQ+mcnqJpmJ/92B4Aw== 241 | dependencies: 242 | "@discordjs/collection" "^0.1.6" 243 | "@discordjs/form-data" "^3.0.1" 244 | abort-controller "^3.0.0" 245 | node-fetch "^2.6.1" 246 | prism-media "^1.2.9" 247 | setimmediate "^1.0.5" 248 | tweetnacl "^1.0.3" 249 | ws "^7.4.4" 250 | 251 | doctrine@^3.0.0: 252 | version "3.0.0" 253 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 254 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 255 | dependencies: 256 | esutils "^2.0.2" 257 | 258 | dotenv@^10.0.0: 259 | version "10.0.0" 260 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" 261 | integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== 262 | 263 | emoji-regex@^8.0.0: 264 | version "8.0.0" 265 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 266 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 267 | 268 | enquirer@^2.3.5: 269 | version "2.3.6" 270 | resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" 271 | integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== 272 | dependencies: 273 | ansi-colors "^4.1.1" 274 | 275 | escape-string-regexp@^1.0.5: 276 | version "1.0.5" 277 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 278 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 279 | 280 | escape-string-regexp@^4.0.0: 281 | version "4.0.0" 282 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 283 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 284 | 285 | eslint-scope@^5.1.1: 286 | version "5.1.1" 287 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" 288 | integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== 289 | dependencies: 290 | esrecurse "^4.3.0" 291 | estraverse "^4.1.1" 292 | 293 | eslint-utils@^2.1.0: 294 | version "2.1.0" 295 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" 296 | integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== 297 | dependencies: 298 | eslint-visitor-keys "^1.1.0" 299 | 300 | eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: 301 | version "1.3.0" 302 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" 303 | integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== 304 | 305 | eslint-visitor-keys@^2.0.0: 306 | version "2.0.0" 307 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" 308 | integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== 309 | 310 | eslint@^7.27.0: 311 | version "7.27.0" 312 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.27.0.tgz#665a1506d8f95655c9274d84bd78f7166b07e9c7" 313 | integrity sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA== 314 | dependencies: 315 | "@babel/code-frame" "7.12.11" 316 | "@eslint/eslintrc" "^0.4.1" 317 | ajv "^6.10.0" 318 | chalk "^4.0.0" 319 | cross-spawn "^7.0.2" 320 | debug "^4.0.1" 321 | doctrine "^3.0.0" 322 | enquirer "^2.3.5" 323 | escape-string-regexp "^4.0.0" 324 | eslint-scope "^5.1.1" 325 | eslint-utils "^2.1.0" 326 | eslint-visitor-keys "^2.0.0" 327 | espree "^7.3.1" 328 | esquery "^1.4.0" 329 | esutils "^2.0.2" 330 | fast-deep-equal "^3.1.3" 331 | file-entry-cache "^6.0.1" 332 | functional-red-black-tree "^1.0.1" 333 | glob-parent "^5.0.0" 334 | globals "^13.6.0" 335 | ignore "^4.0.6" 336 | import-fresh "^3.0.0" 337 | imurmurhash "^0.1.4" 338 | is-glob "^4.0.0" 339 | js-yaml "^3.13.1" 340 | json-stable-stringify-without-jsonify "^1.0.1" 341 | levn "^0.4.1" 342 | lodash.merge "^4.6.2" 343 | minimatch "^3.0.4" 344 | natural-compare "^1.4.0" 345 | optionator "^0.9.1" 346 | progress "^2.0.0" 347 | regexpp "^3.1.0" 348 | semver "^7.2.1" 349 | strip-ansi "^6.0.0" 350 | strip-json-comments "^3.1.0" 351 | table "^6.0.9" 352 | text-table "^0.2.0" 353 | v8-compile-cache "^2.0.3" 354 | 355 | espree@^7.3.0, espree@^7.3.1: 356 | version "7.3.1" 357 | resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" 358 | integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== 359 | dependencies: 360 | acorn "^7.4.0" 361 | acorn-jsx "^5.3.1" 362 | eslint-visitor-keys "^1.3.0" 363 | 364 | esprima@^4.0.0: 365 | version "4.0.1" 366 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 367 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 368 | 369 | esquery@^1.4.0: 370 | version "1.4.0" 371 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" 372 | integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== 373 | dependencies: 374 | estraverse "^5.1.0" 375 | 376 | esrecurse@^4.3.0: 377 | version "4.3.0" 378 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" 379 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 380 | dependencies: 381 | estraverse "^5.2.0" 382 | 383 | estraverse@^4.1.1: 384 | version "4.3.0" 385 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 386 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 387 | 388 | estraverse@^5.1.0, estraverse@^5.2.0: 389 | version "5.2.0" 390 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" 391 | integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== 392 | 393 | esutils@^2.0.2: 394 | version "2.0.3" 395 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 396 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 397 | 398 | event-target-shim@^5.0.0: 399 | version "5.0.1" 400 | resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" 401 | integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== 402 | 403 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 404 | version "3.1.3" 405 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 406 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 407 | 408 | fast-json-stable-stringify@^2.0.0: 409 | version "2.1.0" 410 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 411 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 412 | 413 | fast-levenshtein@^2.0.6: 414 | version "2.0.6" 415 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 416 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 417 | 418 | file-entry-cache@^6.0.1: 419 | version "6.0.1" 420 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" 421 | integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== 422 | dependencies: 423 | flat-cache "^3.0.4" 424 | 425 | flat-cache@^3.0.4: 426 | version "3.0.4" 427 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" 428 | integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== 429 | dependencies: 430 | flatted "^3.1.0" 431 | rimraf "^3.0.2" 432 | 433 | flatted@^3.1.0: 434 | version "3.1.0" 435 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067" 436 | integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA== 437 | 438 | form-data@^4.0.0: 439 | version "4.0.0" 440 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" 441 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 442 | dependencies: 443 | asynckit "^0.4.0" 444 | combined-stream "^1.0.8" 445 | mime-types "^2.1.12" 446 | 447 | fs.realpath@^1.0.0: 448 | version "1.0.0" 449 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 450 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 451 | 452 | functional-red-black-tree@^1.0.1: 453 | version "1.0.1" 454 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 455 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 456 | 457 | glob-parent@^5.0.0: 458 | version "5.1.1" 459 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" 460 | integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== 461 | dependencies: 462 | is-glob "^4.0.1" 463 | 464 | glob@^7.1.3: 465 | version "7.1.6" 466 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 467 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 468 | dependencies: 469 | fs.realpath "^1.0.0" 470 | inflight "^1.0.4" 471 | inherits "2" 472 | minimatch "^3.0.4" 473 | once "^1.3.0" 474 | path-is-absolute "^1.0.0" 475 | 476 | globals@^12.1.0: 477 | version "12.4.0" 478 | resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" 479 | integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== 480 | dependencies: 481 | type-fest "^0.8.1" 482 | 483 | globals@^13.6.0: 484 | version "13.6.0" 485 | resolved "https://registry.yarnpkg.com/globals/-/globals-13.6.0.tgz#d77138e53738567bb96a3916ff6f6b487af20ef7" 486 | integrity sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ== 487 | dependencies: 488 | type-fest "^0.20.2" 489 | 490 | has-flag@^3.0.0: 491 | version "3.0.0" 492 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 493 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 494 | 495 | has-flag@^4.0.0: 496 | version "4.0.0" 497 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 498 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 499 | 500 | ignore@^4.0.6: 501 | version "4.0.6" 502 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" 503 | integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== 504 | 505 | import-fresh@^3.0.0, import-fresh@^3.2.1: 506 | version "3.3.0" 507 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" 508 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 509 | dependencies: 510 | parent-module "^1.0.0" 511 | resolve-from "^4.0.0" 512 | 513 | imurmurhash@^0.1.4: 514 | version "0.1.4" 515 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 516 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 517 | 518 | inflight@^1.0.4: 519 | version "1.0.6" 520 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 521 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 522 | dependencies: 523 | once "^1.3.0" 524 | wrappy "1" 525 | 526 | inherits@2: 527 | version "2.0.4" 528 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 529 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 530 | 531 | is-extglob@^2.1.1: 532 | version "2.1.1" 533 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 534 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 535 | 536 | is-fullwidth-code-point@^3.0.0: 537 | version "3.0.0" 538 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 539 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 540 | 541 | is-glob@^4.0.0, is-glob@^4.0.1: 542 | version "4.0.1" 543 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 544 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 545 | dependencies: 546 | is-extglob "^2.1.1" 547 | 548 | isexe@^2.0.0: 549 | version "2.0.0" 550 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 551 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 552 | 553 | js-tokens@^4.0.0: 554 | version "4.0.0" 555 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 556 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 557 | 558 | js-yaml@^3.13.1: 559 | version "3.14.1" 560 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" 561 | integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== 562 | dependencies: 563 | argparse "^1.0.7" 564 | esprima "^4.0.0" 565 | 566 | json-schema-traverse@^0.4.1: 567 | version "0.4.1" 568 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 569 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 570 | 571 | json-schema-traverse@^1.0.0: 572 | version "1.0.0" 573 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" 574 | integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== 575 | 576 | json-stable-stringify-without-jsonify@^1.0.1: 577 | version "1.0.1" 578 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 579 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 580 | 581 | lavacord@1.1.9: 582 | version "1.1.9" 583 | resolved "https://registry.yarnpkg.com/lavacord/-/lavacord-1.1.9.tgz#e7e355f969eb082a36e20814fdc6a9b01f3f81a1" 584 | integrity sha512-haZghbblO1w3Hodc9q63ZWgV5zA/jB6xFKS17fImK5aIdn0PkKuZ6AsJBxMFpR275v8GNYOxg6cTQBYBQ+batQ== 585 | dependencies: 586 | node-fetch "^2.6.0" 587 | ws "^7.3.0" 588 | 589 | lavasfy@^2.2.1: 590 | version "2.2.1" 591 | resolved "https://registry.yarnpkg.com/lavasfy/-/lavasfy-2.2.1.tgz#e5331f82651e561cefae776fe4c90634329344cd" 592 | integrity sha512-7zCntk7MN/wfvZyBiJcKL+z3BU+4d8ZSx6MaUe1sxhNcYMNiWfc2MPdAde/CQsgOlXhO+5joNiWOb2RU2+Pc8A== 593 | dependencies: 594 | node-superfetch "^0.2.2" 595 | 596 | levn@^0.4.1: 597 | version "0.4.1" 598 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" 599 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 600 | dependencies: 601 | prelude-ls "^1.2.1" 602 | type-check "~0.4.0" 603 | 604 | lodash.clonedeep@^4.5.0: 605 | version "4.5.0" 606 | resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" 607 | integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= 608 | 609 | lodash.merge@^4.6.2: 610 | version "4.6.2" 611 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 612 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 613 | 614 | lodash.truncate@^4.4.2: 615 | version "4.4.2" 616 | resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" 617 | integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= 618 | 619 | lru-cache@^6.0.0: 620 | version "6.0.0" 621 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 622 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 623 | dependencies: 624 | yallist "^4.0.0" 625 | 626 | mime-db@1.45.0: 627 | version "1.45.0" 628 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" 629 | integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== 630 | 631 | mime-types@^2.1.12: 632 | version "2.1.28" 633 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" 634 | integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== 635 | dependencies: 636 | mime-db "1.45.0" 637 | 638 | minimatch@^3.0.4: 639 | version "3.0.4" 640 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 641 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 642 | dependencies: 643 | brace-expansion "^1.1.7" 644 | 645 | ms@2.1.2: 646 | version "2.1.2" 647 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 648 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 649 | 650 | natural-compare@^1.4.0: 651 | version "1.4.0" 652 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 653 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 654 | 655 | node-fetch@^2.6.0, node-fetch@^2.6.1: 656 | version "2.6.1" 657 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" 658 | integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== 659 | 660 | node-superfetch@^0.2.2: 661 | version "0.2.2" 662 | resolved "https://registry.yarnpkg.com/node-superfetch/-/node-superfetch-0.2.2.tgz#e0d4ff43b2290dab09ae9c4b6fd1633521d4f930" 663 | integrity sha512-u/nnm1B5itxgGbMIGOGxLqFRrMFkB9bRbT0YwdM61/KhOXd25PZugX++bzpS+arQimcsIekntapdSSFI2CoCEw== 664 | dependencies: 665 | form-data "^4.0.0" 666 | node-fetch "^2.6.1" 667 | 668 | once@^1.3.0: 669 | version "1.4.0" 670 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 671 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 672 | dependencies: 673 | wrappy "1" 674 | 675 | optionator@^0.9.1: 676 | version "0.9.1" 677 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" 678 | integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== 679 | dependencies: 680 | deep-is "^0.1.3" 681 | fast-levenshtein "^2.0.6" 682 | levn "^0.4.1" 683 | prelude-ls "^1.2.1" 684 | type-check "^0.4.0" 685 | word-wrap "^1.2.3" 686 | 687 | parent-module@^1.0.0: 688 | version "1.0.1" 689 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 690 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 691 | dependencies: 692 | callsites "^3.0.0" 693 | 694 | parse-ms@^2.1.0: 695 | version "2.1.0" 696 | resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" 697 | integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== 698 | 699 | path-is-absolute@^1.0.0: 700 | version "1.0.1" 701 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 702 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 703 | 704 | path-key@^3.1.0: 705 | version "3.1.1" 706 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 707 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 708 | 709 | prelude-ls@^1.2.1: 710 | version "1.2.1" 711 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" 712 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 713 | 714 | pretty-ms@^7.0.1: 715 | version "7.0.1" 716 | resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" 717 | integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== 718 | dependencies: 719 | parse-ms "^2.1.0" 720 | 721 | prism-media@^1.2.9: 722 | version "1.2.9" 723 | resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.2.9.tgz#8d4f97b36efdfc82483eb8d3db64020767866f36" 724 | integrity sha512-UHCYuqHipbTR1ZsXr5eg4JUmHER8Ss4YEb9Azn+9zzJ7/jlTtD1h0lc4g6tNx3eMlB8Mp6bfll0LPMAV4R6r3Q== 725 | 726 | progress@^2.0.0: 727 | version "2.0.3" 728 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 729 | integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== 730 | 731 | punycode@^2.1.0: 732 | version "2.1.1" 733 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 734 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 735 | 736 | regexpp@^3.1.0: 737 | version "3.1.0" 738 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" 739 | integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== 740 | 741 | require-from-string@^2.0.2: 742 | version "2.0.2" 743 | resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" 744 | integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== 745 | 746 | resolve-from@^4.0.0: 747 | version "4.0.0" 748 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 749 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 750 | 751 | rimraf@^3.0.2: 752 | version "3.0.2" 753 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 754 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 755 | dependencies: 756 | glob "^7.1.3" 757 | 758 | semver@^7.2.1: 759 | version "7.3.4" 760 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" 761 | integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== 762 | dependencies: 763 | lru-cache "^6.0.0" 764 | 765 | setimmediate@^1.0.5: 766 | version "1.0.5" 767 | resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" 768 | integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= 769 | 770 | shebang-command@^2.0.0: 771 | version "2.0.0" 772 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 773 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 774 | dependencies: 775 | shebang-regex "^3.0.0" 776 | 777 | shebang-regex@^3.0.0: 778 | version "3.0.0" 779 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 780 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 781 | 782 | slice-ansi@^4.0.0: 783 | version "4.0.0" 784 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" 785 | integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== 786 | dependencies: 787 | ansi-styles "^4.0.0" 788 | astral-regex "^2.0.0" 789 | is-fullwidth-code-point "^3.0.0" 790 | 791 | sprintf-js@~1.0.2: 792 | version "1.0.3" 793 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 794 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 795 | 796 | string-width@^4.2.0: 797 | version "4.2.0" 798 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" 799 | integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== 800 | dependencies: 801 | emoji-regex "^8.0.0" 802 | is-fullwidth-code-point "^3.0.0" 803 | strip-ansi "^6.0.0" 804 | 805 | strip-ansi@^6.0.0: 806 | version "6.0.0" 807 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 808 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 809 | dependencies: 810 | ansi-regex "^5.0.0" 811 | 812 | strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: 813 | version "3.1.1" 814 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 815 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 816 | 817 | supports-color@^5.3.0: 818 | version "5.5.0" 819 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 820 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 821 | dependencies: 822 | has-flag "^3.0.0" 823 | 824 | supports-color@^7.1.0: 825 | version "7.2.0" 826 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 827 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 828 | dependencies: 829 | has-flag "^4.0.0" 830 | 831 | table@^6.0.9: 832 | version "6.7.1" 833 | resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" 834 | integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== 835 | dependencies: 836 | ajv "^8.0.1" 837 | lodash.clonedeep "^4.5.0" 838 | lodash.truncate "^4.4.2" 839 | slice-ansi "^4.0.0" 840 | string-width "^4.2.0" 841 | strip-ansi "^6.0.0" 842 | 843 | text-table@^0.2.0: 844 | version "0.2.0" 845 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 846 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 847 | 848 | tweetnacl@^1.0.3: 849 | version "1.0.3" 850 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" 851 | integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== 852 | 853 | type-check@^0.4.0, type-check@~0.4.0: 854 | version "0.4.0" 855 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" 856 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 857 | dependencies: 858 | prelude-ls "^1.2.1" 859 | 860 | type-fest@^0.20.2: 861 | version "0.20.2" 862 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" 863 | integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== 864 | 865 | type-fest@^0.8.1: 866 | version "0.8.1" 867 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" 868 | integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== 869 | 870 | uri-js@^4.2.2: 871 | version "4.4.0" 872 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" 873 | integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== 874 | dependencies: 875 | punycode "^2.1.0" 876 | 877 | v8-compile-cache@^2.0.3: 878 | version "2.2.0" 879 | resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" 880 | integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== 881 | 882 | which@^2.0.1: 883 | version "2.0.2" 884 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 885 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 886 | dependencies: 887 | isexe "^2.0.0" 888 | 889 | word-wrap@^1.2.3: 890 | version "1.2.3" 891 | resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" 892 | integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== 893 | 894 | wrappy@1: 895 | version "1.0.2" 896 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 897 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 898 | 899 | ws@^7.3.0, ws@^7.4.4: 900 | version "7.4.6" 901 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" 902 | integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== 903 | 904 | yallist@^4.0.0: 905 | version "4.0.0" 906 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 907 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 908 | --------------------------------------------------------------------------------