diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5171c54 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..37f2229 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @SudhanPlayz @DarrenOfficial \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..41180bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Report incorrect or unexpected behavior of the Music Bot +title: "" +labels: "s: unverified, type: bug" +assignees: "" +--- + + + +## Please describe the problem you are having in as much detail as possible: + +## Include a reproducible code sample here, if possible: + +```js +// Place your code here +``` + +## Further details: + +- discord.js version: +- Node.js version: +- Operating system: +- Priority this issue should have – please be realistic and elaborate if possible: + +## Relevant client options: + +- partials: none +- gateway intents: none +- other: none + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..63f6a8a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Discord server + url: https://discord.gg/sbySMS7m3v + about: Please visit our Discord server for questions and support requests. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..f236aa0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Request a feature for the Music Bot +title: "" +labels: "type: enhancement" +assignees: "" +--- + + + +## Is your feature request related to a problem? Please describe. + +A clear and concise description of what the problem is. Eg. I'm always frustrated when [...] + +## Describe the ideal solution + +A clear and concise description of what you want to happen. + +## Describe alternatives you've considered + +A clear and concise description of any alternative solutions or features you've considered. + +## Additional context + +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..b32b9b3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ +## Please describe the changes this PR makes and why it should be merged: + +## Status and versioning classification: + + + +# 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 new file mode 100644 index 0000000..65df635 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +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. + +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 0000000..8543ddc --- /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/.gitignore b/.gitignore new file mode 100644 index 0000000..7a155dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,125 @@ +# Actual ignored paths for this repo +db.json +dbList.json +.guild_dbs/ +dev-config.js + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn +.yarn-integrity +yarn.lock + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Lockfiles +package-lock.json +yarn.lock + +# Volta +.pnp.cjs +.pnp.loader.mjs + +# IDE +.idea/ +.vscode/ +.history/ diff --git a/.replit b/.replit new file mode 100644 index 0000000..a3e78b2 --- /dev/null +++ b/.replit @@ -0,0 +1,6 @@ +run = "node index.js" +language = "Bash" + +[nix] +channel = "stable-22_05" + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7fc268f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at SudhanPlayz@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b67848c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,93 @@ +

Contributing

+ +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at SudhanPlayz@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +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 0000000..02d3b39 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM node:17.9.1-alpine +WORKDIR /usr/src/app +COPY . . +RUN npm install +RUN npm run deploy +CMD [ "node", "index.js" ] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b273948 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,8 @@ +# License for Discord-MusicBot + +- The credits should not be changed. +- 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 diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..9ebe8e8 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: npm start diff --git a/README.md b/README.md new file mode 100644 index 0000000..34fdf6d --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +

Discord Music Bot

+ +## 🚧 | Prerequisites + +- [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_ + +> NOTE: Lavalink is needed for music functionality. You need to have a working Lavalink server to make the bot work. + +## 📝 | Important Note if you're Switching from v4 to v5 + +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) +4. Run `npm run deploy` or `yarn deploy` to initialize the slash commands. _You can do this on your pc locally_ + +## 📝 | Tutorial + +Soon + +## 📝 | [Support Server](https://discord.gg/jEZtK54m7n) + +If you have major coding issues with this bot, please join and ask for help. + +## 📸 | Screenshots + +Soon + +## 🚀 | Deploy + +[![Deploy to heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/BestGamersH/Music-bot) +[![Open in Gitpod](https://camo.githubusercontent.com/76e60919474807718793857d8eb615e7a50b18b04050577e5a35c19421f260a3/68747470733a2f2f676974706f642e696f2f627574746f6e2f6f70656e2d696e2d676974706f642e737667)](https://gitpod.io/#https://github.com/BestGamersH/Music-bot) +[![Run on Repl.it](https://repl.it/badge/github/BestGamersH/Music-bot)](https://repl.it/github/BestGamersH/Music-bot) + + + +## 🌟 | 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/index.js b/api/index.js new file mode 100644 index 0000000..4cb130a --- /dev/null +++ b/api/index.js @@ -0,0 +1,105 @@ +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 router = require("./router"); + +passport.serializeUser(function (user, done) { + done(null, user); +}); + +passport.deserializeUser(function (obj, done) { + done(null, obj); +}); + +class Server extends EventEmitter { + /** + * Create server ;-; + * @param {DiscordMusicBot} client + */ + constructor(client) { + super(); + this.client = client; + getConfig().then(this.init.bind(this)); + } + + init(conf) { + this.config = conf; + this.app = express(); + + this.app.use(express.static(join(__dirname, "..", "public"))); + + // Static Routes for scripts + const dist = join(__dirname, "..", "dashboard", "out", "_next") + + this.app.use("/_next", express.static(dist)); + + // Session and Passport + this.app.use(session({ + resave: false, + saveUninitialized: false, + secret: this.config.cookieSecret, + cookie: { + secure: this.config.website.startsWith("https://"), + sameSite: true, + }, + })); + + this.initPassport(); + + this.app.use(router); + + //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.listen(); + } + + initPassport() { + this.app.use(passport.initialize()); + + const strategy = new DiscordStrategy( + { + clientID: this.config.clientId, + clientSecret: this.config.clientSecret, + callbackURL: this.config.website + "/api/callback", + scope: this.config.scopes.filter(a => !a.startsWith("app")), + scopeSeparator: " ", + }, + function (accessToken, refreshToken, profile, done) { + const data = { + accessToken, + refreshToken, + profile, + }; + + return done(null, data); + }, + ); + passport.use(strategy); + + this.app.use(passport.session()); + } + + listen() { + this.app.listen(this.config.port); + console.log("[SERVER] Listening on port:", this.config.port); + } +} + +module.exports = Server; diff --git a/api/middlewares/auth.js b/api/middlewares/auth.js new file mode 100644 index 0000000..f73c9ae --- /dev/null +++ b/api/middlewares/auth.js @@ -0,0 +1,16 @@ +/** + * @param {import("express").Request} req + * @param {import("express").Response} res + * @param {import("express").NextFunction} next + * @returns {Promise} + */ + +const Auth = (req, res, next) => { + if (!req.user) { + return res.redirect("/login"); + } else { + next(); + } +}; + +module.exports = Auth; diff --git a/api/router.js b/api/router.js new file mode 100644 index 0000000..abeb1e6 --- /dev/null +++ b/api/router.js @@ -0,0 +1,48 @@ +"use strict"; + +const { Router } = require("express"); +const passport = require("passport"); +const { join } = require("path"); +const Auth = require("./middlewares/auth"); + +const dist = join(__dirname, "..", "dashboard", "out"); + +const router = Router(); + +router.get("/", (req, res) => { + res.sendFile(join(dist, "index.html")); +}); + +router.get("/login", (req, res) => { + res.sendFile(join(dist, "login.html")); +}); + +router.get("/api/login", passport.authenticate("discord")); + +router.get("/logout", (req, res) => { + res.sendFile(join(dist, "logout.html")); +}); + +router.get("/api/logout", (req, res) => { + req.session.destroy(() => { + res.redirect("/"); + }); +}); + +router.get("/dashboard", Auth, (_req, res) => { + res.sendFile(join(dist, "dashboard.html")); +}); + +router.get("/servers", Auth, (_req, res) => { + res.sendFile(join(dist, "servers.html")); +}); + +router.get("/api/callback", passport.authenticate('discord', { + failureRedirect: '/', +}), (req, res ) => { + req.session.save(() => { + res.redirect("/"); + }); +}); + +module.exports = router; diff --git a/api/routes/dashboard.js b/api/routes/dashboard.js new file mode 100644 index 0000000..afd9bee --- /dev/null +++ b/api/routes/dashboard.js @@ -0,0 +1,17 @@ +const { Router } = require("express"); +const api = Router(); +const { getClient } = require("../../"); +const Auth = require("../middlewares/auth"); + +api.get("/", Auth, (req, res) => { + const client = getClient(); + let data = { + commandsRan: client.commandsRan, + users: client.users.cache.size, + servers: client.guilds.cache.size, + songsPlayed: client.songsPlayed, + } + res.json(data); +}) + +module.exports = api diff --git a/api/routes/data.js b/api/routes/data.js new file mode 100644 index 0000000..ea74daf --- /dev/null +++ b/api/routes/data.js @@ -0,0 +1,26 @@ +const { Router } = require("express"); +const api = Router(); + +const package = require("../../package.json"); +const { getClient } = require("../../"); + +api.get("/", (req, res) => { + const client = getClient(); + 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") }`, + loggedIn: !!req.user, + }; + res.json(data); +}); + +module.exports = api; diff --git a/app.json b/app.json new file mode 100644 index 0000000..dc0d633 --- /dev/null +++ b/app.json @@ -0,0 +1,39 @@ +{ + "name": "Discord-MusicBot", + "description": "Very simple discord music bot with the discord.js with Song Name playing. It can able to play music with the song name", + "repository": "https://github.com/SudhanPlayz/Discord-MusicBot", + "logo": "https://cdn.discordapp.com/avatars/750613142488481843/e6326038dbe2243ca551ba5b6ecd8bf2.png?size=1024", + "keywords": [ + "node", + "discord", + "youtube", + "music", + "bot", + "lavalink", + "dashboard" + ], + "image": "heroku/nodejs", + "buildpacks": [ + { + "url": "heroku/nodejs" + } + ], + "env": { + "token": { + "description": "The Discord Bot Token (https://discord.com/developers/applications)", + "required": "true" + }, + "clientId": { + "description": "The Discord Bot ClientID", + "required": "true" + }, + "clientSecret": { + "description": "The Discord Bot ClientSecret", + "required": "true" + }, + "website": { + "description": "URL of your webserver (Example: https://domain.xyz). Change this if you want to use the web-dashboard.", + "value": "http://localhost" + } + } +} diff --git a/assets/logo.gif b/assets/logo.gif new file mode 100644 index 0000000..418966d Binary files /dev/null and b/assets/logo.gif differ diff --git a/commands/context/play.js b/commands/context/play.js new file mode 100644 index 0000000..58ddc3f --- /dev/null +++ b/commands/context/play.js @@ -0,0 +1,191 @@ +const { ContextMenuCommandBuilder } = require("@discordjs/builders"); +const { MessageEmbed } = require("discord.js"); +const escapeMarkdown = require('discord.js').Util.escapeMarkdown; + +module.exports = { + command: new ContextMenuCommandBuilder().setName("Play Song").setType(3), + + /** + * This function will handle context menu interaction + * @param {import("../lib/DiscordMusicBot")} client + * @param {import("discord.js").GuildContextMenuInteraction} interaction + */ + run: 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); // Need this because discord api is buggy asf, and without this the bot will not request to speak on a stage - Darren + } + + const ret = await interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(":mag_right: **Searching...**"), + ], + fetchReply: true, + }); + + const query = (interaction.channel.messages.cache.get(interaction.targetId).content ?? await interaction.channel.messages.fetch(interaction.targetId)); + 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(); + } + await 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(); + } + await 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(); + } + var title = escapeMarkdown(res.tracks[0].title) + var title = title.replace(/\]/g, "") + var title = title.replace(/\[/g, "") + let addQueueEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ name: "Added to queue", iconURL: client.config.iconURL }) + .setDescription( + `[${title}](${res.tracks[0].uri})` || "No Title" + ) + .setURL(res.tracks[0].uri) + .addFields( + { + name: "Added by", + value: `<@${interaction.user.id}>`, + inline: true, + }, + { + name: "Duration", + value: res.tracks[0].isStream + ? `\`LIVE 🔴 \`` + : `\`${client.ms(res.tracks[0].duration, { + colonNotation: true, + secondsDecimalDigits: 0, + })}\``, + inline: true, + } + ); + + try { + addQueueEmbed.setThumbnail( + res.tracks[0].displayThumbnail("maxresdefault") + ); + } catch (err) { + addQueueEmbed.setThumbnail(res.tracks[0].thumbnail); + } + + if (player.queue.totalSize > 1) { + addQueueEmbed.addFields({ + name: "Position in queue", + value: `${player.queue.size}`, + inline: true, + }); + } else { + player.queue.previous = player.queue.current; + } + + await 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})`) + .addFields( + { + name: "Enqueued", + value: `\`${res.tracks.length}\` songs`, + inline: true, + }, + { + name: "Playlist duration", + value: `\`${client.ms(res.playlist.duration, { + colonNotation: true, + secondsDecimalDigits: 0, + })}\``, + inline: true, + } + ); + + await interaction.editReply({ embeds: [playlistEmbed] }).catch(this.warn); + } + + if (ret) setTimeout(() => ret.delete().catch(this.warn), 20000); + return ret; + } +}; diff --git a/commands/slash/247.js b/commands/slash/247.js new file mode 100644 index 0000000..64c90a2 --- /dev/null +++ b/commands/slash/247.js @@ -0,0 +1,79 @@ +const colors = require("colors"); +const { MessageEmbed } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); + +const command = new SlashCommand() + .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"}\``) + .setFooter({ + text: `The bot will ${!twentyFourSeven ? "now" : "no longer"} stay connected to the voice channel 24/7.` + }); + 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] }); + }); + +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/autoleave.js b/commands/slash/autoleave.js new file mode 100644 index 0000000..8b8d3d1 --- /dev/null +++ b/commands/slash/autoleave.js @@ -0,0 +1,62 @@ +const colors = require("colors"); +const { MessageEmbed } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); + +const command = new SlashCommand() + .setName("autoleave") + .setDescription("Automatically leaves when everyone leaves the voice channel (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 autoLeaveEmbed = new MessageEmbed().setColor(client.config.embedColor); + const autoLeave = player.get("autoLeave"); + player.set("requester", interaction.guild.me); + + if (!autoLeave || autoLeave === false) { + player.set("autoLeave", true); + } else { + player.set("autoLeave", false); + } + autoLeaveEmbed + .setDescription(`**Auto Leave is** \`${!autoLeave ? "ON" : "OFF"}\``) + .setFooter({ + text: `The player will ${!autoLeave ? "now automatically" : "not automatically"} leave when the voice channel is empty.` + }); + client.warn( + `Player: ${player.options.guild} | [${colors.blue( + "autoLeave" + )}] has been [${colors.blue(!autoLeave ? "ENABLED" : "DISABLED")}] in ${ + client.guilds.cache.get(player.options.guild) + ? client.guilds.cache.get(player.options.guild).name + : "a guild" + }` + ); + + return interaction.reply({ embeds: [autoLeaveEmbed] }); + }); + +module.exports = command; \ No newline at end of file diff --git a/commands/slash/autopause.js b/commands/slash/autopause.js new file mode 100644 index 0000000..1d9572a --- /dev/null +++ b/commands/slash/autopause.js @@ -0,0 +1,62 @@ +const colors = require("colors"); +const { MessageEmbed } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); + +const command = new SlashCommand() + .setName("autopause") + .setDescription("Automatically pause when everyone leaves the voice channel (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 autoPauseEmbed = new MessageEmbed().setColor(client.config.embedColor); + const autoPause = player.get("autoPause"); + player.set("requester", interaction.guild.me); + + if (!autoPause || autoPause === false) { + player.set("autoPause", true); + } else { + player.set("autoPause", false); + } + autoPauseEmbed + .setDescription(`**Auto Pause is** \`${!autoPause ? "ON" : "OFF"}\``) + .setFooter({ + text: `The player will ${!autoPause ? "now be automatically" : "no longer be"} paused when everyone leaves the voice channel.` + }); + client.warn( + `Player: ${player.options.guild} | [${colors.blue( + "AUTOPAUSE" + )}] has been [${colors.blue(!autoPause ? "ENABLED" : "DISABLED")}] in ${ + client.guilds.cache.get(player.options.guild) + ? client.guilds.cache.get(player.options.guild).name + : "a guild" + }` + ); + + return interaction.reply({ embeds: [autoPauseEmbed] }); + }); + +module.exports = command; \ No newline at end of file diff --git a/commands/slash/autoqueue.js b/commands/slash/autoqueue.js new file mode 100644 index 0000000..333b413 --- /dev/null +++ b/commands/slash/autoqueue.js @@ -0,0 +1,65 @@ +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 autoQueueEmbed = 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); + } + autoQueueEmbed + .setDescription(`**Auto Queue is** \`${!autoQueue ? "ON" : "OFF"}\``) + .setFooter({ + text: `Related music will ${!autoQueue ? "now be automatically" : "no longer be"} added to the queue.` + }); + 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: [autoQueueEmbed] }); + }); + +module.exports = command; diff --git a/commands/slash/clean.js b/commands/slash/clean.js new file mode 100644 index 0000000..c123205 --- /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 new file mode 100644 index 0000000..4f2e002 --- /dev/null +++ b/commands/slash/clear.js @@ -0,0 +1,54 @@ +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 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/filters.js b/commands/slash/filters.js new file mode 100644 index 0000000..73455b3 --- /dev/null +++ b/commands/slash/filters.js @@ -0,0 +1,102 @@ +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) + .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 filtersEmbed = new MessageEmbed().setColor(client.config.embedColor); + + if (args == "nightcore") { + filtersEmbed.setDescription("✅ | Nightcore filter is now active!"); + player.nightcore = true; + } else if (args == "bassboost") { + filtersEmbed.setDescription("✅ | BassBoost filter is now on!"); + player.bassboost = true; + } else if (args == "vaporwave") { + filtersEmbed.setDescription("✅ | Vaporwave filter is now on!"); + player.vaporwave = true; + } else if (args == "pop") { + filtersEmbed.setDescription("✅ | Pop filter is now on!"); + player.pop = true; + } else if (args == "soft") { + filtersEmbed.setDescription("✅ | Soft filter is now on!"); + player.soft = true; + } else if (args == "treblebass") { + filtersEmbed.setDescription("✅ | Treblebass filter is now on!"); + player.treblebass = true; + } else if (args == "eightD") { + filtersEmbed.setDescription("✅ | Eight Dimension filter is now on!"); + player.eightD = true; + } else if (args == "karaoke") { + filtersEmbed.setDescription("✅ | Karaoke filter is now on!"); + player.karaoke = true; + } else if (args == "vibrato") { + filtersEmbed.setDescription("✅ | Vibrato filter is now on!"); + player.vibrato = true; + } else if (args == "tremolo") { + filtersEmbed.setDescription("✅ | Tremolo filter is now on!"); + player.tremolo = true; + } else if (args == "off") { + filtersEmbed.setDescription("✅ | EQ has been cleared!"); + player.reset(); + } else { + filtersEmbed.setDescription("❌ | Invalid filter!"); + } + + return interaction.reply({ embeds: [filtersEmbed] }); + }); + +module.exports = command; diff --git a/commands/slash/guildleave.js b/commands/slash/guildleave.js new file mode 100644 index 0000000..beab40f --- /dev/null +++ b/commands/slash/guildleave.js @@ -0,0 +1,56 @@ +const { MessageEmbed, message } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); +const fs = require("fs"); +const path = require("path"); +const { forEach } = require("lodash"); + +const command = new SlashCommand() + .setName("guildleave") + .setDescription("leaves a guild") + .addStringOption((option) => + option + .setName("id") + .setDescription("Enter the guild id to leave (type `list` for guild ids)") + .setRequired(true) + ) + .setRun(async (client, interaction, options) => { + if (interaction.user.id === client.config.adminId) { + try{ + const id = interaction.options.getString('id'); + + if (id.toLowerCase() === 'list'){ + client.guilds.cache.forEach((guild) => { + console.log(`${guild.name} | ${guild.id}`); + }); + const guild = client.guilds.cache.map(guild => ` ${guild.name} | ${guild.id}`); + try{ + return interaction.reply({content:`Guilds:\n\`${guild}\``, ephemeral: true}); + }catch{ + return interaction.reply({content:`check console for list of guilds`, ephemeral: true}); + } + } + + const guild = client.guilds.cache.get(id); + + if(!guild){ + return interaction.reply({content: `\`${id}\` is not a valid guild id`, ephemeral:true}); + } + + await guild.leave().then(c => console.log(`left guild ${id}`)).catch((err) => {console.log(err)}); + return interaction.reply({content:`left guild \`${id}\``, ephemeral: true}); + }catch (error){ + console.log(`there was an error trying to leave guild ${id}`, error); + } + }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/help.js b/commands/slash/help.js new file mode 100644 index 0000000..f2c8385 --- /dev/null +++ b/commands/slash/help.js @@ -0,0 +1,135 @@ +const SlashCommand = require("../../lib/SlashCommand"); +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.helpCmdPerPage); + + // 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.helpCmdPerPage, + pageNo * client.config.helpCmdPerPage + client.config.helpCmdPerPage + ); + + tempArray.forEach((cmd) => { + helpEmbed.addFields({ name: cmd.name, value: cmd.description }); + }); + helpEmbed.addFields({ + name: "Credits", + value: + `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/BestGamersH/Music-bot) | [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", + }); + + collector.on("collect", async (iter) => { + if (iter.customId === "help_cmd_but_1_app") { + pageNo++; + } else if (iter.customId === "help_cmd_but_2_app") { + pageNo--; + } + + helpEmbed.fields = []; + + var tempArray = filteredCommands.slice( + pageNo * client.config.helpCmdPerPage, + pageNo * client.config.helpCmdPerPage + client.config.helpCmdPerPage + ); + + tempArray.forEach((cmd) => { + //console.log(cmd); + helpEmbed + .addFields({ name: cmd.name, value: cmd.description }) + .setFooter({ text: `Page ${pageNo + 1} / ${maxPages}` }); + }); + helpEmbed.addFields({ + name: "Credits", + value: + `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/BestGamersH/Music-bot) | [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; diff --git a/commands/slash/invite.js b/commands/slash/invite.js new file mode 100644 index 0000000..c6b402c --- /dev/null +++ b/commands/slash/invite.js @@ -0,0 +1,30 @@ +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) => { + 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 new file mode 100644 index 0000000..84cf59e --- /dev/null +++ b/commands/slash/loop.js @@ -0,0 +1,51 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +const command = new SlashCommand() + .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 0000000..f90bf23 --- /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/lyrics.js b/commands/slash/lyrics.js new file mode 100644 index 0000000..1c2d3fd --- /dev/null +++ b/commands/slash/lyrics.js @@ -0,0 +1,233 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { + MessageActionRow, + MessageSelectMenu, + MessageButton, + MessageEmbed +} = require("discord.js"); +const { Rlyrics } = require("rlyrics"); +const lyricsApi = new Rlyrics(); + +const command = new SlashCommand() + .setName("lyrics") + .setDescription("Get the lyrics of a song") + .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.editReply({ + 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 currentTitle = ``; + const phrasesToRemove = [ + "Full Video", "Full Audio", "Official Music Video", "Lyrics", "Lyrical Video", + "Feat.", "Ft.", "Official", "Audio", "Video", "HD", "4K", "Remix", + "Extended", "DJ Edit", "with Lyrics", "Lyrics", "Karaoke", + "Instrumental", "Live", "Acoustic", "Cover", "\\(feat\\. .*\\)" + ]; + if (!args) { + currentTitle = player.queue.current.title; + currentTitle = currentTitle + .replace(new RegExp(phrasesToRemove.join('|'), 'gi'), '') + .replace(/\s*([\[\(].*?[\]\)])?\s*(\|.*)?\s*(\*.*)?$/, ''); + } + let query = args ? args : currentTitle; + let lyricsResults = []; + + lyricsApi.search(query).then(async (lyricsData) => { + if (lyricsData.length !== 0) { + for (let i = 0; i < client.config.lyricsMaxResults; i++) { + if (lyricsData[i]) { + lyricsResults.push({ + label: `${lyricsData[i].title}`, + description: `${lyricsData[i].artist}`, + value: i.toString() + }); + } else { break } + } + + const menu = new MessageActionRow().addComponents( + new MessageSelectMenu() + .setCustomId("choose-lyrics") + .setPlaceholder("Choose a song") + .addOptions(lyricsResults), + ); + + let selectedLyrics = await interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `Here are some of the results I found for \`${query}\`. Please choose a song to display lyrics within \`30 seconds\`.` + ), + ], components: [menu], + }); + + const filter = (button) => button.user.id === interaction.user.id; + + const collector = selectedLyrics.createMessageComponentCollector({ + filter, + time: 30000, + }); + + collector.on("collect", async (interaction) => { + if (interaction.isSelectMenu()) { + await interaction.deferUpdate(); + const url = lyricsData[parseInt(interaction.values[0])].url; + + lyricsApi.find(url).then((lyrics) => { + let lyricsText = lyrics.lyrics; + + const button = new MessageActionRow() + .addComponents( + new MessageButton() + .setCustomId('tipsbutton') + .setLabel('Tips') + .setEmoji(`📌`) + .setStyle('SECONDARY'), + new MessageButton() + .setLabel('Source') + .setURL(url) + .setStyle('LINK'), + ); + + const musixmatch_icon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Musixmatch_logo_icon_only.svg/480px-Musixmatch_logo_icon_only.svg.png'; + let lyricsEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setTitle(`${lyrics.name}`) + .setURL(url) + .setThumbnail(lyrics.icon) + .setFooter({ + text: 'Lyrics provided by MusixMatch.', + iconURL: musixmatch_icon + }) + .setDescription(lyricsText); + + if (lyricsText.length === 0) { + lyricsEmbed + .setDescription(`**Unfortunately we're not authorized to show these lyrics.**`) + .setFooter({ + text: 'Lyrics is restricted by MusixMatch.', + iconURL: musixmatch_icon + }) + } + + if (lyricsText.length > 4096) { + lyricsText = lyricsText.substring(0, 4050) + "\n\n[...]"; + lyricsEmbed + .setDescription(lyricsText + `\nTruncated, the lyrics were too long.`) + } + + return interaction.editReply({ + embeds: [lyricsEmbed], + components: [button], + }); + + }) + } + }); + + collector.on("end", async (i) => { + if (i.size == 0) { + selectedLyrics.edit({ + content: null, + embeds: [ + new MessageEmbed() + .setDescription( + `No song is selected. You took too long to select a track.` + ) + .setColor(client.config.embedColor), + ], components: [], + }); + } + }); + + } else { + const button = new MessageActionRow() + .addComponents( + new MessageButton() + .setEmoji(`📌`) + .setCustomId('tipsbutton') + .setLabel('Tips') + .setStyle('SECONDARY'), + ); + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription( + `No results found for \`${query}\`!\nMake sure you typed in your search correctly.`, + ), + ], components: [button], + }); + } + }).catch((err) => { + console.error(err); + return interaction.editReply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription( + `An unknown error has occured, please check your console.`, + ), + ], + }); + }); + + const collector = interaction.channel.createMessageComponentCollector({ + time: 1000 * 3600 + }); + + collector.on('collect', async interaction => { + if (interaction.customId === 'tipsbutton') { + await interaction.deferUpdate(); + await interaction.followUp({ + embeds: [ + new MessageEmbed() + .setTitle(`Lyrics Tips`) + .setColor(client.config.embedColor) + .setDescription( + `Here is some tips to get your song lyrics correctly \n\n\ + 1. Try to add the artist's name in front of the song name.\n\ + 2. Try to search the lyrics manually by providing the song query using your keyboard.\n\ + 3. Avoid searching lyrics in languages other than English.`, + ), + ], ephemeral: true, components: [] + }); + }; + }); + }); + +module.exports = command; diff --git a/commands/slash/move.js b/commands/slash/move.js new file mode 100644 index 0000000..33125d0 --- /dev/null +++ b/commands/slash/move.js @@ -0,0 +1,75 @@ +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 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 new file mode 100644 index 0000000..017d0a1 --- /dev/null +++ b/commands/slash/nowplaying.js @@ -0,0 +1,83 @@ +const { MessageEmbed } = require("discord.js"); +const escapeMarkdown = require('discord.js').Util.escapeMarkdown; +const SlashCommand = require("../../lib/SlashCommand"); +const prettyMilliseconds = require("pretty-ms"); + +const command = new SlashCommand() + .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; + var title = escapeMarkdown(song.title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + 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(`[${ title }](${ song.uri })`); + return interaction.reply({ embeds: [embed] }); + }); +module.exports = command; diff --git a/commands/slash/pause.js b/commands/slash/pause.js new file mode 100644 index 0000000..d70121b --- /dev/null +++ b/commands/slash/pause.js @@ -0,0 +1,58 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +const command = new SlashCommand() + .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 0000000..ea9b7fc --- /dev/null +++ b/commands/slash/ping.js @@ -0,0 +1,69 @@ +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!") + .addFields( + { + name: "API Latency", + value: `\`\`\`yml\n${apiState} | ${apiPing}ms\`\`\``, + inline: true, + }, + { + name: "Bot Latency", + value: `\`\`\`yml\n${botState} | ${botPing}ms\`\`\``, + inline: 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 new file mode 100644 index 0000000..95716ac --- /dev/null +++ b/commands/slash/play.js @@ -0,0 +1,196 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); +const escapeMarkdown = require('discord.js').Util.escapeMarkdown; + +const command = new SlashCommand() + .setName("play") + .setDescription( + "Searches and plays the requested song \nSupports: \nYoutube, Spotify, Deezer, Apple Music" + ) + .addStringOption((option) => + option + .setName("query") + .setDescription("What am I looking for?") + .setAutocomplete(true) + .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); // Need this because discord api is buggy asf, and without this the bot will not request to speak on a stage - Darren + } + + const ret = await interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(":mag_right: **Searching...**"), + ], + fetchReply: true, + }); + + 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(); + } + await 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(); + } + await 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(); + } + var title = escapeMarkdown(res.tracks[0].title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + let addQueueEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setAuthor({ name: "Added to queue", iconURL: client.config.iconURL }) + .setDescription( + `[${title}](${res.tracks[0].uri})` || "No Title" + ) + .setURL(res.tracks[0].uri) + .addFields( + { + name: "Added by", + value: `<@${interaction.user.id}>`, + inline: true, + }, + { + name: "Duration", + value: res.tracks[0].isStream + ? `\`LIVE 🔴 \`` + : `\`${client.ms(res.tracks[0].duration, { + colonNotation: true, + secondsDecimalDigits: 0, + })}\``, + inline: true, + } + ); + + try { + addQueueEmbed.setThumbnail( + res.tracks[0].displayThumbnail("maxresdefault") + ); + } catch (err) { + addQueueEmbed.setThumbnail(res.tracks[0].thumbnail); + } + + if (player.queue.totalSize > 1) { + addQueueEmbed.addFields({ + name: "Position in queue", + value: `${player.queue.size}`, + inline: true, + }); + } else { + player.queue.previous = player.queue.current; + } + + await 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})`) + .addFields( + { + name: "Enqueued", + value: `\`${res.tracks.length}\` songs`, + inline: true, + }, + { + name: "Playlist duration", + value: `\`${client.ms(res.playlist.duration, { + colonNotation: true, + secondsDecimalDigits: 0, + })}\``, + inline: true, + } + ); + + await interaction.editReply({ embeds: [playlistEmbed] }).catch(this.warn); + } + + if (ret) setTimeout(() => ret.delete().catch(this.warn), 20000); + return ret; + }); + +module.exports = command; diff --git a/commands/slash/previous.js b/commands/slash/previous.js new file mode 100644 index 0000000..7cb45da --- /dev/null +++ b/commands/slash/previous.js @@ -0,0 +1,67 @@ +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) => { + 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, + }); + } + + const previousSong = player.queue.previous; + const currentSong = player.queue.current; + const nextSong = player.queue[0] + + if (!previousSong + || previousSong === currentSong + || previousSong === nextSong) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription("There is no previous song in the queue."), + ], + })} + + if (previousSong !== currentSong && previousSong !== nextSong) { + player.queue.splice(0, 0, currentSong) + player.play(previousSong); + } + interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `⏎ | Previous song: **${ previousSong.title }**`, + ), + ], + }); +}); + +module.exports = command; diff --git a/commands/slash/queue.js b/commands/slash/queue.js new file mode 100644 index 0000000..0d21d3e --- /dev/null +++ b/commands/slash/queue.js @@ -0,0 +1,347 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed, MessageButton, MessageActionRow } = require("discord.js"); +const escapeMarkdown = require('discord.js').Util.escapeMarkdown; +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 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; + var title = escapeMarkdown(song.title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + const queueEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`**â™Ē | Now playing:** [${ 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: [queueEmbed], + }); + } 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; + var title = escapeMarkdown(song.title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + const embedTwo = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ 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; + var title = escapeMarkdown(song.title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + const embedThree = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ 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; + let song = player.queue.current; + var title = escapeMarkdown(song.title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + const embedFour = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ 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; + let song = player.queue.current; + var title = escapeMarkdown(song.title) + var title = title.replace(/\]/g,"") + var title = title.replace(/\[/g,"") + const embedFive = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription( + `**â™Ē | Now playing:** [${ 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 new file mode 100644 index 0000000..c208790 --- /dev/null +++ b/commands/slash/reload.js @@ -0,0 +1,89 @@ +const { MessageEmbed, message } = require("discord.js"); +const SlashCommand = require("../../lib/SlashCommand"); +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 client.warn( + "❌ Unable to load Command: " + + file.split(".")[0] + + ", File doesn't have a 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 new file mode 100644 index 0000000..c0a8b39 --- /dev/null +++ b/commands/slash/remove.js @@ -0,0 +1,68 @@ +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 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 removeEmbed = new MessageEmbed() + .setColor(client.config.embedColor) + .setDescription(`Removed track number **${ number }** from queue`); + return interaction.editReply({ embeds: [removeEmbed] }); + }); + +module.exports = command; diff --git a/commands/slash/replay.js b/commands/slash/replay.js new file mode 100644 index 0000000..b3dd2ed --- /dev/null +++ b/commands/slash/replay.js @@ -0,0 +1,51 @@ +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 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 new file mode 100644 index 0000000..cfe9787 --- /dev/null +++ b/commands/slash/resume.js @@ -0,0 +1,57 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +const command = new SlashCommand() + .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 0000000..957aac0 --- /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 sendtoDmEmbed = 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: [sendtoDmEmbed] }); + + 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 new file mode 100644 index 0000000..20a304d --- /dev/null +++ b/commands/slash/search.js @@ -0,0 +1,190 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const prettyMilliseconds = require("pretty-ms"); +const { + 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) => { + 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" + ) + .addFields( + { + name: "Added by", + value: `<@${interaction.user.id}>`, + inline: true, + }, + { + name: "Duration", + value: res.tracks[0].isStream + ? `\`LIVE :red_circle:\`` + : `\`${client.ms(res.tracks[0].duration, { + colonNotation: true, + })}\``, + inline: 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: [], + }); + } + }); + } + }); + +module.exports = command; diff --git a/commands/slash/seek.js b/commands/slash/seek.js new file mode 100644 index 0000000..5540ea8 --- /dev/null +++ b/commands/slash/seek.js @@ -0,0 +1,82 @@ +const SlashCommand = require("../../lib/SlashCommand"); +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 1h 30m | 2h | 80m | 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 rawArgs = interaction.options.getString("time"); + const args = rawArgs.split(' '); + var rawTime = []; + for (i = 0; i < args.length; i++){ + rawTime.push(ms(args[i])); + } + const time = rawTime.reduce((a,b) => a + b, 0); + 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( + `Unable to seek current playing track. This may be due to exceeding track duration or an incorrect time format. Please check and try again`, + ), + ], + }); + } + }); + +module.exports = command; diff --git a/commands/slash/shuffle.js b/commands/slash/shuffle.js new file mode 100644 index 0000000..97bc0f0 --- /dev/null +++ b/commands/slash/shuffle.js @@ -0,0 +1,59 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +const command = new SlashCommand() + .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 new file mode 100644 index 0000000..5482540 --- /dev/null +++ b/commands/slash/skip.js @@ -0,0 +1,59 @@ +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; + 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, + }); + } + const song = player.queue.current; + const autoQueue = player.get("autoQueue"); + if (player.queue[0] == undefined && (!autoQueue || autoQueue === false)) { + return interaction.reply({ + embeds: [ + new MessageEmbed() + .setColor("RED") + .setDescription(`There is nothing after [${ song.title }](${ song.uri }) in the queue.`), + ], + })} + + 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 new file mode 100644 index 0000000..fb1b33c --- /dev/null +++ b/commands/slash/skipto.js @@ -0,0 +1,81 @@ +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 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 new file mode 100644 index 0000000..f521659 --- /dev/null +++ b/commands/slash/stats.js @@ -0,0 +1,89 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const moment = require("moment"); +require("moment-duration-format"); +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 }); + }); + +module.exports = command; diff --git a/commands/slash/stop.js b/commands/slash/stop.js new file mode 100644 index 0000000..7de8d48 --- /dev/null +++ b/commands/slash/stop.js @@ -0,0 +1,55 @@ +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(); + player.set("autoQueue", false); + } 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 new file mode 100644 index 0000000..4aa7d95 --- /dev/null +++ b/commands/slash/summon.js @@ -0,0 +1,36 @@ +const SlashCommand = require("../../lib/SlashCommand"); +const { MessageEmbed } = require("discord.js"); + +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); + 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 new file mode 100644 index 0000000..5074198 --- /dev/null +++ b/commands/slash/volume.js @@ -0,0 +1,68 @@ +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) => { + 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 new file mode 100644 index 0000000..64a7bcb --- /dev/null +++ b/config.js @@ -0,0 +1,48 @@ +module.exports = { + helpCmdPerPage: 10, //- Number of commands per page of help command + lyricsMaxResults: 5, //- Number of results for lyrics command (Do not touch this value if you don't know what you are doing) + adminId: "UserId", //- Replace UserId with the Discord ID of the 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/discord", //- Support Server Link + Issues: "https://github.com/BestGamersH/Music-bot/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. + twentyFourSeven: false, //- When set to true, the bot will never disconnect from the voice channel + autoQueue: false, //- When set to true, related songs will automatically be added to the queue + autoPause: true, //- When set to true, music will automatically be paused if everyone leaves the voice channel + autoLeave: false, //- When set to true, the bot will automatically leave when no one is in the voice channel (can be combined with 24/7 to always be in voice channel until everyone leaves; if 24/7 is on disconnectTime will add a disconnect delay after everyone leaves.) + debug: false, //- Debug mode + cookieSecret: "BestGamersHK is cool", //- 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, dnd and invisible (Note: invisible makes 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 0000000..bffb357 --- /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 0000000..bf920c1 --- /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 0000000..0ca9b95 --- /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 0000000..3acb157 --- /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 0000000..b86a699 --- /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 0000000..f38ab44 --- /dev/null +++ b/dashboard/components/navbar.tsx @@ -0,0 +1,31 @@ +import {Button, Link, Spacer} from "@nextui-org/react"; +import { useRouter } from "next/router"; + +export default function Navbar() { + const router = useRouter(); + + return
+ Discord Music Bot + + + + +
+} diff --git a/dashboard/components/server.tsx b/dashboard/components/server.tsx new file mode 100644 index 0000000..2d4c566 --- /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 0000000..4f11a03 --- /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 0000000..d139cfa --- /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 0000000..440d1e8 --- /dev/null +++ b/dashboard/out/404.html @@ -0,0 +1,12 @@ +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/123-d3ffcfb4730480c6.js b/dashboard/out/_next/static/chunks/123-d3ffcfb4730480c6.js new file mode 100644 index 0000000..74ba7b4 --- /dev/null +++ b/dashboard/out/_next/static/chunks/123-d3ffcfb4730480c6.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[123],{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:$,target:f,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:$,target:"a"===o?f: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),$=r(88),f=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,f.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,$.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:$,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:$,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,f.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,f.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,$.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:$,children:f,...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:$,animated:u,rounded:b,ripple:g,isButtonGroup:!0})),[t,u,r,g,n,i,a,s,l,$]);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})})};F.toString=()=>".nextui-button-group";const R=o.memo(F);var W=(0,$.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 f}});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 $=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&&($.displayName="NextUI.Link"),$.toString=()=>".nextui-link";var f=(0,s.Z)($,{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",(()=>$)),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 $=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:$}},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 $(){var e;return null===(e=(0,o.useContext)(u))||void 0===e?void 0:e.focusManager}const f=["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=f.join(":not([hidden]),")+",[tabindex]:not([disabled]):not([hidden])";f.push('[tabindex]:not([tabindex="-1"]):not([disabled])');const m=f.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(5443)}}]); \ No newline at end of file diff --git a/dashboard/out/_next/static/chunks/732-e52c1d2253f458fa.js b/dashboard/out/_next/static/chunks/732-e52c1d2253f458fa.js new file mode 100644 index 0000000..964ceae --- /dev/null +++ b/dashboard/out/_next/static/chunks/732-e52c1d2253f458fa.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[732],{5446:function(e,t,r){var n=r(5784),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")},5784:function(e,t,r){r.d(t,{Z:function(){return $n}});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==(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,n)),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"],_=[...T,...R];function j(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 j(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 M(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=>M(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,_)}N.propTypes={},N.filterProps=T,Z.propTypes={},Z.filterProps=R,F.propTypes={},F.filterProps=_;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||"hsla"===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,l=a(e,Pe);if(e.vars)throw new Error(c(18));const u=fe(o),d=L(e);let p=f(d,{mixins:D(d.breakpoints,r),palette:u,shadows:ve.slice(),typography:be(u,s),transitions:Ae(i),zIndex:(0,n.Z)({},Ce)});return p=f(p,l),p=t.reduce(((e,t)=>f(e,t)),p),p}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]}},_e=/^((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)-.*))$/,je=Re((function(e){return _e.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)+"*"+Me(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;o0&&Le($)-d&&Ke(f>32?Pt($+";",n,r,d-1):Pt(Ze($," ","")+";",n,r,d-2),c);break;case 59:$+=";";default:if(Ke(w=At($,t,r,l,u,o,s,v,x=[],k=[],d),i),123===y)if(0===u)St($,t,w,w,x,i,d,s,k);else switch(99===p&&110===Be($,3)?100:p){case 100:case 109:case 115:St(e,w,w,n&&Ke(At(e,w,w,0,0,o,s,v,o,x=[],d),k),o,k,d,s,n?x:k);break;default:St($,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($+=Me(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 At(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 Ct(e,t,r){return Je(e,t,r,bt,Me(Xe),He(e,2,-2),0)}function Pt(e,t,r,n){return Je(e,t,r,vt,He(e,0,n),He(e,n+1,-1),n)}var Ot=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)},zt=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]+=Ot(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]+=Me(n)}}while(n=tt());return e}(at(e),t))},Tt=new WeakMap,Rt=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)||Tt.get(r))&&!n){Tt.set(e,!0);for(var o=[],i=zt(t,o),a=r.props,s=0,c=0;s6)switch(Be(e,t+1)){case 109:if(45!==Be(e,t+4))break;case 102:return Ze(e,/(.+:)(.+)-([^]+)/,"$1-webkit-$2-$3$1-moz-"+(108==Be(e,t+3)?"$3":"$2-$3"))+e;case 115:return~Fe(e,"stretch")?jt(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,":",":-webkit-")+e;case 101:return Ze(e,/(.+:)([^;!]+)(;|!.+)?/,"$1-webkit-"+(45===Be(e,14)?"inline-":"")+"box$3$1"+"-webkit-$2$3$1"+"-ms-$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}var Et=[function(e,t,r,n){if(e.length>-1&&!e.return)switch(e.type){case vt:e.return=jt(e.value,e.length);break;case xt:return kt([Qe(e,{value:Ze(e.value,"@","@-webkit-")})],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+)/,"-ms-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?tr:rr},or=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},ir=function(e){var t=e.cache,r=e.serialized,n=e.isStringTag;er(t,r,n);qt((function(){return function(e,t,r){er(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)}));return null},ar=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=or(t,r,s),u=l||nr(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?ur(n)?e[t]:l(e[t]):`${ur(n)?t:l(t)}${l(e[t].toString())}`})),n}var pr=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 fr(e){return"number"!==typeof e?e:`${e}px solid`}const hr=S({prop:"border",themeKey:"borders",transform:fr}),mr=S({prop:"borderTop",themeKey:"borders",transform:fr}),gr=S({prop:"borderRight",themeKey:"borders",transform:fr}),br=S({prop:"borderBottom",themeKey:"borders",transform:fr}),yr=S({prop:"borderLeft",themeKey:"borders",transform:fr}),vr=S({prop:"borderColor",themeKey:"palette"}),xr=S({prop:"borderTopColor",themeKey:"palette"}),kr=S({prop:"borderRightColor",themeKey:"palette"}),wr=S({prop:"borderBottomColor",themeKey:"palette"}),$r=S({prop:"borderLeftColor",themeKey:"palette"}),Sr=e=>{if(void 0!==e.borderRadius&&null!==e.borderRadius){const t=j(e.theme,"shape.borderRadius",4),r=e=>({borderRadius:I(t,e)});return v(e,e.borderRadius,r)}return null};Sr.propTypes={},Sr.filterProps=["borderRadius"];var Ar=pr(hr,mr,gr,br,yr,vr,xr,kr,wr,$r,Sr);var Cr=pr(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 Pr=pr(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 Or=e=>{if(void 0!==e.gap&&null!==e.gap){const t=j(e.theme,"spacing",8),r=e=>({gap:I(t,e)});return v(e,e.gap,r)}return null};Or.propTypes={},Or.filterProps=["gap"];const zr=e=>{if(void 0!==e.columnGap&&null!==e.columnGap){const t=j(e.theme,"spacing",8),r=e=>({columnGap:I(t,e)});return v(e,e.columnGap,r)}return null};zr.propTypes={},zr.filterProps=["columnGap"];const Tr=e=>{if(void 0!==e.rowGap&&null!==e.rowGap){const t=j(e.theme,"spacing",8),r=e=>({rowGap:I(t,e)});return v(e,e.rowGap,r)}return null};Tr.propTypes={},Tr.filterProps=["rowGap"];var Rr=pr(Or,zr,Tr,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 _r=pr(S({prop:"position"}),S({prop:"zIndex",themeKey:"zIndex"}),S({prop:"top"}),S({prop:"right"}),S({prop:"bottom"}),S({prop:"left"}));function jr(e,t){return"grey"===t?t:e}var Er=pr(S({prop:"color",themeKey:"palette",transform:jr}),S({prop:"bgcolor",cssProperty:"backgroundColor",themeKey:"palette",transform:jr}),S({prop:"backgroundColor",themeKey:"palette",transform:jr}));var Ir=S({prop:"boxShadow",themeKey:"shadows"});function Mr(e){return e<=1&&0!==e?100*e+"%":e}const Wr=S({prop:"width",transform:Mr}),Nr=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]||Mr(t)}};return v(e,e.maxWidth,t)}return null};Nr.filterProps=["maxWidth"];const Zr=S({prop:"minWidth",transform:Mr}),Fr=S({prop:"height",transform:Mr}),Br=S({prop:"maxHeight",transform:Mr}),Hr=S({prop:"minHeight",transform:Mr});S({prop:"size",cssProperty:"width",transform:Mr}),S({prop:"size",cssProperty:"height",transform:Mr});var Lr=pr(Wr,Nr,Zr,Fr,Br,Hr,S({prop:"boxSizing"}));const Gr=S({prop:"fontFamily",themeKey:"typography"}),Kr=S({prop:"fontSize",themeKey:"typography"}),Vr=S({prop:"fontStyle",themeKey:"typography"}),Ur=S({prop:"fontWeight",themeKey:"typography"}),Dr=S({prop:"letterSpacing"}),qr=S({prop:"textTransform"}),Xr=S({prop:"lineHeight"}),Yr=S({prop:"textAlign"});var Jr=pr(S({prop:"typography",cssProperty:!1,themeKey:"typography"}),Gr,Kr,Vr,Ur,Dr,Xr,Yr,qr);const Qr={borders:Ar.filterProps,display:Cr.filterProps,flexbox:Pr.filterProps,grid:Rr.filterProps,positions:_r.filterProps,palette:Er.filterProps,shadows:Ir.filterProps,sizing:Lr.filterProps,spacing:B.filterProps,typography:Jr.filterProps},en={borders:Ar,display:Cr,flexbox:Pr,grid:Rr,positions:_r,palette:Er,shadows:Ir,sizing:Lr,spacing:B,typography:Jr};Object.keys(Qr).reduce(((e,t)=>(Qr[t].forEach((r=>{e[r]=en[t]})),e)),{});const tn=function(e=en){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)}}();tn.filterProps=["sx"];var rn=tn;const nn=["name","slot","skipVariantsResolver","skipSx","overridesResolver"],on=["theme"],an=["theme"];function sn(e){return 0===Object.keys(e).length}function cn(e){return"ownerState"!==e&&"theme"!==e&&"sx"!==e&&"as"!==e}const ln=L();const un=function(e={}){const{defaultTheme:t=ln,rootShouldForwardProp:r=cn,slotShouldForwardProp:o=cn,styleFunctionSx:i=rn}=e,s=e=>{const r=sn(e.theme)?t:e.theme;return i((0,n.Z)({},e,{theme:r}))};return s.__mui_systemSx=!0,(e,i={})=>{((e,t)=>{Array.isArray(e.__emotion_styles)&&(e.__emotion_styles=t(e.__emotion_styles))})(e,(e=>e.filter((e=>!(null!=e&&e.__mui_systemSx)))));const{name:c,slot:l,skipVariantsResolver:u,skipSx:d,overridesResolver:p}=i,f=a(i,nn),h=void 0!==u?u:l&&"Root"!==l||!1,m=d||!1;let g=cn;"Root"===l?g=r:l?g=o:function(e){return"string"===typeof e&&e.charCodeAt(0)>96}(e)&&(g=void 0);const b=function(e,t){return cr(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,on);return e((0,n.Z)({theme:sn(o)?t:o},i))}:e)):[];let i=e;c&&p&&o.push((e=>{const r=sn(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=sn(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[dr(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=dr(e.props);n[t]=e.style})),n})(c,r),r,c)})),m||o.push(s);const l=o.length-r.length;if(Array.isArray(e)&&l>0){const t=new Array(l).fill("");i=[...e,...t],i.raw=[...e.raw,...t]}else"function"===typeof e&&e.__emotion_real!==e&&(i=r=>{let{theme:o}=r,i=a(r,an);return e((0,n.Z)({theme:sn(o)?t:o},i))});return b(i,...o)};return b.withConfig&&(y.withConfig=b.withConfig),y}}({defaultTheme:ze,rootShouldForwardProp:e=>cn(e)&&"classes"!==e});var dn=un;const pn=e=>e;var fn=(()=>{let e=pn;return{configure(t){e=t},generate:t=>e(t),reset(){e=pn}}})();const hn={active:"active",checked:"checked",completed:"completed",disabled:"disabled",error:"error",expanded:"expanded",focused:"focused",focusVisible:"focusVisible",required:"required",selected:"selected"};function mn(e,t,r="Mui"){const n=hn[t];return n?`${r}-${n}`:`${fn.generate(e)}-${t}`}function gn(e){return mn("MuiSvgIcon",e)}!function(e,t,r="Mui"){const n={};t.forEach((t=>{n[t]=mn(e,t,r)}))}("MuiSvgIcon",["root","colorPrimary","colorSecondary","colorAction","colorError","colorDisabled","fontSizeInherit","fontSizeSmall","fontSizeMedium","fontSizeLarge"]);var bn=r(5893);const yn=["children","className","color","component","fontSize","htmlColor","inheritViewBox","titleAccess","viewBox"],vn=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&&(e.push(t(n)),r&&r[n]&&e.push(r[n])),e)),[]).join(" ")})),n}({root:["root","inherit"!==t&&`color${u(t)}`,`fontSize${u(r)}`]},gn,n)},xn=dn("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.1875rem"}[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]}})),kn=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,yn),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=vn(g);return(0,bn.jsxs)(xn,(0,n.Z)({as:l,className:(0,s.Z)(y.root,i),focusable:"false",color:d,"aria-hidden":!f||void 0,role:f?"img":void 0,ref:t},b,m,{ownerState:g,children:[o,f?(0,bn.jsx)("title",{children:f}):null]}))}));kn.muiName="SvgIcon";var wn=kn;function $n(e,t){function r(r,o){return(0,bn.jsx)(wn,(0,n.Z)({"data-testid":`${t}Icon`,ref:o},r,{children:e}))}return r.muiName=wn.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 _=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,_]=T(S),[j,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===j.width,t="auto"===_.current;!e&&o&&i&&(j.width{u&&E()}));const M=(0,n.useMemo)((()=>g?"loading":"ready"),[g]);return(0,b.jsxs)(w,{className:(0,A.Z)("nextui-image-container",`nextui-image--${M}`,s),"data-state":M,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":M,alt:e.alt||"",css:{objectFit:d,...f},...h})]})}));g.Ts&&(_.displayName="NextUI.Image"),_.toString=()=>".nextui-image";var j=n.memo(_),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"}}),M=(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)(M,{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=j,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)),_=(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:_})}));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)}),[])}},7568:function(e,t,r){function n(e,t,r,n,o,i,a){try{var s=e[i](a),c=s.value}catch(l){return void r(l)}s.done?t(c):Promise.resolve(c).then(n,o)}function o(e){return function(){var t=this,r=arguments;return new Promise((function(o,i){var a=e.apply(t,r);function s(e){n(a,o,i,s,c,"next",e)}function c(e){n(a,o,i,s,c,"throw",e)}s(void 0)}))}}r.d(t,{Z:function(){return o}})}}]); \ 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 0000000..1225f82 --- /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