diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..5171c5408 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0be057cde..b32b9b389 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,8 +11,8 @@ Please move lines that apply to you out of the comment: - This PR **only** includes non-code changes, like changes to documentation, README, etc. --> - # Important. + - Write in camelCase, not snake_case. - Do not push to master/main without testing your changes first, make a branch if you have to. \ No newline at end of file diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index d2395aca3..65df63564 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -1,7 +1,9 @@ # Seeking support? -We only use this issue tracker for bug reports and feature request. We are not able to provide general support or answer questions in the form of GitHub issues. +We only use this issue tracker for bug reports and feature request. We are not able to provide general support or answer +questions in the form of GitHub issues. -For general questions about the Music Bot and use please use the dedicated support channels in our Discord server: https://discord.gg/sbySMS7m3v +For general questions about the Music Bot and use please use the dedicated support channels in our Discord +server: https://discord.gg/sbySMS7m3v Any issues that don't directly involve a bug or a feature request will likely be closed and redirected. diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..8543ddcb7 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,11 @@ +# Configuration for probot-stale - https://github.com/probot/stale +daysUntilStale: 60 + +daysUntilClose: 5 +exemptLabels: + - Soon + +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. diff --git a/.github/workflows/prettify.yml.no b/.github/workflows/prettify.yml.no deleted file mode 100644 index baa0caebc..000000000 --- a/.github/workflows/prettify.yml.no +++ /dev/null @@ -1,51 +0,0 @@ -name: Prettify Code - -on: - push: - branches: [master, v5] - pull_request: - branches: [master, v5] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: ✨ Prettify the files - run: npx prettier --write . - - - name: 📝 Declaring variables - id: vars - shell: bash - run: | - echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" - - - name: 🔍 Check for any changes - run: | - STATUS_ARGS=$1 - PATHSPEC=$2 - - function check() { - if [[ -z "$(git status --porcelain $STATUS_ARGS $PATHSPEC)" ]]; - then - echo "0" - else - echo "1" - fi - } - - echo ::set-output name=changed::$(check) - - - name: 🚀 Publish to github - run: | - if [ ${{ steps.vars.outputs.changed }} = "1" ]; - then - git add . - git config --local user.email "55418697+sudhanplayz@users.noreply.github.com" - git config --local user.name "SudhanPlayz" - git commit -m "style(${{ steps.vars.outputs.sha_short }}): Prettified the codes" - git push - else - echo "Nothing to prettify" - fi diff --git a/.replit b/.replit index dca57aaf8..0a8a2af05 100644 --- a/.replit +++ b/.replit @@ -1,2 +1,2 @@ -run = "npx node index.js" -language = "NodeJS" +run = "node index.js" +language = "Nix" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a3acf758..b67848c67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,4 +89,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org + [version]: http://contributor-covenant.org/version/1/4/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..54c9d3d90 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:18-alpine + +WORKDIR /usr/src/app + +COPY package*.json ./ + +RUN npm install + +COPY . . + +CMD [ "npm", "run", "deploy" ] + +CMD [ "node", "index.js" ] diff --git a/LICENSE.md b/LICENSE.md index 7f86d429e..b273948ec 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -4,4 +4,5 @@ - The bot-code should be used for **private hosting** and **personal usage** only. - Using the code for public usage is **not allowed**. -> **Note:** if you are found to be violating any of the above stated rule you might be asked to takedown your bot, happy listening!! Incase of any doubts in the license contact owner. \ No newline at end of file +> **Note:** if you are found to be violating any of the above stated rule you might be asked to takedown your bot, happy +> listening!! Incase of any doubts in the license contact owner. \ No newline at end of file diff --git a/README.md b/README.md index 74b45809a..b51f4be24 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ - [Node.js 16+](https://nodejs.org/en/download/) - [Lavalink Server](https://code.darrennathanael.com/how-to-lavalink) -- You'll need to run `npm run deploy` or `yarn deploy`. to initialized the slash commands. _You can do this on your pc locally_ +- You'll need to run `npm run deploy` or `yarn deploy`. to initialized the slash commands. _You can do this on your pc + locally_ > NOTE: Lavalink is needed for music functionality. You need to have a working Lavalink server to make the bot work. @@ -12,7 +13,8 @@ 1. Download and configure v5 in a seperate folder. 2. Kick your bot out of your server. -3. Reinvite the Bot with the right scopes. [Example Invite URL (Change CLIENT_ID)](https://discord.com/oauth2/authorize?client_id=CLIENT_ID&permissions=277083450689&scope=bot%20applications.commands) +3. Reinvite the Bot with the right + scopes. [Example Invite URL (Change CLIENT_ID)](https://discord.com/oauth2/authorize?client_id=CLIENT_ID&permissions=277083450689&scope=bot%20applications.commands) 4. Run `npm run deploy` or `yarn deploy` to initialize the slash commands. _You can do this on your pc locally_ ## 📝 | Tutorial @@ -32,7 +34,7 @@ Soon [![Deploy to heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) [![Open in Gitpod](https://camo.githubusercontent.com/76e60919474807718793857d8eb615e7a50b18b04050577e5a35c19421f260a3/68747470733a2f2f676974706f642e696f2f627574746f6e2f6f70656e2d696e2d676974706f642e737667)](https://gitpod.io/#https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) -## ✨ Contributors +## ✨ | Contributors Contributions are always welcomed :D Make sure to follow [Contributing.md](/CONTRIBUTING.md) @@ -40,4 +42,11 @@ Contributions are always welcomed :D Make sure to follow [Contributing.md](/CONT -Made with 💖 and JavaScript! +## 🌟 | Made with + +- [Discord.js](https://discord.js.org/) +- [Lavalink](https://github.com/freyacodes/Lavalink) with erela.js +- [Express](https://expressjs.com/) +- [Next JS](https://nextjs.org/) +- [Next UI](https://nextui.org) +- [Material UI Icons](https://mui.com/material-ui/material-icons/) diff --git a/api/home/dashboard.js b/api/home/dashboard.js deleted file mode 100644 index 672245839..000000000 --- a/api/home/dashboard.js +++ /dev/null @@ -1,9 +0,0 @@ -const { Router } = require("express"); -const api = Router(); - -api.get("*", (req, res) => - // haha nerds. - res.redirect("https://cdn.darrennathanael.com/video/free.mp4") -); - -module.exports = api; diff --git a/api/index.js b/api/index.js index 1c44928b0..9f41f960a 100644 --- a/api/index.js +++ b/api/index.js @@ -2,55 +2,119 @@ const express = require("express"); const fs = require("fs"); const { EventEmitter } = require("events"); const { join } = require("path"); +const session = require("express-session"); +const DiscordStrategy = require("passport-discord").Strategy; +const passport = require("passport"); const getConfig = require("../util/getConfig"); const DiscordMusicBot = require("../lib/DiscordMusicBot"); +const Auth = require("./middlewares/auth"); -class Server extends EventEmitter { - /** - * Create server ;-; - * @param {DiscordMusicBot} client - */ - constructor(client) { - super(); - getConfig() - .then((conf) => { - this.config = conf; - this.listen(); - }) - .catch((err) => { - throw Error(err); - }); - - this.app = express(); +passport.serializeUser(function (user, done) { + done(null, user); +}); - //API - fs.readdir(join(__dirname, "routes"), (err, files) => { - if (err) return console.log(err); - files.forEach((file) => { - this.app.use( - "/api/" + file.split(".")[0], - require(join(__dirname, "routes") + "/" + file) - ); - }); - }); - fs.readdir(join(__dirname, "home"), (err, files) => { - if (err) return console.log(err); - files.forEach((file) => { - this.app.use( - "/" + file.split(".")[0], - require(join(__dirname, "home") + "/" + file) - ); - }); - }); - this.app.use(express.static(join(__dirname, "..", "public"))); - // this.app.use((req, res) => { - // res.sendFile(join(__dirname, "..", "dashboard", "build", "index.html")); - // }); - } +passport.deserializeUser(function (obj, done) { + done(null, obj); +}); - listen() { - this.app.listen(this.config.port); - } +class Server extends EventEmitter { + /** + * Create server ;-; + * @param {DiscordMusicBot} client + */ + constructor(client) { + super(); + getConfig() + .then((conf) => { + this.config = conf; + this.listen(); + }) + .catch((err) => { + throw Error(err); + }); + + this.app = express(); + + //API + fs.readdir(join(__dirname, "routes"), (err, files) => { + if (err) { + return console.log(err); + } + files.forEach((file) => { + this.app.use( + "/api/" + file.split(".")[0], + require(join(__dirname, "routes") + "/" + file), + ); + }); + }); + + this.app.use(express.static(join(__dirname, "..", "public"))); + + //Static Routes + let dist = join(__dirname, "..", "dashboard", "out") + + this.app.use(express.static(dist)); + this.app.get("/login", (_req, res) => { + res.sendFile(join(dist, "login.html")); + res.redirect("/api/callback") + }); + this.app.get("/logout", (req, res) => { + if (req.user) { + req.logout(); + } + res.sendFile(join(dist, "logout.html")); + }); + this.app.get("/dashboard", Auth, (_req, res) => { + res.sendFile(join(dist, "dashboard.html")); + }); + this.app.get("/servers", Auth, (_req, res) => { + res.sendFile(join(dist, "servers.html")); + }); + + // Session and Passport + this.app.use(session({ + resave: true, + saveUninitialized: true, + secret: client.config.cookieSecret, + cookie: { + secure: client.config.website.startsWith("https://"), + sameSite: true, + }, + })); + this.app.use(passport.initialize()); + this.app.use(passport.session()); + + passport.use( + new DiscordStrategy( + { + clientID: client.config.clientId, + clientSecret: client.config.clientSecret, + callbackURL: client.config.website + "/api/callback", + scope: client.config.scopes.filter(a => !a.startsWith("app")).join(" "), + }, + function (accessToken, refreshToken, profile, done) { + process.nextTick(function () { + return done(null, profile); + }); + }, + ), + ); + + this.app.get( + "/api/callback", + passport.authenticate("discord", { + failureRedirect: "/", + session: true, + }), + function (req, res) { + res.redirect("/dashboard"); + }, + ); + } + + listen() { + this.app.listen(this.config.port); + } } module.exports = Server; diff --git a/api/middlewares/auth.js b/api/middlewares/auth.js new file mode 100644 index 000000000..26d55744f --- /dev/null +++ b/api/middlewares/auth.js @@ -0,0 +1,17 @@ +/** + * @param {import("express").Request} req + * @param {import("express").Response} res + * @param {import("express").NextFunction} next + * @returns {Promise} + */ + +const Auth = (req, res, next) => { + console.log("Middleware", req.user) + if (!req.user) { + return res.redirect("/login"); + } else { + next(); + } +}; + +module.exports = Auth; \ No newline at end of file diff --git a/api/routes/dashboard.js b/api/routes/dashboard.js new file mode 100644 index 000000000..48284ac8f --- /dev/null +++ b/api/routes/dashboard.js @@ -0,0 +1,16 @@ +const { Router } = require("express"); +const api = Router(); +const client = require("../../"); +const Auth = require("../middlewares/auth"); + +api.get("/", Auth, (req, res) => { + let data = { + commandsRan: client.commandsRan, + users: client.users.cache.size, + servers: client.guilds.cache.size, + songsPlayed: client.songsPlayed, + } + res.json(data); +}) + +module.exports = api \ No newline at end of file diff --git a/api/routes/data.js b/api/routes/data.js new file mode 100644 index 000000000..2cf90c4b0 --- /dev/null +++ b/api/routes/data.js @@ -0,0 +1,24 @@ +const { Router } = require("express"); +const api = Router(); + +const package = require("../../package.json"); +const client = require("../../") + +api.get("/", (req, res) => { + let data = { + name: client.user.username, + version: package.version, + commands: client.slashCommands.map(cmd => { + return { + name: cmd.name, + description: cmd.description, + } + }), + inviteURL: `https://discord.com/oauth2/authorize?client_id=${ client.config.clientId + }&permissions=${ client.config.permissions + }&scope=${ client.config.scopes.toString().replace(/,/g, "%20") }`, + } + res.json(data) +}); + +module.exports = api; diff --git a/api/routes/test.js b/api/routes/test.js deleted file mode 100644 index ce013bbc6..000000000 --- a/api/routes/test.js +++ /dev/null @@ -1,6 +0,0 @@ -const { Router } = require("express"); -const api = Router(); - -api.get("*", (req, res) => res.send("amogus")); - -module.exports = api; diff --git a/api/routes/title.js b/api/routes/title.js deleted file mode 100644 index c725977b5..000000000 --- a/api/routes/title.js +++ /dev/null @@ -1,11 +0,0 @@ -const { Router } = require("express"); -const api = Router(); - -// send title in json format -api.get("/", (req, res) => { - res.json({ - name: "Discord Music Bot", - }); -}); - -module.exports = api; diff --git a/commands/context/hi.js b/commands/context/hi.js index 56e2a51a1..f7488046f 100644 --- a/commands/context/hi.js +++ b/commands/context/hi.js @@ -1,14 +1,14 @@ const { ContextMenuCommandBuilder } = require("@discordjs/builders"); module.exports = { - command: new ContextMenuCommandBuilder().setName("Say Hello").setType(2), - - /** - * This function will handle context menu interaction - * @param {import("../lib/DiscordMusicBot")} client - * @param {import("discord.js").GuildContextMenuInteraction} interaction - */ - run: (client, interaction, options) => { - interaction.reply(`<@${interaction.options.getUser("user").id}>, Hello!`); - }, + command: new ContextMenuCommandBuilder().setName("Say Hello").setType(2), + + /** + * This function will handle context menu interaction + * @param {import("../lib/DiscordMusicBot")} client + * @param {import("discord.js").GuildContextMenuInteraction} interaction + */ + run: (client, interaction, options) => { + interaction.reply(`<@${ interaction.options.getUser("user").id }>, Hello!`); + }, }; diff --git a/commands/slash/247.js b/commands/slash/247.js index 243a5e705..994be35bd 100644 --- a/commands/slash/247.js +++ b/commands/slash/247.js @@ -1,50 +1,78 @@ +const colors = require("colors"); const { MessageEmbed } = require("discord.js"); const SlashCommand = require("../../lib/SlashCommand"); const command = new SlashCommand() - .setName("247") - .setDescription("toggles 24/7") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You need to join voice channel first before you can use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } + .setName("247") + .setDescription("Prevents the bot from ever disconnecting from a VC (toggle)") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There's nothing to play 24/7."), + ], + ephemeral: true, + }); + } + + let twentyFourSevenEmbed = new MessageEmbed().setColor( + client.config.embedColor, + ); + const twentyFourSeven = player.get("twentyFourSeven"); + + if (!twentyFourSeven || twentyFourSeven === false) { + player.set("twentyFourSeven", true); + } else { + player.set("twentyFourSeven", false); + } + + twentyFourSevenEmbed.setDescription( + `✅ | **24/7 mode is \`${ !twentyFourSeven? "ON" : "OFF" }\`**`, + ); + client.warn( + `Player: ${ player.options.guild } | [${ colors.blue( + "24/7", + ) }] has been [${ colors.blue( + !twentyFourSeven? "ENABLED" : "DISABLED", + ) }] in ${ + client.guilds.cache.get(player.options.guild) + ? client.guilds.cache.get(player.options.guild).name + : "a guild" + }`, + ); + + if (!player.playing && player.queue.totalSize === 0 && twentyFourSeven) { + player.destroy(); + } + + return interaction.reply({ embeds: [twentyFourSevenEmbed] }); + }); - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me.**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - if (!player) { - return interaction.reply({ - embeds: [client.ErrorEmbed("**There's nothing to play 24/7!**")], - }); - } else if (player.twentyFourSeven) { - player.twentyFourSeven = false; - const embed = client.Embed(`✅ | **24/7 mode is now off.**`); - return interaction.reply({ embeds: [embed] }); - } else { - player.twentyFourSeven = true; - const embed = client.Embed(`✅ | **24/7 mode is now on.**`); - return interaction.reply({ embeds: [embed] }); - } - }); module.exports = command; // check above message, it is a little bit confusing. and erros are not handled. probably should be fixed. // ok use catch ez kom follow meh ;_; // the above message meaning error, if it cant find it or take too long the bot crashed // play commanddddd, if timeout or takes 1000 years to find song it crashed // OKIE, leave the comment here for idk +// Comment very useful, 247 good :+1: +// twentyFourSeven = best; diff --git a/commands/slash/autoqueue.js b/commands/slash/autoqueue.js new file mode 100644 index 000000000..b310502c2 --- /dev/null +++ b/commands/slash/autoqueue.js @@ -0,0 +1,61 @@ +const colors = require("colors"); +const { MessageEmbed } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); + +const command = new SlashCommand() + .setName("autoqueue") + .setDescription("Automatically add songs to the queue (toggle)") + .setRun(async (client, interaction) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There's nothing playing in the queue"), + ], + ephemeral: true, + }); + } + + let embed = new MessageEmbed().setColor(client.config.embedColor); + const autoQueue = player.get("autoQueue"); + player.set("requester", interaction.guild.me); + + if (!autoQueue || autoQueue === false) { + player.set("autoQueue", true); + } else { + player.set("autoQueue", false); + } + embed.setDescription(`Auto Queue is \`${ !autoQueue? "ON" : "OFF" }\``); + client.warn( + `Player: ${ player.options.guild } | [${ colors.blue( + "AUTOQUEUE", + ) }] has been [${ colors.blue(!autoQueue? "ENABLED" : "DISABLED") }] in ${ + client.guilds.cache.get(player.options.guild) + ? client.guilds.cache.get(player.options.guild).name + : "a guild" + }`, + ); + + return interaction.reply({ embeds: [embed] }); + }); + +module.exports = command; diff --git a/commands/slash/clean.js b/commands/slash/clean.js new file mode 100644 index 000000000..c12320542 --- /dev/null +++ b/commands/slash/clean.js @@ -0,0 +1,49 @@ +const SlashCommand = require("../../lib/SlashCommand"); + +const command = new SlashCommand() + .setName("clean") + .setDescription("Cleans the last 100 bot messages from channel.") + .addIntegerOption((option) => + option + .setName("number") + .setDescription("Number of messages to delete.") + .setMinValue(2).setMaxValue(100) + .setRequired(false), + ) + .setRun(async (client, interaction, options) => { + + await interaction.deferReply(); + let number = interaction.options.getInteger("number"); + number = number && number < 100? ++number : 100; + + + interaction.channel.messages.fetch({ + limit: number, + }).then((messages) => { + const botMessages = []; + messages.filter(m => m.author.id === client.user.id).forEach(msg => botMessages.push(msg)) + + botMessages.shift(); + interaction.channel.bulkDelete(botMessages, true) + .then(async deletedMessages => { + //Filtering out messages that did not get deleted. + messages = messages.filter(msg => { + !deletedMessages.some(deletedMsg => deletedMsg == msg); + }); + if (messages.size > 0) { + client.log(`Deleting [${ messages.size }] messages older than 14 days.`) + for (const msg of messages) { + await msg.delete(); + } + } + + await interaction.editReply({ embeds: [client.Embed(`:white_check_mark: | Deleted ${ botMessages.length } bot messages`)] }); + setTimeout(() => { + interaction.deleteReply(); + }, 5000); + }) + + }); + }) + +module.exports = command; diff --git a/commands/slash/clear.js b/commands/slash/clear.js index 348d7d207..c5d6f001e 100644 --- a/commands/slash/clear.js +++ b/commands/slash/clear.js @@ -2,55 +2,53 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("clear") - .setDescription("Clear all tracks from queue") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **Nothing is playing right now...**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command!**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - if (!player.queue || !player.queue.length || player.queue.length === 0) { - let cembed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **Invalid, Not enough track to be cleared.**"); - - return interaction.reply({ embeds: [cembed], ephemeral: true }); - } - - player.queue.clear(); - - let clearembed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`✅ | **Cleared the queue!**`); - - return interaction.reply({ embeds: [clearembed] }); - }); - -module.exports = command; + .setName("clear") + .setDescription("Clear all tracks from queue") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Nothing is playing right now."), + ], + ephemeral: true, + }); + } + + if (!player.queue || !player.queue.length || player.queue.length === 0) { + let cembed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("❌ | **Invalid, Not enough track to be cleared.**"); + + return interaction.reply({ embeds: [cembed], ephemeral: true }); + } + + player.queue.clear(); + + let clearembed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`✅ | **Cleared the queue!**`); + + return interaction.reply({ embeds: [clearembed] }); + }); + +module.exports = command; \ No newline at end of file diff --git a/commands/slash/disconnect.js b/commands/slash/disconnect.js deleted file mode 100644 index e74392747..000000000 --- a/commands/slash/disconnect.js +++ /dev/null @@ -1,44 +0,0 @@ -const SlashCommand = require("../../lib/SlashCommand"); -const { MessageEmbed } = require("discord.js"); - -const command = new SlashCommand() - .setName("disconnect") - .setDescription("Stops the music and leaves the voice channel") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) - return interaction.reply({ - embeds: [client.ErrorEmbed("**Nothing is playing right now...**")], - }); - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command!**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - player.destroy(); - - interaction.reply({ - embeds: [client.Embed(`:wave: | **Disconnected!**`)], - }); - }); - -module.exports = command; diff --git a/commands/slash/filters.js b/commands/slash/filters.js index 136c832e3..ac17414e0 100644 --- a/commands/slash/filters.js +++ b/commands/slash/filters.js @@ -2,102 +2,101 @@ const { MessageEmbed } = require("discord.js"); const SlashCommand = require("../../lib/SlashCommand"); const command = new SlashCommand() - .setName("filters") - .setDescription("add or remove filters") - .addStringOption((option) => - option - .setName("preset") - .setDescription("the preset to add") - .setRequired(true) - .addChoice("Nightcore", "nightcore") - .addChoice("BassBoost", "bassboost") - .addChoice("Vaporwave", "vaporwave") - .addChoice("Pop", "pop") - .addChoice("Soft", "soft") - .addChoice("Treblebass", "treblebass") - .addChoice("Eight Dimension", "eightD") - .addChoice("Karaoke", "karaoke") - .addChoice("Vibrato", "vibrato") - .addChoice("Tremolo", "tremolo") - .addChoice("Reset", "off") - ) - - .setRun(async (client, interaction, options) => { - const args = interaction.options.getString("preset"); - - let player = client.manager.players.get(interaction.guild.id); - - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | There is no music playing in this guild!"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | You must be in a voice channel to use this command!" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | You must be in the same voice channel as the bot to use this command!" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - // create a new embed - let thing = new MessageEmbed().setColor(client.config.embedColor); - - if (args == "nightcore") { - thing.setDescription("✅ | Nightcore filter is now active!"); - player.nightcore = true; - } else if (args == "bassboost") { - thing.setDescription("✅ | BassBoost filter is now on!"); - player.bassboost = true; - } else if (args == "vaporwave") { - thing.setDescription("✅ | Vaporwave filter is now on!"); - player.vaporwave = true; - } else if (args == "pop") { - thing.setDescription("✅ | Pop filter is now on!"); - player.pop = true; - } else if (args == "soft") { - thing.setDescription("✅ | Soft filter is now on!"); - player.soft = true; - } else if (args == "treblebass") { - thing.setDescription("✅ | Treblebass filter is now on!"); - player.treblebass = true; - } else if (args == "eightD") { - thing.setDescription("✅ | Eight Dimension filter is now on!"); - player.eightD = true; - } else if (args == "karaoke") { - thing.setDescription("✅ | Karaoke filter is now on!"); - player.karaoke = true; - } else if (args == "vibrato") { - thing.setDescription("✅ | Vibrato filter is now on!"); - player.vibrato = true; - } else if (args == "tremolo") { - thing.setDescription("✅ | Tremolo filter is now on!"); - player.tremolo = true; - } else if (args == "off") { - thing.setDescription("✅ | EQ has been cleared!"); - player.reset(); - } else { - thing.setDescription("❌ | Invalid filter!"); - } - - return interaction.reply({ embeds: [thing] }); - }); + .setName("filters") + .setDescription("add or remove filters") + .addStringOption((option) => + option + .setName("preset") + .setDescription("the preset to add") + .setRequired(true) + .addChoices( + { name: "Nightcore", value: "nightcore" }, + { name: "BassBoost", value: "bassboost" }, + { name: "Vaporwave", value: "vaporwave" }, + { name: "Pop", value: "pop" }, + { name: "Soft", value: "soft" }, + { name: "Treblebass", value: "treblebass" }, + { name: "Eight Dimension", value: "eightD" }, + { name: "Karaoke", value: "karaoke" }, + { name: "Vibrato", value: "vibrato" }, + { name: "Tremolo", value: "tremolo" }, + { name: "Reset", value: "off" }, + ), + ) + + .setRun(async (client, interaction, options) => { + const args = interaction.options.getString("preset"); + + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There's no music playing."), + ], + ephemeral: true, + }); + } + + // create a new embed + let thing = new MessageEmbed().setColor(client.config.embedColor); + + if (args == "nightcore") { + thing.setDescription("✅ | Nightcore filter is now active!"); + player.nightcore = true; + } else if (args == "bassboost") { + thing.setDescription("✅ | BassBoost filter is now on!"); + player.bassboost = true; + } else if (args == "vaporwave") { + thing.setDescription("✅ | Vaporwave filter is now on!"); + player.vaporwave = true; + } else if (args == "pop") { + thing.setDescription("✅ | Pop filter is now on!"); + player.pop = true; + } else if (args == "soft") { + thing.setDescription("✅ | Soft filter is now on!"); + player.soft = true; + } else if (args == "treblebass") { + thing.setDescription("✅ | Treblebass filter is now on!"); + player.treblebass = true; + } else if (args == "eightD") { + thing.setDescription("✅ | Eight Dimension filter is now on!"); + player.eightD = true; + } else if (args == "karaoke") { + thing.setDescription("✅ | Karaoke filter is now on!"); + player.karaoke = true; + } else if (args == "vibrato") { + thing.setDescription("✅ | Vibrato filter is now on!"); + player.vibrato = true; + } else if (args == "tremolo") { + thing.setDescription("✅ | Tremolo filter is now on!"); + player.tremolo = true; + } else if (args == "off") { + thing.setDescription("✅ | EQ has been cleared!"); + player.reset(); + } else { + thing.setDescription("❌ | Invalid filter!"); + } + + return interaction.reply({ embeds: [thing] }); + }); module.exports = command; diff --git a/commands/slash/grab.js b/commands/slash/grab.js deleted file mode 100644 index 327161328..000000000 --- a/commands/slash/grab.js +++ /dev/null @@ -1,83 +0,0 @@ -const SlashCommand = require("../../lib/SlashCommand"); -const { MessageEmbed } = require("discord.js"); -const prettyMilliseconds = require("pretty-ms"); - -const command = new SlashCommand() - .setName("grab") - .setDescription("Saves current song to your DM's") - .setRun(async (client, interaction) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(":x: | **There's nothing playing**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - ":x: | **You must be in a voice channel to use this command!**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - ":x: | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - const save = new MessageEmbed() - .setColor(client.config.embedColor) - .setAuthor({ - name: "Saved track", - iconURL: `${interaction.user.displayAvatarURL({ dynamic: true })}`, - }) - .setDescription( - `**Saved [${player.queue.current.title}](${player.queue.current.uri}) to your DM**` - ) - .addFields( - { - name: "Track Duration", - value: `\`${prettyMilliseconds(player.queue.current.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Track Author", - value: `\`${player.queue.current.author}\``, - inline: true, - }, - { - name: "Requested Guild", - value: `\`${interaction.guild}\``, - inline: true, - } - ); - - interaction.user.send({ embeds: [save] }); - - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "Please check your **DM**. If you don't receive any message from me please make sure your **DM** is open" - ), - ], - ephemeral: true, - }); - }); - -module.exports = command; diff --git a/commands/slash/help.js b/commands/slash/help.js index ae5db4a53..7f46c691e 100644 --- a/commands/slash/help.js +++ b/commands/slash/help.js @@ -1,78 +1,99 @@ const SlashCommand = require("../../lib/SlashCommand"); -const { Client, Interaction, MessageActionRow, MessageButton, MessageEmbed } = require("discord.js"); +const { + Client, + Interaction, + MessageActionRow, + MessageButton, + MessageEmbed, +} = require("discord.js"); const LoadCommands = require("../../util/loadCommands"); const { filter } = require("lodash"); const command = new SlashCommand() -.setName("help") -.setDescription("Shows this list") -.setRun(async (client, interaction) => { - await interaction.deferReply().catch((_) => {}); - // map the commands name and description to the embed - const commands = await LoadCommands().then((cmds) => { - return [].concat(cmds.slash)/*.concat(cmds.context)*/; - }); - // from commands remove the ones that have "null" in the description - const filteredCommands = commands.filter((cmd) => cmd.description != "null"); - //console.log(filteredCommands); - const totalCmds = filteredCommands.length; - let maxPages = Math.ceil(totalCmds / client.config.cmdPerPage); - - // if git exists, then get commit hash - let gitHash = ""; - try { - gitHash = require("child_process") - .execSync("git rev-parse --short HEAD") - .toString() - .trim(); - } catch (e) { - // do nothing - gitHash = "unknown"; - } - - // default Page No. - let pageNo = 0; - - const helpEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setAuthor({ - name: `Commands of ${client.user.username}`, - iconURL: client.config.iconURL, - }) - .setTimestamp() - .setFooter({text: `Page ${pageNo + 1} / ${maxPages}`}); - - // initial temporary array - var tempArray = filteredCommands.slice(pageNo * client.config.cmdPerPage, (pageNo * client.config.cmdPerPage) + client.config.cmdPerPage); - - tempArray.forEach(cmd => { - helpEmbed.addField(cmd.name, cmd.description) - }); - helpEmbed.addField("Credits", `Discord Music Bot Version: v${ - require("../../package.json").version - }; Build: ${gitHash}` + - "\n" + - `[✨ Support Server](${client.config.supportServer}) | [Issues](${client.config.Issues}) | [Source](https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) | [Invite Me](https://discord.com/oauth2/authorize?client_id=${client.config.clientId}&permissions=${client.config.permissions}&scope=bot%20applications.commands)`); - - - // Construction of the buttons for the embed - const getButtons = (pageNo) => { - return new MessageActionRow().addComponents( - new MessageButton() - .setCustomId("help_cmd_but_2_app") - .setEmoji("◀ī¸") - .setStyle("PRIMARY") - .setDisabled(pageNo == 0), - new MessageButton() - .setCustomId("help_cmd_but_1_app") - .setEmoji("â–ļī¸") - .setStyle("PRIMARY") - .setDisabled(pageNo == (maxPages - 1)), + .setName("help") + .setDescription("Shows this list") + .setRun(async (client, interaction) => { + await interaction.deferReply().catch((_) => { + }); + // map the commands name and description to the embed + const commands = await LoadCommands().then((cmds) => { + return [].concat(cmds.slash) /*.concat(cmds.context)*/; + }); + // from commands remove the ones that have "null" in the description + const filteredCommands = commands.filter( + (cmd) => cmd.description != "null", + ); + //console.log(filteredCommands); + const totalCmds = filteredCommands.length; + let maxPages = Math.ceil(totalCmds / client.config.cmdPerPage); + + // if git exists, then get commit hash + let gitHash = ""; + try { + gitHash = require("child_process") + .execSync("git rev-parse --short HEAD") + .toString() + .trim(); + } catch (e) { + // do nothing + gitHash = "unknown"; + } + + // default Page No. + let pageNo = 0; + + const helpEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ + name: `Commands of ${ client.user.username }`, + iconURL: client.config.iconURL, + }) + .setTimestamp() + .setFooter({ text: `Page ${ pageNo + 1 } / ${ maxPages }` }); + + // initial temporary array + var tempArray = filteredCommands.slice( + pageNo * client.config.cmdPerPage, + pageNo * client.config.cmdPerPage + client.config.cmdPerPage, + ); + + tempArray.forEach((cmd) => { + helpEmbed.addField(cmd.name, cmd.description); + }); + helpEmbed.addField( + "Credits", + `Discord Music Bot Version: v${ + require("../../package.json").version + }; Build: ${ gitHash }` + + "\n" + + `[✨ Support Server](${ client.config.supportServer }) | [Issues](${ client.config.Issues }) | [Source](https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) | [Invite Me](https://discord.com/oauth2/authorize?client_id=${ client.config.clientId }&permissions=${ client.config.permissions }&scope=bot%20applications.commands)`, + ); + + // Construction of the buttons for the embed + const getButtons = (pageNo) => { + return new MessageActionRow().addComponents( + new MessageButton() + .setCustomId("help_cmd_but_2_app") + .setEmoji("◀ī¸") + .setStyle("PRIMARY") + .setDisabled(pageNo == 0), + new MessageButton() + .setCustomId("help_cmd_but_1_app") + .setEmoji("â–ļī¸") + .setStyle("PRIMARY") + .setDisabled(pageNo == maxPages - 1), ); }; - const tempMsg = await interaction.editReply({ embeds: [helpEmbed], components: [getButtons(pageNo)], fetchReply: true }); - const collector = tempMsg.createMessageComponentCollector({ time: 600000, componentType: "BUTTON" }); + const tempMsg = await interaction.editReply({ + embeds: [helpEmbed], + components: [getButtons(pageNo)], + fetchReply: true, + }); + const collector = tempMsg.createMessageComponentCollector({ + time: 600000, + componentType: "BUTTON", + }); collector.on("collect", async (iter) => { if (iter.customId === "help_cmd_but_1_app") { @@ -83,21 +104,31 @@ const command = new SlashCommand() helpEmbed.fields = []; - var tempArray = filteredCommands.slice(pageNo * client.config.cmdPerPage, (pageNo * client.config.cmdPerPage) + client.config.cmdPerPage); + var tempArray = filteredCommands.slice( + pageNo * client.config.cmdPerPage, + pageNo * client.config.cmdPerPage + client.config.cmdPerPage, + ); - tempArray.forEach(cmd => { + tempArray.forEach((cmd) => { //console.log(cmd); - helpEmbed.addField(cmd.name, cmd.description) - .setFooter({text: `Page ${pageNo + 1} / ${maxPages}`}); + helpEmbed + .addField(cmd.name, cmd.description) + .setFooter({ text: `Page ${ pageNo + 1 } / ${ maxPages }` }); + }); + helpEmbed.addField( + "Credits", + `Discord Music Bot Version: v${ + require("../../package.json").version + }; Build: ${ gitHash }` + + "\n" + + `[✨ Support Server](${ client.config.supportServer }) | [Issues](${ client.config.Issues }) | [Source](https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) | [Invite Me](https://discord.com/oauth2/authorize?client_id=${ client.config.clientId }&permissions=${ client.config.permissions }&scope=bot%20applications.commands)`, + ); + await iter.update({ + embeds: [helpEmbed], + components: [getButtons(pageNo)], + fetchReply: true, }); - helpEmbed.addField("Credits", `Discord Music Bot Version: v${ - require("../../package.json").version - }; Build: ${gitHash}` + - "\n" + - `[✨ Support Server](${client.config.supportServer}) | [Issues](${client.config.Issues}) | [Source](https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) | [Invite Me](https://discord.com/oauth2/authorize?client_id=${client.config.clientId}&permissions=${client.config.permissions}&scope=bot%20applications.commands)`); - await iter.update({ embeds: [helpEmbed], components: [getButtons(pageNo)], fetchReply: true }); }); - }); - - module.exports = command; + +module.exports = command; diff --git a/commands/slash/invite.js b/commands/slash/invite.js index cabfa6a3d..c6b402ce2 100644 --- a/commands/slash/invite.js +++ b/commands/slash/invite.js @@ -1,16 +1,30 @@ -const { MessageEmbed } = require("discord.js"); +const { MessageActionRow, MessageButton, MessageEmbed } = require("discord.js"); const SlashCommand = require("../../lib/SlashCommand"); const command = new SlashCommand() - .setName("invite") - .setDescription("Invite me to your server") - .setRun(async (client, interaction, options) => { - const embed = new MessageEmbed() - .setColor(client.config.embedColor) - .setTitle(`Invite me to your server`) - .setDescription( - `You can invite me to your server by clicking [here](https://discord.com/oauth2/authorize?client_id=${client.config.clientId}&permissions=${client.config.permissions}&scope=bot%20applications.commands)` - ); - return interaction.reply({ embeds: [embed] }); - }); + .setName("invite") + .setDescription("Invite me to your server") + .setRun(async (client, interaction, options) => { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setTitle(`Invite me to your server!`), + ], + components: [ + new MessageActionRow().addComponents( + new MessageButton() + .setLabel("Invite me") + .setStyle("LINK") + .setURL( + `https://discord.com/oauth2/authorize?client_id=${ + client.config.clientId + }&permissions=${ + client.config.permissions + }&scope=${ client.config.scopes.toString().replace(/,/g, "%20") }`, + ), + ), + ], + }); + }); module.exports = command; diff --git a/commands/slash/loop.js b/commands/slash/loop.js index 06270760c..84cf59ea0 100644 --- a/commands/slash/loop.js +++ b/commands/slash/loop.js @@ -2,44 +2,50 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("loop") - .setDescription("Loop the current song") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - return interaction.reply({ - embeds: [client.ErrorEmbed("❌ | **Nothing is playing right now...**")], - }); - } - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - if (player.setTrackRepeat(!player.trackRepeat)); - const trackRepeat = player.trackRepeat ? "enabled" : "disabled"; - - let loopembed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`👍 | **Loop has been \`${trackRepeat}\`**`); - interaction.reply({ embeds: [loopembed] }); - }); + .setName("loop") + .setDescription("Loops the current song") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Nothing is playing right now."), + ], + ephemeral: true, + }); + } + + if (player.setTrackRepeat(!player.trackRepeat)) { + ; + } + const trackRepeat = player.trackRepeat? "enabled" : "disabled"; + + interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`👍 | **Loop has been \`${ trackRepeat }\`**`), + ], + }); + }); module.exports = command; diff --git a/commands/slash/loopq.js b/commands/slash/loopq.js new file mode 100644 index 000000000..f90bf23a9 --- /dev/null +++ b/commands/slash/loopq.js @@ -0,0 +1,53 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +const command = new SlashCommand() + .setName("loopq") + .setDescription("Loop the current song queue") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no music playing."), + ], + ephemeral: true, + }); + } + + if (player.setQueueRepeat(!player.queueRepeat)) { + ; + } + const queueRepeat = player.queueRepeat? "enabled" : "disabled"; + + interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `:thumbsup: | **Loop queue is now \`${ queueRepeat }\`**`, + ), + ], + }); + }); + +module.exports = command; diff --git a/commands/slash/loopqueue.js b/commands/slash/loopqueue.js deleted file mode 100644 index 49fd1d416..000000000 --- a/commands/slash/loopqueue.js +++ /dev/null @@ -1,45 +0,0 @@ -const SlashCommand = require("../../lib/SlashCommand"); -const { MessageEmbed } = require("discord.js"); - -const command = new SlashCommand() - .setName("loopqueue") - .setDescription("Loop the queue") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - return interaction.reply({ - embeds: [client.ErrorEmbed("There is no music playing")], - }); - } - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You need to join voice channel first before you can use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me.**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - if (player.setQueueRepeat(!player.queueRepeat)); - const queueRepeat = player.queueRepeat ? "enabled" : "disabled"; - - let loopembed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`:thumbsup: | **Loop queue is now \`${queueRepeat}\`**`); - interaction.reply({ embeds: [loopembed] }); - }); - -module.exports = command; diff --git a/commands/slash/lyrics.js b/commands/slash/lyrics.js index de582acf8..4f1eb561b 100644 --- a/commands/slash/lyrics.js +++ b/commands/slash/lyrics.js @@ -1,110 +1,89 @@ const SlashCommand = require("../../lib/SlashCommand"); -const { MessageEmbed, MessageButton, MessageActionRow } = require("discord.js"); -const load = require("lodash"); +const { MessageEmbed } = require("discord.js"); const fetch = require("node-fetch"); const command = new SlashCommand() - .setName("lyrics") - .setDescription("Shows lyrics of a song") - // get user input - .addStringOption((option) => - option - .setName("song") - .setDescription("The song to get lyrics for") - .setRequired(false) - ) - .setRun(async (client, interaction, options) => { - await interaction.deferReply().catch((_) => {}); - - await interaction.editReply({ - embeds: [client.Embed(":mag_right: **Searching...**")], - }); - - const args = interaction.options.getString("song"); - - let player = client.manager.players.get(interaction.guild.id); - - if (!args && !player) - return interaction.editReply({ - embeds: [client.ErrorEmbed("❌ | **There's nothing playing**")], - }); - - // if no input, search for the current song. if no song console.log("No song input"); - let search = args ? args : player.queue.current.title; - let url = `https://api.darrennathanael.com/lyrics?song=${search}`; - - - // get the lyrics - let lyrics = await fetch(url).then((res) => res.json()); - - // if the status is not ok then send error embed and return - if (lyrics.response !== 200) { - let failEmbed = new MessageEmbed() - .setColor("RED") - .setDescription(`❌ | No lyrics found for ${search}! Please try again.`); - return interaction.editReply({ embeds: [failEmbed] }); - } - - let text = lyrics.lyrics; - - // check for pagination, if needed construct appropriate components, initialize first string to displate on page [0] - if(text.length > 4090){ - text = text.substring(0, 4090) + "..."; + .setName("lyrics") + .setDescription("Prints the lyrics of a song") + // get user input + .addStringOption((option) => + option + .setName("song") + .setDescription("The song to get lyrics for") + .setRequired(false), + ) + .setRun(async (client, interaction, options) => { + await interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("🔎 **Searching...**"), + ], + }); + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + const args = interaction.options.getString("song"); + if (!args && !player) { + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There's nothing playing"), + ], + }); + } + + let search = args? args : player.queue.current.title; + // Lavalink api for lyrics + let url = `https://api.darrennathanael.com/lyrics?song=${ search }`; + + let lyrics = await fetch(url) + .then((res) => { + return res.json(); + }) + .catch((err) => { + return err.name; + }); + if (!lyrics || lyrics.response !== 200 || lyrics === "FetchError") { + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription( + `❌ | No lyrics found for ${ search }!\nMake sure you typed in your search correctly.`, + ), + ], + }); + } + + let text = lyrics.lyrics; let lyricsEmbed = new MessageEmbed() .setColor(client.config.embedColor) - .setTitle(`${lyrics.full_title}`) + .setTitle(`${ lyrics.full_title }`) .setURL(lyrics.url) .setThumbnail(lyrics.thumbnail) .setDescription(text); - - let maxPages = Math.ceil(lyrics.lyrics.length / 4090); - - - // default page - let pageNo = 0; + if (text.length > 4096) { + text = text.substring(0, 4090) + "[...]"; + lyricsEmbed + .setDescription(text) + .setFooter({ text: "Truncated, the lyrics were too long." }); + } - // Construction of the buttons for the embed in case pagination is needed - const getButtons = (pageNo) => { - return new MessageActionRow().addComponents( - new MessageButton() - .setCustomId("lyrics_pag_but_2_app") - .setEmoji("◀ī¸") - .setStyle("PRIMARY") - .setDisabled(pageNo == 0), - new MessageButton() - .setCustomId("lyrics_pag_but_1_app") - .setEmoji("â–ļī¸") - .setStyle("PRIMARY") - .setDisabled(pageNo == (maxPages - 1)), - ); - }; - - const tempMsg = await interaction.editReply({ embeds: [lyricsEmbed], components: [getButtons(pageNo)], fetchReply: true }); - const collector = tempMsg.createMessageComponentCollector({ time: 600000, componentType: "BUTTON" }); - - collector.on("collect", async (iter) => { - if (iter.customId === "lyrics_pag_but_1_app") { - pageNo++; - } else if (iter.customId === "lyrics_pag_but_2_app") { - pageNo--; - } - - text = lyrics.lyrics.substring(pageNo * 4090, (pageNo * 4090) + 4090); - - lyricsEmbed.setDescription(text); - - await iter.update({ embeds: [lyricsEmbed], components: [getButtons(pageNo)], fetchReply: true }); - }); - } else { - return interaction.editReply({embeds: [new MessageEmbed() - .setColor(client.config.embedColor) - .setTitle(`${lyrics.full_title}`) - .setURL(lyrics.url) - .setThumbnail(lyrics.thumbnail) - .setDescription(text) - ]}); - } -}); + return interaction.editReply({ embeds: [lyricsEmbed] }); + }); -module.exports = command; +module.exports = command; diff --git a/commands/slash/move.js b/commands/slash/move.js index f4e345204..33125d0d7 100644 --- a/commands/slash/move.js +++ b/commands/slash/move.js @@ -2,74 +2,74 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("move") - .setDescription("Moves track to a different position") - .addIntegerOption((option) => - option - .setName("track") - .setDescription("The track number to move") - .setRequired(true) - ) - .addIntegerOption((option) => - option - .setName("position") - .setDescription("The position to move the track to") - .setRequired(true) - ) - - .setRun(async (client, interaction) => { - const track = interaction.options.getInteger("track"); - const position = interaction.options.getInteger("position"); - - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(":x: | **There's nothing playing**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("You must be in a voice channel to use this command!"); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You must be in the same voice channel as me to use this command!" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - let trackNum = Number(track) - 1; - if (trackNum < 0 || trackNum > player.queue.length - 1) { - return interaction.reply(":x: | **Invalid track number**"); - } - - let dest = Number(position) - 1; - if (dest < 0 || dest > player.queue.length - 1) { - return interaction.reply(":x: | **Invalid position number**"); - } - - const thing = player.queue[trackNum]; - player.queue.splice(trackNum, 1); - player.queue.splice(dest, 0, thing); - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(":white_check_mark: | **Moved track**"), - ], - }); - }); + .setName("move") + .setDescription("Moves track to a different position") + .addIntegerOption((option) => + option + .setName("track") + .setDescription("The track number to move") + .setRequired(true), + ) + .addIntegerOption((option) => + option + .setName("position") + .setDescription("The position to move the track to") + .setRequired(true), + ) + + .setRun(async (client, interaction) => { + const track = interaction.options.getInteger("track"); + const position = interaction.options.getInteger("position"); + + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There's nothing playing."), + ], + ephemeral: true, + }); + } + + let trackNum = Number(track) - 1; + if (trackNum < 0 || trackNum > player.queue.length - 1) { + return interaction.reply(":x: | **Invalid track number**"); + } + + let dest = Number(position) - 1; + if (dest < 0 || dest > player.queue.length - 1) { + return interaction.reply(":x: | **Invalid position number**"); + } + + const thing = player.queue[trackNum]; + player.queue.splice(trackNum, 1); + player.queue.splice(dest, 0, thing); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(":white_check_mark: | **Moved track**"), + ], + }); + }); module.exports = command; diff --git a/commands/slash/nowplaying.js b/commands/slash/nowplaying.js index bec367265..782861ea6 100644 --- a/commands/slash/nowplaying.js +++ b/commands/slash/nowplaying.js @@ -3,53 +3,77 @@ const SlashCommand = require("../../lib/SlashCommand"); const prettyMilliseconds = require("pretty-ms"); const command = new SlashCommand() - .setName("nowplaying") - .setDescription("Shows the current song playing in the voice channel.") - .setRun(async (client, interaction, options) => { - const player = interaction.client.manager.players.get(interaction.guild.id); - - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("There's nothing playing in the queue"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!player.playing) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("There's nothing playing."); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - const song = player.queue.current; - const embed = new MessageEmbed() - .setColor(client.config.embedColor) - .setTitle("Now playing â™Ē") - // show who requested the song via setField, also show the duration of the song - .setFields([ - { - name: "Requested by", - value: `<@${song.requester.id}>`, - inline: true, - }, - // show duration if live show live - { - name: "Duration", - value: song.isStream - ? `\`LIVE\`` - : `\`${prettyMilliseconds(player.position, { - secondsDecimalDigits: 0, - })} / ${prettyMilliseconds(song.duration, { - secondsDecimalDigits: 0, - })}\``, - inline: true, - }, - ]) - // show the thumbnail of the song using displayThumbnail("maxresdefault") - .setThumbnail(song.displayThumbnail("maxresdefault")) - // show the title of the song and link to it - .setDescription(`[${song.title}](${song.uri})`); - return interaction.reply({ embeds: [embed] }); - }); + .setName("nowplaying") + .setDescription("Shows the song currently playing in the voice channel.") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("The bot isn't in a channel."), + ], + ephemeral: true, + }); + } + + if (!player.playing) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There's nothing playing."), + ], + ephemeral: true, + }); + } + + const song = player.queue.current; + const embed = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ name: "Now Playing", iconURL: client.config.iconURL }) + // show who requested the song via setField, also show the duration of the song + .setFields([ + { + name: "Requested by", + value: `<@${ song.requester.id }>`, + inline: true, + }, + // show duration, if live show live + { + name: "Duration", + value: song.isStream + ? `\`LIVE\`` + : `\`${ prettyMilliseconds(player.position, { + secondsDecimalDigits: 0, + }) } / ${ prettyMilliseconds(song.duration, { + secondsDecimalDigits: 0, + }) }\``, + inline: true, + }, + ]) + // show the thumbnail of the song using displayThumbnail("maxresdefault") + .setThumbnail(song.displayThumbnail("maxresdefault")) + // show the title of the song and link to it + .setDescription(`[${ song.title }](${ song.uri })`); + return interaction.reply({ embeds: [embed] }); + }); module.exports = command; diff --git a/commands/slash/pause.js b/commands/slash/pause.js index dbc173a09..d70121bf9 100644 --- a/commands/slash/pause.js +++ b/commands/slash/pause.js @@ -2,53 +2,57 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("pause") - .setDescription("Pause current playing track") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **Nothing is playing right now...**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command!**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - if (player.paused) { - let pembed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **Current playing track is already paused!**"); - return interaction.reply({ embeds: [pembed], ephemeral: true }); - } - - player.pause(true); - - let pauseembed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`⏸ **Paused!**`); - return interaction.reply({ embeds: [pauseembed] }); - }); + .setName("pause") + .setDescription("Pauses the current playing track") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Nothing is playing."), + ], + ephemeral: true, + }); + } + + if (player.paused) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Current playing track is already paused!"), + ], + ephemeral: true, + }); + } + + player.pause(true); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`⏸ | **Paused!**`), + ], + }); + }); module.exports = command; diff --git a/commands/slash/ping.js b/commands/slash/ping.js new file mode 100644 index 000000000..c6071ef4d --- /dev/null +++ b/commands/slash/ping.js @@ -0,0 +1,67 @@ +const { MessageEmbed } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); + +const command = new SlashCommand() + .setName("ping") + .setDescription("View the bot's latency") + .setRun(async (client, interaction, options) => { + let msg = await interaction.channel.send({ + embeds: [ + new MessageEmbed() + .setDescription("🏓 | Fetching ping...") + .setColor("#6F8FAF"), + ], + }); + + let zap = "⚡"; + let green = "đŸŸĸ"; + let red = "🔴"; + let yellow = "🟡"; + + var botState = zap; + var apiState = zap; + + let apiPing = client.ws.ping; + let botPing = Math.floor(msg.createdAt - interaction.createdAt); + + if (apiPing >= 40 && apiPing < 200) { + apiState = green; + } else if (apiPing >= 200 && apiPing < 400) { + apiState = yellow; + } else if (apiPing >= 400) { + apiState = red; + } + + if (botPing >= 40 && botPing < 200) { + botState = green; + } else if (botPing >= 200 && botPing < 400) { + botState = yellow; + } else if (botPing >= 400) { + botState = red; + } + + msg.delete(); + interaction.reply({ + embeds: [ + new MessageEmbed() + .setTitle("🏓 | Pong!") + .addField( + "API Latency", + `\`\`\`yml\n${ apiState } | ${ apiPing }ms\`\`\``, + true, + ) + .addField( + "Bot Latency", + `\`\`\`yml\n${ botState } | ${ botPing }ms\`\`\``, + true, + ) + .setColor(client.config.embedColor) + .setFooter({ + text: `Requested by ${ interaction.user.tag }`, + iconURL: interaction.user.avatarURL(), + }), + ], + }); + }); + +module.exports = command; diff --git a/commands/slash/play.js b/commands/slash/play.js index 34ff76ea8..19864cc31 100644 --- a/commands/slash/play.js +++ b/commands/slash/play.js @@ -1,150 +1,176 @@ -// const { Manager } = require("erela.js/structures/Manager"); const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("play") - .setDescription("Play music in the voice channel") - .addStringOption((option) => - option - .setName("query") - .setDescription("Search string to search the music") - .setRequired(true) - ) - .setRun(async (client, interaction, options) => { - let channel = await client.getChannel(client, interaction); - if (!channel) return; - - let node = await client.getLavalink(client); - if (!node) { - return interaction.reply({ - embeds: [client.ErrorEmbed("Lavalink node is not connected")], - }); - } - let query = options.getString("query", true); - let player = client.createPlayer(interaction.channel, channel); - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - if (player.state !== "CONNECTED") { - player.connect(); - } - // console.log(player); - // if the channel is a stage channel then request to speak - if (channel.type == "GUILD_STAGE_VOICE") { - setTimeout(() => { - if (interaction.guild.me.voice.suppress == true) { - try { - interaction.guild.me.voice.setSuppressed(false); - } catch (e) { - interaction.guild.me.voice.setRequestToSpeak(true); - } - } - }, 2000); // set timeout are here, because bot sometimes takes time before reconising it's a stage. - } - - await interaction.reply({ - embeds: [client.Embed(":mag_right: **Searching...**")], - }); - - let res = await player.search(query, interaction.user).catch((err) => { - client.error(err); - return { - loadType: "LOAD_FAILED", - }; - }); - - if (res.loadType === "LOAD_FAILED") { - if (!player.queue.current) player.destroy(); - return interaction - .editReply({ - embeds: [client.ErrorEmbed("There was an error while searching")], - }) - .catch(this.warn); - } - - if (res.loadType === "NO_MATCHES") { - if (!player.queue.current) player.destroy(); - return interaction - .editReply({ - embeds: [client.ErrorEmbed("No results were found")], - }) - .catch(this.warn); - } - - if (res.loadType === "TRACK_LOADED" || res.loadType === "SEARCH_RESULT") { - player.queue.add(res.tracks[0]); - if (!player.playing && !player.paused && !player.queue.size) - player.play(); - let addQueueEmbed = client - .Embed() - .setAuthor({ name: "Added to queue", iconURL: client.config.iconURL }) - //.setAuthor("Added to queue", client.config.iconURL) Deprecated soon - .setDescription( - `[${res.tracks[0].title}](${res.tracks[0].uri})` || "No Title" - ) - .setURL(res.tracks[0].uri) - .addField("Author", res.tracks[0].author, true) - .addField( - "Duration", - res.tracks[0].isStream - ? `\`LIVE\`` - : `\`${client.ms(res.tracks[0].duration, { - colonNotation: true, - })}\``, - true - ); - try { - addQueueEmbed.setThumbnail( - res.tracks[0].displayThumbnail("maxresdefault") - ); - } catch (err) { - addQueueEmbed.setThumbnail(res.tracks[0].thumbnail); - } - if (player.queue.totalSize > 1) - addQueueEmbed.addField( - "Position in queue", - `${player.queue.size - 0}`, - true - ); - return interaction - .editReply({ embeds: [addQueueEmbed] }) - .catch(this.warn); - } - - if (res.loadType === "PLAYLIST_LOADED") { - player.queue.add(res.tracks); - if ( - !player.playing && - !player.paused && - player.queue.totalSize === res.tracks.length - ) - player.play(); - let playlistEmbed = client - .Embed() - .setAuthor({ - name: "Playlist added to queue", - iconURL: client.config.iconURL, - }) - //.setAuthor("Playlist added to queue", client.config.iconURL) - .setThumbnail(res.tracks[0].thumbnail) - .setDescription(`[${res.playlist.name}](${query})`) - .addField("Enqueued", `\`${res.tracks.length}\` songs`, false) - .addField( - "Playlist duration", - `\`${client.ms(res.playlist.duration, { - colonNotation: true, - })}\``, - false - ); - return interaction - .editReply({ embeds: [playlistEmbed] }) - .catch(this.warn); - } - }); + .setName("play") + .setDescription( + "Searches and plays the requested song\n__Supports:__\nYoutube, Spotify, Deezer, Apple Music", + ) + .addStringOption((option) => + option + .setName("query") + .setDescription("What am I looking for?") + .setRequired(true), + ) + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.createPlayer(interaction.channel, channel); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (player.state !== "CONNECTED") { + player.connect(); + } + + if (channel.type == "GUILD_STAGE_VOICE") { + setTimeout(() => { + if (interaction.guild.me.voice.suppress == true) { + try { + interaction.guild.me.voice.setSuppressed(false); + } catch (e) { + interaction.guild.me.voice.setRequestToSpeak(true); + } + } + }, 2000); //recognizing it's a stage channel? + } + + await interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(":mag_right: **Searching...**"), + ], + }); + + let query = options.getString("query", true); + let res = await player.search(query, interaction.user).catch((err) => { + client.error(err); + return { + loadType: "LOAD_FAILED", + }; + }); + + if (res.loadType === "LOAD_FAILED") { + if (!player.queue.current) { + player.destroy(); + } + return interaction + .editReply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There was an error while searching"), + ], + }) + .catch(this.warn); + } + + if (res.loadType === "NO_MATCHES") { + if (!player.queue.current) { + player.destroy(); + } + return interaction + .editReply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("No results were found"), + ], + }) + .catch(this.warn); + } + + if (res.loadType === "TRACK_LOADED" || res.loadType === "SEARCH_RESULT") { + player.queue.add(res.tracks[0]); + + if (!player.playing && !player.paused && !player.queue.size) { + player.play(); + } + + let addQueueEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ name: "Added to queue", iconURL: client.config.iconURL }) + .setDescription( + `[${ res.tracks[0].title }](${ res.tracks[0].uri })` || "No Title", + ) + .setURL(res.tracks[0].uri) + .addField("Added by", `<@${ interaction.user.id }>`, true) + .addField( + "Duration", + res.tracks[0].isStream + ? `\`LIVE\`` + : `\`${ client.ms(res.tracks[0].duration, { + colonNotation: true, + }) }\``, + true, + ); + + try { + addQueueEmbed.setThumbnail( + res.tracks[0].displayThumbnail("maxresdefault"), + ); + } catch (err) { + addQueueEmbed.setThumbnail(res.tracks[0].thumbnail); + } + + if (player.queue.totalSize > 1) { + addQueueEmbed.addField( + "Position in queue", + `${ player.queue.size }`, + true, + ); + } else { + player.queue.previous = player.queue.current; + } + + return interaction + .editReply({ embeds: [addQueueEmbed] }) + .catch(this.warn); + } + + if (res.loadType === "PLAYLIST_LOADED") { + player.queue.add(res.tracks); + + if ( + !player.playing && + !player.paused && + player.queue.totalSize === res.tracks.length + ) { + player.play(); + } + + let playlistEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ + name: "Playlist added to queue", + iconURL: client.config.iconURL, + }) + .setThumbnail(res.tracks[0].thumbnail) + .setDescription(`[${ res.playlist.name }](${ query })`) + .addField("Enqueued", `\`${ res.tracks.length }\` songs`, false) + .addField( + "Playlist duration", + `\`${ client.ms(res.playlist.duration, { colonNotation: true }) }\``, + false, + ); + + return interaction + .editReply({ embeds: [playlistEmbed] }) + .catch(this.warn); + } + }); module.exports = command; diff --git a/commands/slash/previous.js b/commands/slash/previous.js index 3070b8d91..89741268c 100644 --- a/commands/slash/previous.js +++ b/commands/slash/previous.js @@ -2,62 +2,63 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("previous") - .setDescription("Go back to the previous song.") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **There's nothing playing in the queue**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - if (!player.queue.previous) - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **There is no previous song in the queue.**"), - ], - }); - - const currentSong = player.queue.current; - player.play(player.queue.previous); - interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `⏎ | Previous song: **${currentSong.title}** by **${currentSong.requester.username}**` - ), - ], - }); - - if (currentSong) player.queue.unshift(currentSong); - }); + .setName("previous") + .setDescription("Go back to the previous song.") + .setRun(async (client, interaction) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There are no previous songs for this session."), + ], + ephemeral: true, + }); + } + + let previousSong = player.queue.previous; + + if (!previousSong) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no previous song in the queue."), + ], + }); + } + + const currentSong = player.queue.current; + player.play(previousSong); + interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `⏎ | Previous song: **${ previousSong.title }**`, + ), + ], + }); + + previousSong = currentSong; + }); module.exports = command; diff --git a/commands/slash/queue.js b/commands/slash/queue.js index d28ba445f..869164202 100644 --- a/commands/slash/queue.js +++ b/commands/slash/queue.js @@ -4,294 +4,327 @@ const load = require("lodash"); const pms = require("pretty-ms"); const command = new SlashCommand() - .setName("queue") - .setDescription("Shows the current queue") - - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("There's nothing playing in the queue"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!player.playing) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("There's nothing playing."); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You have to join voice channel first before you can use this command" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You must be in the same voice channel as me first before you can use this command" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - await interaction.deferReply().catch(() => {}); - - if (!player.queue.size || player.queue.size === 0) { - let song = player.queue.current; - const embed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`**â™Ē | Now playing:** [${song.title}](${song.uri})`) - .addFields( - { - name: "Duration", - value: `\`${pms(player.position, { colonNotation: true })} / ${pms( - player.queue.current.duration, - { colonNotation: true } - )}\``, - inline: true, - }, - { - name: "Volume", - value: `\`${player.volume}\``, - inline: true, - }, - { - name: "Total Tracks", - value: `\`${player.queue.totalSize - 1}\``, - colonNotation: true, - inline: true, - } - ); - - await interaction.editReply({ - embeds: [embed], - }); - } else { - const mapping = player.queue.map( - (t, i) => `\` ${++i} \` [${t.title}](${t.uri}) [${t.requester}]` - ); - - const chunk = load.chunk(mapping, 10); - const pages = chunk.map((s) => s.join("\n")); - let page = interaction.options.getNumber("page"); - if (!page) page = 0; - if (page) page = page - 1; - if (page > pages.length) page = 0; - if (page < 0) page = 0; - - if (player.queue.size < 11 || player.queue.totalSize < 11) { - let song = player.queue.current; - const embedTwo = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `**â™Ē | Now playing:** [${song.title}](${song.uri}) [${player.queue.current.requester}]\n\n**Queued Tracks**\n${pages[page]}` - ) - .addFields( - { - name: "Track Duration", - value: `\`${pms(player.position, { - colonNotation: true, - })} / ${pms(player.queue.current.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks Duration", - value: `\`${pms(player.queue.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks", - value: `\`${player.queue.totalSize - 1}\``, - colonNotation: true, - inline: true, - } - ) - .setFooter({ - text: `Page ${page + 1}/${pages.length}`, - }); - - await interaction - .editReply({ - embeds: [embedTwo], - }) - .catch(() => {}); - } else { - let song = player.queue.current; - const embedThree = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `**â™Ē | Now playing:** [${song.title}](${song.uri}) [${player.queue.current.requester}]\n\n**Queued Tracks**\n${pages[page]}` - ) - .addFields( - { - name: "Track Duration", - value: `\`${pms(player.position, { - colonNotation: true, - })} / ${pms(player.queue.current.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks Duration", - value: `\`${pms(player.queue.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks", - value: `\`${player.queue.totalSize - 1}\``, - colonNotation: true, - inline: true, - } - ) - .setFooter({ - text: `Page ${page + 1}/${pages.length}`, - }); - - const buttonOne = new MessageButton() - .setCustomId("queue_cmd_but_1_app") - .setEmoji("⏭ī¸") - .setStyle("PRIMARY"); - const buttonTwo = new MessageButton() - .setCustomId("queue_cmd_but_2_app") - .setEmoji("⏎ī¸") - .setStyle("PRIMARY"); - - await interaction - .editReply({ - embeds: [embedThree], - components: [ - new MessageActionRow().addComponents([buttonTwo, buttonOne]), - ], - }) - .catch(() => {}); - - const collector = interaction.channel.createMessageComponentCollector({ - filter: (b) => { - if (b.user.id === interaction.user.id) return true; - else - return b - .reply({ - content: `Only **${interaction.user.tag}** can use this button.`, - ephemeral: true, - }) - .catch(() => {}); - }, - time: 60000 * 5, - idle: 30e3, - }); - - collector.on("collect", async (button) => { - if (button.customId === "queue_cmd_but_1_app") { - await button.deferUpdate().catch(() => {}); - page = page + 1 < pages.length ? ++page : 0; - - const embedFour = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `**â™Ē | Now playing:** [${song.title}](${song.uri}) [${player.queue.current.requester}]\n\n**Queued Tracks**\n${pages[page]}` - ) - .addFields( - { - name: "Track Duration", - value: `\`${pms(player.position, { - colonNotation: true, - })} / ${pms(player.queue.current.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks Duration", - value: `\`${pms(player.queue.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks", - value: `\`${player.queue.totalSize - 1}\``, - colonNotation: true, - inline: true, - } - ) - .setFooter({ - text: `Page ${page + 1}/${pages.length}`, - }); - - await interaction.editReply({ - embeds: [embedFour], - components: [ - new MessageActionRow().addComponents([buttonTwo, buttonOne]), - ], - }); - } else if (button.customId === "queue_cmd_but_2_app") { - await button.deferUpdate().catch(() => {}); - page = page > 0 ? --page : pages.length - 1; - - const embedFive = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `**â™Ē | Now playing:** [${song.title}](${song.uri}) [${player.queue.current.requester}]\n\n**Queued Tracks**\n${pages[page]}` - ) - .addFields( - { - name: "Track Duration", - value: `\`${pms(player.position, { - colonNotation: true, - })} / ${pms(player.queue.current.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks Duration", - value: `\`${pms(player.queue.duration, { - colonNotation: true, - })}\``, - inline: true, - }, - { - name: "Total Tracks", - value: `\`${player.queue.totalSize - 1}\``, - colonNotation: true, - inline: true, - } - ) - .setFooter({ - text: `Page ${page + 1}/${pages.length}`, - }); - - await interaction - .editReply({ - embeds: [embedFive], - components: [ - new MessageActionRow().addComponents([buttonTwo, buttonOne]), - ], - }) - .catch(() => {}); - } else return; - }); - } - } - }); + .setName("queue") + .setDescription("Shows the current queue") + + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There are no songs in the queue."), + ], + ephemeral: true, + }); + } + + if (!player.playing) { + const queueEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("There's nothing playing."); + return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); + } + + await interaction.deferReply().catch(() => { + }); + + if (!player.queue.size || player.queue.size === 0) { + let song = player.queue.current; + const embed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`**â™Ē | Now playing:** [${ song.title }](${ song.uri })`) + .addFields( + { + name: "Duration", + value: song.isStream + ? `\`LIVE\`` + : `\`${ pms(player.position, { colonNotation: true }) } / ${ pms( + player.queue.current.duration, + { colonNotation: true }, + ) }\``, + inline: true, + }, + { + name: "Volume", + value: `\`${ player.volume }\``, + inline: true, + }, + { + name: "Total Tracks", + value: `\`${ player.queue.totalSize - 1 }\``, + colonNotation: true, + inline: true, + }, + ); + + await interaction.editReply({ + embeds: [embed], + }); + } else { + let queueDuration = player.queue.duration.valueOf() + if (player.queue.current.isStream) { + queueDuration -= player.queue.current.duration + } + for (let i = 0; i < player.queue.length; i++) { + if (player.queue[i].isStream) { + queueDuration -= player.queue[i].duration + } + } + + const mapping = player.queue.map( + (t, i) => `\` ${ ++i } \` [${ t.title }](${ t.uri }) [${ t.requester }]`, + ); + + const chunk = load.chunk(mapping, 10); + const pages = chunk.map((s) => s.join("\n")); + let page = interaction.options.getNumber("page"); + if (!page) { + page = 0; + } + if (page) { + page = page - 1; + } + if (page > pages.length) { + page = 0; + } + if (page < 0) { + page = 0; + } + + if (player.queue.size < 11 || player.queue.totalSize < 11) { + let song = player.queue.current; + const embedTwo = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ song.title }](${ song.uri }) [${ player.queue.current.requester }]\n\n**Queued Tracks**\n${ pages[page] }`, + ) + .addFields( + { + name: "Track Duration", + value: song.isStream + ? `\`LIVE\`` + : `\`${ pms(player.position, { colonNotation: true }) } / ${ pms( + player.queue.current.duration, + { colonNotation: true }, + ) }\``, + inline: true, + }, + { + name: "Total Tracks Duration", + value: `\`${ pms(queueDuration, { + colonNotation: true, + }) }\``, + inline: true, + }, + { + name: "Total Tracks", + value: `\`${ player.queue.totalSize - 1 }\``, + colonNotation: true, + inline: true, + }, + ) + .setFooter({ + text: `Page ${ page + 1 }/${ pages.length }`, + }); + + await interaction + .editReply({ + embeds: [embedTwo], + }) + .catch(() => { + }); + } else { + let song = player.queue.current; + const embedThree = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ song.title }](${ song.uri }) [${ player.queue.current.requester }]\n\n**Queued Tracks**\n${ pages[page] }`, + ) + .addFields( + { + name: "Track Duration", + value: song.isStream + ? `\`LIVE\`` + : `\`${ pms(player.position, { colonNotation: true }) } / ${ pms( + player.queue.current.duration, + { colonNotation: true }, + ) }\``, + inline: true, + }, + { + name: "Total Tracks Duration", + value: `\`${ pms(queueDuration, { + colonNotation: true, + }) }\``, + inline: true, + }, + { + name: "Total Tracks", + value: `\`${ player.queue.totalSize - 1 }\``, + colonNotation: true, + inline: true, + }, + ) + .setFooter({ + text: `Page ${ page + 1 }/${ pages.length }`, + }); + + const buttonOne = new MessageButton() + .setCustomId("queue_cmd_but_1_app") + .setEmoji("⏭ī¸") + .setStyle("PRIMARY"); + const buttonTwo = new MessageButton() + .setCustomId("queue_cmd_but_2_app") + .setEmoji("⏎ī¸") + .setStyle("PRIMARY"); + + await interaction + .editReply({ + embeds: [embedThree], + components: [ + new MessageActionRow().addComponents([buttonTwo, buttonOne]), + ], + }) + .catch(() => { + }); + + const collector = interaction.channel.createMessageComponentCollector({ + filter: (b) => { + if (b.user.id === interaction.user.id) { + return true; + } else { + return b + .reply({ + content: `Only **${ interaction.user.tag }** can use this button.`, + ephemeral: true, + }) + .catch(() => { + }); + } + }, + time: 60000 * 5, + idle: 30e3, + }); + + collector.on("collect", async (button) => { + if (button.customId === "queue_cmd_but_1_app") { + await button.deferUpdate().catch(() => { + }); + page = page + 1 < pages.length? ++page : 0; + + const embedFour = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ song.title }](${ song.uri }) [${ player.queue.current.requester }]\n\n**Queued Tracks**\n${ pages[page] }`, + ) + .addFields( + { + name: "Track Duration", + value: song.isStream + ? `\`LIVE\`` + : `\`${ pms(player.position, { colonNotation: true }) } / ${ pms( + player.queue.current.duration, + { colonNotation: true }, + ) }\``, + inline: true, + }, + { + name: "Total Tracks Duration", + value: `\`${ pms(queueDuration, { + colonNotation: true, + }) }\``, + inline: true, + }, + { + name: "Total Tracks", + value: `\`${ player.queue.totalSize - 1 }\``, + colonNotation: true, + inline: true, + }, + ) + .setFooter({ + text: `Page ${ page + 1 }/${ pages.length }`, + }); + + await interaction.editReply({ + embeds: [embedFour], + components: [ + new MessageActionRow().addComponents([buttonTwo, buttonOne]), + ], + }); + } else if (button.customId === "queue_cmd_but_2_app") { + await button.deferUpdate().catch(() => { + }); + page = page > 0? --page : pages.length - 1; + + const embedFive = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ song.title }](${ song.uri }) [${ player.queue.current.requester }]\n\n**Queued Tracks**\n${ pages[page] }`, + ) + .addFields( + { + name: "Track Duration", + value: song.isStream + ? `\`LIVE\`` + : `\`${ pms(player.position, { colonNotation: true }) } / ${ pms( + player.queue.current.duration, + { colonNotation: true }, + ) }\``, + inline: true, + }, + { + name: "Total Tracks Duration", + value: `\`${ pms(queueDuration, { + colonNotation: true, + }) }\``, + inline: true, + }, + { + name: "Total Tracks", + value: `\`${ player.queue.totalSize - 1 }\``, + colonNotation: true, + inline: true, + }, + ) + .setFooter({ + text: `Page ${ page + 1 }/${ pages.length }`, + }); + + await interaction + .editReply({ + embeds: [embedFive], + components: [ + new MessageActionRow().addComponents([buttonTwo, buttonOne]), + ], + }) + .catch(() => { + }); + } else { + return; + } + }); + } + } + }); module.exports = command; diff --git a/commands/slash/reload.js b/commands/slash/reload.js index bc2ced5b0..e97f543de 100644 --- a/commands/slash/reload.js +++ b/commands/slash/reload.js @@ -4,79 +4,86 @@ const fs = require("fs"); const path = require("path"); const command = new SlashCommand() - .setName("reload") - .setDescription("Reload all commands") - .setRun(async (client, interaction, options) => { - if (interaction.user.id === client.config.adminId) { - try { - let ContextCommandsDirectory = path.join(__dirname, "..", "context"); - fs.readdir(ContextCommandsDirectory, (err, files) => { - files.forEach((file) => { - delete require.cache[ - require.resolve(ContextCommandsDirectory + "/" + file) - ]; - let cmd = require(ContextCommandsDirectory + "/" + file); - if (!cmd.command || !cmd.run) - return this.warn( - "❌ Unable to load Command: " + - file.split(".")[0] + - ", File doesn't have either command/run" - ); - client.contextCommands.set(file.split(".")[0].toLowerCase(), cmd); - }); - }); - - let SlashCommandsDirectory = path.join(__dirname, "..", "slash"); - fs.readdir(SlashCommandsDirectory, (err, files) => { - files.forEach((file) => { - delete require.cache[ - require.resolve(SlashCommandsDirectory + "/" + file) - ]; - let cmd = require(SlashCommandsDirectory + "/" + file); - - if (!cmd || !cmd.run) - return this.warn( - "❌ Unable to load Command: " + - file.split(".")[0] + - ", File doesn't have an valid command with run function" - ); - client.slashCommands.set(file.split(".")[0].toLowerCase(), cmd); - }); - }); - - const totalCmds = - client.slashCommands.size + client.contextCommands.size; - client.log(`Reloaded ${totalCmds} commands!`); - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`Sucessfully Reloaded \`${totalCmds}\` Commands!`) - .setFooter({text: `${client.user.username} was reloaded by ${interaction.user.username}`}) - .setTimestamp(), - ], ephemeral: true - }); - } catch (err) { - console.log(err); - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "An error has occured. For more details please check console." - ), - ], ephemeral: true - }); - } - } else { - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("You are not authorized to use this command!"), - ], ephemeral: true - }); - } - }); + .setName("reload") + .setDescription("Reload all commands") + .setRun(async (client, interaction, options) => { + if (interaction.user.id === client.config.adminId) { + try { + let ContextCommandsDirectory = path.join(__dirname, "..", "context"); + fs.readdir(ContextCommandsDirectory, (err, files) => { + files.forEach((file) => { + delete require.cache[ + require.resolve(ContextCommandsDirectory + "/" + file) + ]; + let cmd = require(ContextCommandsDirectory + "/" + file); + if (!cmd.command || !cmd.run) { + return this.warn( + "❌ Unable to load Command: " + + file.split(".")[0] + + ", File doesn't have either command/run", + ); + } + client.contextCommands.set(file.split(".")[0].toLowerCase(), cmd); + }); + }); + + let SlashCommandsDirectory = path.join(__dirname, "..", "slash"); + fs.readdir(SlashCommandsDirectory, (err, files) => { + files.forEach((file) => { + delete require.cache[ + require.resolve(SlashCommandsDirectory + "/" + file) + ]; + let cmd = require(SlashCommandsDirectory + "/" + file); + + if (!cmd || !cmd.run) { + return this.warn( + "❌ Unable to load Command: " + + file.split(".")[0] + + ", File doesn't have an valid command with run function", + ); + } + client.slashCommands.set(file.split(".")[0].toLowerCase(), cmd); + }); + }); + + const totalCmds = + client.slashCommands.size + client.contextCommands.size; + client.log(`Reloaded ${ totalCmds } commands!`); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`Sucessfully Reloaded \`${ totalCmds }\` Commands!`) + .setFooter({ + text: `${ client.user.username } was reloaded by ${ interaction.user.username }`, + }) + .setTimestamp(), + ], + ephemeral: true, + }); + } catch (err) { + console.log(err); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + "An error has occured. For more details please check console.", + ), + ], + ephemeral: true, + }); + } + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("You are not authorized to use this command!"), + ], + ephemeral: true, + }); + } + }); module.exports = command; diff --git a/commands/slash/remove.js b/commands/slash/remove.js index 2dd429f04..752cc5c83 100644 --- a/commands/slash/remove.js +++ b/commands/slash/remove.js @@ -2,69 +2,67 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("remove") - .setDescription("Remove track you don't want from queue") - .addNumberOption((option) => - option - .setName("number") - .setDescription("Enter track number.") - .setRequired(true) - ) - - .setRun(async (client, interaction) => { - const args = interaction.options.getNumber("number"); - - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("There's nothing playing in the queue"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You have to join voice channel first before you can use this command" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You must be in the same voice channel as me first before you can use this command" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - await interaction.deferReply(); - - const position = Number(args) - 1; - if (position > player.queue.size) { - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `Current queue has only **${player.queue.size}** track` - ); - return interaction.editReply({ embeds: [thing] }); - } - - const song = player.queue[position]; - player.queue.remove(position); - - const number = position + 1; - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`Removed track number **${number}** from queue`); - return interaction.editReply({ embeds: [thing] }); - }); + .setName("remove") + .setDescription("Remove track you don't want from queue") + .addNumberOption((option) => + option + .setName("number") + .setDescription("Enter track number.") + .setRequired(true), + ) + + .setRun(async (client, interaction) => { + const args = interaction.options.getNumber("number"); + + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There are no songs to remove."), + ], + ephemeral: true, + }); + } + + await interaction.deferReply(); + + const position = Number(args) - 1; + if (position > player.queue.size) { + let thing = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `Current queue has only **${ player.queue.size }** track`, + ); + return interaction.editReply({ embeds: [thing] }); + } + + const song = player.queue[position]; + player.queue.remove(position); + + const number = position + 1; + let thing = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`Removed track number **${ number }** from queue`); + return interaction.editReply({ embeds: [thing] }); + }); module.exports = command; diff --git a/commands/slash/replay.js b/commands/slash/replay.js index 213171720..b3dd2ed0e 100644 --- a/commands/slash/replay.js +++ b/commands/slash/replay.js @@ -2,52 +2,50 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("replay") - .setDescription("Replay current playing track") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const QueueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("There's nothing playing in the queue"); - return interaction.reply({ embeds: [QueueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const JoinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You have to join voice channel first before you can use this command" - ); - return interaction.reply({ embeds: [JoinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const SameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "You must be in the same voice channel as me first before you can use this command" - ); - return interaction.reply({ embeds: [SameEmbed], ephemeral: true }); - } - - await interaction.deferReply(); - - player.seek(0); - - let song = player.queue.current; - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`Replay [${song.title}](${song.uri})`), - ], - }); - }); + .setName("replay") + .setDescription("Replay current playing track") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("I'm not playing anything."), + ], + ephemeral: true, + }); + } + + await interaction.deferReply(); + + player.seek(0); + + let song = player.queue.current; + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`Replay [${ song.title }](${ song.uri })`), + ], + }); + }); module.exports = command; diff --git a/commands/slash/resume.js b/commands/slash/resume.js index 9bc4c63d5..cfe9787e1 100644 --- a/commands/slash/resume.js +++ b/commands/slash/resume.js @@ -2,51 +2,56 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("resume") - .setDescription("Resume current playing track") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | Nothing is playing right now..."); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - if (!player.paused) { - let ResumedEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **Current track is already resumed**"); - return interaction.reply({ embeds: [ResumedEmbed], ephemeral: true }); - } - player.pause(false); - let ResEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`⏯ **Resumed!**`); - return interaction.reply({ embeds: [ResEmbed] }); - }); + .setName("resume") + .setDescription("Resume current track") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no song playing right now."), + ], + ephemeral: true, + }); + } + + if (!player.paused) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Current track is already resumed"), + ], + ephemeral: true, + }); + } + player.pause(false); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`⏯ **Resumed!**`), + ], + }); + }); module.exports = command; diff --git a/commands/slash/save.js b/commands/slash/save.js new file mode 100644 index 000000000..ac018214e --- /dev/null +++ b/commands/slash/save.js @@ -0,0 +1,81 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); +const prettyMilliseconds = require("pretty-ms"); + +const command = new SlashCommand() + .setName("save") + .setDescription("Saves current song to your DM's") + .setRun(async (client, interaction) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no music playing right now."), + ], + ephemeral: true, + }); + } + + const save = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ + name: "Saved track", + iconURL: `${ interaction.user.displayAvatarURL({ dynamic: true }) }`, + }) + .setDescription( + `**Saved [${ player.queue.current.title }](${ player.queue.current.uri }) to your DM**`, + ) + .addFields( + { + name: "Track Duration", + value: `\`${ prettyMilliseconds(player.queue.current.duration, { + colonNotation: true, + }) }\``, + inline: true, + }, + { + name: "Track Author", + value: `\`${ player.queue.current.author }\``, + inline: true, + }, + { + name: "Requested Guild", + value: `\`${ interaction.guild }\``, + inline: true, + }, + ); + + interaction.user.send({ embeds: [save] }); + + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + "Please check your **DMs**. If you didn't receive any message from me please make sure your **DMs** are open", + ), + ], + ephemeral: true, + }); + }); + +module.exports = command; diff --git a/commands/slash/search.js b/commands/slash/search.js index bf1fdda18..b6b27cd16 100644 --- a/commands/slash/search.js +++ b/commands/slash/search.js @@ -1,182 +1,185 @@ const SlashCommand = require("../../lib/SlashCommand"); +const prettyMilliseconds = require("pretty-ms"); const { - MessageEmbed, - MessageActionRow, - MessageSelectMenu, + MessageEmbed, + MessageActionRow, + MessageSelectMenu, } = require("discord.js"); const command = new SlashCommand() - .setName("search") - .setDescription("Search for a song") - .addStringOption((option) => - option - .setName("query") - .setDescription("The song to search for") - .setRequired(true) - ) - - .setRun(async (client, interaction, options) => { - const result = interaction.options.getString("query"); - let player = client.manager.get(interaction.guild.id); - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You need to join voice channel first before you can use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me.**" - ); - - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - if (!player) { - player = client.manager.create({ - guild: interaction.guild.id, - voiceChannel: interaction.member.voice.channel.id, - textChannel: interaction.channel.id, - selfDeafen: client.config.selfDeafen, - volume: client.config.defaultVolume, + .setName("search") + .setDescription("Search for a song") + .addStringOption((option) => + option + .setName("query") + .setDescription("The song to search for") + .setRequired(true), + ) + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.createPlayer(interaction.channel, channel); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + await interaction.deferReply().catch((_) => { + }); + + if (player.state !== "CONNECTED") { + player.connect(); + } + + const search = interaction.options.getString("query"); + let res; + + try { + res = await player.search(search, interaction.user); + if (res.loadType === "LOAD_FAILED") { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setDescription("An error occured while searching for the song") + .setColor("RED"), + ], + ephemeral: true, + }); + } + } catch (err) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setAuthor({ + name: "An error occured while searching for the song", + }) + .setColor("RED"), + ], + ephemeral: true, + }); + } + + if (res.loadType == "NO_MATCHES") { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setDescription(`No results found for \`${ search }\``) + .setColor("RED"), + ], + ephemeral: true, + }); + } else { + let max = 10; + if (res.tracks.length < max) { + max = res.tracks.length; + } + + let resultFromSearch = []; + + res.tracks.slice(0, max).map((track) => { + resultFromSearch.push({ + label: `${ track.title }`, + value: `${ track.uri }`, + description: track.isStream + ? `LIVE` + : `${ prettyMilliseconds(track.duration, { + secondsDecimalDigits: 0, + }) } - ${ track.author }`, + }); + }); + + const menus = new MessageActionRow().addComponents( + new MessageSelectMenu() + .setCustomId("select") + .setPlaceholder("Select a song") + .addOptions(resultFromSearch), + ); + + let choosenTracks = await interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `Here are some of the results I found for \`${ search }\`. Please select track within \`30 seconds\``, + ), + ], + components: [menus], + }); + const filter = (button) => button.user.id === interaction.user.id; + + const tracksCollector = choosenTracks.createMessageComponentCollector({ + filter, + time: 30000, + }); + tracksCollector.on("collect", async (i) => { + if (i.isSelectMenu()) { + await i.deferUpdate(); + let uriFromCollector = i.values[0]; + let trackForPlay; + + trackForPlay = await player?.search( + uriFromCollector, + interaction.user, + ); + player?.queue?.add(trackForPlay.tracks[0]); + if (!player?.playing && !player?.paused && !player?.queue?.size) { + player?.play(); + } + i.editReply({ + content: null, + embeds: [ + new MessageEmbed() + .setAuthor({ + name: "Added to queue", + iconURL: client.config.iconURL, + }) + .setURL(res.tracks[0].uri) + .setThumbnail(res.tracks[0].displayThumbnail("maxresdefault")) + .setDescription( + `[${ trackForPlay?.tracks[0]?.title }](${ trackForPlay?.tracks[0].uri })` || + "No Title", + ) + .addField("Added by", `<@${ interaction.user.id }>`, true) + .addField( + "Duration", + res.tracks[0].isStream + ? `\`LIVE\`` + : `\`${ client.ms(res.tracks[0].duration, { + colonNotation: true, + }) }\``, + true, + ) + .setColor(client.config.embedColor), + ], + components: [], + }); + } + }); + tracksCollector.on("end", async (i) => { + if (i.size == 0) { + choosenTracks.edit({ + content: null, + embeds: [ + new MessageEmbed() + .setDescription( + `No track selected. You took too long to select a track.`, + ) + .setColor(client.config.embedColor), + ], + components: [], + }); + } + }); + } }); - } - - if (player.state !== "CONNECTED") { - player.connect(); - } - - let res; - const search = result; - - try { - res = await player.search(search, interaction.user); - if (res.loadType === "LOAD_FAILED") { - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setDescription("An error occured while searching for the song") - .setColor(client.config.embedColor), - ], - ephemeral: true, - }); - } - } catch (err) { - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setAuthor({ - name: "An error occured while searching for the song", - }) - //.setAuthor("An error occured while searching for the song") - .setColor(client.config.embedColor), - ], - ephemeral: true, - }); - } - - if (res.loadType == "NO_MATCHES") { - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setDescription(`No results found for \`${search}\``) - .setColor(client.config.embedColor), - ], - ephemeral: true, - }); - } else { - let max = 10; - if (res.tracks.length < max) max = res.tracks.length; - - let resultFromSearch = []; - - res.tracks.slice(0, max).map((track) => { - resultFromSearch.push({ - label: `${track.title}`, - value: `${track.uri}`, - }); - }); - - const menus = new MessageActionRow().addComponents( - new MessageSelectMenu() - .setMinValues(1) - .setMaxValues(1) - .setCustomId("select") - .setPlaceholder("Select a song") - .addOptions(resultFromSearch) - ); - - await interaction.deferReply(); - - let choosenTracks = await interaction.editReply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `Here are searched result I found for \`${result}\`. Please select track within \`30 seconds\`` - ), - ], - components: [menus], - }); - const filter = (button) => button.user.id === interaction.user.id; - - const tracksCollector = choosenTracks.createMessageComponentCollector({ - filter, - time: 30000, - }); - tracksCollector.on("collect", async (i) => { - if (i.isSelectMenu()) { - await i.deferUpdate(); - let uriFromCollector = i.values[0]; - let trackForPlay; - - trackForPlay = await player?.search( - uriFromCollector, - interaction.user - ); - player?.queue?.add(trackForPlay.tracks[0]); - if (!player?.playing && !player?.paused && !player?.queue?.size) - player?.play(); - i.editReply({ - content: null, - embeds: [ - new MessageEmbed() - .setDescription( - `Added [${trackForPlay?.tracks[0]?.title}](${trackForPlay?.tracks[0].uri}) [${trackForPlay?.tracks[0]?.requester}]` - ) - .setColor(client.config.embedColor), - ], - components: [], - }); - } - }); - tracksCollector.on("end", async (i) => { - if (i.size == 0) { - choosenTracks.edit({ - content: null, - embeds: [ - new MessageEmbed() - .setDescription( - `No track selected. You took too long to select a track.` - ) - .setColor(client.config.embedColor), - ], - components: [], - }); - } - }); - } - }); module.exports = command; diff --git a/commands/slash/seek.js b/commands/slash/seek.js index 45f9547b4..ee073a1ed 100644 --- a/commands/slash/seek.js +++ b/commands/slash/seek.js @@ -3,84 +3,75 @@ const { MessageEmbed } = require("discord.js"); const ms = require("ms"); const command = new SlashCommand() - .setName("seek") - .setDescription("Seek to a specific time in the current song.") - .addStringOption((option) => - option - .setName("time") - .setDescription("Seek to time you want. Ex 2m | 10s | 53s") - .setRequired(true) - ) - - .setRun(async (client, interaction, options) => { - const args = interaction.options.getString("time"); - - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **There's nothing playing in the queue**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - await interaction.deferReply(); - - const time = ms(args); - const position = player.position; - const duration = player.queue.current.duration; - - if (time <= duration) { - if (time > position) { - player.seek(time); - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `⏊ | **${player.queue.current.title}** has been seeked to **${ms( - time - )}**` - ); - return interaction.editReply({ embeds: [thing] }); - } else { - player.seek(time); - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `⏊ | **${player.queue.current.title}** has been seeked to **${ms( - time - )}**` - ); - return interaction.editReply({ embeds: [thing] }); - } - } else { - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `Cannot seek current playing track. This may happened because seek duration has exceeded track duration` - ); - return interaction.editReply({ embeds: [thing] }); - } - }); + .setName("seek") + .setDescription("Seek to a specific time in the current song.") + .addStringOption((option) => + option + .setName("time") + .setDescription("Seek to time you want. Ex 2m | 10s | 53s") + .setRequired(true), + ) + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no music playing."), + ], + ephemeral: true, + }); + } + + await interaction.deferReply(); + + const args = interaction.options.getString("time"); + const time = ms(args); + const position = player.position; + const duration = player.queue.current.duration; + + if (time <= duration) { + player.seek(time); + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `⏊ | **${ player.queue.current.title }** has been ${ + time < position? "rewound" : "seeked" + } to **${ ms(time) }**`, + ), + ], + }); + } else { + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `Cannot seek current playing track. This may happened because seek duration has exceeded track duration`, + ), + ], + }); + } + }); module.exports = command; diff --git a/commands/slash/shuffle.js b/commands/slash/shuffle.js index faa405da2..97bc0f0de 100644 --- a/commands/slash/shuffle.js +++ b/commands/slash/shuffle.js @@ -2,53 +2,58 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("shuffle") - .setDescription("Shuffle the current queue.") - .setRun(async (client, interaction, options) => { - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **There's nothing playing in the queue**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - if (!player.queue || !player.queue.length || player.queue.length === 0) { - const addEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **There are no songs in the queue.**"); - return interaction.reply({ embeds: [addEmbed], ephemeral: true }); - } - - // if the queue is not empty, shuffle the entire queue - player.queue.shuffle(); - const shuffleEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("🔀 | **Successfully shuffled the queue.**"); - return interaction.reply({ embeds: [shuffleEmbed] }); - }); + .setName("shuffle") + .setDescription("Randomizes the queue") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no music playing."), + ], + ephemeral: true, + }); + } + + if (!player.queue || !player.queue.length || player.queue.length === 0) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There are not enough songs in the queue."), + ], + ephemeral: true, + }); + } + + // if the queue is not empty, shuffle the entire queue + player.queue.shuffle(); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("🔀 | **Successfully shuffled the queue.**"), + ], + }); + }); module.exports = command; diff --git a/commands/slash/skip.js b/commands/slash/skip.js index 1d267c35f..8fda281a4 100644 --- a/commands/slash/skip.js +++ b/commands/slash/skip.js @@ -1,41 +1,49 @@ const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("skip") - .setDescription("Skip the current song") - .setRun(async (client, interaction, options) => { - let channel = await client.getChannel(client, interaction); - if (!channel) return; - let player = client.manager.players.get(interaction.guild.id); - if (!player) - return interaction.reply({ - embeds: [client.ErrorEmbed("There's nothing to skipped!")], - }); - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command!**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - player.stop(); - interaction.reply({ embeds: [client.Embed("✅ | **Skipped!**")] }); - }); + .setName("skip") + .setDescription("Skip the current song") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is nothing to skip."), + ], + ephemeral: true, + }); + } + + player.queue.previous = player.queue.current; + player.stop(); + + interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("✅ | **Skipped!**"), + ], + }); + }); module.exports = command; diff --git a/commands/slash/skipto.js b/commands/slash/skipto.js index 9e20d6cc8..fb1b33ce8 100644 --- a/commands/slash/skipto.js +++ b/commands/slash/skipto.js @@ -2,82 +2,80 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("skipto") - .setDescription("skip to a specific song in the queue") - .addNumberOption((option) => - option - .setName("number") - .setDescription("The number of tracks to skipto") - .setRequired(true) - ) - - .setRun(async (client, interaction, options) => { - const args = interaction.options.getNumber("number"); - //const duration = player.queue.current.duration - - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | There is no music playing in this guild!"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | You must be in a voice channel to use this command!" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | You must be in the same voice channel as the bot to use this command!" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - await interaction.deferReply(); - - const position = Number(args); - - try { - if (!position || position < 0 || position > player.queue.size) { - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | Invalid position!"); - return interaction.editReply({ embeds: [thing] }); - } - - player.queue.remove(0, position - 1); - player.stop(); - - let thing = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("✅ | Skipped to position " + position); - - return interaction.editReply({ embeds: [thing] }); - } catch { - if (position === 1) { - player.stop(); - } - return interaction.editReply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("✅ | Skipped to position " + position), - ], - }); - } - }); + .setName("skipto") + .setDescription("skip to a specific song in the queue") + .addNumberOption((option) => + option + .setName("number") + .setDescription("The number of tracks to skipto") + .setRequired(true), + ) + + .setRun(async (client, interaction, options) => { + const args = interaction.options.getNumber("number"); + //const duration = player.queue.current.duration + + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("I'm not in a channel."), + ], + ephemeral: true, + }); + } + + await interaction.deferReply(); + + const position = Number(args); + + try { + if (!position || position < 0 || position > player.queue.size) { + let thing = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("❌ | Invalid position!"); + return interaction.editReply({ embeds: [thing] }); + } + + player.queue.remove(0, position - 1); + player.stop(); + + let thing = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("✅ | Skipped to position " + position); + + return interaction.editReply({ embeds: [thing] }); + } catch { + if (position === 1) { + player.stop(); + } + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription("✅ | Skipped to position " + position), + ], + }); + } + }); module.exports = command; diff --git a/commands/slash/stats.js b/commands/slash/stats.js index 945a94c57..f52165943 100644 --- a/commands/slash/stats.js +++ b/commands/slash/stats.js @@ -5,85 +5,85 @@ const { MessageEmbed } = require("discord.js"); const os = require("os"); const command = new SlashCommand() - .setName("stats") - .setDescription("Get information about the bot") - .setRun(async (client, interaction) => { - // get OS info - const osver = os.platform() + " " + os.release(); - - // Get nodejs version - const nodeVersion = process.version; - - // get the uptime in a human readable format - const runtime = moment - .duration(client.uptime) - .format("d[ Days]ãƒģh[ Hrs]ãƒģm[ Mins]ãƒģs[ Secs]"); - // show lavalink uptime in a nice format - const lavauptime = moment - .duration(client.manager.nodes.values().next().value.stats.uptime) - .format(" D[d], H[h], m[m]"); - // show lavalink memory usage in a nice format - const lavaram = ( - client.manager.nodes.values().next().value.stats.memory.used / - 1024 / - 1024 - ).toFixed(2); - // sow lavalink memory alocated in a nice format - const lavamemalocated = ( - client.manager.nodes.values().next().value.stats.memory.allocated / - 1024 / - 1024 - ).toFixed(2); - // show system uptime - var sysuptime = moment - .duration(os.uptime() * 1000) - .format("d[ Days]ãƒģh[ Hrs]ãƒģm[ Mins]ãƒģs[ Secs]"); - - // get commit hash and date - let gitHash = "unknown"; - try { - gitHash = require("child_process") - .execSync("git rev-parse HEAD") - .toString() - .trim(); - } catch (e) { - // do nothing - gitHash = "unknown"; - } - - const statsEmbed = new MessageEmbed() - .setTitle(`${client.user.username} Information`) - .setColor(client.config.embedColor) - .setDescription( - `\`\`\`yml\nName: ${client.user.username}#${client.user.discriminator} [${client.user.id}]\nAPI: ${client.ws.ping}ms\nRuntime: ${runtime}\`\`\`` - ) - .setFields([ - { - name: `Lavalink stats`, - value: `\`\`\`yml\nUptime: ${lavauptime}\nRAM: ${lavaram} MB\nPlaying: ${ - client.manager.nodes.values().next().value.stats.playingPlayers - } out of ${ - client.manager.nodes.values().next().value.stats.players - }\`\`\``, - inline: true, - }, - { - name: "Bot stats", - value: `\`\`\`yml\nGuilds: ${ - client.guilds.cache.size - } \nNodeJS: ${nodeVersion}\nDiscordMusicBot: v${ - require("../../package.json").version - } \`\`\``, - inline: true, - }, - { - name: "System stats", - value: `\`\`\`yml\nOS: ${osver}\nUptime: ${sysuptime}\n\`\`\``, - inline: false, - }, - ]) - .setFooter({ text: `Build: ${gitHash}` }); - return interaction.reply({ embeds: [statsEmbed], ephemeral: false }); - }); + .setName("stats") + .setDescription("Get information about the bot") + .setRun(async (client, interaction) => { + // get OS info + const osver = os.platform() + " " + os.release(); + + // Get nodejs version + const nodeVersion = process.version; + + // get the uptime in a human readable format + const runtime = moment + .duration(client.uptime) + .format("d[ Days]ãƒģh[ Hrs]ãƒģm[ Mins]ãƒģs[ Secs]"); + // show lavalink uptime in a nice format + const lavauptime = moment + .duration(client.manager.nodes.values().next().value.stats.uptime) + .format(" D[d], H[h], m[m]"); + // show lavalink memory usage in a nice format + const lavaram = ( + client.manager.nodes.values().next().value.stats.memory.used / + 1024 / + 1024 + ).toFixed(2); + // sow lavalink memory alocated in a nice format + const lavamemalocated = ( + client.manager.nodes.values().next().value.stats.memory.allocated / + 1024 / + 1024 + ).toFixed(2); + // show system uptime + var sysuptime = moment + .duration(os.uptime() * 1000) + .format("d[ Days]ãƒģh[ Hrs]ãƒģm[ Mins]ãƒģs[ Secs]"); + + // get commit hash and date + let gitHash = "unknown"; + try { + gitHash = require("child_process") + .execSync("git rev-parse HEAD") + .toString() + .trim(); + } catch (e) { + // do nothing + gitHash = "unknown"; + } + + const statsEmbed = new MessageEmbed() + .setTitle(`${ client.user.username } Information`) + .setColor(client.config.embedColor) + .setDescription( + `\`\`\`yml\nName: ${ client.user.username }#${ client.user.discriminator } [${ client.user.id }]\nAPI: ${ client.ws.ping }ms\nRuntime: ${ runtime }\`\`\``, + ) + .setFields([ + { + name: `Lavalink stats`, + value: `\`\`\`yml\nUptime: ${ lavauptime }\nRAM: ${ lavaram } MB\nPlaying: ${ + client.manager.nodes.values().next().value.stats.playingPlayers + } out of ${ + client.manager.nodes.values().next().value.stats.players + }\`\`\``, + inline: true, + }, + { + name: "Bot stats", + value: `\`\`\`yml\nGuilds: ${ + client.guilds.cache.size + } \nNodeJS: ${ nodeVersion }\nDiscordMusicBot: v${ + require("../../package.json").version + } \`\`\``, + inline: true, + }, + { + name: "System stats", + value: `\`\`\`yml\nOS: ${ osver }\nUptime: ${ sysuptime }\n\`\`\``, + inline: false, + }, + ]) + .setFooter({ text: `Build: ${ gitHash }` }); + return interaction.reply({ embeds: [statsEmbed], ephemeral: false }); + }); module.exports = command; diff --git a/commands/slash/stop.js b/commands/slash/stop.js new file mode 100644 index 000000000..73a4dcd31 --- /dev/null +++ b/commands/slash/stop.js @@ -0,0 +1,54 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +const command = new SlashCommand() + .setName("stop") + .setDescription("Stops whatever the bot is playing and leaves the voice channel\n(This command will clear the queue)") + + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("I'm not in a channel."), + ], + ephemeral: true, + }); + } + + if (player.twentyFourSeven) { + player.queue.clear(); + player.stop(); + } else { + player.destroy(); + } + + interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`:wave: | **Bye Bye!**`), + ], + }); + }); + +module.exports = command; diff --git a/commands/slash/summon.js b/commands/slash/summon.js index fb5b366da..8e6e4b1d5 100644 --- a/commands/slash/summon.js +++ b/commands/slash/summon.js @@ -1,36 +1,36 @@ const SlashCommand = require("../../lib/SlashCommand"); const command = new SlashCommand() - .setName("summon") - .setDescription("Summons the bot to the channel.") - .setRun(async (client, interaction, options) => { - let channel = await client.getChannel(client, interaction); - let node = await client.getLavalink(client); - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - player = client.createPlayer(interaction.channel, channel); - player.connect(true); - } - - if (channel.id !== player.voiceChannel) { - player.setVoiceChannel(channel.id); - player.connect(); - } - - interaction.reply({ - embeds: [ - client.Embed(`:thumbsup: | **Successfully joined <#${channel.id}>!**`), - ], - }); - }); + .setName("summon") + .setDescription("Summons the bot to the channel.") + .setRun(async (client, interaction, options) => { + let channel = await client.getChannel(client, interaction); + let node = await client.getLavalink(client); + if (!interaction.member.voice.channel) { + const joinEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + "❌ | **You must be in a voice channel to use this command.**", + ); + return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); + } + + let player = client.manager.players.get(interaction.guild.id); + if (!player) { + player = client.createPlayer(interaction.channel, channel); + player.connect(true); + } + + if (channel.id !== player.voiceChannel) { + player.setVoiceChannel(channel.id); + player.connect(); + } + + interaction.reply({ + embeds: [ + client.Embed(`:thumbsup: | **Successfully joined <#${ channel.id }>!**`), + ], + }); + }); module.exports = command; diff --git a/commands/slash/volume.js b/commands/slash/volume.js index ecf2595ff..507419870 100644 --- a/commands/slash/volume.js +++ b/commands/slash/volume.js @@ -2,66 +2,67 @@ const SlashCommand = require("../../lib/SlashCommand"); const { MessageEmbed } = require("discord.js"); const command = new SlashCommand() - .setName("volume") - .setDescription("Change the volume of the current song.") - .addNumberOption((option) => - option - .setName("amount") - .setDescription("Amount of volume you want to change. Ex: 10") - .setRequired(false) - ) - .setRun(async (client, interaction) => { - const category = interaction.options.getNumber("options"); - - let player = client.manager.players.get(interaction.guild.id); - if (!player) { - const queueEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription("❌ | **There's nothing playing in the queue**"); - return interaction.reply({ embeds: [queueEmbed], ephemeral: true }); - } - - if (!interaction.member.voice.channel) { - const joinEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in a voice channel to use this command.**" - ); - return interaction.reply({ embeds: [joinEmbed], ephemeral: true }); - } - - if ( - interaction.guild.me.voice.channel && - !interaction.guild.me.voice.channel.equals( - interaction.member.voice.channel - ) - ) { - const sameEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - "❌ | **You must be in the same voice channel as me to use this command!**" - ); - return interaction.reply({ embeds: [sameEmbed], ephemeral: true }); - } - - let vol = interaction.options.getNumber("amount"); - if (!vol || vol < 1 || vol > 125) { - const NumberEmbed = new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription(`:loud_sound: | **Current volume ${player.volume}**`); - return interaction.reply({ embeds: [NumberEmbed] }); - } - - player.setVolume(vol); - return interaction.reply({ - embeds: [ - new MessageEmbed() - .setColor(client.config.embedColor) - .setDescription( - `:loud_sound: | Successfully set volume to **${player.volume}**` - ), - ], - }); - }); + .setName("volume") + .setDescription("Change the volume of the current song.") + .addNumberOption((option) => + option + .setName("amount") + .setDescription("Amount of volume you want to change. Ex: 10") + .setRequired(false), + ) + .setRun(async (client, interaction) => { + let channel = await client.getChannel(client, interaction); + if (!channel) { + return; + } + + let player; + if (client.manager) { + player = client.manager.players.get(interaction.guild.id); + } else { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("Lavalink node is not connected"), + ], + }); + } + + if (!player) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no music playing."), + ], + ephemeral: true, + }); + } + + let vol = interaction.options.getNumber("amount"); + if (!vol || vol < 1 || vol > 125) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `:loud_sound: | Current volume **${ player.volume }**`, + ), + ], + }); + } + + player.setVolume(vol); + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `:loud_sound: | Successfully set volume to **${ player.volume }**`, + ), + ], + }); + }); module.exports = command; diff --git a/config.js b/config.js index fd09fdec5..76a686df6 100644 --- a/config.js +++ b/config.js @@ -1,42 +1,44 @@ module.exports = { - cmdPerPage: 10, //Number of commands per page of help command - adminId: "UserId", // Admin of the bot - token: process.env.token || process.env['TOKEN'], //Bot's Token - clientId: process.env.clientId || process.env['CLIENT_ID'], //ID of the bot - clientSecret: process.env.clientSecret || process.env['CLIENT_SECRET'], //Client Secret of the bot - port: 4200, //Port of the API and Dashboard - scopes: ["identify", "guilds", "applications.commands"], //Discord OAuth2 Scopes - serverDeafen: true, //If you want bot to stay deafened - defaultVolume: 100, //Sets the default volume of the bot, You can change this number anywhere from 1 to 100 - supportServer: "https://discord.gg/sbySMS7m3v", //Support Server Link - Issues: "https://github.com/SudhanPlayz/Discord-MusicBot/issues", //Bug Report Link - permissions: 277083450689, //Bot Inviting Permissions - disconnectTime: 1800000, //How long should the bot wait before disconnecting from the voice channel. in miliseconds. set to 1 for instant disconnect. - alwaysplay: true, // when set to true music will always play no matter if theres no one in voice channel. - debug: false, //Debug mode - // Lavalink server; optional public lavalink -> https://lavalink-list.darrennathanael.com/ - // The default one should work fine. - nodes: [ - { - identifier: "Main", //- Used for indentifier in stats commands. - host: "lavalink-replit.nonroute1.repl.co", - port: 443, - password: "maybeiwasboring", - retryAmount: 200, //- The amount of times to retry connecting to the node if connection got dropped. - retryDelay: 40, //- Delay between reconnect attempts if connection is lost. - secure: true, //- Can be either true or false. Only use true if ssl is enabled! - }, - ], - embedColor: "ORANGE", //Color of the embeds, hex supported - presence: { - //PresenceData object | https://discord.js.org/#/docs/main/stable/typedef/PresenceData - status: "online", // You can have online, idle, and dnd(invisible too but it make people think the bot is offline) - activities: [ - { - name: "You", //Status Text - type: "LISTENING", // PLAYING, WATCHING, LISTENING, STREAMING - }, - ], - }, - iconURL: "https://images-ext-1.discordapp.net/external/orTaH_49eb1hAcY0RihITSFm4QghReZ6_66phxobYAE/https/i.pinimg.com/originals/c8/75/2a/c8752a100604c900269e1d21cfb9b0c7.gif", //This icon will be in every embed's author field + cmdPerPage: 10, //Number of commands per page of help command + adminId: "UserId", // Admin of the bot + token: process.env.token || "", //Bot's Token + clientId: process.env.clientId || "", //ID of the bot + clientSecret: process.env.clientSecret || "", //Client Secret of the bot + port: 4200, //Port of the API and Dashboard + scopes: ["identify", "guilds", "applications.commands"], //Discord OAuth2 Scopes + serverDeafen: true, //If you want bot to stay deafened + defaultVolume: 100, //Sets the default volume of the bot, You can change this number anywhere from 1 to 100 + supportServer: "https://discord.gg/sbySMS7m3v", //Support Server Link + Issues: "https://github.com/SudhanPlayz/Discord-MusicBot/issues", //Bug Report Link + permissions: 277083450689, //Bot Inviting Permissions + disconnectTime: 30000, //How long should the bot wait before disconnecting from the voice channel. in miliseconds. set to 1 for instant disconnect. + alwaysplay: false, // when set to true music will always play no matter if theres no one in voice channel. + debug: false, //Debug mode + cookieSecret: "CodingWithSudhan is epic",//Cookie Secret + website: "http://localhost:4200",//without the / at the end + // You need a lavalink server for this bot to work!!!! + // Lavalink server; public lavalink -> https://lavalink-list.darrennathanael.com/; create one yourself -> https://darrennathanael.com/post/how-to-lavalink + nodes: [ + { + identifier: "Main Node", //- Used for indentifier in stats commands. + host: "", //- The host name or IP of the lavalink server. + port: 80, // The port that lavalink is listening to. This must be a number! + password: "", //- The password of the lavalink server. + retryAmount: 200, //- The amount of times to retry connecting to the node if connection got dropped. + retryDelay: 40, //- Delay between reconnect attempts if connection is lost. + secure: false, //- Can be either true or false. Only use true if ssl is enabled! + }, + ], + embedColor: "#2f3136", //Color of the embeds, hex supported + presence: { + //PresenceData object | https://discord.js.org/#/docs/main/stable/typedef/PresenceData + status: "online", // You can have online, idle, and dnd(invisible too but it make people think the bot is offline) + activities: [ + { + name: "Music", //Status Text + type: "LISTENING", // PLAYING, WATCHING, LISTENING, STREAMING + }, + ], + }, + iconURL: "https://cdn.darrennathanael.com/icons/spinning_disk.gif", //This icon will be in every embed's author field }; diff --git a/dashboard/.eslintrc.json b/dashboard/.eslintrc.json new file mode 100644 index 000000000..bffb357a7 --- /dev/null +++ b/dashboard/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/dashboard/.gitignore b/dashboard/.gitignore new file mode 100644 index 000000000..bf920c17b --- /dev/null +++ b/dashboard/.gitignore @@ -0,0 +1,31 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel diff --git a/dashboard/README.md b/dashboard/README.md new file mode 100644 index 000000000..0ca9b955d --- /dev/null +++ b/dashboard/README.md @@ -0,0 +1,41 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped +with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed +on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited +in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated +as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions +are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use +the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) +from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/dashboard/components/StatCard.tsx b/dashboard/components/StatCard.tsx new file mode 100644 index 000000000..3acb157fc --- /dev/null +++ b/dashboard/components/StatCard.tsx @@ -0,0 +1,20 @@ +import {Card, Text} from "@nextui-org/react"; +import {ReactNode} from "react"; + +export default function StatCard(props: { + title: string; + amount: number | string; + icon: ReactNode; +}) { + return ( + + +
+ { props.title } + { props.amount } +
+ { props.icon } +
+
+ ) +} \ No newline at end of file diff --git a/dashboard/components/content.tsx b/dashboard/components/content.tsx new file mode 100644 index 000000000..b86a699f9 --- /dev/null +++ b/dashboard/components/content.tsx @@ -0,0 +1,17 @@ +import {PropsWithChildren} from "react"; +import Navbar from "./navbar"; + +export default function Content(props: PropsWithChildren) { + return
+ +
+ { props.children } +
+
+} \ No newline at end of file diff --git a/dashboard/components/navbar.tsx b/dashboard/components/navbar.tsx new file mode 100644 index 000000000..1d8c510db --- /dev/null +++ b/dashboard/components/navbar.tsx @@ -0,0 +1,30 @@ +import {Button, Link, Spacer} from "@nextui-org/react"; +import {useRouter} from "next/router"; + +export default function Navbar() { + const router = useRouter() + return
+ Discord Music Bot + + + + +
+} \ No newline at end of file diff --git a/dashboard/components/server.tsx b/dashboard/components/server.tsx new file mode 100644 index 000000000..2d4c5665d --- /dev/null +++ b/dashboard/components/server.tsx @@ -0,0 +1,32 @@ +import {Avatar, Tooltip} from "@nextui-org/react"; +import Link from "next/link"; + +interface IProps { + icon: string; + name: string; + id: string; +} + +const getColor = () => { + let c = ["gradient", "primary", "secondary", "error", "warning"] + return c[Math.floor(Math.random() * c.length)]; +} + +export default function Server(props: IProps) { + return
+ + + + + +
+} \ No newline at end of file diff --git a/dashboard/next-env.d.ts b/dashboard/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/dashboard/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/dashboard/next.config.js b/dashboard/next.config.js new file mode 100644 index 000000000..d139cfa5c --- /dev/null +++ b/dashboard/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +} + +module.exports = nextConfig diff --git a/dashboard/out/404.html b/dashboard/out/404.html new file mode 100644 index 000000000..1a3ba52e5 --- /dev/null +++ b/dashboard/out/404.html @@ -0,0 +1,1053 @@ + + + + + + 404: This page could not be found + + + + + + + + + + + + + + + +
+
+
+
+ +

+ 404

+

This page could + not be found .

+
+
+
+
+ + + \ No newline at end of file diff --git a/dashboard/out/_next/static/chunks/204-cf80f7b59eff8ee9.js b/dashboard/out/_next/static/chunks/204-cf80f7b59eff8ee9.js new file mode 100644 index 000000000..1c8a9929a --- /dev/null +++ b/dashboard/out/_next/static/chunks/204-cf80f7b59eff8ee9.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[204],{5446:function(e,t,r){var n=r(9666),o=r(5893);t.Z=(0,n.Z)((0,o.jsx)("path",{d:"M12 5v8.55c-.94-.54-2.1-.75-3.33-.32-1.34.48-2.37 1.67-2.61 3.07-.46 2.74 1.86 5.08 4.59 4.65 1.96-.31 3.35-2.11 3.35-4.1V7h2c1.1 0 2-.9 2-2s-.9-2-2-2h-2c-1.1 0-2 .9-2 2z"}),"AudiotrackRounded")},9666:function(e,t,r){r.d(t,{Z:function(){return kn}});var n=r(7462),o=r(7294),i=r.t(o,2);function a(e,t){if(null==e)return{};var r,n,o={},i=Object.keys(e);for(n=0;n=0||(o[r]=e[r]);return o}var s=r(6010);function c(e){let t="https://mui.com/production-error/?code="+e;for(let r=1;r{void 0===r[t]&&(r[t]=e[t])})),r}(t.components[r].defaultProps,o):o}function p(e){return null!==e&&"object"===typeof e&&e.constructor===Object}function f(e,t,r={clone:!0}){const o=r.clone?(0,n.Z)({},e):e;return p(e)&&p(t)&&Object.keys(t).forEach((n=>{"__proto__"!==n&&(p(t[n])&&n in e&&p(e[n])?o[n]=f(e[n],t[n],r):o[n]=t[n])})),o}const h=["values","unit","step"];function m(e){const{values:t={xs:0,sm:600,md:900,lg:1200,xl:1536},unit:r="px",step:o=5}=e,i=a(e,h),s=(e=>{const t=Object.keys(e).map((t=>({key:t,val:e[t]})))||[];return t.sort(((e,t)=>e.val-t.val)),t.reduce(((e,t)=>(0,n.Z)({},e,{[t.key]:t.val})),{})})(t),c=Object.keys(s);function l(e){return`@media (min-width:${"number"===typeof t[e]?t[e]:e}${r})`}function u(e){return`@media (max-width:${("number"===typeof t[e]?t[e]:e)-o/100}${r})`}function d(e,n){const i=c.indexOf(n);return`@media (min-width:${"number"===typeof t[e]?t[e]:e}${r}) and (max-width:${(-1!==i&&"number"===typeof t[c[i]]?t[c[i]]:n)-o/100}${r})`}return(0,n.Z)({keys:c,values:s,up:l,down:u,between:d,only:function(e){return c.indexOf(e)+1`@media (min-width:${b[e]}px)`};function v(e,t,r){const n=e.theme||{};if(Array.isArray(t)){const e=n.breakpoints||y;return t.reduce(((n,o,i)=>(n[e.up(e.keys[i])]=r(t[i]),n)),{})}if("object"===typeof t){const e=n.breakpoints||y;return Object.keys(t).reduce(((n,o)=>{if(-1!==Object.keys(e.values||b).indexOf(o)){n[e.up(o)]=r(t[o],o)}else{const e=o;n[e]=t[e]}return n}),{})}return r(t)}function x(e={}){var t;return(null==e||null==(t=e.keys)?void 0:t.reduce(((t,r)=>(t[e.up(r)]={},t)),{}))||{}}function k(e,t){return e.reduce(((e,t)=>{const r=e[t];return(!r||0===Object.keys(r).length)&&delete e[t],e}),t)}function w(e,t,r=!0){if(!t||"string"!==typeof t)return null;if(e&&e.vars&&r){const r=`vars.${t}`.split(".").reduce(((e,t)=>e&&e[t]?e[t]:null),e);if(null!=r)return r}return t.split(".").reduce(((e,t)=>e&&null!=e[t]?e[t]:null),e)}function $(e,t,r,n=r){let o;return o="function"===typeof e?e(r):Array.isArray(e)?e[r]||n:w(e,r)||n,t&&(o=t(o)),o}var S=function(e){const{prop:t,cssProperty:r=e.prop,themeKey:n,transform:o}=e,i=e=>{if(null==e[t])return null;const i=e[t],a=w(e.theme,n)||{};return v(e,i,(e=>{let n=$(a,o,e);return e===n&&"string"===typeof e&&(n=$(a,o,`${t}${"default"===e?"":l(e)}`,e)),!1===r?n:{[r]:n}}))};return i.propTypes={},i.filterProps=[t],i};var A=function(e,t){return t?f(e,t,{clone:!1}):e};const C={m:"margin",p:"padding"},P={t:"Top",r:"Right",b:"Bottom",l:"Left",x:["Left","Right"],y:["Top","Bottom"]},O={marginX:"mx",marginY:"my",paddingX:"px",paddingY:"py"},z=function(e){const t={};return r=>(void 0===t[r]&&(t[r]=e(r)),t[r])}((e=>{if(e.length>2){if(!O[e])return[e];e=O[e]}const[t,r]=e.split(""),n=C[t],o=P[r]||"";return Array.isArray(o)?o.map((e=>n+e)):[n+o]})),T=["m","mt","mr","mb","ml","mx","my","margin","marginTop","marginRight","marginBottom","marginLeft","marginX","marginY","marginInline","marginInlineStart","marginInlineEnd","marginBlock","marginBlockStart","marginBlockEnd"],R=["p","pt","pr","pb","pl","px","py","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","paddingX","paddingY","paddingInline","paddingInlineStart","paddingInlineEnd","paddingBlock","paddingBlockStart","paddingBlockEnd"],j=[...T,...R];function M(e,t,r,n){var o;const i=null!=(o=w(e,t,!1))?o:r;return"number"===typeof i?e=>"string"===typeof e?e:i*e:Array.isArray(i)?e=>"string"===typeof e?e:i[e]:"function"===typeof i?i:()=>{}}function E(e){return M(e,"spacing",8)}function I(e,t){if("string"===typeof t||null==t)return t;const r=e(Math.abs(t));return t>=0?r:"number"===typeof r?-r:`-${r}`}function _(e,t,r,n){if(-1===t.indexOf(r))return null;const o=function(e,t){return r=>e.reduce(((e,n)=>(e[n]=I(t,r),e)),{})}(z(r),n);return v(e,e[r],o)}function W(e,t){const r=E(e.theme);return Object.keys(e).map((n=>_(e,t,n,r))).reduce(A,{})}function N(e){return W(e,T)}function Z(e){return W(e,R)}function F(e){return W(e,j)}N.propTypes={},N.filterProps=T,Z.propTypes={},Z.filterProps=R,F.propTypes={},F.filterProps=j;var B=F;const H=["breakpoints","palette","spacing","shape"];var L=function(e={},...t){const{breakpoints:r={},palette:o={},spacing:i,shape:s={}}=e,c=a(e,H),l=m(r),u=function(e=8){if(e.mui)return e;const t=E({spacing:e}),r=(...e)=>(0===e.length?[1]:e).map((e=>{const r=t(e);return"number"===typeof r?`${r}px`:r})).join(" ");return r.mui=!0,r}(i);let d=f({breakpoints:l,direction:"ltr",components:{},palette:(0,n.Z)({mode:"light"},o),spacing:u,shape:(0,n.Z)({},g,s)},c);return d=t.reduce(((e,t)=>f(e,t)),d),d};var G=o.createContext(null);var K=function(e=null){const t=o.useContext(G);return t&&(r=t,0!==Object.keys(r).length)?t:e;var r};const V=L();var U=function(e=V){return K(e)};function D(e,t){return(0,n.Z)({toolbar:{minHeight:56,[e.up("xs")]:{"@media (orientation: landscape)":{minHeight:48}},[e.up("sm")]:{minHeight:64}}},t)}function q(e,t=0,r=1){return Math.min(Math.max(t,e),r)}function X(e){if(e.type)return e;if("#"===e.charAt(0))return X(function(e){e=e.slice(1);const t=new RegExp(`.{1,${e.length>=6?2:1}}`,"g");let r=e.match(t);return r&&1===r[0].length&&(r=r.map((e=>e+e))),r?`rgb${4===r.length?"a":""}(${r.map(((e,t)=>t<3?parseInt(e,16):Math.round(parseInt(e,16)/255*1e3)/1e3)).join(", ")})`:""}(e));const t=e.indexOf("("),r=e.substring(0,t);if(-1===["rgb","rgba","hsl","hsla","color"].indexOf(r))throw new Error(c(9,e));let n,o=e.substring(t+1,e.length-1);if("color"===r){if(o=o.split(" "),n=o.shift(),4===o.length&&"/"===o[3].charAt(0)&&(o[3]=o[3].slice(1)),-1===["srgb","display-p3","a98-rgb","prophoto-rgb","rec-2020"].indexOf(n))throw new Error(c(10,n))}else o=o.split(",");return o=o.map((e=>parseFloat(e))),{type:r,values:o,colorSpace:n}}function Y(e){const{type:t,colorSpace:r}=e;let{values:n}=e;return-1!==t.indexOf("rgb")?n=n.map(((e,t)=>t<3?parseInt(e,10):e)):-1!==t.indexOf("hsl")&&(n[1]=`${n[1]}%`,n[2]=`${n[2]}%`),n=-1!==t.indexOf("color")?`${r} ${n.join(" ")}`:`${n.join(", ")}`,`${t}(${n})`}function J(e){let t="hsl"===(e=X(e)).type?X(function(e){e=X(e);const{values:t}=e,r=t[0],n=t[1]/100,o=t[2]/100,i=n*Math.min(o,1-o),a=(e,t=(e+r/30)%12)=>o-i*Math.max(Math.min(t-3,9-t,1),-1);let s="rgb";const c=[Math.round(255*a(0)),Math.round(255*a(8)),Math.round(255*a(4))];return"hsla"===e.type&&(s+="a",c.push(t[3])),Y({type:s,values:c})}(e)).values:e.values;return t=t.map((t=>("color"!==e.type&&(t/=255),t<=.03928?t/12.92:((t+.055)/1.055)**2.4))),Number((.2126*t[0]+.7152*t[1]+.0722*t[2]).toFixed(3))}function Q(e,t){if(e=X(e),t=q(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb")||-1!==e.type.indexOf("color"))for(let r=0;r<3;r+=1)e.values[r]*=1-t;return Y(e)}function ee(e,t){if(e=X(e),t=q(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(let r=0;r<3;r+=1)e.values[r]+=(255-e.values[r])*t;else if(-1!==e.type.indexOf("color"))for(let r=0;r<3;r+=1)e.values[r]+=(1-e.values[r])*t;return Y(e)}var te={black:"#000",white:"#fff"};var re={50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#f5f5f5",A200:"#eeeeee",A400:"#bdbdbd",A700:"#616161"};var ne={50:"#f3e5f5",100:"#e1bee7",200:"#ce93d8",300:"#ba68c8",400:"#ab47bc",500:"#9c27b0",600:"#8e24aa",700:"#7b1fa2",800:"#6a1b9a",900:"#4a148c",A100:"#ea80fc",A200:"#e040fb",A400:"#d500f9",A700:"#aa00ff"};var oe={50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000"};var ie={50:"#fff3e0",100:"#ffe0b2",200:"#ffcc80",300:"#ffb74d",400:"#ffa726",500:"#ff9800",600:"#fb8c00",700:"#f57c00",800:"#ef6c00",900:"#e65100",A100:"#ffd180",A200:"#ffab40",A400:"#ff9100",A700:"#ff6d00"};var ae={50:"#e3f2fd",100:"#bbdefb",200:"#90caf9",300:"#64b5f6",400:"#42a5f5",500:"#2196f3",600:"#1e88e5",700:"#1976d2",800:"#1565c0",900:"#0d47a1",A100:"#82b1ff",A200:"#448aff",A400:"#2979ff",A700:"#2962ff"};var se={50:"#e1f5fe",100:"#b3e5fc",200:"#81d4fa",300:"#4fc3f7",400:"#29b6f6",500:"#03a9f4",600:"#039be5",700:"#0288d1",800:"#0277bd",900:"#01579b",A100:"#80d8ff",A200:"#40c4ff",A400:"#00b0ff",A700:"#0091ea"};var ce={50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853"};const le=["mode","contrastThreshold","tonalOffset"],ue={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.6)",disabled:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:te.white,default:te.white},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.04)",hoverOpacity:.04,selected:"rgba(0, 0, 0, 0.08)",selectedOpacity:.08,disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)",disabledOpacity:.38,focus:"rgba(0, 0, 0, 0.12)",focusOpacity:.12,activatedOpacity:.12}},de={text:{primary:te.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:"#121212",default:"#121212"},action:{active:te.white,hover:"rgba(255, 255, 255, 0.08)",hoverOpacity:.08,selected:"rgba(255, 255, 255, 0.16)",selectedOpacity:.16,disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)",disabledOpacity:.38,focus:"rgba(255, 255, 255, 0.12)",focusOpacity:.12,activatedOpacity:.24}};function pe(e,t,r,n){const o=n.light||n,i=n.dark||1.5*n;e[t]||(e.hasOwnProperty(r)?e[t]=e[r]:"light"===t?e.light=ee(e.main,o):"dark"===t&&(e.dark=Q(e.main,i)))}function fe(e){const{mode:t="light",contrastThreshold:r=3,tonalOffset:o=.2}=e,i=a(e,le),s=e.primary||function(e="light"){return"dark"===e?{main:ae[200],light:ae[50],dark:ae[400]}:{main:ae[700],light:ae[400],dark:ae[800]}}(t),l=e.secondary||function(e="light"){return"dark"===e?{main:ne[200],light:ne[50],dark:ne[400]}:{main:ne[500],light:ne[300],dark:ne[700]}}(t),u=e.error||function(e="light"){return"dark"===e?{main:oe[500],light:oe[300],dark:oe[700]}:{main:oe[700],light:oe[400],dark:oe[800]}}(t),d=e.info||function(e="light"){return"dark"===e?{main:se[400],light:se[300],dark:se[700]}:{main:se[700],light:se[500],dark:se[900]}}(t),p=e.success||function(e="light"){return"dark"===e?{main:ce[400],light:ce[300],dark:ce[700]}:{main:ce[800],light:ce[500],dark:ce[900]}}(t),h=e.warning||function(e="light"){return"dark"===e?{main:ie[400],light:ie[300],dark:ie[700]}:{main:"#ed6c02",light:ie[500],dark:ie[900]}}(t);function m(e){const t=function(e,t){const r=J(e),n=J(t);return(Math.max(r,n)+.05)/(Math.min(r,n)+.05)}(e,de.text.primary)>=r?de.text.primary:ue.text.primary;return t}const g=({color:e,name:t,mainShade:r=500,lightShade:i=300,darkShade:a=700})=>{if(!(e=(0,n.Z)({},e)).main&&e[r]&&(e.main=e[r]),!e.hasOwnProperty("main"))throw new Error(c(11,t?` (${t})`:"",r));if("string"!==typeof e.main)throw new Error(c(12,t?` (${t})`:"",JSON.stringify(e.main)));return pe(e,"light",i,o),pe(e,"dark",a,o),e.contrastText||(e.contrastText=m(e.main)),e},b={dark:de,light:ue};return f((0,n.Z)({common:(0,n.Z)({},te),mode:t,primary:g({color:s,name:"primary"}),secondary:g({color:l,name:"secondary",mainShade:"A400",lightShade:"A200",darkShade:"A700"}),error:g({color:u,name:"error"}),warning:g({color:h,name:"warning"}),info:g({color:d,name:"info"}),success:g({color:p,name:"success"}),grey:re,contrastThreshold:r,getContrastText:m,augmentColor:g,tonalOffset:o},b[t]),i)}const he=["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","fontWeightBold","htmlFontSize","allVariants","pxToRem"];const me={textTransform:"uppercase"},ge='"Roboto", "Helvetica", "Arial", sans-serif';function be(e,t){const r="function"===typeof t?t(e):t,{fontFamily:o=ge,fontSize:i=14,fontWeightLight:s=300,fontWeightRegular:c=400,fontWeightMedium:l=500,fontWeightBold:u=700,htmlFontSize:d=16,allVariants:p,pxToRem:h}=r,m=a(r,he);const g=i/14,b=h||(e=>e/d*g+"rem"),y=(e,t,r,i,a)=>{return(0,n.Z)({fontFamily:o,fontWeight:e,fontSize:b(t),lineHeight:r},o===ge?{letterSpacing:(s=i/t,Math.round(1e5*s)/1e5)+"em"}:{},a,p);var s},v={h1:y(s,96,1.167,-1.5),h2:y(s,60,1.2,-.5),h3:y(c,48,1.167,0),h4:y(c,34,1.235,.25),h5:y(c,24,1.334,0),h6:y(l,20,1.6,.15),subtitle1:y(c,16,1.75,.15),subtitle2:y(l,14,1.57,.1),body1:y(c,16,1.5,.15),body2:y(c,14,1.43,.15),button:y(l,14,1.75,.4,me),caption:y(c,12,1.66,.4),overline:y(c,12,2.66,1,me)};return f((0,n.Z)({htmlFontSize:d,pxToRem:b,fontFamily:o,fontSize:i,fontWeightLight:s,fontWeightRegular:c,fontWeightMedium:l,fontWeightBold:u},v),m,{clone:!1})}function ye(...e){return[`${e[0]}px ${e[1]}px ${e[2]}px ${e[3]}px rgba(0,0,0,0.2)`,`${e[4]}px ${e[5]}px ${e[6]}px ${e[7]}px rgba(0,0,0,0.14)`,`${e[8]}px ${e[9]}px ${e[10]}px ${e[11]}px rgba(0,0,0,0.12)`].join(",")}var ve=["none",ye(0,2,1,-1,0,1,1,0,0,1,3,0),ye(0,3,1,-2,0,2,2,0,0,1,5,0),ye(0,3,3,-2,0,3,4,0,0,1,8,0),ye(0,2,4,-1,0,4,5,0,0,1,10,0),ye(0,3,5,-1,0,5,8,0,0,1,14,0),ye(0,3,5,-1,0,6,10,0,0,1,18,0),ye(0,4,5,-2,0,7,10,1,0,2,16,1),ye(0,5,5,-3,0,8,10,1,0,3,14,2),ye(0,5,6,-3,0,9,12,1,0,3,16,2),ye(0,6,6,-3,0,10,14,1,0,4,18,3),ye(0,6,7,-4,0,11,15,1,0,4,20,3),ye(0,7,8,-4,0,12,17,2,0,5,22,4),ye(0,7,8,-4,0,13,19,2,0,5,24,4),ye(0,7,9,-4,0,14,21,2,0,5,26,4),ye(0,8,9,-5,0,15,22,2,0,6,28,5),ye(0,8,10,-5,0,16,24,2,0,6,30,5),ye(0,8,11,-5,0,17,26,2,0,6,32,5),ye(0,9,11,-5,0,18,28,2,0,7,34,6),ye(0,9,12,-6,0,19,29,2,0,7,36,6),ye(0,10,13,-6,0,20,31,3,0,8,38,7),ye(0,10,13,-6,0,21,33,3,0,8,40,7),ye(0,10,14,-6,0,22,35,3,0,8,42,7),ye(0,11,14,-7,0,23,36,3,0,9,44,8),ye(0,11,15,-7,0,24,38,3,0,9,46,8)];const xe=["duration","easing","delay"],ke={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"},we={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};function $e(e){return`${Math.round(e)}ms`}function Se(e){if(!e)return 0;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}function Ae(e){const t=(0,n.Z)({},ke,e.easing),r=(0,n.Z)({},we,e.duration);return(0,n.Z)({getAutoHeightDuration:Se,create:(e=["all"],n={})=>{const{duration:o=r.standard,easing:i=t.easeInOut,delay:s=0}=n;a(n,xe);return(Array.isArray(e)?e:[e]).map((e=>`${e} ${"string"===typeof o?o:$e(o)} ${i} ${"string"===typeof s?s:$e(s)}`)).join(",")}},e,{easing:t,duration:r})}var Ce={mobileStepper:1e3,fab:1050,speedDial:1050,appBar:1100,drawer:1200,modal:1300,snackbar:1400,tooltip:1500};const Pe=["breakpoints","mixins","spacing","palette","transitions","typography","shape"];function Oe(e={},...t){const{mixins:r={},palette:o={},transitions:i={},typography:s={}}=e,c=a(e,Pe),l=fe(o),u=L(e);let d=f(u,{mixins:D(u.breakpoints,r),palette:l,shadows:ve.slice(),typography:be(l,s),transitions:Ae(i),zIndex:(0,n.Z)({},Ce)});return d=f(d,c),d=t.reduce(((e,t)=>f(e,t)),d),d}var ze=Oe();function Te({props:e,name:t}){return function({props:e,name:t,defaultTheme:r}){return d({theme:U(r),name:t,props:e})}({props:e,name:t,defaultTheme:ze})}var Re=function(e){var t=Object.create(null);return function(r){return void 0===t[r]&&(t[r]=e(r)),t[r]}},je=/^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|abbr|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|enterKeyHint|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|translate|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|incremental|fallback|inert|itemProp|itemScope|itemType|itemID|itemRef|on|option|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/,Me=Re((function(e){return je.test(e)||111===e.charCodeAt(0)&&110===e.charCodeAt(1)&&e.charCodeAt(2)<91}));var Ee=function(){function e(e){var t=this;this._insertTag=function(e){var r;r=0===t.tags.length?t.insertionPoint?t.insertionPoint.nextSibling:t.prepend?t.container.firstChild:t.before:t.tags[t.tags.length-1].nextSibling,t.container.insertBefore(e,r),t.tags.push(e)},this.isSpeedy=void 0===e.speedy||e.speedy,this.tags=[],this.ctr=0,this.nonce=e.nonce,this.key=e.key,this.container=e.container,this.prepend=e.prepend,this.insertionPoint=e.insertionPoint,this.before=null}var t=e.prototype;return t.hydrate=function(e){e.forEach(this._insertTag)},t.insert=function(e){this.ctr%(this.isSpeedy?65e3:1)===0&&this._insertTag(function(e){var t=document.createElement("style");return t.setAttribute("data-emotion",e.key),void 0!==e.nonce&&t.setAttribute("nonce",e.nonce),t.appendChild(document.createTextNode("")),t.setAttribute("data-s",""),t}(this));var t=this.tags[this.tags.length-1];if(this.isSpeedy){var r=function(e){if(e.sheet)return e.sheet;for(var t=0;t0?Be(Ye,--qe):0,Ue--,10===Xe&&(Ue=1,Ve--),Xe}function tt(){return Xe=qe2||it(Xe)>3?"":" "}function ut(e,t){for(;--t&&tt()&&!(Xe<48||Xe>102||Xe>57&&Xe<65||Xe>70&&Xe<97););return ot(e,nt()+(t<6&&32==rt()&&32==tt()))}function dt(e){for(;tt();)switch(Xe){case e:return qe;case 34:case 39:34!==e&&39!==e&&dt(Xe);break;case 40:41===e&&dt(e);break;case 92:tt()}return qe}function pt(e,t){for(;tt()&&e+Xe!==57&&(e+Xe!==84||47!==rt()););return"/*"+ot(t,qe-1)+"*"+_e(47===e?e:tt())}function ft(e){for(;!it(rt());)tt();return ot(e,qe)}var ht="-ms-",mt="-moz-",gt="-webkit-",bt="comm",yt="rule",vt="decl",xt="@keyframes";function kt(e,t){for(var r="",n=Ge(e),o=0;o6)switch(Be(e,t+1)){case 109:if(45!==Be(e,t+4))break;case 102:return Ze(e,/(.+:)(.+)-([^]+)/,"$1-webkit-$2-$3$1"+mt+(108==Be(e,t+3)?"$3":"$2-$3"))+e;case 115:return~Fe(e,"stretch")?$t(Ze(e,"stretch","fill-available"),t)+e:e}break;case 4949:if(115!==Be(e,t+1))break;case 6444:switch(Be(e,Le(e)-3-(~Fe(e,"!important")&&10))){case 107:return Ze(e,":",":"+gt)+e;case 101:return Ze(e,/(.+:)([^;!]+)(;|!.+)?/,"$1"+gt+(45===Be(e,14)?"inline-":"")+"box$3$1"+gt+"$2$3$1"+ht+"$2box$3")+e}break;case 5936:switch(Be(e,t+11)){case 114:return gt+e+ht+Ze(e,/[svh]\w+-[tblr]{2}/,"tb")+e;case 108:return gt+e+ht+Ze(e,/[svh]\w+-[tblr]{2}/,"tb-rl")+e;case 45:return gt+e+ht+Ze(e,/[svh]\w+-[tblr]{2}/,"lr")+e}return gt+e+ht+e+e}return e}function St(e){return st(At("",null,null,null,[""],e=at(e),0,[0],e))}function At(e,t,r,n,o,i,a,s,c){for(var l=0,u=0,d=a,p=0,f=0,h=0,m=1,g=1,b=1,y=0,v="",x=o,k=i,w=n,$=v;g;)switch(h=y,y=tt()){case 40:if(108!=h&&58==$.charCodeAt(d-1)){-1!=Fe($+=Ze(ct(y),"&","&\f"),"&\f")&&(b=-1);break}case 34:case 39:case 91:$+=ct(y);break;case 9:case 10:case 13:case 32:$+=lt(h);break;case 92:$+=ut(nt()-1,7);continue;case 47:switch(rt()){case 42:case 47:Ke(Pt(pt(tt(),nt()),t,r),c);break;default:$+="/"}break;case 123*m:s[l++]=Le($)*b;case 125*m:case 59:case 0:switch(y){case 0:case 125:g=0;case 59+u:f>0&&Le($)-d&&Ke(f>32?Ot($+";",n,r,d-1):Ot(Ze($," ","")+";",n,r,d-2),c);break;case 59:$+=";";default:if(Ke(w=Ct($,t,r,l,u,o,s,v,x=[],k=[],d),i),123===y)if(0===u)At($,t,w,w,x,i,d,s,k);else switch(p){case 100:case 109:case 115:At(e,w,w,n&&Ke(Ct(e,w,w,0,0,o,s,v,o,x=[],d),k),o,k,d,s,n?x:k);break;default:At($,w,w,w,[""],k,0,s,k)}}l=u=f=0,m=b=1,v=$="",d=a;break;case 58:d=1+Le($),f=h;default:if(m<1)if(123==y)--m;else if(125==y&&0==m++&&125==et())continue;switch($+=_e(y),y*m){case 38:b=u>0?1:($+="\f",-1);break;case 44:s[l++]=(Le($)-1)*b,b=1;break;case 64:45===rt()&&($+=ct(tt())),p=rt(),u=d=Le(v=$+=ft(nt())),y++;break;case 45:45===h&&2==Le($)&&(m=0)}}return i}function Ct(e,t,r,n,o,i,a,s,c,l,u){for(var d=o-1,p=0===o?i:[""],f=Ge(p),h=0,m=0,g=0;h0?p[b]+" "+y:Ze(y,/&\f/g,p[b])))&&(c[g++]=v);return Je(e,t,r,0===o?yt:s,c,l,u)}function Pt(e,t,r){return Je(e,t,r,bt,_e(Xe),He(e,2,-2),0)}function Ot(e,t,r,n){return Je(e,t,r,vt,He(e,0,n),He(e,n+1,-1),n)}var zt=function(e,t,r){for(var n=0,o=0;n=o,o=rt(),38===n&&12===o&&(t[r]=1),!it(o);)tt();return ot(e,qe)},Tt=function(e,t){return st(function(e,t){var r=-1,n=44;do{switch(it(n)){case 0:38===n&&12===rt()&&(t[r]=1),e[r]+=zt(qe-1,t,r);break;case 2:e[r]+=ct(n);break;case 4:if(44===n){e[++r]=58===rt()?"&\f":"",t[r]=e[r].length;break}default:e[r]+=_e(n)}}while(n=tt());return e}(at(e),t))},Rt=new WeakMap,jt=function(e){if("rule"===e.type&&e.parent&&!(e.length<1)){for(var t=e.value,r=e.parent,n=e.column===r.column&&e.line===r.line;"rule"!==r.type;)if(!(r=r.parent))return;if((1!==e.props.length||58===t.charCodeAt(0)||Rt.get(r))&&!n){Rt.set(e,!0);for(var o=[],i=Tt(t,o),a=r.props,s=0,c=0;s-1&&!e.return)switch(e.type){case vt:e.return=$t(e.value,e.length);break;case xt:return kt([Qe(e,{value:Ze(e.value,"@","@"+gt)})],n);case yt:if(e.length)return function(e,t){return e.map(t).join("")}(e.props,(function(t){switch(function(e,t){return(e=t.exec(e))?e[0]:e}(t,/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":return kt([Qe(e,{props:[Ze(t,/:(read-\w+)/,":-moz-$1")]})],n);case"::placeholder":return kt([Qe(e,{props:[Ze(t,/:(plac\w+)/,":-webkit-input-$1")]}),Qe(e,{props:[Ze(t,/:(plac\w+)/,":-moz-$1")]}),Qe(e,{props:[Ze(t,/:(plac\w+)/,ht+"input-$1")]})],n)}return""}))}}],It=function(e){var t=e.key;if("css"===t){var r=document.querySelectorAll("style[data-emotion]:not([data-s])");Array.prototype.forEach.call(r,(function(e){-1!==e.getAttribute("data-emotion").indexOf(" ")&&(document.head.appendChild(e),e.setAttribute("data-s",""))}))}var n=e.stylisPlugins||Et;var o,i,a={},s=[];o=e.container||document.head,Array.prototype.forEach.call(document.querySelectorAll('style[data-emotion^="'+t+' "]'),(function(e){for(var t=e.getAttribute("data-emotion").split(" "),r=1;r=4;++n,o-=4)t=1540483477*(65535&(t=255&e.charCodeAt(n)|(255&e.charCodeAt(++n))<<8|(255&e.charCodeAt(++n))<<16|(255&e.charCodeAt(++n))<<24))+(59797*(t>>>16)<<16),r=1540483477*(65535&(t^=t>>>24))+(59797*(t>>>16)<<16)^1540483477*(65535&r)+(59797*(r>>>16)<<16);switch(o){case 3:r^=(255&e.charCodeAt(n+2))<<16;case 2:r^=(255&e.charCodeAt(n+1))<<8;case 1:r=1540483477*(65535&(r^=255&e.charCodeAt(n)))+(59797*(r>>>16)<<16)}return(((r=1540483477*(65535&(r^=r>>>13))+(59797*(r>>>16)<<16))^r>>>15)>>>0).toString(36)},Wt={animationIterationCount:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},Nt=/[A-Z]|^ms/g,Zt=/_EMO_([^_]+?)_([^]*?)_EMO_/g,Ft=function(e){return 45===e.charCodeAt(1)},Bt=function(e){return null!=e&&"boolean"!==typeof e},Ht=Re((function(e){return Ft(e)?e:e.replace(Nt,"-$&").toLowerCase()})),Lt=function(e,t){switch(e){case"animation":case"animationName":if("string"===typeof t)return t.replace(Zt,(function(e,t,r){return Kt={name:t,styles:r,next:Kt},t}))}return 1===Wt[e]||Ft(e)||"number"!==typeof t||0===t?t:t+"px"};function Gt(e,t,r){if(null==r)return"";if(void 0!==r.__emotion_styles)return r;switch(typeof r){case"boolean":return"";case"object":if(1===r.anim)return Kt={name:r.name,styles:r.styles,next:Kt},r.name;if(void 0!==r.styles){var n=r.next;if(void 0!==n)for(;void 0!==n;)Kt={name:n.name,styles:n.styles,next:Kt},n=n.next;return r.styles+";"}return function(e,t,r){var n="";if(Array.isArray(r))for(var o=0;o96?Qt:er},rr=function(e,t,r){var n;if(t){var o=t.shouldForwardProp;n=e.__emotion_forwardProp&&o?function(t){return e.__emotion_forwardProp(t)&&o(t)}:o}return"function"!==typeof n&&r&&(n=e.__emotion_forwardProp),n},nr=i.useInsertionEffect?i.useInsertionEffect:function(e){e()};var or=function(e){var t=e.cache,r=e.serialized,n=e.isStringTag;Jt(t,r,n);var o;o=function(){return function(e,t,r){Jt(e,t,r);var n=e.key+"-"+t.name;if(void 0===e.inserted[t.name]){var o=t;do{e.insert(t===o?"."+n:"",o,e.sheet,!0),o=o.next}while(void 0!==o)}}(t,r,n)},nr(o);return null},ir=function e(t,r){var i,a,s=t.__emotion_real===t,c=s&&t.__emotion_base||t;void 0!==r&&(i=r.label,a=r.target);var l=rr(t,r,s),u=l||tr(c),d=!u("as");return function(){var p=arguments,f=s&&void 0!==t.__emotion_styles?t.__emotion_styles.slice(0):[];if(void 0!==i&&f.push("label:"+i+";"),null==p[0]||void 0===p[0].raw)f.push.apply(f,p);else{0,f.push(p[0][0]);for(var h=p.length,m=1;m{n+="color"===t?lr(n)?e[t]:l(e[t]):`${lr(n)?t:l(t)}${l(e[t].toString())}`})),n}var dr=function(...e){const t=e.reduce(((e,t)=>(t.filterProps.forEach((r=>{e[r]=t})),e)),{}),r=e=>Object.keys(e).reduce(((r,n)=>t[n]?A(r,t[n](e)):r),{});return r.propTypes={},r.filterProps=e.reduce(((e,t)=>e.concat(t.filterProps)),[]),r};function pr(e){return"number"!==typeof e?e:`${e}px solid`}const fr=S({prop:"border",themeKey:"borders",transform:pr}),hr=S({prop:"borderTop",themeKey:"borders",transform:pr}),mr=S({prop:"borderRight",themeKey:"borders",transform:pr}),gr=S({prop:"borderBottom",themeKey:"borders",transform:pr}),br=S({prop:"borderLeft",themeKey:"borders",transform:pr}),yr=S({prop:"borderColor",themeKey:"palette"}),vr=S({prop:"borderTopColor",themeKey:"palette"}),xr=S({prop:"borderRightColor",themeKey:"palette"}),kr=S({prop:"borderBottomColor",themeKey:"palette"}),wr=S({prop:"borderLeftColor",themeKey:"palette"}),$r=e=>{if(void 0!==e.borderRadius&&null!==e.borderRadius){const t=M(e.theme,"shape.borderRadius",4),r=e=>({borderRadius:I(t,e)});return v(e,e.borderRadius,r)}return null};$r.propTypes={},$r.filterProps=["borderRadius"];var Sr=dr(fr,hr,mr,gr,br,yr,vr,xr,kr,wr,$r);var Ar=dr(S({prop:"displayPrint",cssProperty:!1,transform:e=>({"@media print":{display:e}})}),S({prop:"display"}),S({prop:"overflow"}),S({prop:"textOverflow"}),S({prop:"visibility"}),S({prop:"whiteSpace"}));var Cr=dr(S({prop:"flexBasis"}),S({prop:"flexDirection"}),S({prop:"flexWrap"}),S({prop:"justifyContent"}),S({prop:"alignItems"}),S({prop:"alignContent"}),S({prop:"order"}),S({prop:"flex"}),S({prop:"flexGrow"}),S({prop:"flexShrink"}),S({prop:"alignSelf"}),S({prop:"justifyItems"}),S({prop:"justifySelf"}));const Pr=e=>{if(void 0!==e.gap&&null!==e.gap){const t=M(e.theme,"spacing",8),r=e=>({gap:I(t,e)});return v(e,e.gap,r)}return null};Pr.propTypes={},Pr.filterProps=["gap"];const Or=e=>{if(void 0!==e.columnGap&&null!==e.columnGap){const t=M(e.theme,"spacing",8),r=e=>({columnGap:I(t,e)});return v(e,e.columnGap,r)}return null};Or.propTypes={},Or.filterProps=["columnGap"];const zr=e=>{if(void 0!==e.rowGap&&null!==e.rowGap){const t=M(e.theme,"spacing",8),r=e=>({rowGap:I(t,e)});return v(e,e.rowGap,r)}return null};zr.propTypes={},zr.filterProps=["rowGap"];var Tr=dr(Pr,Or,zr,S({prop:"gridColumn"}),S({prop:"gridRow"}),S({prop:"gridAutoFlow"}),S({prop:"gridAutoColumns"}),S({prop:"gridAutoRows"}),S({prop:"gridTemplateColumns"}),S({prop:"gridTemplateRows"}),S({prop:"gridTemplateAreas"}),S({prop:"gridArea"}));var Rr=dr(S({prop:"position"}),S({prop:"zIndex",themeKey:"zIndex"}),S({prop:"top"}),S({prop:"right"}),S({prop:"bottom"}),S({prop:"left"}));var jr=dr(S({prop:"color",themeKey:"palette"}),S({prop:"bgcolor",cssProperty:"backgroundColor",themeKey:"palette"}),S({prop:"backgroundColor",themeKey:"palette"}));var Mr=S({prop:"boxShadow",themeKey:"shadows"});function Er(e){return e<=1&&0!==e?100*e+"%":e}const Ir=S({prop:"width",transform:Er}),_r=e=>{if(void 0!==e.maxWidth&&null!==e.maxWidth){const t=t=>{var r,n,o;return{maxWidth:(null==(r=e.theme)||null==(n=r.breakpoints)||null==(o=n.values)?void 0:o[t])||b[t]||Er(t)}};return v(e,e.maxWidth,t)}return null};_r.filterProps=["maxWidth"];const Wr=S({prop:"minWidth",transform:Er}),Nr=S({prop:"height",transform:Er}),Zr=S({prop:"maxHeight",transform:Er}),Fr=S({prop:"minHeight",transform:Er});S({prop:"size",cssProperty:"width",transform:Er}),S({prop:"size",cssProperty:"height",transform:Er});var Br=dr(Ir,_r,Wr,Nr,Zr,Fr,S({prop:"boxSizing"}));const Hr=S({prop:"fontFamily",themeKey:"typography"}),Lr=S({prop:"fontSize",themeKey:"typography"}),Gr=S({prop:"fontStyle",themeKey:"typography"}),Kr=S({prop:"fontWeight",themeKey:"typography"}),Vr=S({prop:"letterSpacing"}),Ur=S({prop:"textTransform"}),Dr=S({prop:"lineHeight"}),qr=S({prop:"textAlign"});var Xr=dr(S({prop:"typography",cssProperty:!1,themeKey:"typography"}),Hr,Lr,Gr,Kr,Vr,Dr,qr,Ur);const Yr={borders:Sr.filterProps,display:Ar.filterProps,flexbox:Cr.filterProps,grid:Tr.filterProps,positions:Rr.filterProps,palette:jr.filterProps,shadows:Mr.filterProps,sizing:Br.filterProps,spacing:B.filterProps,typography:Xr.filterProps},Jr={borders:Sr,display:Ar,flexbox:Cr,grid:Tr,positions:Rr,palette:jr,shadows:Mr,sizing:Br,spacing:B,typography:Xr};Object.keys(Yr).reduce(((e,t)=>(Yr[t].forEach((r=>{e[r]=Jr[t]})),e)),{});const Qr=function(e=Jr){const t=Object.keys(e).reduce(((t,r)=>(e[r].filterProps.forEach((n=>{t[n]=e[r]})),t)),{});function r(e,r,n){const o={[e]:r,theme:n},i=t[e];return i?i(o):{[e]:r}}return function e(n){const{sx:o,theme:i={}}=n||{};if(!o)return null;function a(n){let o=n;if("function"===typeof n)o=n(i);else if("object"!==typeof n)return n;if(!o)return null;const a=x(i.breakpoints),s=Object.keys(a);let c=a;return Object.keys(o).forEach((n=>{const a=(s=o[n],l=i,"function"===typeof s?s(l):s);var s,l;if(null!==a&&void 0!==a)if("object"===typeof a)if(t[n])c=A(c,r(n,a,i));else{const t=v({theme:i},a,(e=>({[n]:e})));!function(...e){const t=e.reduce(((e,t)=>e.concat(Object.keys(t))),[]),r=new Set(t);return e.every((e=>r.size===Object.keys(e).length))}(t,a)?c=A(c,t):c[n]=e({sx:a,theme:i})}else c=A(c,r(n,a,i))})),k(s,c)}return Array.isArray(o)?o.map(a):a(o)}}();Qr.filterProps=["sx"];var en=Qr;const tn=["name","slot","skipVariantsResolver","skipSx","overridesResolver"],rn=["theme"],nn=["theme"];function on(e){return 0===Object.keys(e).length}function an(e){return"ownerState"!==e&&"theme"!==e&&"sx"!==e&&"as"!==e}const sn=L();const cn=function(e={}){const{defaultTheme:t=sn,rootShouldForwardProp:r=an,slotShouldForwardProp:o=an,styleFunctionSx:i=en}=e;return(e,s={})=>{const{name:c,slot:l,skipVariantsResolver:u,skipSx:d,overridesResolver:p}=s,f=a(s,tn),h=void 0!==u?u:l&&"Root"!==l||!1,m=d||!1;let g=an;"Root"===l?g=r:l&&(g=o);const b=function(e,t){return sr(e,t)}(e,(0,n.Z)({shouldForwardProp:g,label:undefined},f)),y=(e,...r)=>{const o=r?r.map((e=>"function"===typeof e&&e.__emotion_real!==e?r=>{let{theme:o}=r,i=a(r,rn);return e((0,n.Z)({theme:on(o)?t:o},i))}:e)):[];let s=e;c&&p&&o.push((e=>{const r=on(e.theme)?t:e.theme,o=((e,t)=>t.components&&t.components[e]&&t.components[e].styleOverrides?t.components[e].styleOverrides:null)(c,r);if(o){const t={};return Object.entries(o).forEach((([o,i])=>{t[o]="function"===typeof i?i((0,n.Z)({},e,{theme:r})):i})),p(e,t)}return null})),c&&!h&&o.push((e=>{const r=on(e.theme)?t:e.theme;return((e,t,r,n)=>{var o,i;const{ownerState:a={}}=e,s=[],c=null==r||null==(o=r.components)||null==(i=o[n])?void 0:i.variants;return c&&c.forEach((r=>{let n=!0;Object.keys(r.props).forEach((t=>{a[t]!==r.props[t]&&e[t]!==r.props[t]&&(n=!1)})),n&&s.push(t[ur(r.props)])})),s})(e,((e,t)=>{let r=[];t&&t.components&&t.components[e]&&t.components[e].variants&&(r=t.components[e].variants);const n={};return r.forEach((e=>{const t=ur(e.props);n[t]=e.style})),n})(c,r),r,c)})),m||o.push((e=>{const r=on(e.theme)?t:e.theme;return i((0,n.Z)({},e,{theme:r}))}));const l=o.length-r.length;if(Array.isArray(e)&&l>0){const t=new Array(l).fill("");s=[...e,...t],s.raw=[...e.raw,...t]}else"function"===typeof e&&e.__emotion_real!==e&&(s=r=>{let{theme:o}=r,i=a(r,nn);return e((0,n.Z)({theme:on(o)?t:o},i))});return b(s,...o)};return b.withConfig&&(y.withConfig=b.withConfig),y}}({defaultTheme:ze,rootShouldForwardProp:e=>an(e)&&"classes"!==e});var ln=cn;const un=e=>e;var dn=(()=>{let e=un;return{configure(t){e=t},generate:t=>e(t),reset(){e=un}}})();const pn={active:"Mui-active",checked:"Mui-checked",completed:"Mui-completed",disabled:"Mui-disabled",error:"Mui-error",expanded:"Mui-expanded",focused:"Mui-focused",focusVisible:"Mui-focusVisible",required:"Mui-required",selected:"Mui-selected"};function fn(e,t){return pn[t]||`${dn.generate(e)}-${t}`}function hn(e){return fn("MuiSvgIcon",e)}!function(e,t){const r={};t.forEach((t=>{r[t]=fn(e,t)}))}("MuiSvgIcon",["root","colorPrimary","colorSecondary","colorAction","colorError","colorDisabled","fontSizeInherit","fontSizeSmall","fontSizeMedium","fontSizeLarge"]);var mn=r(5893);const gn=["children","className","color","component","fontSize","htmlColor","inheritViewBox","titleAccess","viewBox"],bn=e=>{const{color:t,fontSize:r,classes:n}=e;return function(e,t,r){const n={};return Object.keys(e).forEach((o=>{n[o]=e[o].reduce(((e,n)=>(n&&(r&&r[n]&&e.push(r[n]),e.push(t(n))),e)),[]).join(" ")})),n}({root:["root","inherit"!==t&&`color${u(t)}`,`fontSize${u(r)}`]},hn,n)},yn=ln("svg",{name:"MuiSvgIcon",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:r}=e;return[t.root,"inherit"!==r.color&&t[`color${u(r.color)}`],t[`fontSize${u(r.fontSize)}`]]}})((({theme:e,ownerState:t})=>{var r,n,o,i,a,s,c,l,u,d,p,f,h,m,g,b,y;return{userSelect:"none",width:"1em",height:"1em",display:"inline-block",fill:"currentColor",flexShrink:0,transition:null==(r=e.transitions)||null==(n=r.create)?void 0:n.call(r,"fill",{duration:null==(o=e.transitions)||null==(i=o.duration)?void 0:i.shorter}),fontSize:{inherit:"inherit",small:(null==(a=e.typography)||null==(s=a.pxToRem)?void 0:s.call(a,20))||"1.25rem",medium:(null==(c=e.typography)||null==(l=c.pxToRem)?void 0:l.call(c,24))||"1.5rem",large:(null==(u=e.typography)||null==(d=u.pxToRem)?void 0:d.call(u,35))||"2.1875"}[t.fontSize],color:null!=(p=null==(f=(e.vars||e).palette)||null==(h=f[t.color])?void 0:h.main)?p:{action:null==(m=(e.vars||e).palette)||null==(g=m.action)?void 0:g.active,disabled:null==(b=(e.vars||e).palette)||null==(y=b.action)?void 0:y.disabled,inherit:void 0}[t.color]}})),vn=o.forwardRef((function(e,t){const r=Te({props:e,name:"MuiSvgIcon"}),{children:o,className:i,color:c="inherit",component:l="svg",fontSize:u="medium",htmlColor:d,inheritViewBox:p=!1,titleAccess:f,viewBox:h="0 0 24 24"}=r,m=a(r,gn),g=(0,n.Z)({},r,{color:c,component:l,fontSize:u,instanceFontSize:e.fontSize,inheritViewBox:p,viewBox:h}),b={};p||(b.viewBox=h);const y=bn(g);return(0,mn.jsxs)(yn,(0,n.Z)({as:l,className:(0,s.Z)(y.root,i),ownerState:g,focusable:"false",color:d,"aria-hidden":!f||void 0,role:f?"img":void 0,ref:t},b,m,{children:[o,f?(0,mn.jsx)("title",{children:f}):null]}))}));vn.muiName="SvgIcon";var xn=vn;function kn(e,t){const r=(r,o)=>(0,mn.jsx)(xn,(0,n.Z)({"data-testid":`${t}Icon`,ref:o},r,{children:e}));return r.muiName=xn.muiName,o.memo(o.forwardRef(r))}},7370:function(e,t,r){r.d(t,{ZP:function(){return F}});var n=r(7294),o=r(8375),i=r(9641),a=r(4213),s=r(7354),c=r(9260),l=r(2903);var u=r(6212),d=r(9975);const p=(0,u.zo)("div",{d:"flex",w:"100%",h:"auto",flex:"1 1 auto",fd:"column",jc:"inherit",ai:"inherit",ac:"inherit",py:"$lg",px:"$sm",oy:"auto",position:"relative",ta:"left"}),f=(0,u.zo)("div",{$$cardColor:"$colors$backgroundContrast",$$cardTextColor:"$colors$text",m:0,p:0,br:"$lg",bg:"$$cardColor",color:"$$cardTextColor",position:"relative",display:"flex",overflow:"hidden",fd:"column",width:"100%",height:"auto",boxSizing:"border-box","@motion":{transition:"none"},".nextui-image":{width:"100%"},[`& ${o.q}`]:{zIndex:"$1",".nextui-drip-filler":{opacity:.25,fill:"$accents6"}},variants:{variant:{flat:{bg:"$accents0"},shadow:{dropShadow:"$lg"},bordered:{borderStyle:"solid",borderColor:"$border"}},borderWeight:{light:{bw:"$light"},normal:{bw:"$normal"},bold:{bw:"$bold"},extrabold:{bw:"$extrabold"},black:{bw:"$black"}},disableAnimation:{true:{transition:"none"},false:{transition:"$card"}},isPressable:{true:{cursor:"pointer",us:"none",WebkitTapHighlightColor:"transparent"}},isPressed:{true:{}},isHovered:{true:{dropShadow:"$lg"}}},compoundVariants:[{isPressed:!0,disableAnimation:!1,css:{transform:"scale(0.97)"}},{isHovered:!0,disableAnimation:!1,css:{transform:"translateY(-2px)"}},{isHovered:!0,variant:"shadow",css:{dropShadow:"$xl"}}]},d.UU,d.BM),h=(0,u.zo)("div",{w:"100%",display:"flex",flexShrink:0,zIndex:"$1",jc:"flex-start",ai:"center",overflow:"hidden",color:"inherit",p:"$sm"}),m=(0,u.zo)("div",{w:"100%",h:"auto",p:"$sm",d:"flex",ai:"center",overflow:"hidden",color:"inherit",bblr:"$lg",bbrr:"$lg",variants:{isBlurred:{true:{bf:"saturate(180%) blur(10px)",bg:"$$cardColor"}}}});var g=r(3569),b=r(5893);const y=n.forwardRef((({...e},t)=>{const{as:r,css:u,children:d,...p}=e,{cardRef:h,variant:m,isFocusVisible:g,isPressable:y,isPressed:v,disableAnimation:x,disableRipple:k,borderWeight:w,isHovered:$,getCardProps:S,dripBindings:A}=(e=>{const{ref:t,disableAnimation:r=!1,disableRipple:o=!1,variant:u="shadow",isHoverable:d=!1,borderWeight:p="light",isPressable:f=!1,onClick:h,onPress:m,autoFocus:g,allowTextSelectionOnPress:b=!0,...y}=e,v=(0,l.gy)(t),{onClick:x,...k}=(0,c.Z)(!1,v),w=e=>{r||o||!v.current||x(e)},{isPressed:$,pressProps:S}=(0,s.r7)({isDisabled:!f,onPress:e=>{"keyboard"!==e.pointerType&&"virtual"!==e.pointerType||(w(e),null==h||h(e)),null==m||m(e)},allowTextSelectionOnPress:b,...y}),{hoverProps:A,isHovered:C}=(0,s.XI)({isDisabled:!d,...y}),{isFocusVisible:P,focusProps:O}=(0,i.Fx)({autoFocus:g});S.onClick=e=>{f&&(w(e),null==h||h(e))};const z=(0,n.useCallback)(((e={})=>({...(0,a.dG)(f?{...S,...O}:{},d?A:{},y,e)})),[f,d,S,O,A,y]);return{cardRef:v,variant:u,borderWeight:p,isPressable:f,isHovered:C,isPressed:$,disableAnimation:r,disableRipple:o,dripBindings:k,onDripClickHandler:x,isFocusVisible:P,getCardProps:z}})({...p,ref:t});return(0,b.jsxs)(f,{ref:h,as:r,css:u,variant:m,role:y?"button":"section",borderWeight:w,disableAnimation:x,isPressable:y,isPressed:v,isHovered:$,tabIndex:y?0:-1,isFocusVisible:g,...S(),children:[y&&!x&&!k&&(0,b.jsx)(o.Z,{...A}),d]})}));g.Ts&&(y.displayName="NextUI.Card"),y.toString=()=>".nextui-card";var v=y,x=r(88);const k=(0,u.F4)({"0%":{backgroundPosition:"200% 0"},to:{backgroundPosition:"-200% 0"}}),w=(0,u.zo)("div",{opacity:0,margin:"0 auto",position:"relative",overflow:"hidden",maxWidth:"100%",transition:"transform 250ms ease 0ms, opacity 200ms ease-in 0ms","@motion":{transition:"none"},variants:{ready:{true:{opacity:1},false:{opacity:0}}}}),$=(0,u.zo)("img",{size:"100%",display:"block"}),S=(0,u.zo)("div",{position:"absolute",top:0,left:0,right:0,bottom:0,size:"100%",borderRadius:"inherit",backgroundImage:"linear-gradient(270deg, $colors$accents1, $colors$accents2, $colors$accents2, $colors$accents1)",backgroundSize:"400% 100%",animation:`${k} 5s ease-in-out infinite`,transition:"opacity 300ms ease-out"});var A=r(1309);const C=n.memo((({opacity:e,css:t,className:r,...n})=>(0,b.jsx)(S,{css:{opacity:e,...t},className:(0,A.Z)("nextui-image-skeleton",r),...n})));g.Ts&&(C.displayName="NextUI.ImageSkeleton"),C.toString=()=>".nextui-image-skeleton";var P=(0,x.Z)(C,{opacity:.5,className:""});const O=(e,t)=>{if(!e)return 0;const r=e.includes("px")?+e.split("px")[0]:e.includes("%")?+e.split("%")[0]*t*.01:e;return Number.isNaN(+r)?0:+r};var z=e=>{const[t,r]=(0,n.useState)({width:0,height:0}),o=()=>{const{width:t,height:n}=(e=>{if(!e||"undefined"==typeof window)return{width:0,height:0};const t=e.getBoundingClientRect(),{width:r,height:n}=window.getComputedStyle(e);return{width:O(`${r}`,t.width),height:O(`${n}`,t.height)}})(e.current);r({width:t,height:n})};return(0,n.useEffect)((()=>o()),[e.current]),[t,o]},T=e=>{const[t,r]=(0,n.useState)((()=>"function"==typeof e?e():e)),o=(0,n.useRef)(e);return(0,n.useEffect)((()=>{o.current=t}),[t]),[t,e=>{const t="function"==typeof e?e(o.current):e;o.current=t,r(t)},o]},R=r(6693);const j=n.forwardRef(((e,t)=>{const{src:r,width:o,height:i,showSkeleton:a=!0,className:s,maxDelay:c=3e3,autoResize:u=!1,objectFit:d="scale-down",containerCss:p,css:f,...h}=e,m=(0,l.gy)(t),[g,y]=(0,n.useState)(!0),[v,x]=(0,n.useState)(a),{w:k,h:S}=(0,n.useMemo)((()=>({w:o?"number"==typeof o?`${o}px`:o:"auto",h:i?"number"==typeof i?`${i}px`:i:"auto"})),[o,i]),[C,O,j]=T(S),[M,E]=z(m),I=a&&!!o&&!!i;(0,n.useEffect)((()=>{m.current&&m.current.complete&&(y(!1),x(!1))})),(0,n.useEffect)((()=>{const e=setTimeout((()=>{I&&x(!1),clearTimeout(e)}),c);return()=>clearTimeout(e)}),[g]),(0,n.useEffect)((()=>{if(!u)return;const e=0===M.width,t="auto"===j.current;!e&&o&&i&&(M.width{u&&E()}));const _=(0,n.useMemo)((()=>g?"loading":"ready"),[g]);return(0,b.jsxs)(w,{className:(0,A.Z)("nextui-image-container",`nextui-image--${_}`,s),"data-state":_,ready:!g||v,css:{width:k,height:C,...p},children:[v&&(0,b.jsx)(P,{opacity:1}),(0,b.jsx)($,{ref:m,className:"nextui-image",width:o,height:i,onLoad:()=>{y(!1)},src:r,"data-state":_,alt:e.alt||"",css:{objectFit:d,...f},...h})]})}));g.Ts&&(j.displayName="NextUI.Image"),j.toString=()=>".nextui-image";var M=n.memo(j),E=r(6772);const I=(0,u.zo)("div",{width:"100%",maxWidth:"100%",position:"relative",variants:{color:{default:{bg:"$border"},primary:{bg:"$primary"},secondary:{bg:"$secondary"},success:{bg:"$success"},warning:{bg:"$warning"},error:{bg:"$error"}}},defaultVariants:{color:"default"}}),_=(0,u.zo)("span",{position:"absolute",left:"50%",top:"50%",minHeight:"100%",display:"inline-flex",jc:"center",ai:"center",transform:"translate(-50%, -50%)",padding:"0 $lg",fontSize:"$base",fontWeight:"bold",textTransform:"capitalize",backgroundColor:"$background",zIndex:"$1",variants:{color:{default:{color:"$text"},primary:{color:"$primary"},secondary:{color:"$secondary"},success:{color:"$success"},warning:{color:"$warning"},error:{color:"$error"}}}}),W=({height:e,x:t,y:r,align:o,children:i,textColor:a,css:s,...c})=>{const l=(0,n.useMemo)((()=>o&&"center"!==o?"left"===o||"start"===o?{transform:"translateY(-50%)",left:"7%"}:{transform:"translateY(-50%)",left:"auto",right:"7%"}:""),[o]),u=r?(0,E.m)(r/2):0,d=t?(0,E.m)(t/2):0;return(0,b.jsx)(I,{role:"separator",css:{margin:`${u} ${d}`,height:`calc(${e} * 1px)`,...s},...c,children:i&&(0,b.jsx)(_,{css:{...l},color:a,className:"nextui-divider-text",children:i})})};W.toString=()=>".nextui-divider";const N=n.memo(W);var Z=(0,x.Z)(N,{x:0,y:0,height:1,align:"center"});v.Header=h,v.Body=p,v.Footer=m,v.Image=M,v.Divider=Z;var F=v},6979:function(e,t,r){r.d(t,{Z:function(){return g}});var n=r(7294),o=r(88),i=r(2903),a=r(3569),s=r(3917);const c=(0,r(6212).zo)("p",{variants:{weight:{hairline:{fontWeight:"$hairline"},thin:{fontWeight:"$thin"},light:{fontWeight:"$light"},normal:{fontWeight:"$normal"},medium:{fontWeight:"$medium"},semibold:{fontWeight:"$semibold"},bold:{fontWeight:"$bold"},extrabold:{fontWeight:"$extrabold"},black:{fontWeight:"$black"}}}});var l=r(5893);const u=n.forwardRef(((e,t)=>{const{children:r,tag:o,color:a,transform:u,margin:d,size:p,css:f,...h}=e,m=(0,i.gy)(t),g=(0,n.useMemo)((()=>(0,s.h1)(a)?"default"===a?"$text":`$${a}`:a),[a]),b=(0,n.useMemo)((()=>p?"number"==typeof p?`${p}px`:p:"inherit"),[p]),y=(0,n.useMemo)((()=>d?"number"==typeof d?`${p}px`:d:"inherit"),[d]);return(0,l.jsx)(c,{ref:m,as:o,css:{color:g,fontSize:p?b:"",margin:y,tt:u,...f},...h,children:r})}));a.Ts&&(u.displayName="NextUI.TextChild"),u.toString=()=>".nextui-text-child";const d=n.memo(u);var p=(0,o.Z)(d,{color:"default"});const f=(e,t,r,n)=>{if(!e.length)return t;const o=e.slice(1,e.length);return(0,l.jsx)(p,{tag:e[0],size:r,transform:n,children:f(o,t,r)})},h=n.forwardRef(((e,t)=>{const{h1:r,h2:o,h3:a,h4:s,h5:c,h6:u,b:d,small:h,i:m,span:g,del:b,em:y,blockquote:v,transform:x,size:k,margin:w,children:$,...S}=e,A=(0,i.gy)(t),C={h1:r,h2:o,h3:a,h4:s,h5:c,h6:u,blockquote:v},P={span:g,small:h,b:d,em:y,i:m,del:b},O=Object.keys(C).filter((e=>C[e])),z=Object.keys(P).filter((e=>P[e])),T=(0,n.useMemo)((()=>O[0]?O[0]:z[0]?z[0]:"p"),[O,z]),R=z.filter((e=>e!==T)),j=(0,n.useMemo)((()=>R.length?f(R,$,k,x):$),[R,$,k,x]);return(0,l.jsx)(p,{ref:A,transform:x,tag:T,margin:w,size:k,...S,children:j})}));a.Ts&&(h.displayName="NextUI.Text"),h.toString=()=>".nextui-text";const m=n.memo(h);var g=(0,o.Z)(m,{h1:!1,h2:!1,h3:!1,h4:!1,h5:!1,h6:!1,b:!1,small:!1,transform:"none",i:!1,span:!1,del:!1,em:!1,blockquote:!1,color:"default"})},6693:function(e,t,r){r.d(t,{Z:function(){return o}});var n=r(7294),o=(e,t=!0)=>{(0,n.useEffect)((()=>{const r=()=>e();return t&&r(),window.addEventListener("resize",r),()=>window.removeEventListener("resize",r)}),[])}}}]); \ No newline at end of file diff --git a/dashboard/out/_next/static/chunks/470-351dc976d74fe66a.js b/dashboard/out/_next/static/chunks/470-351dc976d74fe66a.js new file mode 100644 index 000000000..3f4703d14 --- /dev/null +++ b/dashboard/out/_next/static/chunks/470-351dc976d74fe66a.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[470],{1160:function(e,t,r){"use strict";r.d(t,{ZP:function(){return B}});var o=r(7294),n=r(9641),i=r(4213),s=r(7354);function a(e,t,r,o){Object.defineProperty(e,t,{get:r,set:o,enumerable:!0,configurable:!0})}function l(e,t){let r,{elementType:o="button",isDisabled:a,onPress:l,onPressStart:c,onPressEnd:d,onPressChange:u,preventFocusOnPress:b,allowFocusWhenDisabled:p,onClick:g,href:f,target:$,rel:h,type:m="button"}=e;r="button"===o?{type:m,disabled:a}:{role:"button",tabIndex:a?void 0:0,href:"a"===o&&a?void 0:f,target:"a"===o?$:void 0,type:"input"===o?m:void 0,disabled:"input"===o?a:void 0,"aria-disabled":a&&"input"!==o?a:void 0,rel:"a"===o?h:void 0};let{pressProps:x,isPressed:v}=(0,s.r7)({onPressStart:c,onPressEnd:d,onPressChange:u,onPress:l,isDisabled:a,preventFocusOnPress:b,ref:t}),{focusableProps:y}=(0,n.kc)(e,t);p&&(y.tabIndex=a?-1:y.tabIndex);let w=(0,i.dG)(y,x,(0,i.zL)(e,{labelable:!0}));return{isPressed:v,buttonProps:(0,i.dG)(r,w,{"aria-haspopup":e["aria-haspopup"],"aria-expanded":e["aria-expanded"],"aria-controls":e["aria-controls"],"aria-pressed":e["aria-pressed"],onClick:e=>{g&&(g(e),console.warn("onClick is deprecated, please use onPress"))}})}}a({},"useButton",(()=>l));function c(e,t,r){const{isSelected:o}=t,{isPressed:n,buttonProps:s}=l({...e,onPress:(0,i.tS)(t.toggle,e.onPress)},r);return{isPressed:n,buttonProps:(0,i.dG)(s,{"aria-pressed":o})}}a({},"useToggleButton",(()=>c));const d={};var u=(e,t)=>{const r=`[Next UI]${t?` [${t}]`:" "}: ${e}`;"undefined"==typeof console||d[r]||(d[r]=!0,console.warn(r))},b=r(8375);const p=o.createContext({isButtonGroup:!1,disabled:!1});var g=r(6212),f=r(88),$=r(1309),h=r(5893);const m=(0,g.zo)("span",{dflex:"center",position:"absolute",left:"$$buttonPadding",right:"auto",top:"50%",transform:"translateY(-50%)",color:"inherit",zIndex:"$1","& svg":{background:"transparent"},variants:{isAuto:{true:{position:"relative",transform:"none",top:"0%"}},isRight:{true:{right:"$$buttonPadding",left:"auto"}},isSingle:{true:{position:"static",transform:"none"}},isGradientButtonBorder:{true:{}}},compoundVariants:[{isAuto:!0,isRight:!0,isSingle:!1,css:{order:2,ml:"calc($$buttonPadding / 2)",right:"0%",left:"0%"}},{isAuto:!0,isRight:!1,isSingle:!1,css:{order:0,mr:"calc($$buttonPadding / 2)",right:"0%",left:"0%"}},{isSingle:!0,isRight:!1,css:{ml:0}},{isSingle:!0,isRight:!0,css:{mr:0}},{isSingle:!0,isRight:!1,isGradientButtonBorder:!0,css:{mr:"calc($$buttonPadding / 2)"}}]}),x=({children:e,className:t,css:r,...o})=>(0,h.jsx)(m,{className:(0,$.Z)("nextui-button-icon",{"nextui-button-icon-right":o.isRight,"nextui-button-icon-single":o.isSingle},t),css:{...r},...o,children:e});x.toString=()=>".nextui-button-icon";const v=o.memo(x);var y=(0,f.Z)(v,{className:""}),w=r(9260),S=r(9975);var C=(0,g.zo)("button",{$$buttonBorderRadius:"$radii$md",$$buttonPressedScale:.97,dflex:"center",appearance:"none",boxSizing:"border-box",fontWeight:"$medium",us:"none",lineHeight:"$sm",ta:"center",whiteSpace:"nowrap",transition:"$button",position:"relative",overflow:"hidden",border:"none",cursor:"pointer",pe:"auto",p:0,br:"$$buttonBorderRadius","@motion":{transition:"none"},".nextui-button-text":{dflex:"center",zIndex:"$2","p, pre, div":{margin:0}},[`& ${b.q}`]:{zIndex:"$1",".nextui-drip-filler":{opacity:.25,fill:"$accents2"}},variants:{bordered:{true:{bg:"transparent",borderStyle:"solid",color:"$text"}},ghost:{true:{}},color:{default:{bg:"$primary",color:"$primarySolidContrast"},primary:{bg:"$primary",color:"$primarySolidContrast"},secondary:{bg:"$secondary",color:"$secondarySolidContrast"},success:{bg:"$success",color:"$successSolidContrast"},warning:{bg:"$warning",color:"$warningSolidContrast"},error:{bg:"$error",color:"$errorSolidContrast"},gradient:{bg:"$gradient",color:"$primarySolidContrast"}},size:{xs:{$$buttonPadding:"$space$3",$$buttonBorderRadius:"$radii$xs",$$buttonHeight:"$space$10",px:"$3",height:"$$buttonHeight",lh:"$space$10",width:"auto",minWidth:"$20",fontSize:"$xs"},sm:{$$buttonPadding:"$space$5",$$buttonBorderRadius:"$radii$sm",$$buttonHeight:"$space$12",px:"$5",height:"$$buttonHeight",lh:"$space$14",width:"auto",minWidth:"$36",fontSize:"$sm"},md:{$$buttonPadding:"$space$7",$$buttonBorderRadius:"$radii$md",$$buttonHeight:"$space$14",px:"$7",height:"$$buttonHeight",lh:"$space$14",width:"auto",minWidth:"$48",fontSize:"$sm"},lg:{$$buttonPadding:"$space$9",$$buttonBorderRadius:"$radii$base",$$buttonHeight:"$space$16",px:"$9",height:"$$buttonHeight",lh:"$space$15",width:"auto",minWidth:"$60",fontSize:"$md"},xl:{$$buttonPadding:"$space$10",$$buttonBorderRadius:"$radii$xl",$$buttonHeight:"$space$18",px:"$10",height:"$$buttonHeight",lh:"$space$17",width:"auto",minWidth:"$72",fontSize:"$lg"}},borderWeight:{light:{bw:"$light",$$buttonBorderWeight:"$borderWeights$light"},normal:{bw:"$normal",$$buttonBorderWeight:"$borderWeights$normal"},bold:{bw:"$bold",$$buttonBorderWeight:"$borderWeights$bold"},extrabold:{bw:"$extrabold",$$buttonBorderWeight:"$borderWeights$extrabold"},black:{bw:"$black",$$buttonBorderWeight:"$borderWeights$black"}},flat:{true:{color:"$text"}},light:{true:{bg:"transparent",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$accents2"}}}},shadow:{true:{bs:"$sm"}},animated:{false:{transition:"none"}},auto:{true:{width:"auto",minWidth:"min-content"}},rounded:{true:{$$buttonBorderRadius:"$radii$pill"}},isPressed:{true:{}},isHovered:{true:{}},isChildLess:{true:{p:0,width:"$$buttonHeight",height:"$$buttonHeight"}}},compoundVariants:[{isPressed:!0,animated:!0,css:{transform:"scale($$buttonPressedScale)"}},{auto:!0,isChildLess:!1,size:"xs",css:{px:"$5",minWidth:"min-content"}},{auto:!0,isChildLess:!1,size:"sm",css:{px:"$8",minWidth:"min-content"}},{auto:!0,isChildLess:!1,size:"md",css:{px:"$9",minWidth:"min-content"}},{auto:!0,isChildLess:!1,size:"lg",css:{px:"$10",minWidth:"min-content"}},{auto:!0,isChildLess:!1,size:"xl",css:{px:"$11",minWidth:"min-content"}},{shadow:!0,color:"default",css:{normalShadow:"$primaryShadow"}},{shadow:!0,color:"primary",css:{normalShadow:"$primaryShadow"}},{shadow:!0,color:"secondary",css:{normalShadow:"$secondaryShadow"}},{shadow:!0,color:"warning",css:{normalShadow:"$warningShadow"}},{shadow:!0,color:"success",css:{normalShadow:"$successShadow"}},{shadow:!0,color:"error",css:{normalShadow:"$errorShadow"}},{shadow:!0,color:"gradient",css:{normalShadow:"$primaryShadow"}},{light:!0,color:"default",css:{bg:"transparent",color:"$text",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$primaryLightActive"}}}},{light:!0,color:"primary",css:{bg:"transparent",color:"$primary",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$primaryLightActive"}}}},{light:!0,color:"secondary",css:{bg:"transparent",color:"$secondary",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$secondaryLightActive"}}}},{light:!0,color:"warning",css:{bg:"transparent",color:"$warning",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$warningLightActive"}}}},{light:!0,color:"success",css:{bg:"transparent",color:"$success",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$successLightActive"}}}},{light:!0,color:"error",css:{bg:"transparent",color:"$error",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.8,fill:"$errorLightActive"}}}},{bordered:!0,color:"default",css:{bg:"transparent",borderColor:"$primary",color:"$primary",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$primary"}}}},{bordered:!0,color:"primary",css:{bg:"transparent",borderColor:"$primary",color:"$primary",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$primary"}}}},{bordered:!0,color:"secondary",css:{bg:"transparent",borderColor:"$secondary",color:"$secondary",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$secondary"}}}},{bordered:!0,color:"success",css:{bg:"transparent",borderColor:"$success",color:"$success",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$success"}}}},{bordered:!0,color:"warning",css:{bg:"transparent",borderColor:"$warning",color:"$warning",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$warning"}}}},{bordered:!0,color:"error",css:{bg:"transparent",borderColor:"$error",color:"$error",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$error"}}}},{bordered:!0,color:"gradient",css:{bg:"transparent",color:"$text",padding:"$$buttonBorderWeight",bgClip:"content-box, border-box",borderColor:"$primary",backgroundImage:"linear-gradient($background, $background), $gradient",border:"none",[`& ${b.q}`]:{".nextui-drip-filler":{fill:"$secondary"}}}},{ghost:!0,isHovered:!0,color:"default",css:{bg:"$primary",color:"$primarySolidContrast"}},{ghost:!0,isHovered:!0,color:"primary",css:{bg:"$primary",color:"$primarySolidContrast"}},{ghost:!0,isHovered:!0,color:"secondary",css:{bg:"$secondary",color:"$secondarySolidContrast"}},{ghost:!0,isHovered:!0,color:"success",css:{bg:"$success",color:"$successSolidContrast"}},{ghost:!0,isHovered:!0,color:"warning",css:{bg:"$warning",color:"$warningSolidContrast"}},{ghost:!0,isHovered:!0,color:"error",css:{bg:"$error",color:"$errorSolidContrast"}},{ghost:!0,color:"gradient",isHovered:!0,css:{bg:"$gradient",color:"$white"}},{flat:!0,color:"default",css:{bg:"$primaryLight",color:"$primaryLightContrast",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.4,fill:"$primary"}}}},{flat:!0,color:"primary",css:{bg:"$primaryLight",color:"$primaryLightContrast",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.4,fill:"$primary"}}}},{flat:!0,color:"secondary",css:{bg:"$secondaryLight",color:"$secondaryLightContrast",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.4,fill:"$secondary"}}}},{flat:!0,color:"success",css:{bg:"$successLight",color:"$successLightContrast",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.4,fill:"$success"}}}},{flat:!0,color:"warning",css:{bg:"$warningLight",color:"$warningLightContrast",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.4,fill:"$warning"}}}},{flat:!0,color:"error",css:{bg:"$errorLight",color:"$errorLightContrast",[`& ${b.q}`]:{".nextui-drip-filler":{opacity:.4,fill:"$error"}}}},{flat:!0,isHovered:!0,color:"default",css:{bg:"$primaryLightHover"}},{flat:!0,isHovered:!0,color:"primary",css:{bg:"$primaryLightHover"}},{flat:!0,isHovered:!0,color:"secondary",css:{bg:"$secondaryLightHover"}},{flat:!0,isHovered:!0,color:"success",css:{bg:"$successLightHover"}},{flat:!0,isHovered:!0,color:"warning",css:{bg:"$warningLightHover"}},{flat:!0,isHovered:!0,color:"error",css:{bg:"$errorLightHover"}},{flat:!0,isPressed:!0,color:"default",css:{bg:"$primaryLightActive"}},{flat:!0,isPressed:!0,color:"primary",css:{bg:"$primaryLightActive"}},{flat:!0,isPressed:!0,color:"secondary",css:{bg:"$secondaryLightActive"}},{flat:!0,isPressed:!0,color:"success",css:{bg:"$successLightActive"}},{flat:!0,isPressed:!0,color:"warning",css:{bg:"$warningLightActive"}},{flat:!0,isPressed:!0,color:"error",css:{bg:"$errorLightActive"}},{auto:!0,color:"gradient",bordered:!0,css:{".nextui-button-text":{px:"$$buttonPadding"},".nextui-button-icon":{ml:"$$buttonPadding"},".nextui-button-icon-right":{mr:"$$buttonPadding"},".nextui-button-text-left":{pl:0},".nextui-button-text-right":{pr:0}}},{rounded:!0,size:"xs",css:{br:"$pill"}},{rounded:!0,size:"sm",css:{br:"$pill"}},{rounded:!0,size:"md",css:{br:"$pill"}},{rounded:!0,size:"lg",css:{br:"$pill"}},{rounded:!0,size:"xl",css:{br:"$pill"}}],defaultVariants:{color:"default",borderWeight:"normal",animated:!0,size:"md"}},S.BM,S.Oe),k=r(2903),P=r(3569);const N=o.forwardRef((({as:e,css:t,iconLeftCss:r,iconRightCss:a,onClick:c,onPress:d,onPressStart:g,onPressEnd:f,onPressChange:m,onPressUp:x,...v},S)=>{const N=((e,t)=>{var r,o,n,i,s,a,l,c,d,u,b;return t.isButtonGroup?{...e,auto:!0,shadow:!1,bordered:null!=(r=t.bordered)?r:e.bordered,borderWeight:null!=(o=t.borderWeight)?o:e.borderWeight,ghost:null!=(n=t.ghost)?n:e.ghost,ripple:null!=(i=t.ripple)?i:e.ripple,flat:null!=(s=t.flat)?s:e.flat,animated:null!=(a=t.animated)?a:e.animated,rounded:null!=(l=t.rounded)?l:e.rounded,light:null!=(c=t.light)?c:e.light,size:null!=(d=t.size)?d:e.size,color:null!=(u=t.color)?u:e.color,disabled:null!=(b=t.disabled)?b:e.disabled}:e})(v,o.useContext(p)),E=(e=>{if(!e.disabled)return e.auto&&"gradient"===e.color&&(e.bordered||e.ghost)?{px:"$$buttonBorderWeight",py:"$$buttonBorderWeight"}:{};const t={bg:"$accents1",color:"$accents7",transform:"none",boxShadow:"none",pe:"none"};return e.bordered||e.flat||e.ghost||e.light?"gradient"===e.color&&(e.bordered||e.ghost)?{color:"$accents4",backgroundImage:"linear-gradient($background, $background), linear-gradient($accents2, $accents2)",transform:"none",boxShadow:"none",pe:"none",pl:"$$buttonBorderWeight",pr:"$$buttonBorderWeight"}:e.bordered||e.ghost||e.light?{...t,bg:"transparent",borderColor:"$accents4"}:e.flat?{...t,bg:"$accents1"}:{}:t})(N),{flat:L,children:F,disabled:R,animated:W,light:B,ripple:H,bordered:z,auto:j,borderWeight:A,icon:I,iconRight:T,ghost:q,autoFocus:V,className:G,...K}=N,M=e=>{W&&H&&D.current&&Q(e)},D=(0,k.gy)(S),{buttonProps:Z,isPressed:U}=l({...v,onClick:e=>{M(e),null==c||c(e)},isDisabled:R,elementType:e,onPress:e=>{"keyboard"!==e.pointerType&&"virtual"!==e.pointerType||(M(e),null==c||c(e)),null==d||d(e)},onPressStart:g,onPressEnd:f,onPressChange:m,onPressUp:x},D),{hoverProps:O,isHovered:_}=(0,s.XI)({isDisabled:R}),{isFocused:X,isFocusVisible:Y,focusProps:J}=(0,n.Fx)({autoFocus:V}),{onClick:Q,...ee}=(0,w.Z)(!1,D);P.Ts&&"gradient"===N.color&&(L||B)&&u("Using the gradient color on flat and light buttons will have no effect.");const te=I||T,re=0===o.Children.count(F),oe=Boolean(T),ne=(0,o.useMemo)((()=>U?"pressed":_?"hovered":R?"disabled":"ready"),[R,_,U]),ie=(0,o.useMemo)((()=>oe?a:r),[oe,a,r]);return(0,h.jsxs)(C,{as:e,ref:D,borderWeight:A,auto:j,flat:L,light:B,ghost:q,bordered:z||q,"data-state":ne,animated:W,isChildLess:re,isPressed:U,isHovered:_||q&&X,isFocusVisible:Y&&!R,className:(0,$.Z)("nextui-button",`nextui-button--${ne}`,G),css:{...t,...E},...(0,i.dG)(Z,J,O,K),children:[0===o.Children.count(F)?(0,h.jsx)(y,{isSingle:!0,isAuto:j,isRight:oe,css:ie,isGradientButtonBorder:"gradient"===K.color&&(z||q),children:te}):te?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(y,{isSingle:!1,isAuto:j,isRight:oe,css:ie,isGradientButtonBorder:"gradient"===K.color&&(z||q),children:te}),(0,h.jsx)("div",{className:(0,$.Z)("nextui-button-text",{"nextui-button-text-right":oe,"nextui-button-text-left":!oe}),children:F})]}):(0,h.jsx)("span",{className:"nextui-button-text",children:F}),(0,h.jsx)(b.Z,{color:"white",...ee})]})}));P.Ts&&(N.displayName="NextUI.Button"),N.toString=()=>".nextui-button";var E=(0,f.Z)(N,{ghost:!1,bordered:!1,ripple:!0,animated:!0,disabled:!1,autoFocus:!1,auto:!1,className:"",type:"button"});var L=(0,g.zo)("div",{display:"inline-flex",margin:"$3",backgroundColor:"transparent",height:"min-content",[`& ${C}`]:{".nextui-button-text":{top:0}},variants:{vertical:{true:{fd:"column",[`& ${C}`]:{"&:not(:first-child)":{btlr:0,btrr:0},"&:not(:last-child)":{bblr:0,bbrr:0}}},false:{fd:"row",[`& ${C}`]:{"&:not(:first-child)":{btlr:0,bblr:0},"&:not(:last-child)":{btrr:0,bbrr:0}}}},size:{xs:{br:"$xs"},sm:{br:"$sm"},md:{br:"$md"},lg:{br:"$base"},xl:{br:"$xl"}},rounded:{true:{br:"$pill"}},bordered:{true:{bg:"transparent"}},gradient:{true:{pl:0}}},defaultVariants:{vertical:!1},compoundVariants:[{bordered:!0,vertical:!0,css:{[`& ${C}`]:{"&:not(:last-child)":{borderBottom:"none",paddingBottom:"0"}}}},{bordered:!0,vertical:!1,css:{[`& ${C}`]:{"&:not(:first-child)":{borderLeft:"none"}}}},{bordered:!0,vertical:!1,gradient:!0,css:{[`& ${C}`]:{"&:not(:last-child)&:not(:first-child)":{pl:0},"&:last-child":{pl:0}}}}]});const F=e=>{const{disabled:t,size:r,color:n,bordered:i,ghost:s,light:a,flat:l,shadow:c,auto:d,animated:u,rounded:b,ripple:g,borderWeight:f,children:$,...m}=e,x=(0,o.useMemo)((()=>({disabled:t,size:r,color:n,bordered:i,light:a,ghost:s,flat:l,shadow:c,auto:d,borderWeight:f,animated:u,rounded:b,ripple:g,isButtonGroup:!0})),[t,u,r,g,n,i,a,s,l,f]);return(0,h.jsx)(p.Provider,{value:x,children:(0,h.jsx)(L,{size:r,bordered:i||s,gradient:"gradient"===e.color,...m,children:$})})};F.toString=()=>".nextui-button-group";const R=o.memo(F);var W=(0,f.Z)(R,{borderWeight:"normal",size:"md",color:"default"});E.Group=W;var B=E},5208:function(e,t,r){"use strict";r.d(t,{ZP:function(){return $}});var o,n,i=r(7294),s=r(88),a=r(6212),l=r(3917);const c=(0,a.zo)("svg",{ml:"$1",as:"center",display:"inline-flex",color:"currentColor"});var d=(0,a.zo)("a",{display:"inline-flex",alignItems:"baseline",lineHeight:"inherit",textDecoration:"none",width:"fitContent",backgroundImage:"inherit",backgroundColor:"inherit",backgroundClip:"inherit",WebkitTextFillColor:"inherit","&:hover":{opacity:.8},"@motion":{transition:"none"},variants:{color:{default:{color:"$link"},text:{color:"$text"},primary:{color:"$primary"},secondary:{color:"$secondary"},success:{color:"$success"},warning:{color:"$warning"},error:{color:"$error"}},underline:{true:{"&:hover, &:active, &:focus":{textDecoration:"underline"}}},block:{true:{padding:"$2 $4",borderRadius:"$base"}},animated:{true:{transition:"$link"}}},compoundVariants:[{color:"default",block:!0,css:{"&:hover":{backgroundColor:(0,l.jK)(null==(o=a.rS.colors)||null==(n=o.link)?void 0:n.value,.2)}}},{color:"primary",block:!0,css:{"&:hover":{backgroundColor:"$primaryLight"}}},{color:"secondary",block:!0,css:{"&:hover":{backgroundColor:"$secondaryLight"}}},{color:"success",block:!0,css:{"&:hover":{backgroundColor:"$successLight"}}},{color:"warning",block:!0,css:{"&:hover":{backgroundColor:"$warningLight"}}},{color:"error",block:!0,css:{"&:hover":{backgroundColor:"$errorLight"}}}],defaultVariants:{color:"default",animated:!0}}),u=r(5893);const b=()=>(0,u.jsxs)(c,{viewBox:"0 0 24 24",width:"1em",height:"1em",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",fill:"none",shapeRendering:"geometricPrecision",className:"nextui-link-icon",children:[(0,u.jsx)("path",{d:"M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"}),(0,u.jsx)("path",{d:"M15 3h6v6"}),(0,u.jsx)("path",{d:"M10 14L21 3"})]});b.toString=()=>".nextui-link-icon";var p=i.memo(b),g=r(3569);const f=i.forwardRef((({children:e,icon:t,...r},o)=>(0,u.jsx)(d,{...r,ref:o,children:(0,u.jsxs)(u.Fragment,{children:[e,t&&(0,u.jsx)(p,{})]})})));g.Ts&&(f.displayName="NextUI.Link"),f.toString=()=>".nextui-link";var $=(0,s.Z)(f,{icon:!1})},9975:function(e,t,r){"use strict";r.d(t,{BM:function(){return n},Oe:function(){return s},UU:function(){return i}});var o=r(6212);(0,o.iv)({WebkitTapHighlightColor:"transparent","&:focus:not(&:focus-visible)":{boxShadow:"none"},"&:focus":{outline:"none",boxShadow:"0 0 0 2px $colors$background, 0 0 0 4px $colors$primary"},"@safari":{WebkitTapHighlightColor:"transparent",outline:"none"}});const n=(0,o.iv)({variants:{isFocusVisible:{true:{outline:"transparent solid 2px",outlineOffset:"2px",boxShadow:"0 0 0 2px $colors$background, 0 0 0 4px $colors$primary"},false:{outline:"none"}}}}),i=(0,o.iv)({transform:"translateZ(0)",backfaceVisibility:"hidden"}),s=((0,o.iv)({border:"0px",clip:"rect(0px, 0px, 0px, 0px)",height:"1px",width:"1px",margin:"-1px",padding:"0px",overflow:"hidden",whiteSpace:"nowrap",position:"absolute"}),(0,o.iv)({'&[aria-haspopup="true"]&[aria-expanded="true"]':{opacity:.7,transform:"scale(0.97)"}}))},9260:function(e,t,r){"use strict";r.d(t,{Z:function(){return n}});var o=r(7294),n=(e=!1,t)=>{const[r,n]=(0,o.useState)(e),[i,s]=(0,o.useState)(0),[a,l]=(0,o.useState)(0);return{visible:r,x:i,y:a,onClick:e=>{if(!t.current)return;const r=t.current.getBoundingClientRect();n(!0),s(e.clientX-r.left),l(e.clientY-r.top)},onCompleted:()=>{n(!1),s(0),l(0)}}}},3569:function(e,t,r){"use strict";r.d(t,{Ts:function(){return o}});const o=!1},3917:function(e,t,r){"use strict";r.d(t,{jK:function(){return c},h1:function(){return a}});const o=(...e)=>e;o("xs","sm","md","lg","xl");const n=o("default","primary","secondary","success","warning","error","gradient");o("default","primary","secondary","success","warning","error"),o("default","primary","secondary","success","warning","error","invert","gradient"),o("default","primary","secondary","success","warning","error","invert"),o("default","primary","secondary","success","warning","error","dark","lite","alert","purple","violet","gradient","cyan"),o("default","points","points-opacity","gradient","spinner"),o("light","normal","bold","extrabold","black"),o("normal","bold","lighter","bolder","inherit","initial","revert","unset"),o("none","capitalize","uppercase","lowercase","full-width","full-size-kana","inherit","initial","revert","unset");o("default","slient","prevent"),o("hover","click"),o("top","topStart","topEnd","left","leftStart","leftEnd","bottom","bottomStart","bottomEnd","right","rightStart","rightEnd"),o("static","relative","absolute","fixed","sticky","inherit","initial","revert","unset"),o("contain","cover","fill","none","scale-down","inherit","initial","revert","unset"),o("start","center","end","left","right"),o("flex-start","center","flex-end","space-between","space-around","space-evenly"),o("flex-start","flex-end","center","stretch","baseline"),o("stretch","center","flex-start","flex-end","space-between","space-around"),o("row","row-reverse","column","column-reverse"),o("nowrap","wrap","wrap-reverse"),o("flex","block","grid","inline","inline-block","inline-flex","inline-grid"),o("left","right"),o("start","center","end");o("clearable","as","rounded","labelLeft","labelRight","contentLeft","contentRight","contentClickable","contentLeftStyling","contentRightStyling","onContentClick","onClearClick","css"),o("items","disabledKeys","allowDuplicateSelectionEvents","disallowEmptySelection","defaultSelectedKeys","sortDescriptor","onSortChange");o("toggle","replace"),o("none","single","multiple"),o("flat","light","solid","shadow"),o("flat","bordered","shadow");const i=e=>{if("undefined"!=typeof document||!e){const t=s(e)?e.replace("var(","").replace(")",""):`--${e}`;return getComputedStyle(document.documentElement).getPropertyValue(t)}return""},s=e=>!(!e||0!==(null==e?void 0:e.indexOf("var("))),a=e=>null!=n.find((t=>t===e)),l=e=>{const t=s(e)?i(e):e;if("#"===t.charAt(0))return(e=>{const t=e.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,((e,t,r,o)=>`${t}${t}${r}${r}${o}${o}`)),r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(!r)throw new Error(`Next UI: Unsupported ${e} color.`);return[Number.parseInt(r[1],16),Number.parseInt(r[2],16),Number.parseInt(r[3],16)]})(t);const r=t.replace(/ /g,""),o=t.substr(0,4),n=r.match(/\((.+)\)/);if(!o.startsWith("rgb")||!n)throw new Error('Next UI: Only support ["RGB", "RGBA", "HEX"] color.');return n[1].split(",").map((e=>Number.parseFloat(e)))},c=(e,t=1)=>{if(!e)return"";const r=s(e)?i(e):e;if(/#[a-fA-F0-9]{3,6}/g.test(r))return((e,t=1)=>{let r=0,o=0,n=0;return 4==e.length?(r="0x"+e[1]+e[1],o="0x"+e[2]+e[2],n="0x"+e[3]+e[3]):7==e.length&&(r="0x"+e[1]+e[2],o="0x"+e[3]+e[4],n="0x"+e[5]+e[6]),`rgba(${+r}, ${+o},${+n},${t})`})(r,t);if(!/^#|rgb|RGB/.test(r))return r;const[o,n,a]=l(r);return`rgba(${o}, ${n}, ${a}, ${t>1?1:t<0?0:t})`}},6772:function(e,t,r){"use strict";r.d(t,{m:function(){return o}});const o=e=>`calc(${15.25*e}pt + 1px * ${e-1})`},2903:function(e,t,r){"use strict";r.d(t,{gy:function(){return n}});var o=r(7294);"undefined"==typeof window||!window.document||window.document.createElement;function n(e){const t=(0,o.useRef)(null);return(0,o.useImperativeHandle)(e,(()=>t.current)),t}},8375:function(e,t,r){"use strict";r.d(t,{q:function(){return c}});var o=r(7294),n=r(6212),i=r(88),s=r(1309),a=r(5893);const l=(0,n.F4)({"0%":{opacity:0,transform:"scale(0.25)"},"30%":{opacity:1},"80%":{opacity:.5},"100%":{transform:"scale(28)",opacity:0}}),c=(0,n.zo)("div",{position:"absolute",left:0,right:0,top:0,bottom:0,"& svg":{position:"absolute",animation:`350ms linear ${l}`,animationFillMode:"forwards",width:"$md",height:"$md"}}),d=o.memo((({visible:e,x:t,y:r,color:n,onCompleted:i,className:l,...d})=>{const u=(0,o.useRef)(null),b=Number.isNaN(+r)?0:r-10,p=Number.isNaN(+t)?0:t-10;return(0,o.useEffect)((()=>{if(u.current)return u.current.addEventListener("animationend",i),()=>{u.current&&u.current.removeEventListener("animationend",i)}})),e?(0,a.jsx)(c,{ref:u,className:(0,s.Z)("nextui-drip",l),...d,children:(0,a.jsx)("svg",{width:"20",height:"20",viewBox:"0 0 20 20",style:{top:b,left:p},children:(0,a.jsx)("g",{stroke:"none",strokeWidth:"1",fill:"none",fillRule:"evenodd",children:(0,a.jsx)("g",{className:"nextui-drip-filler",fill:n,children:(0,a.jsx)("rect",{width:"100%",height:"100%",rx:"10"})})})})}):null}));t.Z=(0,i.Z)(d,{visible:!1,x:0,y:0,className:""})},9641:function(e,t,r){"use strict";r.d(t,{Fx:function(){return E},kc:function(){return H}});var o=r(7294),n=r(4213),i=r(7354),s=r(6010);function a(e,t,r,o){Object.defineProperty(e,t,{get:r,set:o,enumerable:!0,configurable:!0})}var l={};a(l,"FocusScope",(()=>g)),a(l,"useFocusManager",(()=>f)),a(l,"getFocusableTreeWalker",(()=>k)),a(l,"createFocusManager",(()=>P));function c(e){if("virtual"===(0,i.Jz)()){let t=document.activeElement;(0,n.QB)((()=>{document.activeElement===t&&document.contains(e)&&(0,n.Ao)(e)}))}else(0,n.Ao)(e)}function d(e,t){return"#comment"!==e.nodeName&&function(e){if(!(e instanceof HTMLElement)&&!(e instanceof SVGElement))return!1;let{display:t,visibility:r}=e.style,o="none"!==t&&"hidden"!==r&&"collapse"!==r;if(o){const{getComputedStyle:t}=e.ownerDocument.defaultView;let{display:r,visibility:n}=t(e);o="none"!==r&&"hidden"!==n&&"collapse"!==n}return o}(e)&&function(e,t){return!e.hasAttribute("hidden")&&("DETAILS"!==e.nodeName||!t||"SUMMARY"===t.nodeName||e.hasAttribute("open"))}(e,t)&&(!e.parentElement||d(e.parentElement,e))}a({},"focusSafely",(()=>c));const u=o.createContext(null);let b=null,p=new Map;function g(e){let{children:t,contain:r,restoreFocus:i,autoFocus:s}=e,a=(0,o.useRef)(),l=(0,o.useRef)(),c=(0,o.useRef)([]),d=(0,o.useContext)(u),g=null===d||void 0===d?void 0:d.scopeRef;(0,n.bt)((()=>{let e=a.current.nextSibling,t=[];for(;e&&e!==l.current;)t.push(e),e=e.nextSibling;c.current=t}),[t,g]),(0,n.bt)((()=>(p.set(c,g),()=>{c!==b&&!w(c,b)||g&&!p.has(g)||(b=g),p.delete(c)})),[c,g]),function(e,t){let r=(0,o.useRef)(),i=(0,o.useRef)(null);(0,n.bt)((()=>{let o=e.current;if(!t)return;let n=t=>{if("Tab"!==t.key||t.altKey||t.ctrlKey||t.metaKey||e!==b)return;let r=document.activeElement,o=e.current;if(!v(r,o))return;let n=k(x(o),{tabbable:!0},o);n.currentNode=r;let i=t.shiftKey?n.previousNode():n.nextNode();i||(n.currentNode=t.shiftKey?o[o.length-1].nextElementSibling:o[0].previousElementSibling,i=t.shiftKey?n.previousNode():n.nextNode()),t.preventDefault(),i&&S(i,!0)},s=t=>{!b||w(b,e)?(b=e,r.current=t.target):e!==b||y(t.target,e)?e===b&&(r.current=t.target):r.current?r.current.focus():b&&C(b.current)},a=t=>{i.current=requestAnimationFrame((()=>{e!==b||y(document.activeElement,e)||(b=e,r.current=t.target,r.current.focus())}))};return document.addEventListener("keydown",n,!1),document.addEventListener("focusin",s,!1),o.forEach((e=>e.addEventListener("focusin",s,!1))),o.forEach((e=>e.addEventListener("focusout",a,!1))),()=>{document.removeEventListener("keydown",n,!1),document.removeEventListener("focusin",s,!1),o.forEach((e=>e.removeEventListener("focusin",s,!1))),o.forEach((e=>e.removeEventListener("focusout",a,!1)))}}),[e,t]),(0,o.useEffect)((()=>()=>cancelAnimationFrame(i.current)),[i])}(c,r),function(e,t,r){const i=(0,o.useRef)("undefined"!==typeof document?document.activeElement:null);(0,n.bt)((()=>{let o=i.current;if(!t)return;let n=t=>{if("Tab"!==t.key||t.altKey||t.ctrlKey||t.metaKey)return;let r=document.activeElement;if(!v(r,e.current))return;let n=k(document.body,{tabbable:!0});n.currentNode=r;let i=t.shiftKey?n.previousNode():n.nextNode();if(document.body.contains(o)&&o!==document.body||(o=null),(!i||!v(i,e.current))&&o){n.currentNode=o;do{i=t.shiftKey?n.previousNode():n.nextNode()}while(v(i,e.current));t.preventDefault(),t.stopPropagation(),i?S(i,!0):!function(e){for(let t of p.keys())if(v(e,t.current))return!0;return!1}(o)?r.blur():S(o,!0)}};return r||document.addEventListener("keydown",n,!0),()=>{r||document.removeEventListener("keydown",n,!0),t&&o&&v(document.activeElement,e.current)&&requestAnimationFrame((()=>{document.body.contains(o)&&S(o)}))}}),[e,t,r])}(c,i,r),function(e,t){const r=o.useRef(t);(0,o.useEffect)((()=>{r.current&&(b=e,v(document.activeElement,b.current)||C(e.current)),r.current=!1}),[])}(c,s);let f=function(e){return{focusNext(t={}){let r=e.current,{from:o,tabbable:n,wrap:i}=t,s=o||document.activeElement,a=r[0].previousElementSibling,l=k(x(r),{tabbable:n},r);l.currentNode=v(s,r)?s:a;let c=l.nextNode();return!c&&i&&(l.currentNode=a,c=l.nextNode()),c&&S(c,!0),c},focusPrevious(t={}){let r=e.current,{from:o,tabbable:n,wrap:i}=t,s=o||document.activeElement,a=r[r.length-1].nextElementSibling,l=k(x(r),{tabbable:n},r);l.currentNode=v(s,r)?s:a;let c=l.previousNode();return!c&&i&&(l.currentNode=a,c=l.previousNode()),c&&S(c,!0),c},focusFirst(t={}){let r=e.current,{tabbable:o}=t,n=k(x(r),{tabbable:o},r);n.currentNode=r[0].previousElementSibling;let i=n.nextNode();return i&&S(i,!0),i},focusLast(t={}){let r=e.current,{tabbable:o}=t,n=k(x(r),{tabbable:o},r);n.currentNode=r[r.length-1].nextElementSibling;let i=n.previousNode();return i&&S(i,!0),i}}}(c);return o.createElement(u.Provider,{value:{scopeRef:c,focusManager:f}},o.createElement("span",{"data-focus-scope-start":!0,hidden:!0,ref:a}),t,o.createElement("span",{"data-focus-scope-end":!0,hidden:!0,ref:l}))}function f(){var e;return null===(e=(0,o.useContext)(u))||void 0===e?void 0:e.focusManager}const $=["input:not([disabled]):not([type=hidden])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","a[href]","area[href]","summary","iframe","object","embed","audio[controls]","video[controls]","[contenteditable]"],h=$.join(":not([hidden]),")+",[tabindex]:not([disabled]):not([hidden])";$.push('[tabindex]:not([tabindex="-1"]):not([disabled])');const m=$.join(':not([hidden]):not([tabindex="-1"]),');function x(e){return e[0].parentElement}function v(e,t){return t.some((t=>t.contains(e)))}function y(e,t){for(let r of p.keys())if((r===t||w(t,r))&&v(e,r.current))return!0;return!1}function w(e,t){let r=p.get(t);return!!r&&(r===e||w(e,r))}function S(e,t=!1){if(null==e||t){if(null!=e)try{e.focus()}catch(r){}}else try{c(e)}catch(o){}}function C(e){let t=e[0].previousElementSibling,r=k(x(e),{tabbable:!0},e);r.currentNode=t,S(r.nextNode())}function k(e,t,r){let o=(null===t||void 0===t?void 0:t.tabbable)?m:h,n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode(e){var n;return(null===t||void 0===t||null===(n=t.from)||void 0===n?void 0:n.contains(e))?NodeFilter.FILTER_REJECT:!e.matches(o)||!d(e)||r&&!v(e,r)||(null===t||void 0===t?void 0:t.accept)&&!t.accept(e)?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}});return(null===t||void 0===t?void 0:t.from)&&(n.currentNode=t.from),n}function P(e,t={}){return{focusNext(r={}){let o=e.current,{from:n,tabbable:i=t.tabbable,wrap:s=t.wrap,accept:a=t.accept}=r,l=n||document.activeElement,c=k(o,{tabbable:i,accept:a});o.contains(l)&&(c.currentNode=l);let d=c.nextNode();return!d&&s&&(c.currentNode=o,d=c.nextNode()),d&&S(d,!0),d},focusPrevious(r=t){let o=e.current,{from:n,tabbable:i=t.tabbable,wrap:s=t.wrap,accept:a=t.accept}=r,l=n||document.activeElement,c=k(o,{tabbable:i,accept:a});if(!o.contains(l)){let e=N(c);return e&&S(e,!0),e}c.currentNode=l;let d=c.previousNode();return!d&&s&&(c.currentNode=o,d=N(c)),d&&S(d,!0),d},focusFirst(r=t){let o=e.current,{tabbable:n=t.tabbable,accept:i=t.accept}=r,s=k(o,{tabbable:n,accept:i}).nextNode();return s&&S(s,!0),s},focusLast(r=t){let o=e.current,{tabbable:n=t.tabbable,accept:i=t.accept}=r,s=N(k(o,{tabbable:n,accept:i}));return s&&S(s,!0),s}}}function N(e){let t,r;do{r=e.lastChild(),r&&(t=r)}while(r);return t}a({},"FocusRing",(()=>L));function E(e={}){let{autoFocus:t=!1,isTextInput:r,within:n}=e,s=(0,o.useRef)({isFocused:!1,isFocusVisible:t||(0,i.E)()}),[a,l]=(0,o.useState)(!1),[c,d]=(0,o.useState)((()=>s.current.isFocused&&s.current.isFocusVisible)),u=(0,o.useCallback)((()=>d(s.current.isFocused&&s.current.isFocusVisible)),[]),b=(0,o.useCallback)((e=>{s.current.isFocused=e,l(e),u()}),[u]);(0,i.mG)((e=>{s.current.isFocusVisible=e,u()}),[],{isTextInput:r});let{focusProps:p}=(0,i.KK)({isDisabled:n,onFocusChange:b}),{focusWithinProps:g}=(0,i.L_)({isDisabled:!n,onFocusWithinChange:b});return{isFocused:a,isFocusVisible:s.current.isFocused&&c,focusProps:n?g:p}}function L(e){let{children:t,focusClass:r,focusRingClass:i}=e,{isFocused:a,isFocusVisible:l,focusProps:c}=E(e),d=o.Children.only(t);return o.cloneElement(d,(0,n.dG)(d.props,{...c,className:(0,s.Z)({[r||""]:a,[i||""]:l})}))}a({},"useFocusRing",(()=>E));var F={};a(F,"FocusableProvider",(()=>B)),a(F,"useFocusable",(()=>H));let R=o.createContext(null);function W(e,t){let{children:r,...n}=e,i={...n,ref:t};return o.createElement(R.Provider,{value:i},r)}let B=o.forwardRef(W);function H(e,t){let{focusProps:r}=(0,i.KK)(e),{keyboardProps:s}=(0,i.v5)(e),a=(0,n.dG)(r,s),l=function(e){let t=(0,o.useContext)(R)||{};(0,n.lE)(t,e);let{ref:r,...i}=t;return i}(t),d=e.isDisabled?{}:l,u=(0,o.useRef)(e.autoFocus);return(0,o.useEffect)((()=>{u.current&&t.current&&c(t.current),u.current=!1}),[t]),{focusableProps:(0,n.dG)({...a,tabIndex:e.excludeFromTabOrder&&!e.isDisabled?-1:void 0},d)}}},9008:function(e,t,r){e.exports=r(3121)},1163:function(e,t,r){e.exports=r(880)}}]); \ No newline at end of file diff --git a/dashboard/out/_next/static/chunks/framework-4556c45dd113b893.js b/dashboard/out/_next/static/chunks/framework-4556c45dd113b893.js new file mode 100644 index 000000000..1225f82ac --- /dev/null +++ b/dashboard/out/_next/static/chunks/framework-4556c45dd113b893.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[774],{4448:function(e,n,t){var r=t(7294),l=t(3840);function a(e){for(var n="https://reactjs.org/docs/error-decoder.html?invariant="+e,t=1;t