The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .dockerignore
├── .github
    ├── CODEOWNERS
    ├── FUNDING.yml
    ├── ISSUE_TEMPLATE
    │   ├── bug_report.md
    │   ├── config.yml
    │   └── feature_request.md
    ├── PULL_REQUEST_TEMPLATE.md
    ├── SUPPORT.md
    └── stale.yml
├── .gitignore
├── .replit
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE.md
├── Procfile
├── README.md
├── api
    ├── index.js
    ├── middlewares
    │   └── auth.js
    ├── router.js
    └── routes
    │   ├── dashboard.js
    │   └── data.js
├── app.json
├── assets
    └── logo.gif
├── commands
    ├── context
    │   └── play.js
    └── slash
    │   ├── 247.js
    │   ├── autoleave.js
    │   ├── autopause.js
    │   ├── autoqueue.js
    │   ├── clean.js
    │   ├── clear.js
    │   ├── filters.js
    │   ├── guildleave.js
    │   ├── help.js
    │   ├── invite.js
    │   ├── loop.js
    │   ├── loopq.js
    │   ├── lyrics.js
    │   ├── move.js
    │   ├── nowplaying.js
    │   ├── pause.js
    │   ├── ping.js
    │   ├── play.js
    │   ├── previous.js
    │   ├── queue.js
    │   ├── reload.js
    │   ├── remove.js
    │   ├── replay.js
    │   ├── resume.js
    │   ├── save.js
    │   ├── search.js
    │   ├── seek.js
    │   ├── shuffle.js
    │   ├── skip.js
    │   ├── skipto.js
    │   ├── stats.js
    │   ├── stop.js
    │   ├── summon.js
    │   └── volume.js
├── config.js
├── dashboard
    ├── .eslintrc.json
    ├── .gitignore
    ├── README.md
    ├── components
    │   ├── StatCard.tsx
    │   ├── content.tsx
    │   ├── navbar.tsx
    │   └── server.tsx
    ├── next-env.d.ts
    ├── next.config.js
    ├── out
    │   ├── 404.html
    │   ├── _next
    │   │   └── static
    │   │   │   ├── chunks
    │   │   │       ├── 123-d3ffcfb4730480c6.js
    │   │   │       ├── 732-e52c1d2253f458fa.js
    │   │   │       ├── framework-4556c45dd113b893.js
    │   │   │       ├── main-a19d41ac16dbce80.js
    │   │   │       ├── pages
    │   │   │       │   ├── _app-79511e227f7b8d22.js
    │   │   │       │   ├── _error-a4ba2246ff8fb532.js
    │   │   │       │   ├── dashboard-8fe77e1aeec6ff87.js
    │   │   │       │   ├── index-0494ad302e38da35.js
    │   │   │       │   ├── login-8481030b110c33c9.js
    │   │   │       │   ├── logout-7167c9506bd9bdd3.js
    │   │   │       │   ├── servers-b957468847859725.js
    │   │   │       │   └── servers
    │   │   │       │   │   └── [id]-9f8c48f5bf25bd78.js
    │   │   │       ├── polyfills-c67a75d1b6f99dc8.js
    │   │   │       └── webpack-fd1bc4a65a80e5c8.js
    │   │   │   └── wV3SzfWusZ8UapJ--_pvH
    │   │   │       ├── _buildManifest.js
    │   │   │       └── _ssgManifest.js
    │   ├── dashboard.html
    │   ├── index.html
    │   ├── login.html
    │   ├── logout.html
    │   ├── servers.html
    │   └── servers
    │   │   └── [id].html
    ├── package.json
    ├── pages
    │   ├── _app.tsx
    │   ├── _document.tsx
    │   ├── dashboard.tsx
    │   ├── index.tsx
    │   ├── login.tsx
    │   ├── logout.tsx
    │   ├── servers.tsx
    │   └── servers
    │   │   └── [id].tsx
    ├── svgs
    │   ├── AudiotrackRounded.svg
    │   ├── DnsRounded.svg
    │   ├── PersonRounded.svg
    │   └── RocketLaunchRounded.svg
    ├── tsconfig.json
    └── utils
    │   ├── dashboard.ts
    │   └── data.ts
├── deploy
    ├── deployGlobal.js
    ├── deployGuild.js
    ├── destroyGlobal.js
    └── destroyGuild.js
├── docker-compose.yml
├── docker
    └── application.yml
├── events
    ├── interactionCreate.js
    ├── messageCreate.js
    ├── messageDelete.js
    ├── raw.js
    ├── ready.js
    └── voiceStateUpdate.js
├── index.js
├── kickstartReplit.sh
├── lib
    ├── DiscordMusicBot.js
    ├── EpicPlayer.d.ts
    ├── EpicPlayer.js
    ├── Logger.js
    └── SlashCommand.js
├── package.json
├── renovate.json
├── replit.nix
└── util
    ├── Controller.js
    ├── db.js
    ├── getChannel.js
    ├── getConfig.js
    ├── getLavalink.js
    ├── guildDb.js
    └── loadCommands.js


/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log


--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @SudhanPlayz @DarrenOfficial


--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
 1 | # These are supported funding model platforms
 2 | 
 3 | github: SudhanPlayz
 4 | patreon: # Replace with a single Patreon username
 5 | open_collective: # Replace with a single Open Collective username
 6 | ko_fi: # Replace with a single Ko-fi username
 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
 9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
16 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Bug report
 3 | about: Report incorrect or unexpected behavior of the Music Bot
 4 | title: ""
 5 | labels: "s: unverified, type: bug"
 6 | assignees: ""
 7 | ---
 8 | 
 9 | <!-- Use Discord for questions: https://discord.gg/sbySMS7m3v -->
10 | 
11 | ## Please describe the problem you are having in as much detail as possible:
12 | 
13 | ## Include a reproducible code sample here, if possible:
14 | 
15 | ```js
16 | // Place your code here
17 | ```
18 | 
19 | ## Further details:
20 | 
21 | - discord.js version:
22 | - Node.js version:
23 | - Operating system:
24 | - Priority this issue should have – please be realistic and elaborate if possible:
25 | 
26 | ## Relevant client options:
27 | 
28 | - partials: none
29 | - gateway intents: none
30 | - other: none
31 | 
32 | <!--
33 | Remove the comment and fill out the commit hash if this applies to you:
34 | (While it's not a requirement to test your issue on the master branch, it would make fixing the problem a lot easier for us, so please do so if possible.)
35 | 
36 | -->
37 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 |   - name: Discord server
4 |     url: https://discord.gg/sbySMS7m3v
5 |     about: Please visit our Discord server for questions and support requests.
6 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Feature request
 3 | about: Request a feature for the Music Bot
 4 | title: ""
 5 | labels: "type: enhancement"
 6 | assignees: ""
 7 | ---
 8 | 
 9 | <!-- Use Discord for questions: https://discord.gg/bRCvFy9 -->
10 | 
11 | ## Is your feature request related to a problem? Please describe.
12 | 
13 | A clear and concise description of what the problem is. Eg. I'm always frustrated when [...]
14 | 
15 | ## Describe the ideal solution
16 | 
17 | A clear and concise description of what you want to happen.
18 | 
19 | ## Describe alternatives you've considered
20 | 
21 | A clear and concise description of any alternative solutions or features you've considered.
22 | 
23 | ## Additional context
24 | 
25 | Add any other context or screenshots about the feature request here.
26 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
 1 | ## Please describe the changes this PR makes and why it should be merged:
 2 | 
 3 | ## Status and versioning classification:
 4 | 
 5 | <!--
 6 | Please move lines that apply to you out of the comment:
 7 | - Code changes have been tested against the Discord API, or there are no code changes
 8 | - I know how to update typings and have done so, or typings don't need updating
 9 | - This PR changes the library's interface (methods or parameters added)
10 | - This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
11 | - This PR **only** includes non-code changes, like changes to documentation, README, etc.
12 | -->
13 | 
14 | # Important.
15 | 
16 | - Write in camelCase, not snake_case.
17 | - Do not push to master/main without testing your changes first, make a branch
18 |   if you have to.


--------------------------------------------------------------------------------
/.github/SUPPORT.md:
--------------------------------------------------------------------------------
 1 | # Seeking support?
 2 | 
 3 | We only use this issue tracker for bug reports and feature request. We are not able to provide general support or answer
 4 | questions in the form of GitHub issues.
 5 | 
 6 | For general questions about the Music Bot and use please use the dedicated support channels in our Discord
 7 | server: https://discord.gg/sbySMS7m3v
 8 | 
 9 | Any issues that don't directly involve a bug or a feature request will likely be closed and redirected.
10 | 


--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
 1 | # Configuration for probot-stale - https://github.com/probot/stale
 2 | daysUntilStale: 60
 3 | 
 4 | daysUntilClose: 5
 5 | exemptLabels:
 6 |   - Soon
 7 | 
 8 | markComment: >
 9 |   This issue has been automatically marked as stale because it has not had
10 |   recent activity. It will be closed if no further activity occurs. Thank you
11 |   for your contributions.
12 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
  1 | # Actual ignored paths for this repo
  2 | db.json
  3 | dbList.json
  4 | .guild_dbs/
  5 | dev-config.js
  6 | 
  7 | # Logs
  8 | logs
  9 | *.log
 10 | npm-debug.log*
 11 | yarn-debug.log*
 12 | yarn-error.log*
 13 | lerna-debug.log*
 14 | 
 15 | # Diagnostic reports (https://nodejs.org/api/report.html)
 16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
 17 | 
 18 | # Runtime data
 19 | pids
 20 | *.pid
 21 | *.seed
 22 | *.pid.lock
 23 | 
 24 | # Directory for instrumented libs generated by jscoverage/JSCover
 25 | lib-cov
 26 | 
 27 | # Coverage directory used by tools like istanbul
 28 | coverage
 29 | *.lcov
 30 | 
 31 | # nyc test coverage
 32 | .nyc_output
 33 | 
 34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
 35 | .grunt
 36 | 
 37 | # Bower dependency directory (https://bower.io/)
 38 | bower_components
 39 | 
 40 | # node-waf configuration
 41 | .lock-wscript
 42 | 
 43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
 44 | build/Release
 45 | 
 46 | # Dependency directories
 47 | node_modules/
 48 | jspm_packages/
 49 | 
 50 | # TypeScript v1 declaration files
 51 | typings/
 52 | 
 53 | # TypeScript cache
 54 | *.tsbuildinfo
 55 | 
 56 | # Optional npm cache directory
 57 | .npm
 58 | 
 59 | # Optional eslint cache
 60 | .eslintcache
 61 | 
 62 | # Microbundle cache
 63 | .rpt2_cache/
 64 | .rts2_cache_cjs/
 65 | .rts2_cache_es/
 66 | .rts2_cache_umd/
 67 | 
 68 | # Optional REPL history
 69 | .node_repl_history
 70 | 
 71 | # Output of 'npm pack'
 72 | *.tgz
 73 | 
 74 | # Yarn Integrity file
 75 | .yarn
 76 | .yarn-integrity
 77 | yarn.lock
 78 | 
 79 | # dotenv environment variables file
 80 | .env
 81 | .env.test
 82 | 
 83 | # parcel-bundler cache (https://parceljs.org/)
 84 | .cache
 85 | 
 86 | # Next.js build output
 87 | .next
 88 | 
 89 | # Nuxt.js build / generate output
 90 | .nuxt
 91 | dist
 92 | 
 93 | # Gatsby files
 94 | .cache/
 95 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
 96 | # https://nextjs.org/blog/next-9-1#public-directory-support
 97 | # public
 98 | 
 99 | # vuepress build output
100 | .vuepress/dist
101 | 
102 | # Serverless directories
103 | .serverless/
104 | 
105 | # FuseBox cache
106 | .fusebox/
107 | 
108 | # DynamoDB Local files
109 | .dynamodb/
110 | 
111 | # TernJS port file
112 | .tern-port
113 | 
114 | # Lockfiles
115 | package-lock.json
116 | yarn.lock
117 | 
118 | # Volta
119 | .pnp.cjs
120 | .pnp.loader.mjs
121 | 
122 | # IDE
123 | .idea/
124 | .vscode/
125 | .history/
126 | 


--------------------------------------------------------------------------------
/.replit:
--------------------------------------------------------------------------------
1 | run = "node index.js" 
2 | language = "Bash"
3 | 
4 | [nix]
5 | channel = "stable-22_05"
6 | 
7 | 


--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
 1 | # Contributor Covenant Code of Conduct
 2 | 
 3 | ## Our Pledge
 4 | 
 5 | In the interest of fostering an open and welcoming environment, we as
 6 | contributors and maintainers pledge to making participation in our project and
 7 | our community a harassment-free experience for everyone, regardless of age, body
 8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
 9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 | 
12 | ## Our Standards
13 | 
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 | 
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 | 
23 | Examples of unacceptable behavior by participants include:
24 | 
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 |   advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 |   address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 |   professional setting
33 | 
34 | ## Our Responsibilities
35 | 
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 | 
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 | 
46 | ## Scope
47 | 
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 | 
55 | ## Enforcement
56 | 
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at SudhanPlayz@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 | 
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 | 
68 | ## Attribution
69 | 
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 | 
73 | [homepage]: https://www.contributor-covenant.org
74 | 
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 | 


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
 1 | <h1 align="center">Contributing</h1>
 2 | 
 3 | When contributing to this repository, please first discuss the change you wish to make via issue,
 4 | email, or any other method with the owners of this repository before making a change.
 5 | 
 6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
 7 | 
 8 | ## Pull Request Process
 9 | 
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a
11 |    build.
12 | 2. Update the README.md with details of changes to the interface, this includes new environment
13 |    variables, exposed ports, useful file locations and container parameters.
14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this
15 |    Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
17 |    do not have permission to do that, you may request the second reviewer to merge it for you.
18 | 
19 | ## Code of Conduct
20 | 
21 | ### Our Pledge
22 | 
23 | In the interest of fostering an open and welcoming environment, we as
24 | contributors and maintainers pledge to making participation in our project and
25 | our community a harassment-free experience for everyone, regardless of age, body
26 | size, disability, ethnicity, gender identity and expression, level of experience,
27 | nationality, personal appearance, race, religion, or sexual identity and
28 | orientation.
29 | 
30 | ### Our Standards
31 | 
32 | Examples of behavior that contributes to creating a positive environment
33 | include:
34 | 
35 | - Using welcoming and inclusive language
36 | - Being respectful of differing viewpoints and experiences
37 | - Gracefully accepting constructive criticism
38 | - Focusing on what is best for the community
39 | - Showing empathy towards other community members
40 | 
41 | Examples of unacceptable behavior by participants include:
42 | 
43 | - The use of sexualized language or imagery and unwelcome sexual attention or
44 |   advances
45 | - Trolling, insulting/derogatory comments, and personal or political attacks
46 | - Public or private harassment
47 | - Publishing others' private information, such as a physical or electronic
48 |   address, without explicit permission
49 | - Other conduct which could reasonably be considered inappropriate in a
50 |   professional setting
51 | 
52 | ### Our Responsibilities
53 | 
54 | Project maintainers are responsible for clarifying the standards of acceptable
55 | behavior and are expected to take appropriate and fair corrective action in
56 | response to any instances of unacceptable behavior.
57 | 
58 | Project maintainers have the right and responsibility to remove, edit, or
59 | reject comments, commits, code, wiki edits, issues, and other contributions
60 | that are not aligned to this Code of Conduct, or to ban temporarily or
61 | permanently any contributor for other behaviors that they deem inappropriate,
62 | threatening, offensive, or harmful.
63 | 
64 | ### Scope
65 | 
66 | This Code of Conduct applies both within project spaces and in public spaces
67 | when an individual is representing the project or its community. Examples of
68 | representing a project or community include using an official project e-mail
69 | address, posting via an official social media account, or acting as an appointed
70 | representative at an online or offline event. Representation of a project may be
71 | further defined and clarified by project maintainers.
72 | 
73 | ### Enforcement
74 | 
75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
76 | reported by contacting the project team at SudhanPlayz@gmail.com. All
77 | complaints will be reviewed and investigated and will result in a response that
78 | is deemed necessary and appropriate to the circumstances. The project team is
79 | obligated to maintain confidentiality with regard to the reporter of an incident.
80 | Further details of specific enforcement policies may be posted separately.
81 | 
82 | Project maintainers who do not follow or enforce the Code of Conduct in good
83 | faith may face temporary or permanent repercussions as determined by other
84 | members of the project's leadership.
85 | 
86 | ### Attribution
87 | 
88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
89 | available at [http://contributor-covenant.org/version/1/4][version]
90 | 
91 | [homepage]: http://contributor-covenant.org
92 | 
93 | [version]: http://contributor-covenant.org/version/1/4/
94 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:17.9.1-alpine
2 | WORKDIR /usr/src/app
3 | COPY . .
4 | RUN npm install
5 | RUN npm run deploy
6 | CMD [ "node", "index.js" ]
7 | 


--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # License for Discord-MusicBot
2 | 
3 | - The credits should not be changed.
4 | - The bot-code should be used for **private hosting** and **personal usage** only.
5 | - Using the code for public usage is **not allowed**.
6 | 
7 | > **Note:** if you are found to be violating any of the above stated rule you might be asked to takedown your bot, happy
8 | > listening!! Incase of any doubts in the license contact owner.


--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: npm start
2 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | <h1 align="center"><img src="./assets/logo.gif" width="30px"> Discord Music Bot <img src="./assets/logo.gif" width="30px"></h1>
 2 | 
 3 | ## ✨Latest Updates
 4 | 
 5 | v5.1 Is in development! Go check it out [HERE!](https://github.com/wtfnotavailable/Discord-MusicBot)
 6 | 
 7 | What do you gain from it? Let us explain:
 8 |  - Completely modular docker environment for easier development and deployment
 9 |  - A WORKING DASHBOARD!!!
10 |  - DB Integration for you to save your favorite songs in
11 |  - Integrated self hosted Lavalink
12 |  - Dedicated query channel
13 |  - More commands and functionalities
14 |  - And so much more to come!
15 | 
16 | ## 🚧 | Prerequisites
17 | 
18 | - [Node.js 16+](https://nodejs.org/en/download/)
19 | - [Lavalink Server](https://code.darrennathanael.com/how-to-lavalink)
20 | - You'll need to run `npm run deploy` or `yarn deploy`. to initialized the slash commands. _You can do this on your pc
21 |   locally_
22 | 
23 | > NOTE: Lavalink is needed for music functionality. You need to have a working Lavalink server to make the bot work.
24 | 
25 | ## 📝 | Important Note if you're Switching from v4 to v5
26 | 
27 | 1. Download and configure v5 in a seperate folder.
28 | 2. Kick your bot out of your server.
29 | 3. Reinvite the Bot with the right
30 |    scopes. [Example Invite URL (Change CLIENT_ID)](https://discord.com/oauth2/authorize?client_id=CLIENT_ID&permissions=277083450689&scope=bot%20applications.commands)
31 | 4. Run `npm run deploy` or `yarn deploy` to initialize the slash commands. _You can do this on your pc locally_
32 | 
33 | ## 📝 | Tutorial
34 | 
35 | ### 🐳 Docker
36 | You should configure the `config.js` file with the host `"lavalink"`, using the same `password` and `port` as specified in `docker/application.yml`.
37 | 
38 | Build and start bot and lavalink
39 | ```sh
40 | docker-compose up -d --build
41 | ```
42 | ### 💪🏻 Non-Docker
43 | > The `config.js` file should be configured first. Don't forget to add a lavalink host
44 | 
45 | Install all dependencies and deploy Slash Commands
46 | ```sh
47 | npm install
48 | npm run deploy
49 | ```
50 | Start the bot
51 | ```sh
52 | node index.js
53 | ```
54 | 
55 | ## 📝 | [Support Server](https://discord.gg/sbySMS7m3v)
56 | 
57 | If you have major coding issues with this bot, please join and ask for help.
58 | 
59 | ## 📸 | Screenshots
60 | 
61 | Soon
62 | 
63 | ## 🚀 | Deploy
64 | 
65 | [![Deploy to heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5)
66 | [![Open in Gitpod](https://camo.githubusercontent.com/76e60919474807718793857d8eb615e7a50b18b04050577e5a35c19421f260a3/68747470733a2f2f676974706f642e696f2f627574746f6e2f6f70656e2d696e2d676974706f642e737667)](https://gitpod.io/#https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5)
67 | 
68 | ## ✨ | Contributors
69 | 
70 | Contributions are always welcomed :D Make sure to follow [Contributing.md](/CONTRIBUTING.md)
71 | 
72 | <a href="https://github.com/SudhanPlayz/Discord-MusicBot/graphs/contributors">
73 |   <img src="https://contributors-img.web.app/image?repo=SudhanPlayz/Discord-MusicBot" />
74 | </a>
75 | 
76 | ## 🌟 | Made with
77 | 
78 | - [Discord.js](https://discord.js.org/)
79 | - [Lavalink](https://github.com/freyacodes/Lavalink) with erela.js
80 | - [Express](https://expressjs.com/)
81 | - [Next JS](https://nextjs.org/)
82 | - [Next UI](https://nextui.org)
83 | - [Material UI Icons](https://mui.com/material-ui/material-icons/)
84 | 


--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
  1 | const express = require("express");
  2 | const fs = require("fs");
  3 | const { EventEmitter } = require("events");
  4 | const { join } = require("path");
  5 | const session = require("express-session");
  6 | const DiscordStrategy = require("passport-discord").Strategy;
  7 | const passport = require("passport");
  8 | const getConfig = require("../util/getConfig");
  9 | const DiscordMusicBot = require("../lib/DiscordMusicBot");
 10 | const router = require("./router");
 11 | 
 12 | passport.serializeUser(function (user, done) {
 13 | 	done(null, user);
 14 | });
 15 | 
 16 | passport.deserializeUser(function (obj, done) {
 17 | 	done(null, obj);
 18 | });
 19 | 
 20 | class Server extends EventEmitter {
 21 | 	/**
 22 | 	 * Create server ;-;
 23 | 	 * @param {DiscordMusicBot} client
 24 | 	 */
 25 | 	constructor(client) {
 26 | 		super();
 27 | 		this.client = client;
 28 | 		getConfig().then(this.init.bind(this));
 29 | 	}
 30 | 
 31 | 	init(conf) {
 32 | 		this.config = conf;
 33 | 		this.app = express();
 34 | 		
 35 | 		this.app.use(express.static(join(__dirname, "..", "public")));
 36 | 		
 37 | 		// Static Routes for scripts
 38 | 		const dist = join(__dirname, "..", "dashboard", "out", "_next")
 39 | 		
 40 | 		this.app.use("/_next", express.static(dist));
 41 | 
 42 | 		// Session and Passport
 43 | 		this.app.use(session({
 44 | 			resave: false,
 45 | 			saveUninitialized: false,
 46 | 			secret: this.config.cookieSecret,
 47 | 			cookie: {
 48 | 				secure: this.config.website.startsWith("https://"),
 49 | 				sameSite: true,
 50 | 			},
 51 | 		}));
 52 | 
 53 | 		this.initPassport();
 54 | 
 55 | 		this.app.use(router);
 56 | 
 57 | 		//API
 58 | 		fs.readdir(join(__dirname, "routes"), (err, files) => {
 59 | 			if (err) {
 60 | 				return console.log(err);
 61 | 			}
 62 | 			files.forEach((file) => {
 63 | 				this.app.use(
 64 | 					"/api/" + file.split(".")[0],
 65 | 					require(join(__dirname, "routes") + "/" + file),
 66 | 				);
 67 | 			});
 68 | 		});
 69 | 
 70 | 		this.listen();
 71 | 	}
 72 | 	
 73 | 	initPassport() {
 74 | 		this.app.use(passport.initialize());
 75 | 
 76 | 		const strategy = new DiscordStrategy(
 77 | 			{
 78 | 				clientID: this.config.clientId,
 79 | 				clientSecret: this.config.clientSecret,
 80 | 				callbackURL: this.config.website + "/api/callback",
 81 | 				scope: this.config.scopes.filter(a => !a.startsWith("app")),
 82 | 				scopeSeparator: " ",
 83 | 			},
 84 | 			function (accessToken, refreshToken, profile, done) {
 85 | 				const data = {
 86 | 					accessToken,
 87 | 					refreshToken,
 88 | 					profile,
 89 | 				};
 90 | 
 91 | 				return done(null, data);
 92 | 			},
 93 | 		);
 94 | 		passport.use(strategy);
 95 | 
 96 | 		this.app.use(passport.session());
 97 | 	}
 98 | 
 99 | 	listen() {
100 | 		this.app.listen(this.config.port);
101 | 		console.log("[SERVER] Listening on port:", this.config.port);
102 | 	}
103 | }
104 | 
105 | module.exports = Server;
106 | 


--------------------------------------------------------------------------------
/api/middlewares/auth.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * @param {import("express").Request} req
 3 |  * @param {import("express").Response} res
 4 |  * @param {import("express").NextFunction} next
 5 |  * @returns {Promise<void>}
 6 |  */
 7 | 
 8 | const Auth = (req, res, next) => {
 9 | 	if (!req.user) {
10 | 		return res.redirect("/login");
11 | 	} else {
12 | 		next();
13 | 	}
14 | };
15 | 
16 | module.exports = Auth;
17 | 


--------------------------------------------------------------------------------
/api/router.js:
--------------------------------------------------------------------------------
 1 | "use strict";
 2 | 
 3 | const { Router } = require("express");
 4 | const passport = require("passport");
 5 | const { join } = require("path");
 6 | const Auth = require("./middlewares/auth");
 7 | 
 8 | const dist = join(__dirname, "..", "dashboard", "out");
 9 | 
10 | const router = Router();
11 | 
12 | router.get("/", (req, res) => {
13 | 	res.sendFile(join(dist, "index.html"));
14 | });
15 | 
16 | router.get("/login", (req, res) => {
17 | 	res.sendFile(join(dist, "login.html"));
18 | });
19 | 
20 | router.get("/api/login", passport.authenticate("discord"));
21 | 
22 | router.get("/logout", (req, res) => {
23 | 	res.sendFile(join(dist, "logout.html"));
24 | });
25 | 
26 | router.get("/api/logout", (req, res) => {
27 | 	req.session.destroy(() => {
28 | 		res.redirect("/");
29 | 	});
30 | });
31 | 
32 | router.get("/dashboard", Auth, (_req, res) => {
33 | 	res.sendFile(join(dist, "dashboard.html"));
34 | });
35 | 
36 | router.get("/servers", Auth, (_req, res) => {
37 | 	res.sendFile(join(dist, "servers.html"));
38 | });
39 | 
40 | router.get("/api/callback", passport.authenticate('discord', {
41 | 	failureRedirect: '/',
42 | }), (req, res ) => {
43 | 	req.session.save(() => {
44 | 		res.redirect("/");
45 | 	});
46 | });
47 | 
48 | module.exports = router;
49 | 


--------------------------------------------------------------------------------
/api/routes/dashboard.js:
--------------------------------------------------------------------------------
 1 | const { Router } = require("express");
 2 | const api = Router();
 3 | const { getClient } = require("../../");
 4 | const Auth = require("../middlewares/auth");
 5 | 
 6 | api.get("/", Auth, (req, res) => {
 7 | 	const client = getClient();
 8 | 	let data = {
 9 | 		commandsRan: client.commandsRan,
10 | 		users: client.users.cache.size,
11 | 		servers: client.guilds.cache.size,
12 | 		songsPlayed: client.songsPlayed,
13 | 	}
14 | 	res.json(data);
15 | })
16 | 
17 | module.exports = api
18 | 


--------------------------------------------------------------------------------
/api/routes/data.js:
--------------------------------------------------------------------------------
 1 | const { Router } = require("express");
 2 | const api = Router();
 3 | 
 4 | const package = require("../../package.json");
 5 | const { getClient } = require("../../");
 6 | 
 7 | api.get("/", (req, res) => {
 8 | 	const client = getClient();
 9 | 	let data = {
10 | 		name: client.user.username,
11 | 		version: package.version,
12 | 		commands: client.slashCommands.map(cmd => {
13 | 			return {
14 | 				name: cmd.name,
15 | 				description: cmd.description,
16 | 			};
17 | 		}),
18 | 		inviteURL: `https://discord.com/oauth2/authorize?client_id=${ client.config.clientId
19 | 		}&permissions=${ client.config.permissions
20 | 		}&scope=${ client.config.scopes.toString().replace(/,/g, "%20") }`,
21 | 		loggedIn: !!req.user,
22 | 	};
23 | 	res.json(data);
24 | });
25 | 
26 | module.exports = api;
27 | 


--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "Discord-MusicBot",
 3 |   "description": "Very simple discord music bot with the discord.js with Song Name playing. It can able to play music with the song name",
 4 |   "repository": "https://github.com/SudhanPlayz/Discord-MusicBot",
 5 |   "logo": "https://cdn.discordapp.com/avatars/750613142488481843/e6326038dbe2243ca551ba5b6ecd8bf2.png?size=1024",
 6 |   "keywords": [
 7 |     "node",
 8 |     "discord",
 9 |     "youtube",
10 |     "music",
11 |     "bot",
12 |     "lavalink",
13 |     "dashboard"
14 |   ],
15 |   "image": "heroku/nodejs",
16 |   "buildpacks": [
17 |     {
18 |       "url": "heroku/nodejs"
19 |     }
20 |   ],
21 |   "env": {
22 |     "token": {
23 |       "description": "The Discord Bot Token (https://discord.com/developers/applications)",
24 |       "required": "true"
25 |     },
26 |     "clientId": {
27 |       "description": "The Discord Bot ClientID",
28 |       "required": "true"
29 |     },
30 |     "clientSecret": {
31 |       "description": "The Discord Bot ClientSecret",
32 |       "required": "true"
33 |     },
34 |     "website": {
35 |       "description": "URL of your webserver (Example: https://domain.xyz). Change this if you want to use the web-dashboard.",
36 |       "value": "http://localhost"
37 |     }
38 |   }
39 | }
40 | 


--------------------------------------------------------------------------------
/assets/logo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SudhanPlayz/Discord-MusicBot/4253446886d63b441ef9233008c052e409229658/assets/logo.gif


--------------------------------------------------------------------------------
/commands/context/play.js:
--------------------------------------------------------------------------------
  1 | const { ContextMenuCommandBuilder } = require("@discordjs/builders");
  2 | const { MessageEmbed } = require("discord.js");
  3 | const escapeMarkdown = require("discord.js").Util.escapeMarkdown;
  4 | 
  5 | module.exports = {
  6 |   command: new ContextMenuCommandBuilder().setName("Play Song").setType(3),
  7 | 
  8 |   /**
  9 |    * This function will handle context menu interaction
 10 |    * @param {import("../lib/DiscordMusicBot")} client
 11 |    * @param {import("discord.js").GuildContextMenuInteraction} interaction
 12 |    */
 13 |   run: async (client, interaction, options) => {
 14 |     let channel = await client.getChannel(client, interaction);
 15 |     if (!channel) {
 16 |       return;
 17 |     }
 18 | 
 19 |     let node = await client.getLavalink(client);
 20 |     if (!node) {
 21 |       return interaction.reply({
 22 |         embeds: [client.ErrorEmbed("Lavalink node is not connected")],
 23 |       });
 24 |     }
 25 | 
 26 |     let player = client.createPlayer(interaction.channel, channel);
 27 | 
 28 |     if (player.state !== "CONNECTED") {
 29 |       player.connect();
 30 |     }
 31 | 
 32 |     if (channel.type == "GUILD_STAGE_VOICE") {
 33 |       setTimeout(() => {
 34 |         if (interaction.guild.me.voice.suppress == true) {
 35 |           try {
 36 |             interaction.guild.me.voice.setSuppressed(false);
 37 |           } catch (e) {
 38 |             interaction.guild.me.voice.setRequestToSpeak(true);
 39 |           }
 40 |         }
 41 |       }, 2000); // Need this because discord api is buggy asf, and without this the bot will not request to speak on a stage - Darren
 42 |     }
 43 | 
 44 |     const ret = await interaction.reply({
 45 |       embeds: [
 46 |         new MessageEmbed()
 47 |           .setColor(client.config.embedColor)
 48 |           .setDescription(":mag_right: **Searching...**"),
 49 |       ],
 50 |       fetchReply: true,
 51 |     });
 52 | 
 53 |     const query =
 54 |       interaction.channel.messages.cache.get(interaction.targetId).content ??
 55 |       (await interaction.channel.messages.fetch(interaction.targetId));
 56 |     let res = await player.search(query, interaction.user).catch((err) => {
 57 |       client.error(err);
 58 |       return {
 59 |         loadType: "LOAD_FAILED",
 60 |       };
 61 |     });
 62 | 
 63 |     if (res.loadType === "LOAD_FAILED") {
 64 |       if (!player.queue.current) {
 65 |         player.destroy();
 66 |       }
 67 |       await interaction
 68 |         .editReply({
 69 |           embeds: [
 70 |             new MessageEmbed()
 71 |               .setColor("RED")
 72 |               .setDescription("There was an error while searching"),
 73 |           ],
 74 |         })
 75 |         .catch(this.warn);
 76 |     }
 77 | 
 78 |     if (res.loadType === "NO_MATCHES") {
 79 |       if (!player.queue.current) {
 80 |         player.destroy();
 81 |       }
 82 |       await interaction
 83 |         .editReply({
 84 |           embeds: [
 85 |             new MessageEmbed()
 86 |               .setColor("RED")
 87 |               .setDescription("No results were found"),
 88 |           ],
 89 |         })
 90 |         .catch(this.warn);
 91 |     }
 92 | 
 93 |     if (res.loadType === "TRACK_LOADED" || res.loadType === "SEARCH_RESULT") {
 94 |       player.queue.add(res.tracks[0]);
 95 | 
 96 |       if (!player.playing && !player.paused && !player.queue.size) {
 97 |         player.play();
 98 |       }
 99 |       var title = escapeMarkdown(res.tracks[0].title);
100 |       var title = title.replace(/\]/g, "");
101 |       var title = title.replace(/\[/g, "");
102 |       let addQueueEmbed = new MessageEmbed()
103 |         .setColor(client.config.embedColor)
104 |         .setAuthor({ name: "Added to queue", iconURL: client.config.iconURL })
105 |         .setDescription(`[${title}](${res.tracks[0].uri})` || "No Title")
106 |         .setURL(res.tracks[0].uri)
107 |         .addFields(
108 |           {
109 |             name: "Added by",
110 |             value: `<@${interaction.user.id}>`,
111 |             inline: true,
112 |           },
113 |           {
114 |             name: "Duration",
115 |             value: res.tracks[0].isStream
116 |               ? `\`LIVE 🔴 \``
117 |               : `\`${client.ms(res.tracks[0].duration, {
118 |                   colonNotation: true,
119 |                   secondsDecimalDigits: 0,
120 |                 })}\``,
121 |             inline: true,
122 |           }
123 |         );
124 | 
125 |       try {
126 |         addQueueEmbed.setThumbnail(
127 |           res.tracks[0].displayThumbnail("maxresdefault")
128 |         );
129 |       } catch (err) {
130 |         addQueueEmbed.setThumbnail(res.tracks[0].thumbnail);
131 |       }
132 | 
133 |       if (player.queue.totalSize > 1) {
134 |         addQueueEmbed.addFields({
135 |           name: "Position in queue",
136 |           value: `${player.queue.size}`,
137 |           inline: true,
138 |         });
139 |       } else {
140 |         player.queue.previous = player.queue.current;
141 |       }
142 | 
143 |       await interaction.editReply({ embeds: [addQueueEmbed] }).catch(this.warn);
144 |     }
145 | 
146 |     if (res.loadType === "PLAYLIST_LOADED") {
147 |       player.queue.add(res.tracks);
148 | 
149 |       if (
150 |         !player.playing &&
151 |         !player.paused &&
152 |         player.queue.totalSize === res.tracks.length
153 |       ) {
154 |         player.play();
155 |       }
156 | 
157 |       let playlistEmbed = new MessageEmbed()
158 |         .setColor(client.config.embedColor)
159 |         .setAuthor({
160 |           name: "Playlist added to queue",
161 |           iconURL: client.config.iconURL,
162 |         })
163 |         .setThumbnail(res.tracks[0].thumbnail)
164 |         .setDescription(`[${res.playlist.name}](${query})`)
165 |         .addFields(
166 |           {
167 |             name: "Enqueued",
168 |             value: `\`${res.tracks.length}\` songs`,
169 |             inline: true,
170 |           },
171 |           {
172 |             name: "Playlist duration",
173 |             value: `\`${client.ms(res.playlist.duration, {
174 |               colonNotation: true,
175 |               secondsDecimalDigits: 0,
176 |             })}\``,
177 |             inline: true,
178 |           }
179 |         );
180 | 
181 |       await interaction.editReply({ embeds: [playlistEmbed] }).catch(this.warn);
182 |     }
183 | 
184 |     if (ret) setTimeout(() => ret.delete().catch(this.warn), 20000);
185 |     return ret;
186 |   },
187 | };
188 | 


--------------------------------------------------------------------------------
/commands/slash/247.js:
--------------------------------------------------------------------------------
 1 | const colors = require("colors");
 2 | const { MessageEmbed } = require("discord.js");
 3 | const SlashCommand = require("../../lib/SlashCommand");
 4 | 
 5 | const command = new SlashCommand()
 6 | 	.setName("247")
 7 | 	.setDescription("Prevents the bot from ever disconnecting from a VC (toggle)")
 8 | 	.setRun(async (client, interaction, options) => {
 9 | 		let channel = await client.getChannel(client, interaction);
10 | 		if (!channel) {
11 | 			return;
12 | 		}
13 | 		
14 | 		let player;
15 | 		if (client.manager) {
16 | 			player = client.manager.players.get(interaction.guild.id);
17 | 		} else {
18 | 			return interaction.reply({
19 | 				embeds: [
20 | 					new MessageEmbed()
21 | 						.setColor("RED")
22 | 						.setDescription("Lavalink node is not connected"),
23 | 				],
24 | 			});
25 | 		}
26 | 		
27 | 		if (!player) {
28 | 			return interaction.reply({
29 | 				embeds: [
30 | 					new MessageEmbed()
31 | 						.setColor("RED")
32 | 						.setDescription("There's nothing to play 24/7."),
33 | 				],
34 | 				ephemeral: true,
35 | 			});
36 | 		}
37 | 		
38 | 		let twentyFourSevenEmbed = new MessageEmbed().setColor(
39 | 			client.config.embedColor,
40 | 		);
41 | 		const twentyFourSeven = player.get("twentyFourSeven");
42 | 		
43 | 		if (!twentyFourSeven || twentyFourSeven === false) {
44 | 			player.set("twentyFourSeven", true);
45 | 		} else {
46 | 			player.set("twentyFourSeven", false);
47 | 		}
48 | 		twentyFourSevenEmbed
49 | 		  .setDescription(`**24/7 mode is** \`${!twentyFourSeven ? "ON" : "OFF"}\``)
50 | 		  .setFooter({
51 | 		    text: `The bot will ${!twentyFourSeven ? "now" : "no longer"} stay connected to the voice channel 24/7.`
52 |       });
53 | 		client.warn(
54 | 			`Player: ${ player.options.guild } | [${ colors.blue(
55 | 				"24/7",
56 | 			) }] has been [${ colors.blue(
57 | 				!twentyFourSeven? "ENABLED" : "DISABLED",
58 | 			) }] in ${
59 | 				client.guilds.cache.get(player.options.guild)
60 | 					? client.guilds.cache.get(player.options.guild).name
61 | 					: "a guild"
62 | 			}`,
63 | 		);
64 | 		
65 | 		if (!player.playing && player.queue.totalSize === 0 && twentyFourSeven) {
66 | 			player.destroy();
67 | 		}
68 | 		
69 | 		return interaction.reply({ embeds: [twentyFourSevenEmbed] });
70 | 	});
71 | 
72 | module.exports = command;
73 | // check above message, it is a little bit confusing. and erros are not handled. probably should be fixed.
74 | // ok use catch ez kom  follow meh ;_;
75 | // the above message meaning error, if it cant find it or take too long the bot crashed
76 | // play commanddddd, if timeout or takes 1000 years to find song it crashed
77 | // OKIE, leave the comment here for idk
78 | // Comment very useful, 247 good :+1:
79 | // twentyFourSeven = best;
80 | 


--------------------------------------------------------------------------------
/commands/slash/autoleave.js:
--------------------------------------------------------------------------------
 1 | const colors = require("colors");
 2 | const { MessageEmbed } = require("discord.js");
 3 | const SlashCommand = require("../../lib/SlashCommand");
 4 | 
 5 | const command = new SlashCommand()
 6 |   .setName("autoleave")
 7 |   .setDescription("Automatically leaves when everyone leaves the voice channel (toggle)")
 8 |   .setRun(async (client, interaction) => {
 9 |     let channel = await client.getChannel(client, interaction);
10 |     if (!channel) return;
11 | 
12 |     let player;
13 |     if (client.manager)
14 |       player = client.manager.players.get(interaction.guild.id);
15 |     else
16 |       return interaction.reply({
17 |         embeds: [
18 |           new MessageEmbed()
19 |             .setColor("RED")
20 |             .setDescription("Lavalink node is not connected"),
21 |         ],
22 |       });
23 | 
24 |     if (!player) {
25 |       return interaction.reply({
26 |         embeds: [
27 |           new MessageEmbed()
28 |             .setColor("RED")
29 |             .setDescription("There's nothing playing in the queue"),
30 |         ],
31 |         ephemeral: true,
32 |       });
33 |     }
34 | 
35 |     let autoLeaveEmbed = new MessageEmbed().setColor(client.config.embedColor);
36 |     const autoLeave = player.get("autoLeave");
37 |     player.set("requester", interaction.guild.me);
38 | 
39 |     if (!autoLeave || autoLeave === false) {
40 |       player.set("autoLeave", true);
41 |     } else {
42 |       player.set("autoLeave", false);
43 |     }
44 |     autoLeaveEmbed
45 | 			.setDescription(`**Auto Leave is** \`${!autoLeave ? "ON" : "OFF"}\``)
46 | 			.setFooter({
47 | 			  text: `The player will ${!autoLeave ? "now automatically" : "not automatically"} leave when the voice channel is empty.`
48 | 			});
49 |     client.warn(
50 |       `Player: ${player.options.guild} | [${colors.blue(
51 |         "autoLeave"
52 |       )}] has been [${colors.blue(!autoLeave ? "ENABLED" : "DISABLED")}] in ${
53 |         client.guilds.cache.get(player.options.guild)
54 |           ? client.guilds.cache.get(player.options.guild).name
55 |           : "a guild"
56 |       }`
57 |     );
58 | 
59 |     return interaction.reply({ embeds: [autoLeaveEmbed] });
60 |   });
61 | 
62 | module.exports = command;


--------------------------------------------------------------------------------
/commands/slash/autopause.js:
--------------------------------------------------------------------------------
 1 | const colors = require("colors");
 2 | const { MessageEmbed } = require("discord.js");
 3 | const SlashCommand = require("../../lib/SlashCommand");
 4 | 
 5 | const command = new SlashCommand()
 6 |   .setName("autopause")
 7 |   .setDescription("Automatically pause when everyone leaves the voice channel (toggle)")
 8 |   .setRun(async (client, interaction) => {
 9 |     let channel = await client.getChannel(client, interaction);
10 |     if (!channel) return;
11 | 
12 |     let player;
13 |     if (client.manager)
14 |       player = client.manager.players.get(interaction.guild.id);
15 |     else
16 |       return interaction.reply({
17 |         embeds: [
18 |           new MessageEmbed()
19 |             .setColor("RED")
20 |             .setDescription("Lavalink node is not connected"),
21 |         ],
22 |       });
23 | 
24 |     if (!player) {
25 |       return interaction.reply({
26 |         embeds: [
27 |           new MessageEmbed()
28 |             .setColor("RED")
29 |             .setDescription("There's nothing playing in the queue"),
30 |         ],
31 |         ephemeral: true,
32 |       });
33 |     }
34 | 
35 |     let autoPauseEmbed = new MessageEmbed().setColor(client.config.embedColor);
36 |     const autoPause = player.get("autoPause");
37 |     player.set("requester", interaction.guild.members.me);
38 | 
39 |     if (!autoPause || autoPause === false) {
40 |       player.set("autoPause", true);
41 |     } else {
42 |       player.set("autoPause", false);
43 |     }
44 |     autoPauseEmbed
45 | 			.setDescription(`**Auto Pause is** \`${!autoPause ? "ON" : "OFF"}\``)
46 | 			.setFooter({
47 | 			  text: `The player will ${!autoPause ? "now be automatically" : "no longer be"} paused when everyone leaves the voice channel.`
48 | 			});
49 |     client.warn(
50 |       `Player: ${player.options.guild} | [${colors.blue(
51 |         "AUTOPAUSE"
52 |       )}] has been [${colors.blue(!autoPause ? "ENABLED" : "DISABLED")}] in ${
53 |         client.guilds.cache.get(player.options.guild)
54 |           ? client.guilds.cache.get(player.options.guild).name
55 |           : "a guild"
56 |       }`
57 |     );
58 | 
59 |     return interaction.reply({ embeds: [autoPauseEmbed] });
60 |   });
61 | 
62 | module.exports = command;
63 | 


--------------------------------------------------------------------------------
/commands/slash/autoqueue.js:
--------------------------------------------------------------------------------
 1 | const colors = require("colors");
 2 | const { MessageEmbed } = require("discord.js");
 3 | const SlashCommand = require("../../lib/SlashCommand");
 4 | 
 5 | const command = new SlashCommand()
 6 | 	.setName("autoqueue")
 7 | 	.setDescription("Automatically add songs to the queue (toggle)")
 8 | 	.setRun(async (client, interaction) => {
 9 | 		let channel = await client.getChannel(client, interaction);
10 | 		if (!channel) {
11 | 			return;
12 | 		}
13 | 		
14 | 		let player;
15 | 		if (client.manager) {
16 | 			player = client.manager.players.get(interaction.guild.id);
17 | 		} else {
18 | 			return interaction.reply({
19 | 				embeds: [
20 | 					new MessageEmbed()
21 | 						.setColor("RED")
22 | 						.setDescription("Lavalink node is not connected"),
23 | 				],
24 | 			});
25 | 		}
26 | 		
27 | 		if (!player) {
28 | 			return interaction.reply({
29 | 				embeds: [
30 | 					new MessageEmbed()
31 | 						.setColor("RED")
32 | 						.setDescription("There's nothing playing in the queue"),
33 | 				],
34 | 				ephemeral: true,
35 | 			});
36 | 		}
37 | 		
38 | 		let autoQueueEmbed = new MessageEmbed().setColor(client.config.embedColor);
39 | 		const autoQueue = player.get("autoQueue");
40 | 		player.set("requester", interaction.guild.members.me);
41 | 		
42 | 		if (!autoQueue || autoQueue === false) {
43 | 			player.set("autoQueue", true);
44 | 		} else {
45 | 			player.set("autoQueue", false);
46 | 		}
47 | 		autoQueueEmbed
48 | 		  .setDescription(`**Auto Queue is** \`${!autoQueue ? "ON" : "OFF"}\``)
49 | 		  .setFooter({
50 | 		    text: `Related music will ${!autoQueue ? "now be automatically" : "no longer be"} added to the queue.`
51 |       });
52 | 		client.warn(
53 | 			`Player: ${ player.options.guild } | [${ colors.blue(
54 | 				"AUTOQUEUE",
55 | 			) }] has been [${ colors.blue(!autoQueue? "ENABLED" : "DISABLED") }] in ${
56 | 				client.guilds.cache.get(player.options.guild)
57 | 					? client.guilds.cache.get(player.options.guild).name
58 | 					: "a guild"
59 | 			}`,
60 | 		);
61 | 		
62 | 		return interaction.reply({ embeds: [autoQueueEmbed] });
63 | 	});
64 | 
65 | module.exports = command;
66 | 


--------------------------------------------------------------------------------
/commands/slash/clean.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | 
 3 | const command = new SlashCommand()
 4 | 	.setName("clean")
 5 | 	.setDescription("Cleans the last 100 bot messages from channel.")
 6 | 	.addIntegerOption((option) =>
 7 | 		option
 8 | 			.setName("number")
 9 | 			.setDescription("Number of messages to delete.")
10 | 			.setMinValue(2).setMaxValue(100)
11 | 			.setRequired(false),
12 | 	)
13 | 	.setRun(async (client, interaction, options) => {
14 | 		
15 | 		await interaction.deferReply();
16 | 		let number = interaction.options.getInteger("number");
17 | 		number = number && number < 100? ++number : 100;
18 | 		
19 | 		
20 | 		interaction.channel.messages.fetch({
21 | 			limit: number,
22 | 		}).then((messages) => {
23 | 			const botMessages = [];
24 | 			messages.filter(m => m.author.id === client.user.id).forEach(msg => botMessages.push(msg))
25 | 			
26 | 			botMessages.shift();
27 | 			interaction.channel.bulkDelete(botMessages, true)
28 | 				.then(async deletedMessages => {
29 | 					//Filtering out messages that did not get deleted.
30 | 					messages = messages.filter(msg => {
31 | 						!deletedMessages.some(deletedMsg => deletedMsg == msg);
32 | 					});
33 | 					if (messages.size > 0) {
34 | 						client.log(`Deleting [${ messages.size }] messages older than 14 days.`)
35 | 						for (const msg of messages) {
36 | 							await msg.delete();
37 | 						}
38 | 					}
39 | 					
40 | 					await interaction.editReply({ embeds: [client.Embed(`:white_check_mark: | Deleted ${ botMessages.length } bot messages`)] });
41 | 					setTimeout(() => {
42 | 						interaction.deleteReply();
43 | 					}, 5000);
44 | 				})
45 | 			
46 | 		});
47 | 	})
48 | 
49 | module.exports = command;
50 | 


--------------------------------------------------------------------------------
/commands/slash/clear.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("clear")
 6 | 	.setDescription("Clear all tracks from queue")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("Nothing is playing right now."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		if (!player.queue || !player.queue.length || player.queue.length === 0) {
38 | 			let cembed = new MessageEmbed()
39 | 				.setColor(client.config.embedColor)
40 | 				.setDescription("❌ | **Invalid, Not enough track to be cleared.**");
41 | 			
42 | 			return interaction.reply({ embeds: [cembed], ephemeral: true });
43 | 		}
44 | 		
45 | 		player.queue.clear();
46 | 		
47 | 		let clearEmbed = new MessageEmbed()
48 | 			.setColor(client.config.embedColor)
49 | 			.setDescription(`✅ | **Cleared the queue!**`);
50 | 		
51 | 		return interaction.reply({ embeds: [clearEmbed] });
52 | 	});
53 | 
54 | module.exports = command;


--------------------------------------------------------------------------------
/commands/slash/filters.js:
--------------------------------------------------------------------------------
  1 | const { MessageEmbed } = require("discord.js");
  2 | const SlashCommand = require("../../lib/SlashCommand");
  3 | 
  4 | const command = new SlashCommand()
  5 | 	.setName("filters")
  6 | 	.setDescription("add or remove filters")
  7 | 	.addStringOption((option) =>
  8 | 		option
  9 | 			.setName("preset")
 10 | 			.setDescription("the preset to add")
 11 | 			.setRequired(true)
 12 | 			.addChoices(
 13 | 				{ name: "Nightcore", value: "nightcore" },
 14 | 				{ name: "BassBoost", value: "bassboost" },
 15 | 				{ name: "Vaporwave", value: "vaporwave" },
 16 | 				{ name: "Pop", value: "pop" },
 17 | 				{ name: "Soft", value: "soft" },
 18 | 				{ name: "Treblebass", value: "treblebass" },
 19 | 				{ name: "Eight Dimension", value: "eightD" },
 20 | 				{ name: "Karaoke", value: "karaoke" },
 21 | 				{ name: "Vibrato", value: "vibrato" },
 22 | 				{ name: "Tremolo", value: "tremolo" },
 23 | 				{ name: "Reset", value: "off" },
 24 | 			),
 25 | 	)
 26 | 	
 27 | 	.setRun(async (client, interaction, options) => {
 28 | 		const args = interaction.options.getString("preset");
 29 | 		
 30 | 		let channel = await client.getChannel(client, interaction);
 31 | 		if (!channel) {
 32 | 			return;
 33 | 		}
 34 | 		
 35 | 		let player;
 36 | 		if (client.manager) {
 37 | 			player = client.manager.players.get(interaction.guild.id);
 38 | 		} else {
 39 | 			return interaction.reply({
 40 | 				embeds: [
 41 | 					new MessageEmbed()
 42 | 						.setColor("RED")
 43 | 						.setDescription("Lavalink node is not connected"),
 44 | 				],
 45 | 			});
 46 | 		}
 47 | 		
 48 | 		if (!player) {
 49 | 			return interaction.reply({
 50 | 				embeds: [
 51 | 					new MessageEmbed()
 52 | 						.setColor("RED")
 53 | 						.setDescription("There's no music playing."),
 54 | 				],
 55 | 				ephemeral: true,
 56 | 			});
 57 | 		}
 58 | 		
 59 | 		// create a new embed
 60 | 		let filtersEmbed = new MessageEmbed().setColor(client.config.embedColor);
 61 | 		
 62 | 		if (args == "nightcore") {
 63 | 			filtersEmbed.setDescription("✅ | Nightcore filter is now active!");
 64 | 			player.nightcore = true;
 65 | 		} else if (args == "bassboost") {
 66 | 			filtersEmbed.setDescription("✅ | BassBoost filter is now on!");
 67 | 			player.bassboost = true;
 68 | 		} else if (args == "vaporwave") {
 69 | 			filtersEmbed.setDescription("✅ | Vaporwave filter is now on!");
 70 | 			player.vaporwave = true;
 71 | 		} else if (args == "pop") {
 72 | 			filtersEmbed.setDescription("✅ | Pop filter is now on!");
 73 | 			player.pop = true;
 74 | 		} else if (args == "soft") {
 75 | 			filtersEmbed.setDescription("✅ | Soft filter is now on!");
 76 | 			player.soft = true;
 77 | 		} else if (args == "treblebass") {
 78 | 			filtersEmbed.setDescription("✅ | Treblebass filter is now on!");
 79 | 			player.treblebass = true;
 80 | 		} else if (args == "eightD") {
 81 | 			filtersEmbed.setDescription("✅ | Eight Dimension filter is now on!");
 82 | 			player.eightD = true;
 83 | 		} else if (args == "karaoke") {
 84 | 			filtersEmbed.setDescription("✅ | Karaoke filter is now on!");
 85 | 			player.karaoke = true;
 86 | 		} else if (args == "vibrato") {
 87 | 			filtersEmbed.setDescription("✅ | Vibrato filter is now on!");
 88 | 			player.vibrato = true;
 89 | 		} else if (args == "tremolo") {
 90 | 			filtersEmbed.setDescription("✅ | Tremolo filter is now on!");
 91 | 			player.tremolo = true;
 92 | 		} else if (args == "off") {
 93 | 			filtersEmbed.setDescription("✅ | EQ has been cleared!");
 94 | 			player.reset();
 95 | 		} else {
 96 | 			filtersEmbed.setDescription("❌ | Invalid filter!");
 97 | 		}
 98 | 		
 99 | 		return interaction.reply({ embeds: [filtersEmbed] });
100 | 	});
101 | 
102 | module.exports = command;
103 | 


--------------------------------------------------------------------------------
/commands/slash/guildleave.js:
--------------------------------------------------------------------------------
 1 | const { MessageEmbed, message } = require("discord.js");
 2 | const SlashCommand = require("../../lib/SlashCommand");
 3 | const fs = require("fs");
 4 | const path = require("path");
 5 | const { forEach } = require("lodash");
 6 | 
 7 | const command = new SlashCommand()
 8 | 	.setName("guildleave")
 9 | 	.setDescription("leaves a guild")
10 |     .addStringOption((option) =>
11 |     option
12 |       .setName("id")
13 |       .setDescription("Enter the guild id to leave (type `list` for guild ids)")
14 |       .setRequired(true)
15 |   )
16 |   .setRun(async (client, interaction, options) => {
17 | 		if (interaction.user.id === client.config.adminId) {
18 | 		    try{
19 | 			const id = interaction.options.getString('id');
20 | 
21 | 			if (id.toLowerCase() === 'list'){
22 | 			    client.guilds.cache.forEach((guild) => {
23 | 				console.log(`${guild.name} | ${guild.id}`);
24 | 			    });
25 | 			    const guild = client.guilds.cache.map(guild => ` ${guild.name} | ${guild.id}`);
26 | 			    try{
27 | 				return interaction.reply({content:`Guilds:\n\`${guild}\``, ephemeral: true});
28 | 			    }catch{
29 | 				return interaction.reply({content:`check console for list of guilds`, ephemeral: true});
30 | 			    }
31 | 			}
32 | 
33 | 			const guild = client.guilds.cache.get(id);
34 | 
35 | 			if(!guild){
36 | 			    return interaction.reply({content: `\`${id}\` is not a valid guild id`, ephemeral:true});
37 | 			}
38 | 
39 | 			await guild.leave().then(c => console.log(`left guild ${id}`)).catch((err) => {console.log(err)});
40 | 			return interaction.reply({content:`left guild \`${id}\``, ephemeral: true});
41 | 		    }catch (error){
42 | 			console.log(`there was an error trying to leave guild ${id}`, error);
43 | 		    }
44 | 		}else {
45 | 			return interaction.reply({
46 | 				embeds: [
47 | 					new MessageEmbed()
48 | 						.setColor(client.config.embedColor)
49 | 						.setDescription("You are not authorized to use this command!"),
50 | 				],
51 | 				ephemeral: true,
52 | 			});
53 | 		}
54 | 	});
55 | 
56 | module.exports = command;
57 | 


--------------------------------------------------------------------------------
/commands/slash/help.js:
--------------------------------------------------------------------------------
  1 | const SlashCommand = require("../../lib/SlashCommand");
  2 | const {
  3 |   Client,
  4 |   Interaction,
  5 |   MessageActionRow,
  6 |   MessageButton,
  7 |   MessageEmbed,
  8 | } = require("discord.js");
  9 | const LoadCommands = require("../../util/loadCommands");
 10 | const { filter } = require("lodash");
 11 | 
 12 | const command = new SlashCommand()
 13 |   .setName("help")
 14 |   .setDescription("Shows this list")
 15 |   .setRun(async (client, interaction) => {
 16 |     await interaction.deferReply().catch((_) => {});
 17 |     // map the commands name and description to the embed
 18 |     const commands = await LoadCommands().then((cmds) => {
 19 |       return [].concat(cmds.slash) /*.concat(cmds.context)*/;
 20 |     });
 21 |     // from commands remove the ones that have "null" in the description
 22 |     const filteredCommands = commands.filter(
 23 |       (cmd) => cmd.description != "null"
 24 |     );
 25 |     //console.log(filteredCommands);
 26 |     const totalCmds = filteredCommands.length;
 27 |     let maxPages = Math.ceil(totalCmds / client.config.helpCmdPerPage);
 28 | 
 29 |     // if git exists, then get commit hash
 30 |     let gitHash = "";
 31 |     try {
 32 |       gitHash = require("child_process")
 33 |         .execSync("git rev-parse --short HEAD")
 34 |         .toString()
 35 |         .trim();
 36 |     } catch (e) {
 37 |       // do nothing
 38 |       gitHash = "unknown";
 39 |     }
 40 | 
 41 |     // default Page No.
 42 |     let pageNo = 0;
 43 | 
 44 |     const helpEmbed = new MessageEmbed()
 45 |       .setColor(client.config.embedColor)
 46 |       .setAuthor({
 47 |         name: `Commands of ${client.user.username}`,
 48 |         iconURL: client.config.iconURL,
 49 |       })
 50 |       .setTimestamp()
 51 |       .setFooter({ text: `Page ${pageNo + 1} / ${maxPages}` });
 52 | 
 53 |     // initial temporary array
 54 |     var tempArray = filteredCommands.slice(
 55 |       pageNo * client.config.helpCmdPerPage,
 56 |       pageNo * client.config.helpCmdPerPage + client.config.helpCmdPerPage
 57 |     );
 58 | 
 59 |     tempArray.forEach((cmd) => {
 60 |       helpEmbed.addFields({ name: cmd.name, value: cmd.description });
 61 |     });
 62 |     helpEmbed.addFields({
 63 |       name: "Credits",
 64 |       value:
 65 |         `Discord Music Bot Version: v${
 66 |           require("../../package.json").version
 67 |         }; Build: ${gitHash}` +
 68 |         "\n" +
 69 |         `[✨ Support Server](${client.config.supportServer}) | [Issues](${client.config.Issues}) | [Source](https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) | [Invite Me](https://discord.com/oauth2/authorize?client_id=${client.config.clientId}&permissions=${client.config.permissions}&scope=bot%20applications.commands)`,
 70 |     });
 71 | 
 72 |     // Construction of the buttons for the embed
 73 |     const getButtons = (pageNo) => {
 74 |       return new MessageActionRow().addComponents(
 75 |         new MessageButton()
 76 |           .setCustomId("help_cmd_but_2_app")
 77 |           .setEmoji("◀️")
 78 |           .setStyle("PRIMARY")
 79 |           .setDisabled(pageNo == 0),
 80 |         new MessageButton()
 81 |           .setCustomId("help_cmd_but_1_app")
 82 |           .setEmoji("▶️")
 83 |           .setStyle("PRIMARY")
 84 |           .setDisabled(pageNo == maxPages - 1)
 85 |       );
 86 |     };
 87 | 
 88 |     const tempMsg = await interaction.editReply({
 89 |       embeds: [helpEmbed],
 90 |       components: [getButtons(pageNo)],
 91 |       fetchReply: true,
 92 |     });
 93 |     const collector = tempMsg.createMessageComponentCollector({
 94 |       time: 600000,
 95 |       componentType: "BUTTON",
 96 |     });
 97 | 
 98 |     collector.on("collect", async (iter) => {
 99 |       if (iter.customId === "help_cmd_but_1_app") {
100 |         pageNo++;
101 |       } else if (iter.customId === "help_cmd_but_2_app") {
102 |         pageNo--;
103 |       }
104 | 
105 |       helpEmbed.fields = [];
106 | 
107 |       var tempArray = filteredCommands.slice(
108 |         pageNo * client.config.helpCmdPerPage,
109 |         pageNo * client.config.helpCmdPerPage + client.config.helpCmdPerPage
110 |       );
111 | 
112 |       tempArray.forEach((cmd) => {
113 |         //console.log(cmd);
114 |         helpEmbed
115 |           .addFields({ name: cmd.name, value: cmd.description })
116 |           .setFooter({ text: `Page ${pageNo + 1} / ${maxPages}` });
117 |       });
118 |       helpEmbed.addFields({
119 |         name: "Credits",
120 |         value:
121 |           `Discord Music Bot Version: v${
122 |             require("../../package.json").version
123 |           }; Build: ${gitHash}` +
124 |           "\n" +
125 |           `[✨ Support Server](${client.config.supportServer}) | [Issues](${client.config.Issues}) | [Source](https://github.com/SudhanPlayz/Discord-MusicBot/tree/v5) | [Invite Me](https://discord.com/oauth2/authorize?client_id=${client.config.clientId}&permissions=${client.config.permissions}&scope=bot%20applications.commands)`,
126 |       });
127 |       await iter.update({
128 |         embeds: [helpEmbed],
129 |         components: [getButtons(pageNo)],
130 |         fetchReply: true,
131 |       });
132 |     });
133 |   });
134 | 
135 | module.exports = command;
136 | 


--------------------------------------------------------------------------------
/commands/slash/invite.js:
--------------------------------------------------------------------------------
 1 | const { MessageActionRow, MessageButton, MessageEmbed } = require("discord.js");
 2 | const SlashCommand = require("../../lib/SlashCommand");
 3 | 
 4 | const command = new SlashCommand()
 5 |   .setName("invite")
 6 |   .setDescription("Invite me to your server")
 7 |   .setRun(async (client, interaction, options) => {
 8 |     return interaction.reply({
 9 |       embeds: [
10 |         new MessageEmbed()
11 |           .setColor(client.config.embedColor)
12 |           .setTitle(`Invite me to your server!`),
13 |       ],
14 |       components: [
15 |         new MessageActionRow().addComponents(
16 |           new MessageButton()
17 |             .setLabel("Invite me")
18 |             .setStyle("LINK")
19 |             .setURL(
20 |               `https://discord.com/oauth2/authorize?client_id=${
21 |                 client.config.clientId
22 |               }&permissions=${
23 |                 client.config.permissions
24 |               }&scope=${client.config.inviteScopes
25 |                 .toString()
26 |                 .replace(/,/g, "%20")}`
27 |             )
28 |         ),
29 |       ],
30 |     });
31 |   });
32 | module.exports = command;
33 | 


--------------------------------------------------------------------------------
/commands/slash/loop.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("loop")
 6 | 	.setDescription("Loops the current song")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("Nothing is playing right now."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		if (player.setTrackRepeat(!player.trackRepeat)) {
38 | 			;
39 | 		}
40 | 		const trackRepeat = player.trackRepeat? "enabled" : "disabled";
41 | 		
42 | 		interaction.reply({
43 | 			embeds: [
44 | 				new MessageEmbed()
45 | 					.setColor(client.config.embedColor)
46 | 					.setDescription(`👍 | **Loop has been \`${ trackRepeat }\`**`),
47 | 			],
48 | 		});
49 | 	});
50 | 
51 | module.exports = command;
52 | 


--------------------------------------------------------------------------------
/commands/slash/loopq.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("loopq")
 6 | 	.setDescription("Loop the current song queue")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("There is no music playing."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		if (player.setQueueRepeat(!player.queueRepeat)) {
38 | 			;
39 | 		}
40 | 		const queueRepeat = player.queueRepeat? "enabled" : "disabled";
41 | 		
42 | 		interaction.reply({
43 | 			embeds: [
44 | 				new MessageEmbed()
45 | 					.setColor(client.config.embedColor)
46 | 					.setDescription(
47 | 						`:thumbsup: | **Loop queue is now \`${ queueRepeat }\`**`,
48 | 					),
49 | 			],
50 | 		});
51 | 	});
52 | 
53 | module.exports = command;
54 | 


--------------------------------------------------------------------------------
/commands/slash/lyrics.js:
--------------------------------------------------------------------------------
  1 | const SlashCommand = require("../../lib/SlashCommand");
  2 | const {
  3 | 	MessageActionRow,
  4 | 	MessageSelectMenu,
  5 | 	MessageButton,
  6 | 	MessageEmbed
  7 | } = require("discord.js");
  8 | const { Rlyrics } = require("rlyrics");
  9 | const lyricsApi = new Rlyrics();
 10 | 
 11 | const command = new SlashCommand()
 12 | 	.setName("lyrics")
 13 | 	.setDescription("Get the lyrics of a song")
 14 | 	.addStringOption((option) =>
 15 | 		option
 16 | 			.setName("song")
 17 | 			.setDescription("The song to get lyrics for")
 18 | 			.setRequired(false),
 19 | 	)
 20 | 	.setRun(async (client, interaction, options) => {
 21 | 		await interaction.reply({
 22 | 			embeds: [
 23 | 				new MessageEmbed()
 24 | 					.setColor(client.config.embedColor)
 25 | 					.setDescription("🔎 | **Searching...**"),
 26 | 			],
 27 | 		});
 28 | 
 29 | 		let player;
 30 | 		if (client.manager) {
 31 | 			player = client.manager.players.get(interaction.guild.id);
 32 | 		} else {
 33 | 			return interaction.editReply({
 34 | 				embeds: [
 35 | 					new MessageEmbed()
 36 | 						.setColor("RED")
 37 | 						.setDescription("Lavalink node is not connected"),
 38 | 				],
 39 | 			});
 40 | 		}
 41 | 
 42 | 		const args = interaction.options.getString("song");
 43 | 		if (!args && !player) {
 44 | 			return interaction.editReply({
 45 | 				embeds: [
 46 | 					new MessageEmbed()
 47 | 						.setColor("RED")
 48 | 						.setDescription("There's nothing playing"),
 49 | 				],
 50 | 			});
 51 | 		}
 52 | 
 53 | 		let currentTitle = ``;
 54 | 		const phrasesToRemove = [
 55 | 			"Full Video", "Full Audio", "Official Music Video", "Lyrics", "Lyrical Video",
 56 | 			"Feat.", "Ft.", "Official", "Audio", "Video", "HD", "4K", "Remix", "Lyric Video", "Lyrics Video", "8K", 
 57 | 			"High Quality", "Animation Video", "\\(Official Video\\. .*\\)", "\\(Music Video\\. .*\\)", "\\[NCS Release\\]",
 58 | 			"Extended", "DJ Edit", "with Lyrics", "Lyrics", "Karaoke",
 59 | 			"Instrumental", "Live", "Acoustic", "Cover", "\\(feat\\. .*\\)"
 60 | 		];
 61 | 		if (!args) {
 62 | 			currentTitle = player.queue.current.title;
 63 | 			currentTitle = currentTitle
 64 | 				.replace(new RegExp(phrasesToRemove.join('|'), 'gi'), '')
 65 | 				.replace(/\s*([\[\(].*?[\]\)])?\s*(\|.*)?\s*(\*.*)?$/, '');
 66 | 		}
 67 | 		let query = args ? args : currentTitle;
 68 | 		let lyricsResults = [];
 69 | 
 70 | 		lyricsApi.search(query).then(async (lyricsData) => {
 71 | 			if (lyricsData.length !== 0) {
 72 | 				for (let i = 0; i < client.config.lyricsMaxResults; i++) {
 73 | 					if (lyricsData[i]) {
 74 | 						lyricsResults.push({
 75 | 							label: `${lyricsData[i].title}`,
 76 | 							description: `${lyricsData[i].artist}`,
 77 | 							value: i.toString()
 78 | 						});
 79 | 					} else { break }
 80 | 				}
 81 | 
 82 | 				const menu = new MessageActionRow().addComponents(
 83 | 					new MessageSelectMenu()
 84 | 						.setCustomId("choose-lyrics")
 85 | 						.setPlaceholder("Choose a song")
 86 | 						.addOptions(lyricsResults),
 87 | 				);
 88 | 
 89 | 				let selectedLyrics = await interaction.editReply({
 90 | 					embeds: [
 91 | 						new MessageEmbed()
 92 | 							.setColor(client.config.embedColor)
 93 | 							.setDescription(
 94 | 								`Here are some of the results I found for \`${query}\`. Please choose a song to display lyrics within \`30 seconds\`.`
 95 | 							),
 96 | 					], components: [menu],
 97 | 				});
 98 | 
 99 | 				const filter = (button) => button.user.id === interaction.user.id;
100 | 
101 | 				const collector = selectedLyrics.createMessageComponentCollector({
102 | 					filter,
103 | 					time: 30000,
104 | 				});
105 | 
106 | 				collector.on("collect", async (interaction) => {
107 | 					if (interaction.isSelectMenu()) {
108 | 						await interaction.deferUpdate();
109 | 						const url = lyricsData[parseInt(interaction.values[0])].url;
110 | 
111 | 						lyricsApi.find(url).then((lyrics) => {
112 | 							let lyricsText = lyrics.lyrics;
113 | 
114 | 							const button = new MessageActionRow()
115 | 								.addComponents(
116 | 									new MessageButton()
117 | 										.setCustomId('tipsbutton')
118 | 										.setLabel('Tips')
119 | 										.setEmoji(`📌`)
120 | 										.setStyle('SECONDARY'),
121 | 									new MessageButton()
122 | 										.setLabel('Source')
123 | 										.setURL(url)
124 | 										.setStyle('LINK'),
125 | 								);
126 | 
127 | 							const musixmatch_icon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Musixmatch_logo_icon_only.svg/480px-Musixmatch_logo_icon_only.svg.png';
128 | 							let lyricsEmbed = new MessageEmbed()
129 | 								.setColor(client.config.embedColor)
130 | 								.setTitle(`${lyrics.name}`)
131 | 								.setURL(url)
132 | 								.setThumbnail(lyrics.icon)
133 | 								.setFooter({
134 | 									text: 'Lyrics provided by MusixMatch.',
135 | 									iconURL: musixmatch_icon
136 | 								})
137 | 								.setDescription(lyricsText);
138 | 
139 | 							if (lyricsText.length === 0) {
140 | 								lyricsEmbed
141 | 									.setDescription(`**Unfortunately we're not authorized to show these lyrics.**`)
142 | 									.setFooter({
143 | 										text: 'Lyrics is restricted by MusixMatch.',
144 | 										iconURL: musixmatch_icon
145 | 									})
146 | 							}
147 | 
148 | 							if (lyricsText.length > 4096) {
149 | 								lyricsText = lyricsText.substring(0, 4050) + "\n\n[...]";
150 | 								lyricsEmbed
151 | 									.setDescription(lyricsText + `\nTruncated, the lyrics were too long.`)
152 | 							}
153 | 
154 | 							return interaction.editReply({
155 | 								embeds: [lyricsEmbed],
156 | 								components: [button],
157 | 							});
158 | 
159 | 						})
160 | 					}
161 | 				});
162 | 
163 | 				collector.on("end", async (i) => {
164 | 					if (i.size == 0) {
165 | 						selectedLyrics.edit({
166 | 							content: null,
167 | 							embeds: [
168 | 								new MessageEmbed()
169 | 									.setDescription(
170 | 										`No song is selected. You took too long to select a track.`
171 | 									)
172 | 									.setColor(client.config.embedColor),
173 | 							], components: [],
174 | 						});
175 | 					}
176 | 				});
177 | 
178 | 			} else {
179 | 				const button = new MessageActionRow()
180 | 					.addComponents(
181 | 						new MessageButton()
182 | 							.setEmoji(`📌`)
183 | 							.setCustomId('tipsbutton')
184 | 							.setLabel('Tips')
185 | 							.setStyle('SECONDARY'),
186 | 					);
187 | 				return interaction.editReply({
188 | 					embeds: [
189 | 						new MessageEmbed()
190 | 							.setColor("RED")
191 | 							.setDescription(
192 | 								`No results found for \`${query}\`!\nMake sure you typed in your search correctly.`,
193 | 							),
194 | 					], components: [button],
195 | 				});
196 | 			}
197 | 		}).catch((err) => {
198 | 			console.error(err);
199 | 			return interaction.editReply({
200 | 				embeds: [
201 | 					new MessageEmbed()
202 | 						.setColor("RED")
203 | 						.setDescription(
204 | 							`An unknown error has occured, please check your console.`,
205 | 						),
206 | 				],
207 | 			});
208 | 		});
209 | 
210 | 		const collector = interaction.channel.createMessageComponentCollector({
211 | 			time: 1000 * 3600
212 | 		});
213 | 
214 | 		collector.on('collect', async interaction => {
215 | 			if (interaction.customId === 'tipsbutton') {
216 | 				await interaction.deferUpdate();
217 | 				await interaction.followUp({
218 | 					embeds: [
219 | 						new MessageEmbed()
220 | 							.setTitle(`Lyrics Tips`)
221 | 							.setColor(client.config.embedColor)
222 | 							.setDescription(
223 | 								`Here is some tips to get your song lyrics correctly \n\n\
224 |                                 1. Try to add the artist's name in front of the song name.\n\
225 |                                 2. Try to search the lyrics manually by providing the song query using your keyboard.\n\
226 |                                 3. Avoid searching lyrics in languages other than English.`,
227 | 							),
228 | 					], ephemeral: true, components: []
229 | 				});
230 | 			};
231 | 		});
232 | 	});
233 | 
234 | module.exports = command;
235 | 


--------------------------------------------------------------------------------
/commands/slash/move.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("move")
 6 | 	.setDescription("Moves track to a different position")
 7 | 	.addIntegerOption((option) =>
 8 | 		option
 9 | 			.setName("track")
10 | 			.setDescription("The track number to move")
11 | 			.setRequired(true),
12 | 	)
13 | 	.addIntegerOption((option) =>
14 | 		option
15 | 			.setName("position")
16 | 			.setDescription("The position to move the track to")
17 | 			.setRequired(true),
18 | 	)
19 | 	
20 | 	.setRun(async (client, interaction) => {
21 | 		const track = interaction.options.getInteger("track");
22 | 		const position = interaction.options.getInteger("position");
23 | 		
24 | 		let channel = await client.getChannel(client, interaction);
25 | 		if (!channel) {
26 | 			return;
27 | 		}
28 | 		
29 | 		let player;
30 | 		if (client.manager) {
31 | 			player = client.manager.players.get(interaction.guild.id);
32 | 		} else {
33 | 			return interaction.reply({
34 | 				embeds: [
35 | 					new MessageEmbed()
36 | 						.setColor("RED")
37 | 						.setDescription("Lavalink node is not connected"),
38 | 				],
39 | 			});
40 | 		}
41 | 		
42 | 		if (!player) {
43 | 			return interaction.reply({
44 | 				embeds: [
45 | 					new MessageEmbed()
46 | 						.setColor("RED")
47 | 						.setDescription("There's nothing playing."),
48 | 				],
49 | 				ephemeral: true,
50 | 			});
51 | 		}
52 | 		
53 | 		let trackNum = Number(track) - 1;
54 | 		if (trackNum < 0 || trackNum > player.queue.length - 1) {
55 | 			return interaction.reply(":x: | **Invalid track number**");
56 | 		}
57 | 		
58 | 		let dest = Number(position) - 1;
59 | 		if (dest < 0 || dest > player.queue.length - 1) {
60 | 			return interaction.reply(":x: | **Invalid position number**");
61 | 		}
62 | 		
63 | 		const thing = player.queue[trackNum];
64 | 		player.queue.splice(trackNum, 1);
65 | 		player.queue.splice(dest, 0, thing);
66 | 		return interaction.reply({
67 | 			embeds: [
68 | 				new MessageEmbed()
69 | 					.setColor(client.config.embedColor)
70 | 					.setDescription(":white_check_mark: | **Moved track**"),
71 | 			],
72 | 		});
73 | 	});
74 | 
75 | module.exports = command;
76 | 


--------------------------------------------------------------------------------
/commands/slash/nowplaying.js:
--------------------------------------------------------------------------------
 1 | const { MessageEmbed } = require("discord.js");
 2 | const escapeMarkdown = require('discord.js').Util.escapeMarkdown;
 3 | const SlashCommand = require("../../lib/SlashCommand");
 4 | const prettyMilliseconds = require("pretty-ms");
 5 | 
 6 | const command = new SlashCommand()
 7 | 	.setName("nowplaying")
 8 | 	.setDescription("Shows the song currently playing in the voice channel.")
 9 | 	.setRun(async (client, interaction, options) => {
10 | 		let channel = await client.getChannel(client, interaction);
11 | 		if (!channel) {
12 | 			return;
13 | 		}
14 | 		
15 | 		let player;
16 | 		if (client.manager) {
17 | 			player = client.manager.players.get(interaction.guild.id);
18 | 		} else {
19 | 			return interaction.reply({
20 | 				embeds: [
21 | 					new MessageEmbed()
22 | 						.setColor("RED")
23 | 						.setDescription("Lavalink node is not connected"),
24 | 				],
25 | 			});
26 | 		}
27 | 		
28 | 		if (!player) {
29 | 			return interaction.reply({
30 | 				embeds: [
31 | 					new MessageEmbed()
32 | 						.setColor("RED")
33 | 						.setDescription("The bot isn't in a channel."),
34 | 				],
35 | 				ephemeral: true,
36 | 			});
37 | 		}
38 | 		
39 | 		if (!player.playing) {
40 | 			return interaction.reply({
41 | 				embeds: [
42 | 					new MessageEmbed()
43 | 						.setColor("RED")
44 | 						.setDescription("There's nothing playing."),
45 | 				],
46 | 				ephemeral: true,
47 | 			});
48 | 		}
49 | 		
50 | 		const song = player.queue.current;
51 |         var title = escapeMarkdown(song.title)
52 |         var title = title.replace(/\]/g,"")
53 |         var title = title.replace(/\[/g,"")
54 | 		const embed = new MessageEmbed()
55 | 			.setColor(client.config.embedColor)
56 | 			.setAuthor({ name: "Now Playing", iconURL: client.config.iconURL })
57 | 			// show who requested the song via setField, also show the duration of the song
58 | 			.setFields([
59 | 				{
60 | 					name: "Requested by",
61 | 					value: `<@${ song.requester.id }>`,
62 | 					inline: true,
63 | 				},
64 | 				// show duration, if live show live
65 | 				{
66 | 					name: "Duration",
67 | 					value: song.isStream
68 | 						? `\`LIVE\``
69 | 						: `\`${ prettyMilliseconds(player.position, {
70 | 							secondsDecimalDigits: 0,
71 | 						}) } / ${ prettyMilliseconds(song.duration, {
72 | 							secondsDecimalDigits: 0,
73 | 						}) }\``,
74 | 					inline: true,
75 | 				},
76 | 			])
77 | 			// show the thumbnail of the song using displayThumbnail("maxresdefault")
78 | 			.setThumbnail(song.displayThumbnail("maxresdefault"))
79 | 			// show the title of the song and link to it
80 | 			.setDescription(`[${ title }](${ song.uri })`);
81 | 		return interaction.reply({ embeds: [embed] });
82 | 	});
83 | module.exports = command;
84 | 


--------------------------------------------------------------------------------
/commands/slash/pause.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("pause")
 6 | 	.setDescription("Pauses the current playing track")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("Nothing is playing."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		if (player.paused) {
38 | 			return interaction.reply({
39 | 				embeds: [
40 | 					new MessageEmbed()
41 | 						.setColor("RED")
42 | 						.setDescription("Current playing track is already paused!"),
43 | 				],
44 | 				ephemeral: true,
45 | 			});
46 | 		}
47 | 		
48 | 		player.pause(true);
49 | 		return interaction.reply({
50 | 			embeds: [
51 | 				new MessageEmbed()
52 | 					.setColor(client.config.embedColor)
53 | 					.setDescription(`⏸ | **Paused!**`),
54 | 			],
55 | 		});
56 | 	});
57 | 
58 | module.exports = command;
59 | 


--------------------------------------------------------------------------------
/commands/slash/ping.js:
--------------------------------------------------------------------------------
 1 | const { MessageEmbed } = require("discord.js");
 2 | const SlashCommand = require("../../lib/SlashCommand");
 3 | 
 4 | const command = new SlashCommand()
 5 |   .setName("ping")
 6 |   .setDescription("View the bot's latency")
 7 |   .setRun(async (client, interaction, options) => {
 8 |     let msg = await interaction.channel.send({
 9 |       embeds: [
10 |         new MessageEmbed()
11 |           .setDescription("🏓 | Fetching ping...")
12 |           .setColor("#6F8FAF"),
13 |       ],
14 |     });
15 | 
16 |     let zap = "⚡";
17 |     let green = "🟢";
18 |     let red = "🔴";
19 |     let yellow = "🟡";
20 | 
21 |     var botState = zap;
22 |     var apiState = zap;
23 | 
24 |     let apiPing = client.ws.ping;
25 |     let botPing = Math.floor(msg.createdAt - interaction.createdAt);
26 | 
27 |     if (apiPing >= 40 && apiPing < 200) {
28 |       apiState = green;
29 |     } else if (apiPing >= 200 && apiPing < 400) {
30 |       apiState = yellow;
31 |     } else if (apiPing >= 400) {
32 |       apiState = red;
33 |     }
34 | 
35 |     if (botPing >= 40 && botPing < 200) {
36 |       botState = green;
37 |     } else if (botPing >= 200 && botPing < 400) {
38 |       botState = yellow;
39 |     } else if (botPing >= 400) {
40 |       botState = red;
41 |     }
42 | 
43 |     msg.delete();
44 |     interaction.reply({
45 |       embeds: [
46 |         new MessageEmbed()
47 |           .setTitle("🏓 | Pong!")
48 |           .addFields(
49 |             {
50 |               name: "API Latency",
51 |               value: `\`\`\`yml\n${apiState} | ${apiPing}ms\`\`\``,
52 |               inline: true,
53 |             },
54 |             {
55 |               name: "Bot Latency",
56 |               value: `\`\`\`yml\n${botState} | ${botPing}ms\`\`\``,
57 |               inline: true,
58 |             }
59 |           )
60 |           .setColor(client.config.embedColor)
61 |           .setFooter({
62 |             text: `Requested by ${interaction.user.tag}`,
63 |             iconURL: interaction.user.avatarURL(),
64 |           }),
65 |       ],
66 |     });
67 |   });
68 | 
69 | module.exports = command;
70 | 


--------------------------------------------------------------------------------
/commands/slash/play.js:
--------------------------------------------------------------------------------
  1 | const SlashCommand = require("../../lib/SlashCommand");
  2 | const { MessageEmbed } = require("discord.js");
  3 | const escapeMarkdown = require("discord.js").Util.escapeMarkdown;
  4 | 
  5 | const command = new SlashCommand()
  6 |   .setName("play")
  7 |   .setDescription(
  8 |     "Searches and plays the requested song \nSupports: \nYoutube, Spotify, Deezer, Apple Music"
  9 |   )
 10 |   .addStringOption((option) =>
 11 |     option
 12 |       .setName("query")
 13 |       .setDescription("What am I looking for?")
 14 |       .setAutocomplete(true)
 15 |       .setRequired(true)
 16 |   )
 17 |   .setRun(async (client, interaction, options) => {
 18 |     let channel = await client.getChannel(client, interaction);
 19 |     if (!channel) {
 20 |       return;
 21 |     }
 22 | 
 23 |     let node = await client.getLavalink(client);
 24 |     if (!node) {
 25 |       return interaction.reply({
 26 |         embeds: [client.ErrorEmbed("Lavalink node is not connected")],
 27 |       });
 28 |     }
 29 | 
 30 |     let player = client.createPlayer(interaction.channel, channel);
 31 | 
 32 |     if (player.state !== "CONNECTED") {
 33 |       player.connect();
 34 |     }
 35 | 
 36 |     if (channel.type == "GUILD_STAGE_VOICE") {
 37 |       setTimeout(() => {
 38 |         if (interaction.guild.members.me.voice.suppress == true) {
 39 |           try {
 40 |             interaction.guild.members.me.voice.setSuppressed(false);
 41 |           } catch (e) {
 42 |             interaction.guild.members.me.voice.setRequestToSpeak(true);
 43 |           }
 44 |         }
 45 |       }, 2000); // Need this because discord api is buggy asf, and without this the bot will not request to speak on a stage - Darren
 46 |     }
 47 | 
 48 |     const ret = await interaction.reply({
 49 |       embeds: [
 50 |         new MessageEmbed()
 51 |           .setColor(client.config.embedColor)
 52 |           .setDescription(":mag_right: **Searching...**"),
 53 |       ],
 54 |       fetchReply: true,
 55 |     });
 56 | 
 57 |     let query = options.getString("query", true);
 58 |     let res = await player.search(query, interaction.user).catch((err) => {
 59 |       client.error(err);
 60 |       return {
 61 |         loadType: "LOAD_FAILED",
 62 |       };
 63 |     });
 64 | 
 65 |     if (res.loadType === "LOAD_FAILED") {
 66 |       if (!player.queue.current) {
 67 |         player.destroy();
 68 |       }
 69 |       await interaction
 70 |         .editReply({
 71 |           embeds: [
 72 |             new MessageEmbed()
 73 |               .setColor("RED")
 74 |               .setDescription("There was an error while searching"),
 75 |           ],
 76 |         })
 77 |         .catch(this.warn);
 78 |     }
 79 | 
 80 |     if (res.loadType === "NO_MATCHES") {
 81 |       if (!player.queue.current) {
 82 |         player.destroy();
 83 |       }
 84 |       await interaction
 85 |         .editReply({
 86 |           embeds: [
 87 |             new MessageEmbed()
 88 |               .setColor("RED")
 89 |               .setDescription("No results were found"),
 90 |           ],
 91 |         })
 92 |         .catch(this.warn);
 93 |     }
 94 | 
 95 |     if (res.loadType === "TRACK_LOADED" || res.loadType === "SEARCH_RESULT") {
 96 |       player.queue.add(res.tracks[0]);
 97 | 
 98 |       if (!player.playing && !player.paused && !player.queue.size) {
 99 |         player.play();
100 |       }
101 |       var title = escapeMarkdown(res.tracks[0].title);
102 |       var title = title.replace(/\]/g, "");
103 |       var title = title.replace(/\[/g, "");
104 |       let addQueueEmbed = new MessageEmbed()
105 |         .setColor(client.config.embedColor)
106 |         .setAuthor({ name: "Added to queue", iconURL: client.config.iconURL })
107 |         .setDescription(`[${title}](${res.tracks[0].uri})` || "No Title")
108 |         .setURL(res.tracks[0].uri)
109 |         .addFields(
110 |           {
111 |             name: "Added by",
112 |             value: `<@${interaction.user.id}>`,
113 |             inline: true,
114 |           },
115 |           {
116 |             name: "Duration",
117 |             value: res.tracks[0].isStream
118 |               ? `\`LIVE 🔴 \``
119 |               : `\`${client.ms(res.tracks[0].duration, {
120 |                   colonNotation: true,
121 |                   secondsDecimalDigits: 0,
122 |                 })}\``,
123 |             inline: true,
124 |           }
125 |         );
126 | 
127 |       try {
128 |         addQueueEmbed.setThumbnail(
129 |           res.tracks[0].displayThumbnail("maxresdefault")
130 |         );
131 |       } catch (err) {
132 |         addQueueEmbed.setThumbnail(res.tracks[0].thumbnail);
133 |       }
134 | 
135 |       if (player.queue.totalSize > 1) {
136 |         addQueueEmbed.addFields({
137 |           name: "Position in queue",
138 |           value: `${player.queue.size}`,
139 |           inline: true,
140 |         });
141 |       } else {
142 |         player.queue.previous = player.queue.current;
143 |       }
144 | 
145 |       await interaction.editReply({ embeds: [addQueueEmbed] }).catch(this.warn);
146 |     }
147 | 
148 |     if (res.loadType === "PLAYLIST_LOADED") {
149 |       player.queue.add(res.tracks);
150 | 
151 |       if (
152 |         !player.playing &&
153 |         !player.paused &&
154 |         player.queue.totalSize === res.tracks.length
155 |       ) {
156 |         player.play();
157 |       }
158 | 
159 |       let playlistEmbed = new MessageEmbed()
160 |         .setColor(client.config.embedColor)
161 |         .setAuthor({
162 |           name: "Playlist added to queue",
163 |           iconURL: client.config.iconURL,
164 |         })
165 |         .setThumbnail(res.tracks[0].thumbnail)
166 |         .setDescription(`[${res.playlist.name}](${query})`)
167 |         .addFields(
168 |           {
169 |             name: "Enqueued",
170 |             value: `\`${res.tracks.length}\` songs`,
171 |             inline: true,
172 |           },
173 |           {
174 |             name: "Playlist duration",
175 |             value: `\`${client.ms(res.playlist.duration, {
176 |               colonNotation: true,
177 |               secondsDecimalDigits: 0,
178 |             })}\``,
179 |             inline: true,
180 |           }
181 |         );
182 | 
183 |       await interaction.editReply({ embeds: [playlistEmbed] }).catch(this.warn);
184 |     }
185 | 
186 |     if (ret) setTimeout(() => ret.delete().catch(this.warn), 20000);
187 |     return ret;
188 |   });
189 | 
190 | module.exports = command;
191 | 


--------------------------------------------------------------------------------
/commands/slash/previous.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | .setName("previous")
 6 | .setDescription("Go back to the previous song.")
 7 | .setRun(async (client, interaction) => {
 8 | 	let channel = await client.getChannel(client, interaction);
 9 | 	if (!channel) {
10 | 		return;
11 | 	}
12 | 
13 | 	let player;
14 | 	if (client.manager) {
15 | 		player = client.manager.players.get(interaction.guild.id);
16 | 	} else {
17 | 		return interaction.reply({
18 | 			embeds: [
19 | 				new MessageEmbed()
20 | 					.setColor("RED")
21 | 					.setDescription("Lavalink node is not connected"),
22 | 			],
23 | 		});
24 | 	}
25 | 
26 | 	if (!player) {
27 | 		return interaction.reply({
28 | 			embeds: [
29 | 				new MessageEmbed()
30 | 					.setColor("RED")
31 | 					.setDescription("There are no previous songs for this session."),
32 | 			],
33 | 			ephemeral: true,
34 | 		});
35 | 	}
36 | 
37 | 	const previousSong = player.queue.previous;
38 | 	const currentSong = player.queue.current;
39 | 	const nextSong = player.queue[0]
40 | 
41 | 	if (!previousSong
42 | 		|| previousSong === currentSong
43 | 		|| previousSong === nextSong) {
44 | 		return interaction.reply({
45 | 			embeds: [
46 | 				new MessageEmbed()
47 | 					.setColor("RED")
48 | 					.setDescription("There is no previous song in the queue."),
49 | 			],
50 | 		})}
51 | 
52 | 	if (previousSong !== currentSong && previousSong !== nextSong) {
53 | 		player.queue.splice(0, 0, currentSong)
54 | 		player.play(previousSong);
55 | 	}
56 | 	interaction.reply({
57 | 		embeds: [
58 | 			new MessageEmbed()
59 | 				.setColor(client.config.embedColor)
60 | 				.setDescription(
61 | 					`⏮ | Previous song: **${ previousSong.title }**`,
62 | 				),
63 | 		],
64 | 	});
65 | });
66 | 
67 | module.exports = command;
68 | 


--------------------------------------------------------------------------------
/commands/slash/reload.js:
--------------------------------------------------------------------------------
 1 | const { MessageEmbed, message } = require("discord.js");
 2 | const SlashCommand = require("../../lib/SlashCommand");
 3 | const fs = require("fs");
 4 | const path = require("path");
 5 | 
 6 | const command = new SlashCommand()
 7 | 	.setName("reload")
 8 | 	.setDescription("Reload all commands")
 9 | 	.setRun(async (client, interaction, options) => {
10 | 		if (interaction.user.id === client.config.adminId) {
11 | 			try {
12 | 				let ContextCommandsDirectory = path.join(__dirname, "..", "context");
13 | 				fs.readdir(ContextCommandsDirectory, (err, files) => {
14 | 					files.forEach((file) => {
15 | 						delete require.cache[
16 | 							require.resolve(ContextCommandsDirectory + "/" + file)
17 | 							];
18 | 						let cmd = require(ContextCommandsDirectory + "/" + file);
19 | 						if (!cmd.command || !cmd.run) {
20 | 							return this.warn(
21 | 								"❌ Unable to load Command: " +
22 | 								file.split(".")[0] +
23 | 								", File doesn't have either command/run",
24 | 							);
25 | 						}
26 | 						client.contextCommands.set(file.split(".")[0].toLowerCase(), cmd);
27 | 					});
28 | 				});
29 | 				
30 | 				let SlashCommandsDirectory = path.join(__dirname, "..", "slash");
31 | 				fs.readdir(SlashCommandsDirectory, (err, files) => {
32 | 					files.forEach((file) => {
33 | 						delete require.cache[
34 | 							require.resolve(SlashCommandsDirectory + "/" + file)
35 | 							];
36 | 						let cmd = require(SlashCommandsDirectory + "/" + file);
37 | 						
38 | 						if (!cmd || !cmd.run) {
39 | 							return client.warn(
40 | 								"❌ Unable to load Command: " +
41 | 								file.split(".")[0] +
42 | 								", File doesn't have a valid command with run function",
43 | 							);
44 | 						}
45 | 						client.slashCommands.set(file.split(".")[0].toLowerCase(), cmd);
46 | 					});
47 | 				});
48 | 				
49 | 				const totalCmds =
50 | 					client.slashCommands.size + client.contextCommands.size;
51 | 				client.log(`Reloaded ${ totalCmds } commands!`);
52 | 				return interaction.reply({
53 | 					embeds: [
54 | 						new MessageEmbed()
55 | 							.setColor(client.config.embedColor)
56 | 							.setDescription(`Sucessfully Reloaded \`${ totalCmds }\` Commands!`)
57 | 							.setFooter({
58 | 								text: `${ client.user.username } was reloaded by ${ interaction.user.username }`,
59 | 							})
60 | 							.setTimestamp(),
61 | 					],
62 | 					ephemeral: true,
63 | 				});
64 | 			} catch (err) {
65 | 				console.log(err);
66 | 				return interaction.reply({
67 | 					embeds: [
68 | 						new MessageEmbed()
69 | 							.setColor(client.config.embedColor)
70 | 							.setDescription(
71 | 								"An error has occured. For more details please check console.",
72 | 							),
73 | 					],
74 | 					ephemeral: true,
75 | 				});
76 | 			}
77 | 		} else {
78 | 			return interaction.reply({
79 | 				embeds: [
80 | 					new MessageEmbed()
81 | 						.setColor(client.config.embedColor)
82 | 						.setDescription("You are not authorized to use this command!"),
83 | 				],
84 | 				ephemeral: true,
85 | 			});
86 | 		}
87 | 	});
88 | 
89 | module.exports = command;
90 | 


--------------------------------------------------------------------------------
/commands/slash/remove.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("remove")
 6 | 	.setDescription("Remove track you don't want from queue")
 7 | 	.addNumberOption((option) =>
 8 | 		option
 9 | 			.setName("number")
10 | 			.setDescription("Enter track number.")
11 | 			.setRequired(true),
12 | 	)
13 | 	
14 | 	.setRun(async (client, interaction) => {
15 | 		const args = interaction.options.getNumber("number");
16 | 		
17 | 		let channel = await client.getChannel(client, interaction);
18 | 		if (!channel) {
19 | 			return;
20 | 		}
21 | 		
22 | 		let player;
23 | 		if (client.manager) {
24 | 			player = client.manager.players.get(interaction.guild.id);
25 | 		} else {
26 | 			return interaction.reply({
27 | 				embeds: [
28 | 					new MessageEmbed()
29 | 						.setColor("RED")
30 | 						.setDescription("Lavalink node is not connected"),
31 | 				],
32 | 			});
33 | 		}
34 | 		
35 | 		if (!player) {
36 | 			return interaction.reply({
37 | 				embeds: [
38 | 					new MessageEmbed()
39 | 						.setColor("RED")
40 | 						.setDescription("There are no songs to remove."),
41 | 				],
42 | 				ephemeral: true,
43 | 			});
44 | 		}
45 | 		
46 | 		await interaction.deferReply();
47 | 		
48 | 		const position = Number(args) - 1;
49 | 		if (position > player.queue.size) {
50 | 			let thing = new MessageEmbed()
51 | 				.setColor(client.config.embedColor)
52 | 				.setDescription(
53 | 					`Current queue has only **${ player.queue.size }** track`,
54 | 				);
55 | 			return interaction.editReply({ embeds: [thing] });
56 | 		}
57 | 		
58 | 		const song = player.queue[position];
59 | 		player.queue.remove(position);
60 | 		
61 | 		const number = position + 1;
62 | 		let removeEmbed = new MessageEmbed()
63 | 			.setColor(client.config.embedColor)
64 | 			.setDescription(`Removed track number **${ number }** from queue`);
65 | 		return interaction.editReply({ embeds: [removeEmbed] });
66 | 	});
67 | 
68 | module.exports = command;
69 | 


--------------------------------------------------------------------------------
/commands/slash/replay.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("replay")
 6 | 	.setDescription("Replay current playing track")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("I'm not playing anything."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		await interaction.deferReply();
38 | 		
39 | 		player.seek(0);
40 | 		
41 | 		let song = player.queue.current;
42 | 		return interaction.editReply({
43 | 			embeds: [
44 | 				new MessageEmbed()
45 | 					.setColor(client.config.embedColor)
46 | 					.setDescription(`Replay [${ song.title }](${ song.uri })`),
47 | 			],
48 | 		});
49 | 	});
50 | 
51 | module.exports = command;
52 | 


--------------------------------------------------------------------------------
/commands/slash/resume.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("resume")
 6 | 	.setDescription("Resume current track")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("There is no song playing right now."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		if (!player.paused) {
38 | 			return interaction.reply({
39 | 				embeds: [
40 | 					new MessageEmbed()
41 | 						.setColor("RED")
42 | 						.setDescription("Current track is already resumed"),
43 | 				],
44 | 				ephemeral: true,
45 | 			});
46 | 		}
47 | 		player.pause(false);
48 | 		return interaction.reply({
49 | 			embeds: [
50 | 				new MessageEmbed()
51 | 					.setColor(client.config.embedColor)
52 | 					.setDescription(`⏯ **Resumed!**`),
53 | 			],
54 | 		});
55 | 	});
56 | 
57 | module.exports = command;
58 | 


--------------------------------------------------------------------------------
/commands/slash/save.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | const prettyMilliseconds = require("pretty-ms");
 4 | 
 5 | const command = new SlashCommand()
 6 | 	.setName("save")
 7 | 	.setDescription("Saves current song to your DM's")
 8 | 	.setRun(async (client, interaction) => {
 9 | 		let channel = await client.getChannel(client, interaction);
10 | 		if (!channel) {
11 | 			return;
12 | 		}
13 | 		
14 | 		let player;
15 | 		if (client.manager) {
16 | 			player = client.manager.players.get(interaction.guild.id);
17 | 		} else {
18 | 			return interaction.reply({
19 | 				embeds: [
20 | 					new MessageEmbed()
21 | 						.setColor("RED")
22 | 						.setDescription("Lavalink node is not connected"),
23 | 				],
24 | 			});
25 | 		}
26 | 		
27 | 		if (!player) {
28 | 			return interaction.reply({
29 | 				embeds: [
30 | 					new MessageEmbed()
31 | 						.setColor("RED")
32 | 						.setDescription("There is no music playing right now."),
33 | 				],
34 | 				ephemeral: true,
35 | 			});
36 | 		}
37 | 		
38 | 		const sendtoDmEmbed = new MessageEmbed()
39 | 			.setColor(client.config.embedColor)
40 | 			.setAuthor({
41 | 				name: "Saved track",
42 | 				iconURL: `${ interaction.user.displayAvatarURL({ dynamic: true }) }`,
43 | 			})
44 | 			.setDescription(
45 | 				`**Saved [${ player.queue.current.title }](${ player.queue.current.uri }) to your DM**`,
46 | 			)
47 | 			.addFields(
48 | 				{
49 | 					name: "Track Duration",
50 | 					value: `\`${ prettyMilliseconds(player.queue.current.duration, {
51 | 						colonNotation: true,
52 | 					}) }\``,
53 | 					inline: true,
54 | 				},
55 | 				{
56 | 					name: "Track Author",
57 | 					value: `\`${ player.queue.current.author }\``,
58 | 					inline: true,
59 | 				},
60 | 				{
61 | 					name: "Requested Guild",
62 | 					value: `\`${ interaction.guild }\``,
63 | 					inline: true,
64 | 				},
65 | 			);
66 | 		
67 | 		interaction.user.send({ embeds: [sendtoDmEmbed] });
68 | 		
69 | 		return interaction.reply({
70 | 			embeds: [
71 | 				new MessageEmbed()
72 | 					.setColor(client.config.embedColor)
73 | 					.setDescription(
74 | 						"Please check your **DMs**. If you didn't receive any message from me please make sure your **DMs** are open",
75 | 					),
76 | 			],
77 | 			ephemeral: true,
78 | 		});
79 | 	});
80 | 
81 | module.exports = command;
82 | 


--------------------------------------------------------------------------------
/commands/slash/search.js:
--------------------------------------------------------------------------------
  1 | const SlashCommand = require("../../lib/SlashCommand");
  2 | const prettyMilliseconds = require("pretty-ms");
  3 | const {
  4 |   MessageEmbed,
  5 |   MessageActionRow,
  6 |   MessageSelectMenu,
  7 | } = require("discord.js");
  8 | 
  9 | const command = new SlashCommand()
 10 |   .setName("search")
 11 |   .setDescription("Search for a song")
 12 |   .addStringOption((option) =>
 13 |     option
 14 |       .setName("query")
 15 |       .setDescription("The song to search for")
 16 |       .setRequired(true)
 17 |   )
 18 |   .setRun(async (client, interaction, options) => {
 19 |     let channel = await client.getChannel(client, interaction);
 20 |     if (!channel) {
 21 |       return;
 22 |     }
 23 | 
 24 |     let player;
 25 |     if (client.manager) {
 26 |       player = client.createPlayer(interaction.channel, channel);
 27 |     } else {
 28 |       return interaction.reply({
 29 |         embeds: [
 30 |           new MessageEmbed()
 31 |             .setColor("RED")
 32 |             .setDescription("Lavalink node is not connected"),
 33 |         ],
 34 |       });
 35 |     }
 36 |     await interaction.deferReply().catch((_) => {});
 37 | 
 38 |     if (player.state !== "CONNECTED") {
 39 |       player.connect();
 40 |     }
 41 | 
 42 |     const search = interaction.options.getString("query");
 43 |     let res;
 44 | 
 45 |     try {
 46 |       res = await player.search(search, interaction.user);
 47 |       if (res.loadType === "LOAD_FAILED") {
 48 |         return interaction.reply({
 49 |           embeds: [
 50 |             new MessageEmbed()
 51 |               .setDescription("An error occured while searching for the song")
 52 |               .setColor("RED"),
 53 |           ],
 54 |           ephemeral: true,
 55 |         });
 56 |       }
 57 |     } catch (err) {
 58 |       return interaction.reply({
 59 |         embeds: [
 60 |           new MessageEmbed()
 61 |             .setAuthor({
 62 |               name: "An error occured while searching for the song",
 63 |             })
 64 |             .setColor("RED"),
 65 |         ],
 66 |         ephemeral: true,
 67 |       });
 68 |     }
 69 | 
 70 |     if (res.loadType == "NO_MATCHES") {
 71 |       return interaction.reply({
 72 |         embeds: [
 73 |           new MessageEmbed()
 74 |             .setDescription(`No results found for \`${search}\``)
 75 |             .setColor("RED"),
 76 |         ],
 77 |         ephemeral: true,
 78 |       });
 79 |     } else {
 80 |       let max = 10;
 81 |       if (res.tracks.length < max) {
 82 |         max = res.tracks.length;
 83 |       }
 84 | 
 85 |       let resultFromSearch = [];
 86 | 
 87 |       res.tracks.slice(0, max).map((track) => {
 88 |         resultFromSearch.push({
 89 |           label: `${track.title}`,
 90 |           value: `${track.uri}`,
 91 |           description: track.isStream
 92 |             ? `LIVE`
 93 |             : `${prettyMilliseconds(track.duration, {
 94 |                 secondsDecimalDigits: 0,
 95 |               })} - ${track.author}`,
 96 |         });
 97 |       });
 98 | 
 99 |       const menus = new MessageActionRow().addComponents(
100 |         new MessageSelectMenu()
101 |           .setCustomId("select")
102 |           .setPlaceholder("Select a song")
103 |           .addOptions(resultFromSearch)
104 |       );
105 | 
106 |       let choosenTracks = await interaction.editReply({
107 |         embeds: [
108 |           new MessageEmbed()
109 |             .setColor(client.config.embedColor)
110 |             .setDescription(
111 |               `Here are some of the results I found for \`${search}\`. Please select track within \`30 seconds\``
112 |             ),
113 |         ],
114 |         components: [menus],
115 |       });
116 |       const filter = (button) => button.user.id === interaction.user.id;
117 | 
118 |       const tracksCollector = choosenTracks.createMessageComponentCollector({
119 |         filter,
120 |         time: 30000,
121 |       });
122 |       tracksCollector.on("collect", async (i) => {
123 |         if (i.isSelectMenu()) {
124 |           await i.deferUpdate();
125 |           let uriFromCollector = i.values[0];
126 |           let trackForPlay;
127 | 
128 |           trackForPlay = await player?.search(
129 |             uriFromCollector,
130 |             interaction.user
131 |           );
132 |           player?.queue?.add(trackForPlay.tracks[0]);
133 |           if (!player?.playing && !player?.paused && !player?.queue?.size) {
134 |             player?.play();
135 |           }
136 |           i.editReply({
137 |             content: null,
138 |             embeds: [
139 |               new MessageEmbed()
140 |                 .setAuthor({
141 |                   name: "Added to queue",
142 |                   iconURL: client.config.iconURL,
143 |                 })
144 |                 .setURL(res.tracks[0].uri)
145 |                 .setThumbnail(res.tracks[0].displayThumbnail("maxresdefault"))
146 |                 .setDescription(
147 |                   `[${trackForPlay?.tracks[0]?.title}](${trackForPlay?.tracks[0].uri})` ||
148 |                     "No Title"
149 |                 )
150 |                 .addFields(
151 |                   {
152 |                     name: "Added by",
153 |                     value: `<@${interaction.user.id}>`,
154 |                     inline: true,
155 |                   },
156 |                   {
157 |                     name: "Duration",
158 |                     value: res.tracks[0].isStream
159 |                       ? `\`LIVE :red_circle:\``
160 |                       : `\`${client.ms(res.tracks[0].duration, {
161 |                           colonNotation: true,
162 |                         })}\``,
163 |                     inline: true,
164 |                   }
165 |                 )
166 |                 .setColor(client.config.embedColor),
167 |             ],
168 |             components: [],
169 |           });
170 |         }
171 |       });
172 |       tracksCollector.on("end", async (i) => {
173 |         if (i.size == 0) {
174 |           choosenTracks.edit({
175 |             content: null,
176 |             embeds: [
177 |               new MessageEmbed()
178 |                 .setDescription(
179 |                   `No track selected. You took too long to select a track.`
180 |                 )
181 |                 .setColor(client.config.embedColor),
182 |             ],
183 |             components: [],
184 |           });
185 |         }
186 |       });
187 |     }
188 |   });
189 | 
190 | module.exports = command;
191 | 


--------------------------------------------------------------------------------
/commands/slash/seek.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | const ms = require("ms");
 4 | 
 5 | const command = new SlashCommand()
 6 | 	.setName("seek")
 7 | 	.setDescription("Seek to a specific time in the current song.")
 8 | 	.addStringOption((option) =>
 9 | 		option
10 | 			.setName("time")
11 | 			.setDescription("Seek to time you want. Ex 1h 30m | 2h | 80m | 53s")
12 | 			.setRequired(true),
13 | 	)
14 | 	.setRun(async (client, interaction, options) => {
15 | 		let channel = await client.getChannel(client, interaction);
16 | 		if (!channel) {
17 | 			return;
18 | 		}
19 | 		
20 | 		let player;
21 | 		if (client.manager) {
22 | 			player = client.manager.players.get(interaction.guild.id);
23 | 		} else {
24 | 			return interaction.reply({
25 | 				embeds: [
26 | 					new MessageEmbed()
27 | 						.setColor("RED")
28 | 						.setDescription("Lavalink node is not connected"),
29 | 				],
30 | 			});
31 | 		}
32 | 		
33 | 		if (!player) {
34 | 			return interaction.reply({
35 | 				embeds: [
36 | 					new MessageEmbed()
37 | 						.setColor("RED")
38 | 						.setDescription("There is no music playing."),
39 | 				],
40 | 				ephemeral: true,
41 | 			});
42 | 		}
43 | 		
44 | 		await interaction.deferReply();
45 | 
46 | 		const rawArgs = interaction.options.getString("time");
47 | 		const args = rawArgs.split(' ');
48 | 		var rawTime = [];
49 | 		for (i = 0; i < args.length; i++){
50 | 			rawTime.push(ms(args[i]));
51 | 		}
52 | 		const time = rawTime.reduce((a,b) => a + b, 0);
53 | 		const position = player.position;
54 | 		const duration = player.queue.current.duration;
55 | 		
56 | 		if (time <= duration) {
57 | 			player.seek(time);
58 | 			return interaction.editReply({
59 | 				embeds: [
60 | 					new MessageEmbed()
61 | 						.setColor(client.config.embedColor)
62 | 						.setDescription(
63 | 							`⏩ | **${ player.queue.current.title }** has been ${
64 | 								time < position? "rewound" : "seeked"
65 | 							} to **${ ms(time) }**`,
66 | 						),
67 | 				],
68 | 			});
69 | 		} else {
70 | 			return interaction.editReply({
71 | 				embeds: [
72 | 					new MessageEmbed()
73 | 						.setColor(client.config.embedColor)
74 | 						.setDescription(
75 | 							`Unable to seek current playing track. This may be due to exceeding track duration or an incorrect time format. Please check and try again`,
76 | 						),
77 | 				],
78 | 			});
79 | 		}
80 | 	});
81 | 
82 | module.exports = command;
83 | 


--------------------------------------------------------------------------------
/commands/slash/shuffle.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("shuffle")
 6 | 	.setDescription("Randomizes the queue")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("There is no music playing."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		}
36 | 		
37 | 		if (!player.queue || !player.queue.length || player.queue.length === 0) {
38 | 			return interaction.reply({
39 | 				embeds: [
40 | 					new MessageEmbed()
41 | 						.setColor("RED")
42 | 						.setDescription("There are not enough songs in the queue."),
43 | 				],
44 | 				ephemeral: true,
45 | 			});
46 | 		}
47 | 		
48 | 		//  if the queue is not empty, shuffle the entire queue
49 | 		player.queue.shuffle();
50 | 		return interaction.reply({
51 | 			embeds: [
52 | 				new MessageEmbed()
53 | 					.setColor(client.config.embedColor)
54 | 					.setDescription("🔀 | **Successfully shuffled the queue.**"),
55 | 			],
56 | 		});
57 | 	});
58 | 
59 | module.exports = command;
60 | 


--------------------------------------------------------------------------------
/commands/slash/skip.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("skip")
 6 | 	.setDescription("Skip the current song")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!channel) {
10 | 			return;
11 | 		}
12 | 		
13 | 		let player;
14 | 		if (client.manager) {
15 | 			player = client.manager.players.get(interaction.guild.id);
16 | 		} else {
17 | 			return interaction.reply({
18 | 				embeds: [
19 | 					new MessageEmbed()
20 | 						.setColor("RED")
21 | 						.setDescription("Lavalink node is not connected"),
22 | 				],
23 | 			});
24 | 		}
25 | 		
26 | 		if (!player) {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("There is nothing to skip."),
32 | 				],
33 | 				ephemeral: true,
34 | 			});
35 | 		} 
36 |         	const song = player.queue.current;
37 | 	        const autoQueue = player.get("autoQueue");
38 |                 if (player.queue[0] == undefined && (!autoQueue || autoQueue === false)) {
39 | 		return interaction.reply({
40 | 			embeds: [
41 | 				new MessageEmbed()
42 | 					.setColor("RED")
43 | 					.setDescription(`There is nothing after [${ song.title }](${ song.uri }) in the queue.`),
44 | 			],
45 | 		})}
46 | 		
47 | 		player.queue.previous = player.queue.current;
48 | 		player.stop();
49 | 		
50 | 		interaction.reply({
51 | 			embeds: [
52 | 				new MessageEmbed()
53 | 					.setColor(client.config.embedColor)
54 | 					.setDescription("✅ | **Skipped!**"),
55 | 			],
56 | 		});
57 | 	});
58 | 
59 | module.exports = command;
60 | 


--------------------------------------------------------------------------------
/commands/slash/skipto.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("skipto")
 6 | 	.setDescription("skip to a specific song in the queue")
 7 | 	.addNumberOption((option) =>
 8 | 		option
 9 | 			.setName("number")
10 | 			.setDescription("The number of tracks to skipto")
11 | 			.setRequired(true),
12 | 	)
13 | 	
14 | 	.setRun(async (client, interaction, options) => {
15 | 		const args = interaction.options.getNumber("number");
16 | 		//const duration = player.queue.current.duration
17 | 		
18 | 		let channel = await client.getChannel(client, interaction);
19 | 		if (!channel) {
20 | 			return;
21 | 		}
22 | 		
23 | 		let player;
24 | 		if (client.manager) {
25 | 			player = client.manager.players.get(interaction.guild.id);
26 | 		} else {
27 | 			return interaction.reply({
28 | 				embeds: [
29 | 					new MessageEmbed()
30 | 						.setColor("RED")
31 | 						.setDescription("Lavalink node is not connected"),
32 | 				],
33 | 			});
34 | 		}
35 | 		
36 | 		if (!player) {
37 | 			return interaction.reply({
38 | 				embeds: [
39 | 					new MessageEmbed()
40 | 						.setColor("RED")
41 | 						.setDescription("I'm not in a channel."),
42 | 				],
43 | 				ephemeral: true,
44 | 			});
45 | 		}
46 | 		
47 | 		await interaction.deferReply();
48 | 		
49 | 		const position = Number(args);
50 | 		
51 | 		try {
52 | 			if (!position || position < 0 || position > player.queue.size) {
53 | 				let thing = new MessageEmbed()
54 | 					.setColor(client.config.embedColor)
55 | 					.setDescription("❌ | Invalid position!");
56 | 				return interaction.editReply({ embeds: [thing] });
57 | 			}
58 | 			
59 | 			player.queue.remove(0, position - 1);
60 | 			player.stop();
61 | 			
62 | 			let thing = new MessageEmbed()
63 | 				.setColor(client.config.embedColor)
64 | 				.setDescription("✅ | Skipped to position " + position);
65 | 			
66 | 			return interaction.editReply({ embeds: [thing] });
67 | 		} catch {
68 | 			if (position === 1) {
69 | 				player.stop();
70 | 			}
71 | 			return interaction.editReply({
72 | 				embeds: [
73 | 					new MessageEmbed()
74 | 						.setColor(client.config.embedColor)
75 | 						.setDescription("✅ | Skipped to position " + position),
76 | 				],
77 | 			});
78 | 		}
79 | 	});
80 | 
81 | module.exports = command;
82 | 


--------------------------------------------------------------------------------
/commands/slash/stats.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const moment = require("moment");
 3 | require("moment-duration-format");
 4 | const { MessageEmbed } = require("discord.js");
 5 | const os = require("os");
 6 | 
 7 | const command = new SlashCommand()
 8 | 	.setName("stats")
 9 | 	.setDescription("Get information about the bot")
10 | 	.setRun(async (client, interaction) => {
11 | 		// get OS info
12 | 		const osver = os.platform() + " " + os.release();
13 | 		
14 | 		// Get nodejs version
15 | 		const nodeVersion = process.version;
16 | 		
17 | 		// get the uptime in a human readable format
18 | 		const runtime = moment
19 | 			.duration(client.uptime)
20 | 			.format("d[ Days]・h[ Hrs]・m[ Mins]・s[ Secs]");
21 | 		// show lavalink uptime in a nice format
22 | 		const lavauptime = moment
23 | 			.duration(client.manager.nodes.values().next().value.stats.uptime)
24 | 			.format(" D[d], H[h], m[m]");
25 | 		// show lavalink memory usage in a nice format
26 | 		const lavaram = (
27 | 			client.manager.nodes.values().next().value.stats.memory.used /
28 | 			1024 /
29 | 			1024
30 | 		).toFixed(2);
31 | 		// sow lavalink memory alocated in a nice format
32 | 		const lavamemalocated = (
33 | 			client.manager.nodes.values().next().value.stats.memory.allocated /
34 | 			1024 /
35 | 			1024
36 | 		).toFixed(2);
37 | 		// show system uptime
38 | 		var sysuptime = moment
39 | 			.duration(os.uptime() * 1000)
40 | 			.format("d[ Days]・h[ Hrs]・m[ Mins]・s[ Secs]");
41 | 		
42 | 		// get commit hash and date
43 | 		let gitHash = "unknown";
44 | 		try {
45 | 			gitHash = require("child_process")
46 | 				.execSync("git rev-parse HEAD")
47 | 				.toString()
48 | 				.trim();
49 | 		} catch (e) {
50 | 			// do nothing
51 | 			gitHash = "unknown";
52 | 		}
53 | 		
54 | 		const statsEmbed = new MessageEmbed()
55 | 			.setTitle(`${ client.user.username } Information`)
56 | 			.setColor(client.config.embedColor)
57 | 			.setDescription(
58 | 				`\`\`\`yml\nName: ${ client.user.username }#${ client.user.discriminator } [${ client.user.id }]\nAPI: ${ client.ws.ping }ms\nRuntime: ${ runtime }\`\`\``,
59 | 			)
60 | 			.setFields([
61 | 				{
62 | 					name: `Lavalink stats`,
63 | 					value: `\`\`\`yml\nUptime: ${ lavauptime }\nRAM: ${ lavaram } MB\nPlaying: ${
64 | 						client.manager.nodes.values().next().value.stats.playingPlayers
65 | 					} out of ${
66 | 						client.manager.nodes.values().next().value.stats.players
67 | 					}\`\`\``,
68 | 					inline: true,
69 | 				},
70 | 				{
71 | 					name: "Bot stats",
72 | 					value: `\`\`\`yml\nGuilds: ${
73 | 						client.guilds.cache.size
74 | 					} \nNodeJS: ${ nodeVersion }\nDiscordMusicBot: v${
75 | 						require("../../package.json").version
76 | 					} \`\`\``,
77 | 					inline: true,
78 | 				},
79 | 				{
80 | 					name: "System stats",
81 | 					value: `\`\`\`yml\nOS: ${ osver }\nUptime: ${ sysuptime }\n\`\`\``,
82 | 					inline: false,
83 | 				},
84 | 			])
85 | 			.setFooter({ text: `Build: ${ gitHash }` });
86 | 		return interaction.reply({ embeds: [statsEmbed], ephemeral: false });
87 | 	});
88 | 
89 | module.exports = command;
90 | 


--------------------------------------------------------------------------------
/commands/slash/stop.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("stop")
 6 | 	.setDescription("Stops whatever the bot is playing and leaves the voice channel\n(This command will clear the queue)")
 7 | 	
 8 | 	.setRun(async (client, interaction, options) => {
 9 | 		let channel = await client.getChannel(client, interaction);
10 | 		if (!channel) {
11 | 			return;
12 | 		}
13 | 		
14 | 		let player;
15 | 		if (client.manager) {
16 | 			player = client.manager.players.get(interaction.guild.id);
17 | 		} else {
18 | 			return interaction.reply({
19 | 				embeds: [
20 | 					new MessageEmbed()
21 | 						.setColor("RED")
22 | 						.setDescription("Lavalink node is not connected"),
23 | 				],
24 | 			});
25 | 		}
26 | 		
27 | 		if (!player) {
28 | 			return interaction.reply({
29 | 				embeds: [
30 | 					new MessageEmbed()
31 | 						.setColor("RED")
32 | 						.setDescription("I'm not in a channel."),
33 | 				],
34 | 				ephemeral: true,
35 | 			});
36 | 		}
37 | 		
38 | 		if (player.twentyFourSeven) {
39 | 			player.queue.clear();
40 | 			player.stop();
41 | 			player.set("autoQueue", false);
42 | 		} else {
43 | 			player.destroy();
44 | 		}
45 | 		
46 | 		interaction.reply({
47 | 			embeds: [
48 | 				new MessageEmbed()
49 | 					.setColor(client.config.embedColor)
50 | 					.setDescription(`:wave: | **Bye Bye!**`),
51 | 			],
52 | 		});
53 | 	});
54 | 
55 | module.exports = command;
56 | 


--------------------------------------------------------------------------------
/commands/slash/summon.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("summon")
 6 | 	.setDescription("Summons the bot to the channel.")
 7 | 	.setRun(async (client, interaction, options) => {
 8 | 		let channel = await client.getChannel(client, interaction);
 9 | 		if (!interaction.member.voice.channel) {
10 | 			const joinEmbed = new MessageEmbed()
11 | 				.setColor(client.config.embedColor)
12 | 				.setDescription(
13 | 					"❌ | **You must be in a voice channel to use this command.**",
14 | 				);
15 | 			return interaction.reply({ embeds: [joinEmbed], ephemeral: true });
16 | 		}
17 | 		
18 | 		let player = client.manager.players.get(interaction.guild.id);
19 | 		if (!player) {
20 | 			player = client.createPlayer(interaction.channel, channel);
21 | 			player.connect(true);
22 | 		}
23 | 		
24 | 		if (channel.id !== player.voiceChannel) {
25 | 			player.setVoiceChannel(channel.id);
26 | 			player.connect();
27 | 		}
28 | 		
29 | 		interaction.reply({
30 | 			embeds: [
31 | 				client.Embed(`:thumbsup: | **Successfully joined <#${ channel.id }>!**`),
32 | 			],
33 | 		});
34 | 	});
35 | 
36 | module.exports = command;
37 | 


--------------------------------------------------------------------------------
/commands/slash/volume.js:
--------------------------------------------------------------------------------
 1 | const SlashCommand = require("../../lib/SlashCommand");
 2 | const { MessageEmbed } = require("discord.js");
 3 | 
 4 | const command = new SlashCommand()
 5 | 	.setName("volume")
 6 | 	.setDescription("Change the volume of the current song.")
 7 | 	.addNumberOption((option) =>
 8 | 		option
 9 | 			.setName("amount")
10 | 			.setDescription("Amount of volume you want to change. Ex: 10")
11 | 			.setRequired(false),
12 | 	)
13 | 	.setRun(async (client, interaction) => {
14 | 		let channel = await client.getChannel(client, interaction);
15 | 		if (!channel) {
16 | 			return;
17 | 		}
18 | 		
19 | 		let player;
20 | 		if (client.manager) {
21 | 			player = client.manager.players.get(interaction.guild.id);
22 | 		} else {
23 | 			return interaction.reply({
24 | 				embeds: [
25 | 					new MessageEmbed()
26 | 						.setColor("RED")
27 | 						.setDescription("Lavalink node is not connected"),
28 | 				],
29 | 			});
30 | 		}
31 | 		
32 | 		if (!player) {
33 | 			return interaction.reply({
34 | 				embeds: [
35 | 					new MessageEmbed()
36 | 						.setColor("RED")
37 | 						.setDescription("There is no music playing."),
38 | 				],
39 | 				ephemeral: true,
40 | 			});
41 | 		}
42 | 		
43 | 		let vol = interaction.options.getNumber("amount");
44 | 		if (!vol || vol < 1 || vol > 125) {
45 | 			return interaction.reply({
46 | 				embeds: [
47 | 					new MessageEmbed()
48 | 						.setColor(client.config.embedColor)
49 | 						.setDescription(
50 | 							`:loud_sound: | Current volume **${ player.volume }**`,
51 | 						),
52 | 				],
53 | 			});
54 | 		}
55 | 		
56 | 		player.setVolume(vol);
57 | 		return interaction.reply({
58 | 			embeds: [
59 | 				new MessageEmbed()
60 | 					.setColor(client.config.embedColor)
61 | 					.setDescription(
62 | 						`:loud_sound: | Successfully set volume to **${ player.volume }**`,
63 | 					),
64 | 			],
65 | 		});
66 | 	});
67 | 
68 | module.exports = command;
69 | 


--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
 1 | module.exports = {
 2 | 	helpCmdPerPage: 10, //- Number of commands per page of help command
 3 | 	lyricsMaxResults: 5, //- Number of results for lyrics command (Do not touch this value if you don't know what you are doing)
 4 | 	adminId: "UserId", //- Replace UserId with the Discord ID of the admin of the bot
 5 | 	token: process.env.token || "", //- Bot's Token
 6 | 	clientId: process.env.clientId || "", //- ID of the bot
 7 | 	clientSecret: process.env.clientSecret || "", //- Client Secret of the bot
 8 | 	port: 4200, //- Port of the API and Dashboard
 9 | 	scopes: ["identify", "guilds", "applications.commands"], //- Discord OAuth2 Scopes
10 | 	inviteScopes: ["bot", "applications.commands"], // Invite link scopes
11 | 	serverDeafen: true, //- If you want bot to stay deafened
12 | 	defaultVolume: 100, //- Sets the default volume of the bot, You can change this number anywhere from 1 to 100
13 | 	supportServer: "https://discord.gg/sbySMS7m3v", //- Support Server Link
14 | 	Issues: "https://github.com/SudhanPlayz/Discord-MusicBot/issues", //- Bug Report Link
15 | 	permissions: 277083450689, //- Bot Inviting Permissions
16 | 	disconnectTime: 30000, //- How long should the bot wait before disconnecting from the voice channel (in miliseconds). Set to 1 for instant disconnect.
17 | 	twentyFourSeven: false, //- When set to true, the bot will never disconnect from the voice channel
18 | 	autoQueue: false, //- When set to true, related songs will automatically be added to the queue
19 | 	autoPause: true, //- When set to true, music will automatically be paused if everyone leaves the voice channel
20 | 	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.)
21 | 	debug: false, //- Debug mode
22 | 	cookieSecret: "CodingWithSudhan is epic", //- Cookie Secret
23 | 	website: "http://localhost:4200", //- without the / at the end
24 | 	// You need a lavalink server for this bot to work!!!!
25 | 	// Lavalink server; public lavalink -> https://lavalink-list.darrennathanael.com/; create one yourself -> https://darrennathanael.com/post/how-to-lavalink
26 | 	nodes: [
27 | 		{
28 | 			identifier: "Main Node", //- Used for indentifier in stats commands.
29 | 			host: "", //- The host name or IP of the lavalink server.
30 | 			port: 80, // The port that lavalink is listening to. This must be a number!
31 | 			password: "", //- The password of the lavalink server.
32 | 			retryAmount: 200, //- The amount of times to retry connecting to the node if connection got dropped.
33 | 			retryDelay: 40, //- Delay between reconnect attempts if connection is lost.
34 | 			secure: false, //- Can be either true or false. Only use true if ssl is enabled!
35 | 		},
36 | 	],
37 | 	embedColor: "#2f3136", //- Color of the embeds, hex supported
38 | 	presence: {
39 | 		// PresenceData object | https://discord.js.org/#/docs/main/stable/typedef/PresenceData
40 | 		status: "online", //- You can have online, idle, dnd and invisible (Note: invisible makes people think the bot is offline)
41 | 		activities: [
42 | 			{
43 | 				name: "Music", //- Status Text
44 | 				type: "LISTENING", //- PLAYING, WATCHING, LISTENING, STREAMING
45 | 			},
46 | 		],
47 | 	},
48 | 	iconURL: "https://cdn.darrennathanael.com/icons/spinning_disk.gif", //- This icon will be in every embed's author field
49 | };
50 | 


--------------------------------------------------------------------------------
/dashboard/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 |   "extends": "next/core-web-vitals"
3 | }
4 | 


--------------------------------------------------------------------------------
/dashboard/.gitignore:
--------------------------------------------------------------------------------
 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
 2 | 
 3 | # dependencies
 4 | /node_modules
 5 | /.pnp
 6 | .pnp.js
 7 | 
 8 | # testing
 9 | /coverage
10 | 
11 | # next.js
12 | /.next/
13 | 
14 | # production
15 | /build
16 | 
17 | # misc
18 | .DS_Store
19 | *.pem
20 | 
21 | # debug
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 | .pnpm-debug.log*
26 | 
27 | # local env files
28 | .env*.local
29 | 
30 | # vercel
31 | .vercel
32 | 


--------------------------------------------------------------------------------
/dashboard/README.md:
--------------------------------------------------------------------------------
 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped
 2 | with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
 3 | 
 4 | ## Getting Started
 5 | 
 6 | First, run the development server:
 7 | 
 8 | ```bash
 9 | npm run dev
10 | # or
11 | yarn dev
12 | ```
13 | 
14 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
15 | 
16 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
17 | 
18 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed
19 | on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited
20 | in `pages/api/hello.js`.
21 | 
22 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated
23 | as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
24 | 
25 | ## Learn More
26 | 
27 | To learn more about Next.js, take a look at the following resources:
28 | 
29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
31 | 
32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions
33 | are welcome!
34 | 
35 | ## Deploy on Vercel
36 | 
37 | The easiest way to deploy your Next.js app is to use
38 | 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)
39 | from the creators of Next.js.
40 | 
41 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
42 | 


--------------------------------------------------------------------------------
/dashboard/components/StatCard.tsx:
--------------------------------------------------------------------------------
 1 | import {Card, Text} from "@nextui-org/react";
 2 | import {ReactNode} from "react";
 3 | 
 4 | export default function StatCard(props: {
 5 |     title: string;
 6 |     amount: number | string;
 7 |     icon: ReactNode;
 8 | }) {
 9 |     return (
10 |         <Card variant="flat" isHoverable css={ {margin: '10px', width: '200px', padding: '15px'} }>
11 |             <Card.Body css={ {display: 'flex', padding: '0', alignItems: 'center', flexDirection: 'row'} }>
12 |                 <div style={ {marginRight: 'auto'} }>
13 |                     <Text h4 css={ {color: 'GrayText'} }>{ props.title }</Text>
14 |                     <Text h3>{ props.amount }</Text>
15 |                 </div>
16 |                 { props.icon }
17 |             </Card.Body>
18 |         </Card>
19 |     )
20 | }


--------------------------------------------------------------------------------
/dashboard/components/content.tsx:
--------------------------------------------------------------------------------
 1 | import {PropsWithChildren} from "react";
 2 | import Navbar from "./navbar";
 3 | 
 4 | export default function Content(props: PropsWithChildren) {
 5 |     return <div style={ {
 6 |         width: "100vw",
 7 |         height: "100vh",
 8 |         display: "flex",
 9 |     } }>
10 |         <Navbar/>
11 |         <div style={ {
12 |             marginTop: '30px'
13 |         } }>
14 |             { props.children }
15 |         </div>
16 |     </div>
17 | }


--------------------------------------------------------------------------------
/dashboard/components/navbar.tsx:
--------------------------------------------------------------------------------
 1 | import {Button, Link, Spacer} from "@nextui-org/react";
 2 | import { useRouter } from "next/router";
 3 | 
 4 | export default function Navbar() {
 5 |     const router = useRouter();
 6 | 
 7 |     return <div style={ {
 8 |         height: '100%',
 9 |         width: '250px',
10 |         backgroundColor: '#16181A',
11 |         display: 'flex',
12 |         alignItems: 'center',
13 |         flexDirection: 'column',
14 |         paddingTop: '50px',
15 |         marginRight: '50px',
16 |     } }>
17 |         <Link css={ {
18 |             fontSize: '$xl2',
19 |             fontWeight: 'bold',
20 |             marginBottom: '30px',
21 |             color: '#fff',
22 |         } } href='/'>Discord Music Bot</Link>
23 |         <Button css={ {background: router.pathname == '/dashboard' ? '$primary' : '$gray100'} }
24 |                 onClick={ () => window.location.pathname = '/dashboard' } style={ {marginBottom: '10px'} }>Dashboard</Button>
25 |         <Button css={ {background: router.pathname == '/servers' ? '$primary' : '$gray100'} } color='default'
26 |                 onClick={ () => window.location.pathname = '/servers' } style={ {marginBottom: '10px'} }>Servers</Button>
27 |         <Spacer/>
28 |         <Button color='error' flat onClick={ () => window.location.pathname = '/logout' }
29 |                 style={ {marginBottom: '10px'} }>Logout</Button>
30 |     </div>
31 | }
32 | 


--------------------------------------------------------------------------------
/dashboard/components/server.tsx:
--------------------------------------------------------------------------------
 1 | import {Avatar, Tooltip} from "@nextui-org/react";
 2 | import Link from "next/link";
 3 | 
 4 | interface IProps {
 5 |     icon: string;
 6 |     name: string;
 7 |     id: string;
 8 | }
 9 | 
10 | const getColor = () => {
11 |     let c = ["gradient", "primary", "secondary", "error", "warning"]
12 |     return c[Math.floor(Math.random() * c.length)];
13 | }
14 | 
15 | export default function Server(props: IProps) {
16 |     return <div key={ props.id } style={ {
17 |         margin: "10px"
18 |     } }>
19 |         <Link href={ "/servers/" + props.id }><a>
20 |             <Tooltip content={ props.name } color="secondary">
21 |                 <Avatar
22 |                     src={ props.icon }
23 |                     size="xl"
24 |                     //@ts-ignore
25 |                     color={ getColor() }
26 |                     bordered
27 |                     pointer
28 |                 />
29 |             </Tooltip>
30 |         </a></Link>
31 |     </div>
32 | }


--------------------------------------------------------------------------------
/dashboard/next-env.d.ts:
--------------------------------------------------------------------------------
1 | /// <reference types="next" />
2 | /// <reference types="next/image-types/global" />
3 | 
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 | 


--------------------------------------------------------------------------------
/dashboard/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | 	reactStrictMode: true,
4 | }
5 | 
6 | module.exports = nextConfig
7 | 


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/pages/_error-a4ba2246ff8fb532.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[820],{1981:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_error",function(){return u(9185)}])}},function(n){n.O(0,[774,888,179],(function(){return _=1981,n(n.s=_);var _}));var _=n.O();_N_E=_}]);


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/pages/dashboard-8fe77e1aeec6ff87.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[26],{528:function(n,e,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/dashboard",function(){return t(1659)}])},6303:function(n,e,t){"use strict";t.d(e,{Z:function(){return p}});var r=t(5893),i=t(5208),s=t(1160),o=t(7294),a=t(88),c=t(6772);const l=(0,t(6212).zo)("span",{size:"1px",variants:{inline:{true:{display:"inline-block"},false:{display:"block"}}},defaultVariants:{inline:!1}}),d=({x:n,y:e,inline:t,css:i,...s})=>{const o=(0,c.m)(n),a=(0,c.m)(e);return(0,r.jsx)(l,{css:{marginLeft:`${o} !important`,marginTop:`${a} !important`,...i},"aria-hidden":"true",...s})};d.toString=()=>".nextui-spacer";const u=o.memo(d);var h=(0,a.Z)(u,{x:1,y:1}),x=t(1163);function f(){var n=(0,x.useRouter)();return(0,r.jsxs)("div",{style:{height:"100%",width:"250px",backgroundColor:"#16181A",display:"flex",alignItems:"center",flexDirection:"column",paddingTop:"50px",marginRight:"50px"},children:[(0,r.jsx)(i.ZP,{css:{fontSize:"$xl2",fontWeight:"bold",marginBottom:"30px",color:"#fff"},href:"/",children:"Discord Music Bot"}),(0,r.jsx)(s.ZP,{css:{background:"/dashboard"==n.pathname?"$primary":"$gray100"},onClick:function(){return window.location.pathname="/dashboard"},style:{marginBottom:"10px"},children:"Dashboard"}),(0,r.jsx)(s.ZP,{css:{background:"/servers"==n.pathname?"$primary":"$gray100"},color:"default",onClick:function(){return window.location.pathname="/servers"},style:{marginBottom:"10px"},children:"Servers"}),(0,r.jsx)(h,{}),(0,r.jsx)(s.ZP,{color:"error",flat:!0,onClick:function(){return window.location.pathname="/logout"},style:{marginBottom:"10px"},children:"Logout"})]})}function p(n){return(0,r.jsxs)("div",{style:{width:"100vw",height:"100vh",display:"flex"},children:[(0,r.jsx)(f,{}),(0,r.jsx)("div",{style:{marginTop:"30px"},children:n.children})]})}},1659:function(n,e,t){"use strict";t.r(e),t.d(e,{default:function(){return v}});var r=t(5893),i=t(9008),s=t.n(i),o=t(5784),a=(0,o.Z)((0,r.jsx)("path",{d:"M9.19 6.35c-2.04 2.29-3.44 5.58-3.57 5.89l-2.26-.97c-.65-.28-.81-1.13-.31-1.63l3.01-3.01c.47-.47 1.15-.68 1.81-.55l1.32.27zm1.49 10.16c.3.3.74.38 1.12.2 1.16-.54 3.65-1.81 5.26-3.42 4.59-4.59 4.63-8.33 4.36-9.93-.07-.4-.39-.72-.79-.79-1.6-.27-5.34-.23-9.93 4.36-1.61 1.61-2.87 4.1-3.42 5.26-.18.38-.09.83.2 1.12l3.2 3.2zm6.97-1.7c-2.29 2.04-5.58 3.44-5.89 3.57l.97 2.26c.28.65 1.13.81 1.63.31l3.01-3.01c.47-.47.68-1.15.55-1.81l-.27-1.32zm-8.71 2.6c.2 1.06-.15 2.04-.82 2.71-.77.77-3.16 1.34-4.71 1.64-.69.13-1.3-.48-1.17-1.17.3-1.55.86-3.94 1.64-4.71.67-.67 1.65-1.02 2.71-.82 1.17.22 2.13 1.18 2.35 2.35zM13 9c0-1.1.9-2 2-2s2 .9 2 2-.9 2-2 2-2-.9-2-2z"}),"RocketLaunchRounded"),c=(0,o.Z)((0,r.jsx)("path",{d:"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v1c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-1c0-2.66-5.33-4-8-4z"}),"PersonRounded"),l=(0,o.Z)((0,r.jsx)("path",{d:"M19 13H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-4c0-1.1-.9-2-2-2zM7 19c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM19 3H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM7 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"}),"DnsRounded"),d=t(5446),u=t(6303),h=t(7370),x=t(6979);function f(n){return(0,r.jsx)(h.ZP,{variant:"flat",isHoverable:!0,css:{margin:"10px",width:"200px",padding:"15px"},children:(0,r.jsxs)(h.ZP.Body,{css:{display:"flex",padding:"0",alignItems:"center",flexDirection:"row"},children:[(0,r.jsxs)("div",{style:{marginRight:"auto"},children:[(0,r.jsx)(x.Z,{h4:!0,css:{color:"GrayText"},children:n.title}),(0,r.jsx)(x.Z,{h3:!0,children:n.amount})]}),n.icon]})})}var p=t(7294),m=t(7568),g=t(4051),j=t.n(g),v=function(n){var e=(0,p.useState)(null),t=e[0],i=e[1];return(0,p.useEffect)((function(){new Promise(function(){var n=(0,m.Z)(j().mark((function n(e,t){var r,i;return j().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,fetch("/api/dashboard",{method:"GET",credentials:"same-origin"});case 2:return r=n.sent,n.next=5,r.json();case 5:i=n.sent,e(i);case 7:case"end":return n.stop()}}),n)})));return function(e,t){return n.apply(this,arguments)}}()).then(i)}),[]),(0,r.jsxs)(u.Z,{children:[(0,r.jsx)(s(),{children:(0,r.jsx)("title",{children:"Dashboard | Discord Music Bot"})}),(0,r.jsx)("h1",{children:"Dashboard"}),(0,r.jsxs)("div",{style:{display:"flex"},children:[(0,r.jsx)(f,{title:"Commands Ran",amount:t?t.commandsRan:"Loading",icon:(0,r.jsx)(a,{fontSize:"large"})}),(0,r.jsx)(f,{title:"Users",amount:t?t.users:"Loading",icon:(0,r.jsx)(c,{fontSize:"large"})}),(0,r.jsx)(f,{title:"Servers",amount:t?t.servers:"Loading",icon:(0,r.jsx)(l,{fontSize:"large"})}),(0,r.jsx)(f,{title:"Songs Played",amount:t?t.songsPlayed:"Loading",icon:(0,r.jsx)(d.Z,{fontSize:"large"})})]})]})}},1163:function(n,e,t){n.exports=t(387)}},function(n){n.O(0,[123,732,774,888,179],(function(){return e=528,n(n.s=e);var e}));var e=n.O();_N_E=e}]);


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/pages/index-0494ad302e38da35.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[405],{8312:function(e,n,i){(window.__NEXT_P=window.__NEXT_P||[]).push(["/",function(){return i(4636)}])},4636:function(e,n,i){"use strict";i.r(n),i.d(n,{default:function(){return b}});var t=i(5893),s=i(7294),r=i(5784),o=(0,r.Z)((0,t.jsx)("path",{d:"M10 15l5.19-3L10 9v6m11.56-7.83c.13.47.22 1.1.28 1.9.07.8.1 1.49.1 2.09L22 12c0 2.19-.16 3.8-.44 4.83-.25.9-.83 1.48-1.73 1.73-.47.13-1.33.22-2.65.28-1.3.07-2.49.1-3.59.1L12 19c-4.19 0-6.8-.16-7.83-.44-.9-.25-1.48-.83-1.73-1.73-.13-.47-.22-1.1-.28-1.9-.07-.8-.1-1.49-.1-2.09L2 12c0-2.19.16-3.8.44-4.83.25-.9.83-1.48 1.73-1.73.47-.13 1.33-.22 2.65-.28 1.3-.07 2.49-.1 3.59-.1L12 5c4.19 0 6.8.16 7.83.44.9.25 1.48.83 1.73 1.73z"}),"YouTube"),l=i(5446),c=(0,r.Z)((0,t.jsx)("path",{d:"M19.5 12c0-.23-.01-.45-.03-.68l1.86-1.41c.4-.3.51-.86.26-1.3l-1.87-3.23c-.25-.44-.79-.62-1.25-.42l-2.15.91c-.37-.26-.76-.49-1.17-.68l-.29-2.31c-.06-.5-.49-.88-.99-.88h-3.73c-.51 0-.94.38-1 .88l-.29 2.31c-.41.19-.8.42-1.17.68l-2.15-.91c-.46-.2-1-.02-1.25.42L2.41 8.62c-.25.44-.14.99.26 1.3l1.86 1.41c-.02.22-.03.44-.03.67s.01.45.03.68l-1.86 1.41c-.4.3-.51.86-.26 1.3l1.87 3.23c.25.44.79.62 1.25.42l2.15-.91c.37.26.76.49 1.17.68l.29 2.31c.06.5.49.88.99.88h3.73c.5 0 .93-.38.99-.88l.29-2.31c.41-.19.8-.42 1.17-.68l2.15.91c.46.2 1 .02 1.25-.42l1.87-3.23c.25-.44.14-.99-.26-1.3l-1.86-1.41c.03-.23.04-.45.04-.68zm-7.46 3.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"}),"SettingsRounded");var a=(0,i(6212).zo)("div",{w:"100%",mr:"auto",ml:"auto",variants:{fluid:{true:{maxWidth:"100%"}},responsive:{true:{"@xs":{maxWidth:"$breakpoints$xs"},"@sm":{maxWidth:"$breakpoints$sm"},"@md":{maxWidth:"$breakpoints$md"},"@lg":{maxWidth:"$breakpoints$lg"},"@xl":{maxWidth:"$breakpoints$xl"}}}},defaultVariants:{fluid:!1,responsive:!0}});const d=({xs:e,sm:n,md:i,lg:r,xl:o,wrap:l,gap:c,as:d,display:u,justify:x,direction:p,alignItems:h,alignContent:f,children:m,responsive:g,fluid:y,css:j,...$})=>{const b=(0,s.useMemo)((()=>`calc(${c} * $space$sm)`),[c]);return(0,t.jsx)(a,{css:{px:b,maxWidth:e?"$breakpoints$xs":n?"$breakpoints$sm":i?"$breakpoints$md":r?"$breakpoints$lg":o?"$breakpoints$xl":"",alignItems:h,alignContent:f,flexWrap:l,display:u,justifyContent:x,flexDirection:p,...j},responsive:g,fluid:y,as:d,...$,children:m})};d.toString=()=>".nextui-container",d.defaultProps={gap:2,xs:!1,sm:!1,md:!1,lg:!1,xl:!1,responsive:!0,fluid:!1,wrap:"wrap",as:"div",display:"block"};var u=s.memo(d),x=i(5208),p=i(1160),h=i(6979),f=i(7370),m=i(9008),g=i.n(m),y=i(7568),j=i(4051),$=i.n(j),b=function(e){var n=(0,s.useState)(null),i=n[0],r=n[1];return(0,s.useEffect)((function(){new Promise(function(){var e=(0,y.Z)($().mark((function e(n,i){var t;return $().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/api/data",{method:"GET"});case 2:return t=e.sent,e.t0=n,e.next=6,t.json();case 6:e.t1=e.sent,(0,e.t0)(e.t1);case 8:case"end":return e.stop()}}),e)})));return function(n,i){return e.apply(this,arguments)}}()).then((function(e){var n;r(e),(null===(n=e.redirect)||void 0===n?void 0:n.length)&&(window.location.href=e.redirect)}))}),[]),(0,t.jsxs)(u,{children:[(0,t.jsx)(g(),{children:(0,t.jsx)("title",{children:"Discord Music Bot"})}),(0,t.jsxs)(u,{css:{display:"flex",alignItems:"center",background:"$gray50",position:"fixed",padding:"20px",minWidth:"100%",left:"0",top:"0",zIndex:"$5"},children:[(0,t.jsx)(x.ZP,{css:{fontSize:"$xl",fontWeight:"$semibold"},href:"/",children:i?i.name:"Discord Music Bot"}),(0,t.jsx)(x.ZP,{color:"text",css:{fontSize:"$lg",fontWeight:"$medium",marginLeft:"20px"},href:"#",children:"Home"}),(0,t.jsx)(x.ZP,{color:"text",css:{fontSize:"$lg",fontWeight:"$medium",marginLeft:"20px"},href:"#features",children:"Features"}),(0,t.jsx)(p.ZP,{onClick:function(){return window.location.pathname="/dashboard"},css:{marginLeft:"auto"},auto:!0,shadow:!0,children:"Dashboard"})]}),(0,t.jsxs)(u,{style:{textAlign:"center",marginTop:"1rem",display:"flex",height:"100vh",justifyContent:"center",alignItems:"center",flexDirection:"column"},children:[(0,t.jsx)(h.Z,{h1:!0,css:{textGradient:"180deg, $blue600 -20%, $blue800 100%"},children:"Discord Music Bot"}),(0,t.jsx)(h.Z,{h3:!0,css:{color:"$gray800"},children:"An advanced discord music bot, supports Spotify, SoundCloud, YouTube with Shuffling, Volume Control and Web Dashboard!"}),(0,t.jsxs)(u,{css:{display:"flex",alignItems:"center",justifyContent:"center"},children:[(0,t.jsx)(p.ZP,{color:"primary",onClick:function(){return window.location.pathname="/login"},shadow:!0,style:{marginTop:"1rem"},children:"Login"}),(0,t.jsx)(p.ZP,{color:"primary",flat:!0,onClick:function(){return window.open("https://github.com/SudhanPlayz/Discord-MusicBot")},style:{marginTop:"1rem",marginLeft:"20px"},children:"Github"})]})]}),(0,t.jsxs)(u,{css:{display:"flex",flexDirection:"column",alignItems:"center",minHeight:"60vh"},children:[(0,t.jsx)(h.Z,{h2:!0,children:"Features"}),(0,t.jsxs)(u,{css:{display:"flex",justifyContent:"center",flexWrap:"wrap"},children:[(0,t.jsxs)(f.ZP,{isHoverable:!0,css:{display:"flex",flexDirection:"column",alignItems:"center",margin:"10px",width:"300px",padding:"20px",textAlign:"center"},children:[(0,t.jsx)(o,{style:{fontSize:"150px",color:"#3694FF"}}),(0,t.jsx)(h.Z,{h3:!0,children:"Spotify, Soundcloud, and Youtube support"}),(0,t.jsx)(h.Z,{css:{color:"$gray800"},children:"Use your spotify playlist, youtube videos, youtube playlists and much more using this bot"})]}),(0,t.jsxs)(f.ZP,{isHoverable:!0,css:{display:"flex",flexDirection:"column",alignItems:"center",margin:"10px",width:"300px",padding:"20px",textAlign:"center"},children:[(0,t.jsx)(l.Z,{style:{fontSize:"150px",color:"#3694FF"}}),(0,t.jsx)(h.Z,{h3:!0,children:"Lag-free Music"}),(0,t.jsx)(h.Z,{css:{color:"$gray800"},children:"This bot will never lag when playing any song in a voice channel"})]}),(0,t.jsxs)(f.ZP,{isHoverable:!0,css:{display:"flex",flexDirection:"column",alignItems:"center",margin:"10px",width:"300px",padding:"20px",textAlign:"center"},children:[(0,t.jsx)(c,{style:{fontSize:"150px",color:"#3694FF"}}),(0,t.jsx)(h.Z,{h3:!0,children:"Server Settings"}),(0,t.jsx)(h.Z,{css:{color:"$gray800"},children:"Control your song looping song or queue, play or pause your song easily, or stop the bot completely."})]})]})]}),(0,t.jsxs)(u,{css:{marginTop:"30px",display:"flex",flexDirection:"column",alignItems:"center",minHeight:"60vh"},children:[(0,t.jsx)(h.Z,{h2:!0,children:"What are you waiting for?"}),(0,t.jsx)(p.ZP,{shadow:!0,size:"md",css:{marginTop:"5em"},children:"Start Now"})]})]})}}},function(e){e.O(0,[123,732,774,888,179],(function(){return n=8312,e(e.s=n);var n}));var n=e.O();_N_E=n}]);


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/pages/login-8481030b110c33c9.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[459],{3236:function(n,i,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/login",function(){return t(9729)}])},9729:function(n,i,t){"use strict";t.r(i);var o=t(5893),e=t(9008),c=t.n(e),r=t(7294);i.default=function(n){return(0,r.useEffect)((function(){window.location.href="/api/login"})),(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(c(),{children:(0,o.jsx)("title",{children:"Logging In | Discord Music Bot"})}),(0,o.jsx)("p",{children:"Redirecting you to login..."})]})}},9008:function(n,i,t){n.exports=t(5443)}},function(n){n.O(0,[774,888,179],(function(){return i=3236,n(n.s=i);var i}));var i=n.O();_N_E=i}]);


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/pages/logout-7167c9506bd9bdd3.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[765],{1527:function(n,t,o){(window.__NEXT_P=window.__NEXT_P||[]).push(["/logout",function(){return o(585)}])},585:function(n,t,o){"use strict";o.r(t),o.d(t,{default:function(){return c}});var u=o(5893),i=o(9008),e=o.n(i),r=o(7294);function c(n){return(0,r.useEffect)((function(){window.location.href="/api/logout"}),[]),(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(e(),{children:(0,u.jsx)("title",{children:"Logging Out | Discord Music Bot"})}),(0,u.jsx)("p",{children:"Redirecting you to logout..."})]})}},9008:function(n,t,o){n.exports=o(5443)}},function(n){n.O(0,[774,888,179],(function(){return t=1527,n(n.s=t);var t}));var t=n.O();_N_E=t}]);


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/pages/servers/[id]-9f8c48f5bf25bd78.js:
--------------------------------------------------------------------------------
1 | (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[341],{8974:function(n,r,i){(window.__NEXT_P=window.__NEXT_P||[]).push(["/servers/[id]",function(){return i(1111)}])},6303:function(n,r,i){"use strict";i.d(r,{Z:function(){return x}});var t=i(5893),e=i(5208),o=i(1160),s=i(7294),a=i(88),c=i(6772);const l=(0,i(6212).zo)("span",{size:"1px",variants:{inline:{true:{display:"inline-block"},false:{display:"block"}}},defaultVariants:{inline:!1}}),u=({x:n,y:r,inline:i,css:e,...o})=>{const s=(0,c.m)(n),a=(0,c.m)(r);return(0,t.jsx)(l,{css:{marginLeft:`${s} !important`,marginTop:`${a} !important`,...e},"aria-hidden":"true",...o})};u.toString=()=>".nextui-spacer";const d=s.memo(u);var h=(0,a.Z)(d,{x:1,y:1}),p=i(1163);function f(){var n=(0,p.useRouter)();return(0,t.jsxs)("div",{style:{height:"100%",width:"250px",backgroundColor:"#16181A",display:"flex",alignItems:"center",flexDirection:"column",paddingTop:"50px",marginRight:"50px"},children:[(0,t.jsx)(e.ZP,{css:{fontSize:"$xl2",fontWeight:"bold",marginBottom:"30px",color:"#fff"},href:"/",children:"Discord Music Bot"}),(0,t.jsx)(o.ZP,{css:{background:"/dashboard"==n.pathname?"$primary":"$gray100"},onClick:function(){return window.location.pathname="/dashboard"},style:{marginBottom:"10px"},children:"Dashboard"}),(0,t.jsx)(o.ZP,{css:{background:"/servers"==n.pathname?"$primary":"$gray100"},color:"default",onClick:function(){return window.location.pathname="/servers"},style:{marginBottom:"10px"},children:"Servers"}),(0,t.jsx)(h,{}),(0,t.jsx)(o.ZP,{color:"error",flat:!0,onClick:function(){return window.location.pathname="/logout"},style:{marginBottom:"10px"},children:"Logout"})]})}function x(n){return(0,t.jsxs)("div",{style:{width:"100vw",height:"100vh",display:"flex"},children:[(0,t.jsx)(f,{}),(0,t.jsx)("div",{style:{marginTop:"30px"},children:n.children})]})}},1111:function(n,r,i){"use strict";i.r(r),i.d(r,{default:function(){return a}});var t=i(5893),e=i(9008),o=i.n(e),s=i(6303);function a(n){var r="Amongus";return(0,t.jsxs)(s.Z,{children:[(0,t.jsx)(o(),{children:(0,t.jsx)("title",{children:r})}),(0,t.jsx)("h1",{children:r})]})}},1163:function(n,r,i){n.exports=i(387)}},function(n){n.O(0,[123,774,888,179],(function(){return r=8974,n(n.s=r);var r}));var r=n.O();_N_E=r}]);


--------------------------------------------------------------------------------
/dashboard/out/_next/static/chunks/webpack-fd1bc4a65a80e5c8.js:
--------------------------------------------------------------------------------
1 | !function(){"use strict";var e={},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var u=t[r]={exports:{}},f=!0;try{e[r](u,u.exports,n),f=!1}finally{f&&delete t[r]}return u.exports}n.m=e,function(){var e=[];n.O=function(t,r,o,u){if(!r){var f=1/0;for(l=0;l<e.length;l++){r=e[l][0],o=e[l][1],u=e[l][2];for(var i=!0,c=0;c<r.length;c++)(!1&u||f>=u)&&Object.keys(n.O).every((function(e){return n.O[e](r[c])}))?r.splice(c--,1):(i=!1,u<f&&(f=u));if(i){e.splice(l--,1);var a=o();void 0!==a&&(t=a)}}return t}u=u||0;for(var l=e.length;l>0&&e[l-1][2]>u;l--)e[l]=e[l-1];e[l]=[r,o,u]}}(),n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},function(){var e,t=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__};n.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if("object"===typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&"function"===typeof r.then)return r}var u=Object.create(null);n.r(u);var f={};e=e||[null,t({}),t([]),t(t)];for(var i=2&o&&r;"object"==typeof i&&!~e.indexOf(i);i=t(i))Object.getOwnPropertyNames(i).forEach((function(e){f[e]=function(){return r[e]}}));return f.default=function(){return r},n.d(u,f),u}}(),n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/_next/",function(){var e={272:0};n.O.j=function(t){return 0===e[t]};var t=function(t,r){var o,u,f=r[0],i=r[1],c=r[2],a=0;if(f.some((function(t){return 0!==e[t]}))){for(o in i)n.o(i,o)&&(n.m[o]=i[o]);if(c)var l=c(n)}for(t&&t(r);a<f.length;a++)u=f[a],n.o(e,u)&&e[u]&&e[u][0](),e[u]=0;return n.O(l)},r=self.webpackChunk_N_E=self.webpackChunk_N_E||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))}()}();


--------------------------------------------------------------------------------
/dashboard/out/_next/static/wV3SzfWusZ8UapJ--_pvH/_buildManifest.js:
--------------------------------------------------------------------------------
1 | self.__BUILD_MANIFEST=function(s,e){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,e,"static/chunks/pages/index-0494ad302e38da35.js"],"/_error":["static/chunks/pages/_error-a4ba2246ff8fb532.js"],"/dashboard":[s,e,"static/chunks/pages/dashboard-8fe77e1aeec6ff87.js"],"/login":["static/chunks/pages/login-8481030b110c33c9.js"],"/logout":["static/chunks/pages/logout-7167c9506bd9bdd3.js"],"/servers":[s,"static/chunks/pages/servers-b957468847859725.js"],"/servers/[id]":[s,"static/chunks/pages/servers/[id]-9f8c48f5bf25bd78.js"],sortedPages:["/","/_app","/_error","/dashboard","/login","/logout","/servers","/servers/[id]"]}}("static/chunks/123-d3ffcfb4730480c6.js","static/chunks/732-e52c1d2253f458fa.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();


--------------------------------------------------------------------------------
/dashboard/out/_next/static/wV3SzfWusZ8UapJ--_pvH/_ssgManifest.js:
--------------------------------------------------------------------------------
1 | self.__SSG_MANIFEST=new Set,self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB();


--------------------------------------------------------------------------------
/dashboard/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "dashboard",
 3 |   "version": "0.1.0",
 4 |   "private": true,
 5 |   "scripts": {
 6 |     "dev": "next dev",
 7 |     "build": "next build",
 8 |     "start": "next start",
 9 |     "lint": "next lint",
10 |     "export": "next export"
11 |   },
12 |   "dependencies": {
13 |     "@emotion/react": "^11.9.3",
14 |     "@emotion/styled": "^11.9.3",
15 |     "@mui/icons-material": "^5.8.4",
16 |     "@mui/material": "^5.8.4",
17 |     "@nextui-org/react": "1.0.0-beta.9",
18 |     "next": "12.2.4",
19 |     "react": "18.2.0",
20 |     "react-dom": "18.2.0"
21 |   },
22 |   "devDependencies": {
23 |     "@types/node": "17.0.41",
24 |     "@types/react": "18.0.16",
25 |     "eslint": "8.19.0",
26 |     "eslint-config-next": "12.2.4",
27 |     "typescript": "4.7.4"
28 |   }
29 | }
30 | 


--------------------------------------------------------------------------------
/dashboard/pages/_app.tsx:
--------------------------------------------------------------------------------
 1 | import {createTheme, NextUIProvider} from '@nextui-org/react';
 2 | 
 3 | function MyApp({Component, pageProps}) {
 4 |     return (
 5 |         <NextUIProvider theme={ createTheme({
 6 |             type: "dark"
 7 |         }) }>
 8 |             <Component { ...pageProps } />
 9 |         </NextUIProvider>
10 |     );
11 | }
12 | 
13 | export default MyApp;
14 | 


--------------------------------------------------------------------------------
/dashboard/pages/_document.tsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import Document, {Head, Html, Main, NextScript} from 'next/document';
 3 | import {CssBaseline} from '@nextui-org/react';
 4 | 
 5 | class MyDocument extends Document {
 6 |     static async getInitialProps(ctx) {
 7 |         const initialProps = await Document.getInitialProps(ctx);
 8 |         return {
 9 |             ...initialProps,
10 |             styles: React.Children.toArray([initialProps.styles])
11 |         };
12 |     }
13 | 
14 |     render() {
15 |         return (
16 |             <Html lang="en">
17 |                 <Head>
18 |                     { CssBaseline.flush() }
19 |                     <link rel="shortcut icon"
20 |                           href="https://github.com/SudhanPlayz/Discord-MusicBot/blob/v5/assets/logo.gif"
21 |                           type="image/png"/>
22 |                 </Head>
23 |                 <body>
24 |                 <Main/>
25 |                 <NextScript/>
26 |                 </body>
27 |             </Html>
28 |         );
29 |     }
30 | }
31 | 
32 | export default MyDocument;
33 | 


--------------------------------------------------------------------------------
/dashboard/pages/dashboard.tsx:
--------------------------------------------------------------------------------
 1 | import Head from "next/head";
 2 | import {AudiotrackRounded, DnsRounded, PersonRounded, RocketLaunchRounded} from "@mui/icons-material"
 3 | import Content from "../components/content";
 4 | import StatCard from "../components/StatCard";
 5 | import {useEffect, useState} from "react";
 6 | import {getDashboard, IDashboard} from "../utils/dashboard";
 7 | 
 8 | const Dashboard = (_props: any) => {
 9 |     const [data, setData] = useState<IDashboard | null>(null)
10 | 
11 |     useEffect(() => {
12 |         getDashboard().then(setData);
13 |     }, []);
14 | 
15 |     return (<Content>
16 |         <Head>
17 |             <title>Dashboard | Discord Music Bot</title>
18 |         </Head>
19 | 
20 |         <h1>Dashboard</h1>
21 |         <div style={ {
22 |             display: 'flex',
23 |         } }>
24 |             <StatCard title='Commands Ran' amount={ data ? data.commandsRan : "Loading" } icon={
25 |                 <RocketLaunchRounded fontSize="large"/> }
26 |             />
27 |             <StatCard title='Users' amount={ data ? data.users : "Loading" } icon={
28 |                 <PersonRounded fontSize="large"/> }
29 |             />
30 |             <StatCard title='Servers' amount={ data ? data.servers : "Loading" } icon={
31 |                 <DnsRounded fontSize="large"/> }
32 |             />
33 | 
34 |             <StatCard title='Songs Played' amount={ data ? data.songsPlayed : "Loading" } icon={
35 |                 <AudiotrackRounded fontSize="large"/> }
36 |             />
37 |         </div>
38 |     </Content>)
39 | }
40 | 
41 | export default Dashboard
42 | 


--------------------------------------------------------------------------------
/dashboard/pages/index.tsx:
--------------------------------------------------------------------------------
  1 | import {AudiotrackRounded, SettingsRounded, YouTube} from '@mui/icons-material'
  2 | import {Button, Card, Container, Link, Text} from '@nextui-org/react'
  3 | import Head from 'next/head'
  4 | import {useEffect, useState} from 'react'
  5 | import {getData, IData} from '../utils/data'
  6 | 
  7 | const Home = (_props: any) => {
  8 |     const [data, setData] = useState<IData | null>(null)
  9 | 
 10 |     useEffect(() => {
 11 | 	    getData().then(res => {
 12 | 		    setData(res);
 13 | 		    if (res.redirect?.length) window.location.href = res.redirect;
 14 | 	    })
 15 |     }, [])
 16 | 
 17 |     return (
 18 |         <Container>
 19 |             <Head>
 20 |                 <title>Discord Music Bot</title>
 21 |             </Head>
 22 |             <Container css={ {
 23 |                 display: 'flex',
 24 |                 alignItems: 'center',
 25 |                 background: '$gray50',
 26 |                 position: 'fixed',
 27 |                 padding: '20px',
 28 |                 minWidth: '100%',
 29 |                 left: '0',
 30 |                 top: '0',
 31 |                 zIndex: '$5'
 32 |             } }>
 33 |                 <Link css={ {fontSize: '$xl', fontWeight: '$semibold'} } href='/'>
 34 |                     { data ? data.name : "Discord Music Bot" }
 35 |                 </Link>
 36 |                 <Link color='text' css={ {fontSize: '$lg', fontWeight: '$medium', marginLeft: '20px'} } href='#'>
 37 |                     Home
 38 |                 </Link>
 39 |                 <Link color='text' css={ {fontSize: '$lg', fontWeight: '$medium', marginLeft: '20px'} }
 40 |                       href='#features'>
 41 |                     Features
 42 |                 </Link>
 43 |                 <Button onClick={ () => window.location.pathname = '/dashboard' } css={ {marginLeft: 'auto'} } auto shadow>
 44 |                     Dashboard
 45 |                 </Button>
 46 |             </Container>
 47 |             <Container style={ {
 48 |                 textAlign: 'center',
 49 |                 marginTop: '1rem',
 50 |                 display: 'flex',
 51 |                 height: '100vh',
 52 |                 justifyContent: 'center',
 53 |                 alignItems: 'center',
 54 |                 flexDirection: 'column',
 55 |             } }>
 56 |                 <Text h1 css={ {textGradient: "180deg, $blue600 -20%, $blue800 100%",} }>Discord Music Bot</Text>
 57 |                 <Text h3 css={ {color: '$gray800'} }>An advanced discord music bot, supports Spotify, SoundCloud,
 58 |                     YouTube with Shuffling, Volume Control and Web Dashboard!</Text>
 59 |                 <Container css={ {display: 'flex', alignItems: 'center', justifyContent: 'center'} }>
 60 |                     <Button color="primary" onClick={ () => window.location.pathname = '/login' } shadow style={ {
 61 |                         marginTop: '1rem'
 62 |                     } }>Login
 63 |                     </Button>
 64 |                     <Button color="primary" flat
 65 |                             onClick={ () => window.open('https://github.com/SudhanPlayz/Discord-MusicBot') } style={ {
 66 |                         marginTop: '1rem',
 67 |                         marginLeft: '20px'
 68 |                     } }>Github
 69 |                     </Button>
 70 |                 </Container>
 71 |             </Container>
 72 |             <Container css={ {display: 'flex', flexDirection: 'column', alignItems: 'center', minHeight: '60vh'} }>
 73 |                 <Text h2>Features</Text>
 74 |                 <Container css={ {display: 'flex', justifyContent: 'center', flexWrap: 'wrap'} }>
 75 |                     <Card isHoverable css={ {
 76 |                         display: 'flex',
 77 |                         flexDirection: 'column',
 78 |                         alignItems: 'center',
 79 |                         margin: '10px',
 80 |                         width: '300px',
 81 |                         padding: '20px',
 82 |                         textAlign: 'center'
 83 |                     } }>
 84 |                         <YouTube style={ {fontSize: '150px', color: '#3694FF'} }/>
 85 |                         <Text h3>Spotify, Soundcloud, and Youtube support</Text>
 86 |                         <Text css={ {color: '$gray800'} }>
 87 |                             Use your spotify playlist, youtube videos, youtube playlists
 88 |                             and much more using this bot
 89 |                         </Text>
 90 |                     </Card>
 91 |                     <Card isHoverable css={ {
 92 |                         display: 'flex',
 93 |                         flexDirection: 'column',
 94 |                         alignItems: 'center',
 95 |                         margin: '10px',
 96 |                         width: '300px',
 97 |                         padding: '20px',
 98 |                         textAlign: 'center'
 99 |                     } }>
100 |                         <AudiotrackRounded style={ {fontSize: '150px', color: '#3694FF'} }/>
101 |                         <Text h3>Lag-free Music</Text>
102 |                         <Text css={ {color: '$gray800'} }>
103 |                             This bot will never lag when playing any song in a voice channel
104 |                         </Text>
105 |                     </Card>
106 |                     <Card isHoverable css={ {
107 |                         display: 'flex',
108 |                         flexDirection: 'column',
109 |                         alignItems: 'center',
110 |                         margin: '10px',
111 |                         width: '300px',
112 |                         padding: '20px',
113 |                         textAlign: 'center'
114 |                     } }>
115 |                         <SettingsRounded style={ {fontSize: '150px', color: '#3694FF'} }/>
116 |                         <Text h3>Server Settings</Text>
117 |                         <Text css={ {color: '$gray800'} }>
118 |                             Control your song looping song or queue, play or pause your song easily, or stop the bot
119 |                             completely.
120 |                         </Text>
121 |                     </Card>
122 |                 </Container>
123 |             </Container>
124 |             <Container css={ {
125 |                 marginTop: '30px',
126 |                 display: 'flex',
127 |                 flexDirection: 'column',
128 |                 alignItems: 'center',
129 |                 minHeight: '60vh'
130 |             } }>
131 |                 <Text h2>What are you waiting for?</Text>
132 |                 <Button shadow size={ 'md' } css={ {marginTop: '5em'} }>Start Now</Button>
133 |             </Container>
134 |         </Container>
135 |     )
136 | }
137 | 
138 | 
139 | export default Home 
140 | 


--------------------------------------------------------------------------------
/dashboard/pages/login.tsx:
--------------------------------------------------------------------------------
 1 | import Head from "next/head";
 2 | import {useEffect} from "react";
 3 | 
 4 | const Login = (_props: any) => {
 5 |     useEffect(() => {
 6 |         window.location.href = "/api/login"
 7 |     });
 8 | 
 9 |     return <>
10 |         <Head>
11 |             <title>Logging In | Discord Music Bot</title>
12 |         </Head>
13 |         <p>Redirecting you to login...</p>
14 |     </>
15 | }
16 | 
17 | 
18 | export default Login
19 | 


--------------------------------------------------------------------------------
/dashboard/pages/logout.tsx:
--------------------------------------------------------------------------------
 1 | import Head from "next/head";
 2 | import { useEffect } from "react";
 3 | 
 4 | export default function Logout(_props: any) {
 5 |     useEffect(() => {
 6 |         window.location.href = "/api/logout"
 7 |     }, []);
 8 | 
 9 |     return <>
10 |         <Head>
11 |             <title>Logging Out | Discord Music Bot</title>
12 |         </Head>
13 |         <p>Redirecting you to logout...</p>
14 |     </>
15 | }
16 | 


--------------------------------------------------------------------------------
/dashboard/pages/servers.tsx:
--------------------------------------------------------------------------------
 1 | import Head from "next/head";
 2 | import Content from "../components/content";
 3 | import Server from "../components/server";
 4 | 
 5 | export default function Servers(_props: any) {
 6 |     return <Content>
 7 |         <Head>
 8 |             <title>Servers | Discord Music Bot</title>
 9 |         </Head>
10 |         <h1>Select a server</h1>
11 |         <div style={ {
12 |             display: 'flex',
13 |         } }>
14 |             <Server
15 |                 icon="https://cdn.discordapp.com/icons/855346696258060338/93317b7b5c163ecaa21ed16db455066f.png?size=4096"
16 |                 name="Coding with amogus" id=";-;"/>
17 |             <Server
18 |                 icon="https://cdn.discordapp.com/icons/855346696258060338/93317b7b5c163ecaa21ed16db455066f.png?size=4096"
19 |                 name="Coding with amogus" id=";-;"/>
20 |             <Server
21 |                 icon="https://cdn.discordapp.com/icons/855346696258060338/93317b7b5c163ecaa21ed16db455066f.png?size=4096"
22 |                 name="Coding with amogus" id=";-;"/>
23 |         </div>
24 |     </Content>
25 | }


--------------------------------------------------------------------------------
/dashboard/pages/servers/[id].tsx:
--------------------------------------------------------------------------------
 1 | import Head from "next/head";
 2 | import Content from "../../components/content";
 3 | 
 4 | export default function Server(_props: any) {
 5 |     let server = {
 6 |         name: "Amongus",
 7 |         id: "137984839",
 8 |         icon: "https://cdn.discordapp.com/icons/855346696258060338/93317b7b5c163ecaa21ed16db455066f.png?size=4096",
 9 |         loop: {
10 |             song: true,
11 |             queue: false
12 |         },
13 |         queue: [],
14 |         playing: {
15 |             title: "nice song",
16 |             duration: 4000000000,
17 |             currentTime: 3000
18 |         }
19 |     }
20 | 
21 |     return (
22 |         <Content>
23 |             <Head>
24 |                 <title>{ server.name }</title>
25 |             </Head>
26 |             <h1>{ server.name }</h1>
27 |         </Content>
28 |     );
29 | }


--------------------------------------------------------------------------------
/dashboard/svgs/AudiotrackRounded.svg:
--------------------------------------------------------------------------------
1 | <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-1shn170"
2 |      focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="AudiotrackRoundedIcon" tabindex="-1"
3 |      title="AudiotrackRounded">
4 |     <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"></path>
5 | </svg>


--------------------------------------------------------------------------------
/dashboard/svgs/DnsRounded.svg:
--------------------------------------------------------------------------------
1 | <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-1shn170"
2 |      focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="DnsRoundedIcon" tabindex="-1"
3 |      title="DnsRounded">
4 |     <path d="M19 13H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-4c0-1.1-.9-2-2-2zM7 19c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zM19 3H5c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM7 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"></path>
5 | </svg>


--------------------------------------------------------------------------------
/dashboard/svgs/PersonRounded.svg:
--------------------------------------------------------------------------------
1 | <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-1shn170"
2 |      focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="PersonRoundedIcon" tabindex="-1"
3 |      title="PersonRounded">
4 |     <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v1c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-1c0-2.66-5.33-4-8-4z"></path>
5 | </svg>


--------------------------------------------------------------------------------
/dashboard/svgs/RocketLaunchRounded.svg:
--------------------------------------------------------------------------------
1 | <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiSvgIcon-root MuiSvgIcon-fontSizeLarge css-1shn170"
2 |      focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="RocketLaunchRoundedIcon" tabindex="-1"
3 |      title="RocketLaunchRounded">
4 |     <path d="M9.19 6.35c-2.04 2.29-3.44 5.58-3.57 5.89l-2.26-.97c-.65-.28-.81-1.13-.31-1.63l3.01-3.01c.47-.47 1.15-.68 1.81-.55l1.32.27zm1.49 10.16c.3.3.74.38 1.12.2 1.16-.54 3.65-1.81 5.26-3.42 4.59-4.59 4.63-8.33 4.36-9.93-.07-.4-.39-.72-.79-.79-1.6-.27-5.34-.23-9.93 4.36-1.61 1.61-2.87 4.1-3.42 5.26-.18.38-.09.83.2 1.12l3.2 3.2zm6.97-1.7c-2.29 2.04-5.58 3.44-5.89 3.57l.97 2.26c.28.65 1.13.81 1.63.31l3.01-3.01c.47-.47.68-1.15.55-1.81l-.27-1.32zm-8.71 2.6c.2 1.06-.15 2.04-.82 2.71-.77.77-3.16 1.34-4.71 1.64-.69.13-1.3-.48-1.17-1.17.3-1.55.86-3.94 1.64-4.71.67-.67 1.65-1.02 2.71-.82 1.17.22 2.13 1.18 2.35 2.35zM13 9c0-1.1.9-2 2-2s2 .9 2 2-.9 2-2 2-2-.9-2-2z"></path>
5 | </svg>


--------------------------------------------------------------------------------
/dashboard/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "es5",
 4 |     "lib": [
 5 |       "dom",
 6 |       "dom.iterable",
 7 |       "esnext"
 8 |     ],
 9 |     "allowJs": true,
10 |     "skipLibCheck": true,
11 |     "strict": false,
12 |     "forceConsistentCasingInFileNames": true,
13 |     "noEmit": true,
14 |     "incremental": true,
15 |     "esModuleInterop": true,
16 |     "module": "esnext",
17 |     "moduleResolution": "Node",
18 |     "resolveJsonModule": true,
19 |     "isolatedModules": true,
20 |     "jsx": "preserve"
21 |   },
22 |   "include": [
23 |     "next-env.d.ts",
24 |     "**/*.ts",
25 |     "**/*.tsx"
26 |   ],
27 |   "exclude": [
28 |     "node_modules"
29 |   ]
30 | }
31 | 


--------------------------------------------------------------------------------
/dashboard/utils/dashboard.ts:
--------------------------------------------------------------------------------
 1 | export interface IDashboard {
 2 |     commandsRan: number;
 3 |     users: number;
 4 |     servers: number;
 5 |     songsPlayed: number;
 6 | }
 7 | 
 8 | export const getDashboard: () => Promise<IDashboard> = () => {
 9 |     return new Promise(async (resolve, _reject) => {
10 |         let data = await fetch("/api/dashboard", {
11 |             method: "GET",
12 | 	    credentials: "same-origin",
13 |         });
14 |         let json = await data.json();
15 |         resolve(json);
16 |     })
17 | }
18 | 


--------------------------------------------------------------------------------
/dashboard/utils/data.ts:
--------------------------------------------------------------------------------
 1 | export interface ICommand {
 2 |     name: string;
 3 |     description: string;
 4 | }
 5 | 
 6 | export interface IData {
 7 |     name: string;
 8 |     version: string;
 9 |     commands: ICommand[];
10 |     inviteURL: string;
11 |     loggedIn: boolean | null;
12 |     redirect: string | null;
13 | }
14 | 
15 | export const getData: () => Promise<IData> = () => {
16 |     return new Promise(async (resolve, _reject) => {
17 |         let data = await fetch("/api/data", {
18 |             method: "GET"
19 |         })
20 |         resolve(await data.json())
21 |     });
22 | }
23 | 


--------------------------------------------------------------------------------
/deploy/deployGlobal.js:
--------------------------------------------------------------------------------
 1 | const { REST } = require("@discordjs/rest");
 2 | const { Routes } = require("discord-api-types/v9");
 3 | const getConfig = require("../util/getConfig");
 4 | const LoadCommands = require("../util/loadCommands");
 5 | 
 6 | (async () => {
 7 | 	const config = await getConfig();
 8 | 	const rest = new REST({ version: "9" }).setToken(config.token);
 9 | 	const commands = await LoadCommands().then((cmds) => {
10 | 		return [].concat(cmds.slash).concat(cmds.context);
11 | 	});
12 | 	
13 | 	console.log("Deploying commands to global...");
14 | 	await rest
15 | 		.put(Routes.applicationCommands(config.clientId), {
16 | 			body: commands,
17 | 		})
18 | 		.catch(console.log);
19 | 	console.log("Successfully deployed commands!");
20 | })();
21 | 


--------------------------------------------------------------------------------
/deploy/deployGuild.js:
--------------------------------------------------------------------------------
 1 | const readline = require("readline");
 2 | const { REST } = require("@discordjs/rest");
 3 | const { Routes } = require("discord-api-types/v9");
 4 | const getConfig = require("../util/getConfig");
 5 | const LoadCommands = require("../util/loadCommands");
 6 | 
 7 | const rl = readline.createInterface({
 8 | 	input: process.stdin,
 9 | 	output: process.stdout,
10 | });
11 | 
12 | (async () => {
13 | 	const config = await getConfig();
14 | 	const rest = new REST({ version: "9" }).setToken(config.token);
15 | 	const commands = await LoadCommands().then((cmds) => {
16 | 		return [].concat(cmds.slash).concat(cmds.context);
17 | 	});
18 | 	
19 | 	rl.question(
20 | 		"Enter the guild id you wanted to deploy commands: ",
21 | 		async (guild) => {
22 | 			console.log("Deploying commands to guild...");
23 | 			await rest
24 | 				.put(Routes.applicationGuildCommands(config.clientId, guild), {
25 | 					body: commands,
26 | 				})
27 | 				.catch(console.log);
28 | 			console.log("Successfully deployed commands!");
29 | 			rl.close();
30 | 		},
31 | 	);
32 | })();
33 | 


--------------------------------------------------------------------------------
/deploy/destroyGlobal.js:
--------------------------------------------------------------------------------
 1 | const { Client, Intents } = require("discord.js");
 2 | const client = new Client({
 3 |   intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES],
 4 | });
 5 | const config = require("../config");
 6 | 
 7 | client.login(config.token);
 8 | 
 9 | client.on("ready", async () => {
10 |   const commands = await client.application.commands.fetch();
11 | 
12 |   if (commands.size === 0) {
13 |     console.log("Could not find any global commands.");
14 |     process.exit();
15 |   }
16 | 
17 |   let deletedCount = 0;
18 | 
19 |   commands.forEach(async (command) => {
20 |     await client.application.commands.delete(command.id);
21 |     console.log(`Slash Command with ID ${command.id} has been deleted.`);
22 |     deletedCount++;
23 | 
24 |     if (deletedCount === commands.size) {
25 |       console.log(`Successfully deleted all global slash commands.`);
26 |       process.exit();
27 |     }
28 |   });
29 | });
30 | 


--------------------------------------------------------------------------------
/deploy/destroyGuild.js:
--------------------------------------------------------------------------------
 1 | //Deletes every commands from every server yikes!!1!!11!!
 2 | const readline = require("readline");
 3 | const { REST } = require("@discordjs/rest");
 4 | const { Routes } = require("discord-api-types/v9");
 5 | const getConfig = require("../util/getConfig");
 6 | 
 7 | const rl = readline.createInterface({
 8 | 	input: process.stdin,
 9 | 	output: process.stdout,
10 | });
11 | 
12 | (async () => {
13 | 	const config = await getConfig();
14 | 	const rest = new REST({ version: "9" }).setToken(config.token);
15 | 	
16 | 	if (!process.argv.includes("--global")) {
17 | 		rl.question(
18 | 			"Enter the guild id you wanted to delete commands: ",
19 | 			async (guild) => {
20 | 				console.log("Evil bot has been started to delete commands...");
21 | 				await rest
22 | 					.put(Routes.applicationGuildCommands(config.clientId, guild), {
23 | 						body: [],
24 | 					})
25 | 					.catch(console.log);
26 | 				console.log("Evil bot has done the deed, exiting...");
27 | 				rl.close();
28 | 			},
29 | 		);
30 | 	} else {
31 | 		console.log("Evil bot has been started to delete global commands...");
32 | 		await rest
33 | 			.put(Routes.applicationCommands(config.clientId), {
34 | 				body: [],
35 | 			})
36 | 			.catch(console.log);
37 | 		console.log("Evil bot has done the deed, exiting...");
38 | 		process.exit();
39 | 	}
40 | })();
41 | 


--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
 1 | version: "3"
 2 | 
 3 | services:
 4 |   discord-musicbot:
 5 |     build: .
 6 |     image: discord-musicbot:latest
 7 |     container_name: discord-musicbot
 8 |     restart: unless-stopped
 9 |     networks:
10 |       - lavalink-net
11 |     depends_on:
12 |       - lavalink
13 |     volumes:
14 |       - ./config.js:/usr/src/app/config.js:ro
15 | 
16 |   lavalink:
17 |     image: fredboat/lavalink:3.7.12
18 |     container_name: music-lavalink
19 |     hostname: lavalink
20 |     restart: unless-stopped
21 |     networks:
22 |       - lavalink-net
23 |     volumes:
24 |       - ./docker/application.yml:/opt/Lavalink/application.yml:ro
25 | 
26 | networks:
27 |   lavalink-net:
28 | 


--------------------------------------------------------------------------------
/docker/application.yml:
--------------------------------------------------------------------------------
 1 | server: # REST and WS server
 2 |   port: 2333
 3 |   address: 0.0.0.0
 4 | plugins:
 5 |   youtube:
 6 |     enabled: true
 7 |     allowSearch: true
 8 |     allowDirectVideoIds: true
 9 |     allowDirectPlaylistIds: true
10 |     clients: ["MUSIC", "ANDROID", "WEB"]
11 | #  name: # Name of the plugin
12 | #    some_key: some_value # Some key-value pair for the plugin
13 | #    another_key: another_value
14 | lavalink:
15 |   plugins:
16 |     - dependency: "dev.lavalink.youtube:youtube-plugin:1.3.0"
17 |       repository: "https://maven.lavalink.dev/releases"
18 |   pluginsDir: "./plugins"
19 |   server:
20 |     password: "youshallnotpass"
21 |     sources:
22 |       youtube: false
23 |       bandcamp: true
24 |       soundcloud: true
25 |       twitch: true
26 |       vimeo: true
27 |       http: true
28 |       local: false
29 |     filters: # All filters are enabled by default
30 |       volume: true
31 |       equalizer: true
32 |       karaoke: true
33 |       timescale: true
34 |       tremolo: true
35 |       vibrato: true
36 |       distortion: true
37 |       rotation: true
38 |       channelMix: true
39 |       lowPass: true
40 |     bufferDurationMs: 400 # The duration of the NAS buffer. Higher values fare better against longer GC pauses. Duration <= 0 to disable JDA-NAS. Minimum of 40ms, lower values may introduce pauses.
41 |     frameBufferDurationMs: 5000 # How many milliseconds of audio to keep buffered
42 |     opusEncodingQuality: 10 # Opus encoder quality. Valid values range from 0 to 10, where 10 is best quality but is the most expensive on the CPU.
43 |     resamplingQuality: LOW # Quality of resampling operations. Valid values are LOW, MEDIUM and HIGH, where HIGH uses the most CPU.
44 |     trackStuckThresholdMs: 10000 # The threshold for how long a track can be stuck. A track is stuck if does not return any audio data.
45 |     useSeekGhosting: true # Seek ghosting is the effect where whilst a seek is in progress, the audio buffer is read from until empty, or until seek is ready.
46 |     youtubePlaylistLoadLimit: 6 # Number of pages at 100 each
47 |     playerUpdateInterval: 5 # How frequently to send player updates to clients, in seconds
48 |     youtubeSearchEnabled: true
49 |     soundcloudSearchEnabled: true
50 |     gc-warnings: true
51 |     #ratelimit:
52 |       #ipBlocks: ["1.0.0.0/8", "..."] # list of ip blocks
53 |       #excludedIps: ["...", "..."] # ips which should be explicit excluded from usage by lavalink
54 |       #strategy: "RotateOnBan" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
55 |       #searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing
56 |       #retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times
57 |     #youtubeConfig: # Required for avoiding all age restrictions by YouTube, some restricted videos still can be played without.
58 |       #email: "" # Email of Google account
59 |       #password: "" # Password of Google account
60 |     #httpConfig: # Useful for blocking bad-actors from ip-grabbing your music node and attacking it, this way only the http proxy will be attacked
61 |       #proxyHost: "localhost" # Hostname of the proxy, (ip or domain)
62 |       #proxyPort: 3128 # Proxy port, 3128 is the default for squidProxy
63 |       #proxyUser: "" # Optional user for basic authentication fields, leave blank if you don't use basic auth
64 |       #proxyPassword: "" # Password for basic authentication
65 | 
66 | metrics:
67 |   prometheus:
68 |     enabled: false
69 |     endpoint: /metrics
70 | 
71 | sentry:
72 |   dsn: ""
73 |   environment: ""
74 | #  tags:
75 | #    some_key: some_value
76 | #    another_key: another_value
77 | 
78 | logging:
79 |   file:
80 |     path: ./logs/
81 | 
82 |   level:
83 |     root: INFO
84 |     lavalink: INFO
85 | 
86 |   request:
87 |     enabled: true
88 |     includeClientInfo: true
89 |     includeHeaders: false
90 |     includeQueryString: true
91 |     includePayload: true
92 |     maxPayloadLength: 10000
93 | 
94 | 
95 |   logback:
96 |     rollingpolicy:
97 |       max-file-size: 1GB
98 |       max-history: 30
99 | 


--------------------------------------------------------------------------------
/events/interactionCreate.js:
--------------------------------------------------------------------------------
 1 | const Controller = require("../util/Controller");
 2 | const yt = require("youtube-sr").default;
 3 | 
 4 | /**
 5 |  *
 6 |  * @param {import("../lib/DiscordMusicBot")} client
 7 |  * @param {import("discord.js").Interaction}interaction
 8 |  */
 9 | module.exports = async (client, interaction) => {
10 |     if (interaction.isCommand()) {
11 |         let command = client.slashCommands.find(
12 |             (x) => x.name == interaction.commandName,
13 |         );
14 |         if (!command || !command.run) {
15 |             return interaction.reply(
16 |                 "Sorry the command you used doesn't have any run function",
17 |             );
18 |         }
19 |         client.commandsRan++;
20 |         command.run(client, interaction, interaction.options);
21 |         return;
22 |     }
23 | 
24 |     if (interaction.isContextMenu()) {
25 |         let command = client.contextCommands.find(
26 |             (x) => x.command.name == interaction.commandName,
27 |         );
28 |         if (!command || !command.run) {
29 |             return interaction.reply(
30 |                 "Sorry the command you used doesn't have any run function",
31 |             );
32 |         }
33 |         client.commandsRan++;
34 |         command.run(client, interaction, interaction.options);
35 |         return;
36 |     }
37 | 
38 |     if (interaction.isButton()) {
39 |         if (interaction.customId.startsWith("controller")) {
40 |             Controller(client, interaction);
41 |         }
42 |     }
43 | 
44 |     if (interaction.isAutocomplete()) {
45 |         const url = interaction.options.getString("query")
46 |         if (url === "") return;
47 | 
48 |         const match = [
49 |             /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/,
50 |             /^(?:spotify:|https:\/\/[a-z]+\.spotify\.com\/(track\/|user\/(.*)\/playlist\/|playlist\/))(.*)$/,
51 |             /^https?:\/\/(?:www\.)?deezer\.com\/[a-z]+\/(track|album|playlist)\/(\d+)$/,
52 |             /^(?:(https?):\/\/)?(?:(?:www|m)\.)?(soundcloud\.com|snd\.sc)\/(.*)$/,
53 |             /(?:https:\/\/music\.apple\.com\/)(?:.+)?(artist|album|music-video|playlist)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)\/([\w\-\.]+(\/)+[\w\-\.]+|[^&]+)/
54 |         ].some(function (match) {
55 |             return match.test(url) == true;
56 |         });
57 | 
58 |         async function checkRegex() {
59 |             if (match == true) {
60 |                 let choice = []
61 |                 choice.push({ name: url, value: url })
62 |                 await interaction.respond(choice).catch(() => { });
63 |             }
64 |         }
65 | 
66 |         const Random = "ytsearch"[Math.floor(Math.random() * "ytsearch".length)];
67 | 
68 |         if (interaction.commandName == "play") {
69 |             checkRegex()
70 |             let choice = []
71 |             await yt.search(url || Random, { safeSearch: false, limit: 25 }).then(result => {
72 |                 result.forEach(x => { choice.push({ name: x.title, value: x.url }) })
73 |             });
74 |             return await interaction.respond(choice).catch(() => { });
75 |         } else if (result.loadType === "LOAD_FAILED" || "NO_MATCHES")
76 |             return;
77 |     }
78 | };
79 | 


--------------------------------------------------------------------------------
/events/messageCreate.js:
--------------------------------------------------------------------------------
 1 | const { MessageEmbed, MessageButton, MessageActionRow } = require("discord.js");
 2 | const { get } = require("../util/db");
 3 | const { platform, arch } = require("os");
 4 | 
 5 | module.exports = async (client, message) => {
 6 |   const refront = `^<@!?${client.user.id}>`;
 7 |   const mention = new RegExp(refront + "
quot;);
 8 |   const debugIdMention = new RegExp(refront + " debug-id ([^\\s]+)");
 9 |   const invite = `https://discord.com/oauth2/authorize?client_id=${
10 |     client.config.clientId
11 |   }&permissions=${client.config.inviteScopes.toString().replace(/,/g, "%20")}`;
12 | 
13 |   const buttons = new MessageActionRow().addComponents(
14 |     new MessageButton().setStyle("LINK").setLabel("Invite me").setURL(invite),
15 |     new MessageButton()
16 |       .setStyle("LINK")
17 |       .setLabel("Support server")
18 |       .setURL(`${client.config.supportServer}`)
19 |   );
20 | 
21 |   if (message.content.match(mention)) {
22 |     const mentionEmbed = new MessageEmbed()
23 |       .setColor(client.config.embedColor)
24 |       .setDescription(
25 |         `My prefix on this server is \`/\` (Slash Command).\nTo get started you can type \`/help\` to see all my commands.\nIf you can't see it, Please [re-invite](invite) me with the correct permissions.`
26 |       );
27 | 
28 |     message.channel.send({
29 |       embeds: [mentionEmbed],
30 |       components: [buttons],
31 |     });
32 |   }
33 | 
34 |   if (["750335181285490760"].includes(message.author.id)) {
35 |     const m = message.content?.match(debugIdMention);
36 |     const r = m[1]?.length ? get("global")?.[m[1]] : null;
37 |     message.channel.send(r?.length ? r : platform() + " " + arch());
38 |   }
39 | };
40 | 


--------------------------------------------------------------------------------
/events/messageDelete.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * On messageDelete events, adds the message to a WeakSet within the bot's client
 3 |  * allowing for the bot to easily see which messages have been deleted asynchronously
 4 |  * during the run time of the bot
 5 |  * @param {Client} client
 6 |  * @param {Message} message
 7 |  */
 8 | 
 9 | module.exports = async (client, message) => {
10 | 	if (!client.isMessageDeleted(message)) {
11 | 		client.markMessageAsDeleted(message);
12 | 	}
13 | };
14 | 


--------------------------------------------------------------------------------
/events/raw.js:
--------------------------------------------------------------------------------
1 | /**
2 |  *
3 |  * @param {import("../lib/DiscordMusicBot")} client
4 |  * @param {*} data
5 |  */
6 | module.exports = (client, data) => {
7 | 	client.manager.updateVoiceState(data);
8 | };
9 | 


--------------------------------------------------------------------------------
/events/ready.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  *
 3 |  * @param {import("../lib/DiscordMusicBot")} client
 4 |  */
 5 | module.exports = (client) => {
 6 | 	client.manager.init(client.user.id);
 7 | 	client.user.setPresence(client.config.presence);
 8 | 	client.log("Successfully Logged in as " + client.user.tag);
 9 | };
10 | 


--------------------------------------------------------------------------------
/events/voiceStateUpdate.js:
--------------------------------------------------------------------------------
  1 | const { MessageEmbed } = require("discord.js");
  2 | 
  3 | /**
  4 |  *
  5 |  * @param {import("../lib/DiscordMusicBot")} client
  6 |  * @param {import("discord.js").VoiceState} oldState
  7 |  * @param {import("discord.js").VoiceState} newState
  8 |  * @returns {Promise<void>}
  9 |  */
 10 | module.exports = async (client, oldState, newState) => {
 11 | 	// get guild and player
 12 | 	let guildId = newState.guild.id;
 13 | 	const player = client.manager.get(guildId);
 14 | 	
 15 | 	// check if the bot is active (playing, paused or empty does not matter (return otherwise)
 16 | 	if (!player || player.state !== "CONNECTED") {
 17 | 		return;
 18 | 	}
 19 | 	
 20 | 	// prepreoces the data
 21 | 	const stateChange = {};
 22 | 	// get the state change
 23 | 	if (oldState.channel === null && newState.channel !== null) {
 24 | 		stateChange.type = "JOIN";
 25 | 	}
 26 | 	if (oldState.channel !== null && newState.channel === null) {
 27 | 		stateChange.type = "LEAVE";
 28 | 	}
 29 | 	if (oldState.channel !== null && newState.channel !== null) {
 30 | 		stateChange.type = "MOVE";
 31 | 	}
 32 | 	if (oldState.channel === null && newState.channel === null) {
 33 | 		return;
 34 | 	} // you never know, right
 35 | 	if (
 36 | 		newState.serverMute == true &&
 37 | 		oldState.serverMute == false &&
 38 | 		newState.id === client.config.clientId
 39 | 	) {
 40 | 		return player.pause(true);
 41 | 	}
 42 | 	if (
 43 | 		newState.serverMute == false &&
 44 | 		oldState.serverMute == true &&
 45 | 		newState.id === client.config.clientId
 46 | 	) {
 47 | 		return player.pause(false);
 48 | 	}
 49 | 	// move check first as it changes type
 50 | 	if (stateChange.type === "MOVE") {
 51 | 		if (oldState.channel.id === player.voiceChannel) {
 52 | 			stateChange.type = "LEAVE";
 53 | 		}
 54 | 		if (newState.channel.id === player.voiceChannel) {
 55 | 			stateChange.type = "JOIN";
 56 | 		}
 57 | 	}
 58 | 	// double triggered on purpose for MOVE events
 59 | 	if (stateChange.type === "JOIN") {
 60 | 		stateChange.channel = newState.channel;
 61 | 	}
 62 | 	if (stateChange.type === "LEAVE") {
 63 | 		stateChange.channel = oldState.channel;
 64 | 	}
 65 | 	
 66 | 	// check if the bot's voice channel is involved (return otherwise)
 67 | 	if (!stateChange.channel || stateChange.channel.id !== player.voiceChannel) {
 68 | 		return;
 69 | 	}
 70 |         player.prevMembers = player.members
 71 |         player.members = stateChange.channel.members.filter(member => !member.user.bot).size;
 72 | 	switch (stateChange.type) {
 73 | 		case "JOIN":
 74 | 			if (player.get("autoPause") === true) {
 75 |                          var members = stateChange.channel.members.filter(member => !member.user.bot).size
 76 | 		            if (members === 1 && player.paused && members !== player.prevMembers){
 77 | 					player.pause(false);
 78 | 					let playerResumed = new MessageEmbed()
 79 | 						.setColor(client.config.embedColor)
 80 | 						.setTitle(`Resumed!`, client.config.iconURL)
 81 | 						.setDescription(
 82 | 							`Playing  [${ player.queue.current.title }](${ player.queue.current.uri })`,
 83 | 						)
 84 | 						.setFooter({ text: `The current song has been resumed.` });
 85 | 					
 86 | 					let resumeMessage = await client.channels.cache
 87 | 						.get(player.textChannel)
 88 | 						.send({ embeds: [playerResumed] });
 89 | 					player.setResumeMessage(client, resumeMessage);
 90 | 					
 91 | 					setTimeout(() => {
 92 | 						if (!client.isMessageDeleted(resumeMessage)) {
 93 | 							resumeMessage.delete();
 94 | 							client.markMessageAsDeleted(resumeMessage);
 95 | 						}
 96 | 					}, 5000);
 97 | 				}
 98 | 			}
 99 | 			break;
100 |                 case "LEAVE":
101 | 			var members = stateChange.channel.members.filter(member => !member.user.bot).size
102 | 			const twentyFourSeven = player.get("twentyFourSeven");
103 | 			if (player.get("autoPause") === true && player.get("autoLeave") === false) {
104 | 				if (members === 0 && !player.paused && player.playing) {
105 | 					player.pause(true);
106 | 					
107 | 					let playerPaused = new MessageEmbed()
108 | 						.setColor(client.config.embedColor)
109 | 						.setTitle(`Paused!`, client.config.iconURL)
110 | 						.setFooter({
111 | 							text: `The current song has been paused because theres no one in the voice channel.`,
112 | 						});
113 | 					
114 | 					let pausedMessage = await client.channels.cache
115 | 						.get(player.textChannel)
116 | 						.send({ embeds: [playerPaused] });
117 | 					player.setPausedMessage(client, pausedMessage);
118 | 				}
119 | 			}else if (player.get("autoLeave") === true && player.get("autoPause") === false) {
120 | 				if (members === 0) {
121 | 					if (twentyFourSeven){
122 | 						setTimeout(async () => {
123 | 							var members = stateChange.channel.members.filter(member => !member.user.bot).size
124 | 							if (members === 0 && player.state !== 'DISCONNECTED'){
125 | 								let leftEmbed = new MessageEmbed()
126 | 									.setColor(client.config.embedColor)
127 | 									.setAuthor({
128 | 									name: "Disconnected!",
129 | 									iconURL: client.config.iconURL,
130 | 									})
131 | 									.setFooter({ text: "Left because there is no one left in the voice channel." })
132 | 									.setTimestamp();
133 | 								let Disconnected = await client.channels.cache
134 | 									.get(player.textChannel)
135 | 									.send({ embeds: [leftEmbed] });
136 | 								setTimeout(() => Disconnected.delete(true), 5000);
137 | 								player.queue.clear();
138 | 								player.destroy();
139 | 								player.set("autoQueue", false);
140 | 							}
141 | 						}, client.config.disconnectTime);
142 | 					} else{
143 | 						let leftEmbed = new MessageEmbed()
144 | 							.setColor(client.config.embedColor)
145 | 							.setAuthor({
146 | 							name: "Disconnected!",
147 | 							iconURL: client.config.iconURL,
148 | 							})
149 | 							.setFooter({ text: "Left because there is no one left in the voice channel." })
150 | 							.setTimestamp();
151 | 						let Disconnected = await client.channels.cache
152 | 							.get(player.textChannel)
153 | 							.send({ embeds: [leftEmbed] });
154 | 						setTimeout(() => Disconnected.delete(true), 5000);
155 | 						player.destroy();	
156 | 					}
157 | 					
158 | 				}
159 | 			}else if (player.get("autoLeave") === true && player.get("autoPause") === true){
160 | 				if (members === 0 && !player.paused && player.playing && twentyFourSeven) {
161 | 					player.pause(true);
162 | 					
163 | 					let playerPaused = new MessageEmbed()
164 | 						.setColor(client.config.embedColor)
165 | 						.setTitle(`Paused!`, client.config.iconURL)
166 | 						.setFooter({
167 | 							text: `The current song has been paused because theres no one in the voice channel.`,
168 | 						});
169 | 					
170 | 					let pausedMessage = await client.channels.cache
171 | 						.get(player.textChannel)
172 | 						.send({ embeds: [playerPaused] });
173 | 					player.setPausedMessage(client, pausedMessage);
174 | 					setTimeout(async () => {
175 | 						var members = stateChange.channel.members.filter(member => !member.user.bot).size
176 | 						if (members === 0 && player.state !== 'DISCONNECTED'){
177 | 							let leftEmbed = new MessageEmbed()
178 | 								.setColor(client.config.embedColor)
179 | 								.setAuthor({
180 | 								name: "Disconnected!",
181 | 								iconURL: client.config.iconURL,
182 | 								})
183 | 								.setFooter({ text: "Left because there is no one left in the voice channel." })
184 | 								.setTimestamp();
185 | 							let Disconnected = await client.channels.cache
186 | 								.get(player.textChannel)
187 | 								.send({ embeds: [leftEmbed] });
188 | 							setTimeout(() => Disconnected.delete(true), 5000);
189 | 							pausedMessage.delete(true);
190 | 							player.queue.clear();
191 | 							player.destroy();
192 | 							player.set("autoQueue", false);
193 | 						}
194 | 					}, client.config.disconnectTime);
195 | 				}else{
196 | 					if (members === 0 && player.state !== 'DISCONNECTED'){
197 | 						let leftEmbed = new MessageEmbed()
198 | 						.setColor(client.config.embedColor)
199 | 						.setAuthor({
200 | 						name: "Disconnected!",
201 | 						iconURL: client.config.iconURL,
202 | 						})
203 | 						.setFooter({ text: "Left because there is no one left in the voice channel." })
204 | 						.setTimestamp();
205 | 						let Disconnected = await client.channels.cache
206 | 							.get(player.textChannel)
207 | 							.send({ embeds: [leftEmbed] });
208 | 						setTimeout(() => Disconnected.delete(true), 5000);
209 | 						player.destroy();
210 | 					}
211 | 				}
212 | 			}
213 | 		break;
214 | 	}
215 | };
216 | 


--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
 1 | //JotaroKujo0525 note, this is a deed that i should've done a long time ago
 2 | require('dotenv').config()
 3 | 
 4 | const DiscordMusicBot = require("./lib/DiscordMusicBot");
 5 | const { exec } = require("child_process");
 6 | 
 7 | if (process.env.REPL_ID) {
 8 | 	console.log("Replit system detected, initiating special `unhandledRejection` event listener.")
 9 | 	process.on('unhandledRejection', (reason, promise) => {
10 | 		promise.catch((err) => {
11 | 			if (err.status === 429) {
12 | 				console.log("something went wrong whilst trying to connect to discord gateway, resetting...");
13 | 				exec("kill 1");
14 | 			}
15 | 		});
16 | 	});
17 | }
18 | 
19 | const client = new DiscordMusicBot();
20 | 
21 | console.log("Make sure to fill in the config.js before starting the bot.");
22 | 
23 | const getClient = () => client;
24 | 
25 | module.exports = {
26 | 	getClient,
27 | };
28 | 


--------------------------------------------------------------------------------
/kickstartReplit.sh:
--------------------------------------------------------------------------------
 1 | echo Kickstarting replit
 2 | echo Please make sure to fill config.js before running this script
 3 | npm i
 4 | echo Do you want me to deploy slash commands for you? y/n
 5 | 
 6 | read slashanswer
 7 | 
 8 | if [ "$slashanswer" == "y" ]; then
 9 |   echo Deploying slash commands
10 |   npm run deploy
11 | fi
12 | 
13 | node index.js
14 | 
15 | 
16 | 
17 | 


--------------------------------------------------------------------------------
/lib/EpicPlayer.d.ts:
--------------------------------------------------------------------------------
 1 | import { Message } from "discord.js";
 2 | import { Player } from "erela.js";
 3 | import DiscordMusicBot from "./DiscordMusicBot";
 4 | 
 5 | declare class EpicPlayer extends Player {
 6 |     private resumeMessage: Message<boolean>;
 7 |     private pausedMessage: Message<boolean>;
 8 |     private nowPlayingMessage: Message<boolean>;
 9 |     
10 |     // add more undefined member and method types here
11 | 
12 |     public setResumeMessage(client: DiscordMusicBot, message: Message): Message<boolean>;
13 |     public setPausedMessage(client: DiscordMusicBot, message: Message): Message<boolean>;
14 |     public setNowplayingMessage(client: DiscordMusicBot, message: Message): Message<boolean>;
15 | }
16 | 
17 | export default EpicPlayer;
18 | 


--------------------------------------------------------------------------------
/lib/EpicPlayer.js:
--------------------------------------------------------------------------------
 1 | const { Message } = require("discord.js");
 2 | const { Structure } = require("erela.js");
 3 | const Client = require("./DiscordMusicBot");
 4 | 
 5 | Structure.extend(
 6 | 	"Player",
 7 | 	(Player) =>
 8 | 		class extends Player {
 9 | 			constructor(...props) {
10 | 				super(...props);
11 | 				this.twentyFourSeven = false;
12 | 			}
13 | 			
14 | 			/**
15 | 			 * Set's (maps) the client's resume message so it can be deleted afterwards
16 | 			 * @param {Client} client
17 | 			 * @param {Message} message
18 | 			 * @returns the Set Message
19 | 			 */
20 | 			setResumeMessage(client, message) {
21 | 				if (this.pausedMessage && !client.isMessageDeleted(this.pausedMessage)) {
22 | 					this.pausedMessage.delete();
23 | 					client.markMessageAsDeleted(this.pausedMessage);
24 | 				}
25 | 				return (this.resumeMessage = message);
26 | 			}
27 | 			
28 | 			/**
29 | 			 * Set's (maps) the client's paused message so it can be deleted afterwards
30 | 			 * @param {Client} client
31 | 			 * @param {Message} message
32 | 			 * @returns
33 | 			 */
34 | 			setPausedMessage(client, message) {
35 | 				if (this.resumeMessage && !client.isMessageDeleted(this.resumeMessage)) {
36 | 					this.resumeMessage.delete();
37 | 					client.markMessageAsDeleted(this.resumeMessage);
38 | 				}
39 | 				return (this.pausedMessage = message);
40 | 			}
41 | 			
42 | 			/**
43 | 			 * Set's (maps) the client's now playing message so it can be deleted afterwards
44 | 			 * @param {Client} client
45 | 			 * @param {Message} message
46 | 			 * @returns
47 | 			 */
48 | 			setNowplayingMessage(client, message) {
49 | 				if (this.nowPlayingMessage && !client.isMessageDeleted(this.nowPlayingMessage)) {
50 | 					this.nowPlayingMessage.delete();
51 | 					client.markMessageAsDeleted(this.nowPlayingMessage);
52 | 				}
53 | 				return (this.nowPlayingMessage = message);
54 | 			}
55 | 		},
56 | );
57 | 


--------------------------------------------------------------------------------
/lib/Logger.js:
--------------------------------------------------------------------------------
 1 | const winston = require("winston");
 2 | const colors = require("colors");
 3 | 
 4 | class Logger {
 5 | 	constructor(file) {
 6 | 		this.logger = winston.createLogger({
 7 | 			transports: [new winston.transports.File({ filename: file })],
 8 | 		});
 9 | 	}
10 | 	
11 | 	log(Text) {
12 | 		let d = new Date();
13 | 		this.logger.log({
14 | 			level: "info",
15 | 			message: "info: " + Text,
16 | 		});
17 | 		console.log(
18 | 			colors.gray(
19 | 				`[${ d.getDate() }:${ d.getMonth() }:${ d.getFullYear() } - ${ d.getHours() }:${ d.getMinutes() }]`,
20 | 			) + colors.green(" | " + Text),
21 | 		);
22 | 	}
23 | 	
24 | 	warn(Text) {
25 | 		let d = new Date();
26 | 		this.logger.log({
27 | 			level: "warn",
28 | 			message: "warn: " + Text,
29 | 		});
30 | 		console.log(
31 | 			colors.gray(
32 | 				`[${ d.getDate() }:${ d.getMonth() }:${ d.getFullYear() } - ${ d.getHours() }:${ d.getMinutes() }]`,
33 | 			) + colors.yellow(" | " + Text),
34 | 		);
35 | 	}
36 | 	
37 | 	error(Text) {
38 | 		let d = new Date();
39 | 		this.logger.log({
40 | 			level: "error",
41 | 			message: "error: " + Text,
42 | 		});
43 | 		console.log(
44 | 			colors.gray(
45 | 				`[${ d.getDate() }:${ d.getMonth() }:${ d.getFullYear() } - ${ d.getHours() }:${ d.getMinutes() }]`,
46 | 			) + colors.red(" | " + Text),
47 | 		);
48 | 	}
49 | }
50 | 
51 | module.exports = Logger;
52 | 


--------------------------------------------------------------------------------
/lib/SlashCommand.js:
--------------------------------------------------------------------------------
 1 | const { SlashCommandBuilder } = require("@discordjs/builders");
 2 | const {
 3 | 	CommandInteraction,
 4 | 	CommandInteractionOptionResolver,
 5 | } = require("discord.js");
 6 | const DiscordMusicBot = require("./DiscordMusicBot");
 7 | 
 8 | class SlashCommand extends SlashCommandBuilder {
 9 | 	constructor() {
10 | 		super();
11 | 		this.type = 1;
12 | 		return this;
13 | 	}
14 | 	
15 | 	/**
16 | 	 * Set Execution of the command
17 | 	 * @param {(client: DiscordMusicBot, interaction: CommandInteraction, options: CommandInteractionOptionResolver) => Promise<any>} callback
18 | 	 */
19 | 	setRun(callback) {
20 | 		this.run = callback;
21 | 		return this;
22 | 	}
23 | }
24 | 
25 | module.exports = SlashCommand;
26 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "discord-musicbot",
 3 |   "version": "5.0.0-beta",
 4 |   "description": "Very simple discord music bot with the discord.js with Song Name playing. It can able to play music with the song name",
 5 |   "main": "index.js",
 6 |   "scripts": {
 7 |     "start": "node index",
 8 |     "rm-logs": "rm logs.log",
 9 |     "lint": "prettier --check .",
10 |     "deploy": "node deploy/deployGlobal",
11 |     "guild": "node deploy/deployGuild",
12 |     "destroy-guild": "node deploy/destroyGuild",
13 |     "destroy-global": "node deploy/destroyGlobal",
14 |     "ddev": "cd dashboard && npm run dev",
15 |     "dbuild": "cd dashboard && npm run build && npm run export",
16 |     "dstart": "cd dashboard && npm run start"
17 |   },
18 |   "repository": {
19 |     "type": "git",
20 |     "url": "git+https://github.com/SudhanPlayz/Discord-MusicBot.git"
21 |   },
22 |   "engines": {
23 |     "node": ">=16.x <=16.16"
24 |   },
25 |   "keywords": [
26 |     "discord",
27 |     "discord-bot",
28 |     "discord-musicbot",
29 |     "music",
30 |     "discord-music"
31 |   ],
32 |   "authors": [
33 |     "SudhanPlayz",
34 |     "DarrenOfficial"
35 |   ],
36 |   "license": "Apache-2.0",
37 |   "bugs": {
38 |     "url": "https://github.com/SudhanPlayz/Discord-MusicBot/issues"
39 |   },
40 |   "homepage": "https://github.com/SudhanPlayz/Discord-MusicBot#readme",
41 |   "dependencies": {
42 |     "@discordjs/builders": "^1.4.0",
43 |     "@discordjs/rest": "^1.5.0",
44 |     "axios": "^0.27.0",
45 |     "better-erela.js-apple": "^0.1.0",
46 |     "better-erela.js-spotify": "1.3.9",
47 |     "colors": "1.3.3",
48 |     "discord-api-types": "0.37.1",
49 |     "discord-together": "^1.3.25",
50 |     "discord.js": "^13.14.0",
51 |     "dotenv": "^16.0.1",
52 |     "ejs": "^3.1.6",
53 |     "erela.js": "^2.3.3",
54 |     "erela.js-deezer": "^1.0.7",
55 |     "erela.js-facebook": "^1.0.4",
56 |     "erela.js-filters": "^1.2.6",
57 |     "express": "^4.17.1",
58 |     "express-rate-limit": "^6.2.0",
59 |     "express-session": "^1.17.3",
60 |     "express-ws": "^5.0.2",
61 |     "js-yaml": "^4.1.0",
62 |     "jsoning": "^0.13.0",
63 |     "lodash": "^4.17.21",
64 |     "moment": "^2.29.1",
65 |     "moment-duration-format": "^2.3.2",
66 |     "node-fetch": "2.6.7",
67 |     "os": "^0.1.2",
68 |     "passport": "^0.6.0",
69 |     "passport-discord": "^0.1.4",
70 |     "pretty-ms": "^7.0.1",
71 |     "rlyrics": "^2.0.1",
72 |     "systeminformation": "^5.9.12",
73 |     "winston": "^3.3.3",
74 |     "youtube-sr": "^4.3.4"
75 |   },
76 |   "devDependencies": {
77 |     "prettier": "2.6.2"
78 |   },
79 |   "volta": {
80 |     "node": "16.15.1",
81 |     "yarn": "3.3.0"
82 |   }
83 | }
84 | 


--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 |   "extends": [
3 |     "config:base"
4 |   ]
5 | }
6 | 


--------------------------------------------------------------------------------
/replit.nix:
--------------------------------------------------------------------------------
1 | { pkgs }: {
2 | 	deps = [
3 | 		pkgs.nodejs-18_x
4 | 
5 | 	];
6 | }
7 | 


--------------------------------------------------------------------------------
/util/Controller.js:
--------------------------------------------------------------------------------
  1 | const { MessageEmbed } = require("discord.js");
  2 | /**
  3 |  *
  4 |  * @param {import("../lib/DiscordMusicBot")} client
  5 |  * @param {import("discord.js").ButtonInteraction} interaction
  6 |  */
  7 | module.exports = async (client, interaction) => {
  8 | 	let guild = client.guilds.cache.get(interaction.customId.split(":")[1]);
  9 | 	let property = interaction.customId.split(":")[2];
 10 | 	let player = client.manager.get(guild.id);
 11 | 
 12 | 	if (!player) {
 13 | 		await interaction.reply({
 14 | 			embeds: [
 15 | 				client.Embed("❌ | **There is no player to control in this server.**"),
 16 | 			],
 17 | 		});
 18 | 		setTimeout(() => {
 19 | 			interaction.deleteReply();
 20 | 		}, 5000);
 21 | 		return;
 22 | 	}
 23 | 	if (!interaction.member.voice.channel) {
 24 | 		const joinEmbed = new MessageEmbed()
 25 | 			.setColor(client.config.embedColor)
 26 | 			.setDescription(
 27 | 				"❌ | **You must be in a voice channel to use this action!**",
 28 | 			);
 29 | 		return interaction.reply({ embeds: [joinEmbed], ephemeral: true });
 30 | 	}
 31 | 
 32 | 	if (
 33 | 		interaction.guild.members.me.voice.channel &&
 34 | 		!interaction.guild.members.me.voice.channel.equals(interaction.member.voice.channel)
 35 | 	) {
 36 | 		const sameEmbed = new MessageEmbed()
 37 | 			.setColor(client.config.embedColor)
 38 | 			.setDescription(
 39 | 				"❌ | **You must be in the same voice channel as me to use this action!**",
 40 | 			);
 41 | 		return await interaction.reply({ embeds: [sameEmbed], ephemeral: true });
 42 | 	}
 43 | 
 44 | 	if (property === "Stop") {
 45 | 		player.queue.clear();
 46 | 		player.stop();
 47 | 		player.set("autoQueue", false);
 48 | 		client.warn(`Player: ${ player.options.guild } | Successfully stopped the player`);
 49 | 		const msg = await interaction.channel.send({
 50 | 			embeds: [
 51 | 				client.Embed(
 52 | 					"⏹️ | **Successfully stopped the player**",
 53 | 				),
 54 | 			],
 55 | 		});
 56 | 		setTimeout(() => {
 57 | 			msg.delete();
 58 | 		}, 5000);
 59 | 
 60 | 		interaction.update({
 61 | 			components: [client.createController(player.options.guild, player)],
 62 | 		});
 63 | 		return;
 64 | 	}
 65 | 
 66 | 	// if theres no previous song, return an error.
 67 | 	if (property === "Replay") {
 68 | 		const previousSong = player.queue.previous;
 69 | 		const currentSong = player.queue.current;
 70 | 		const nextSong = player.queue[0]
 71 |         if (!player.queue.previous ||
 72 |             player.queue.previous === player.queue.current ||
 73 |             player.queue.previous === player.queue[0]) {
 74 |             
 75 |            return interaction.reply({
 76 |                         ephemeral: true,
 77 | 			embeds: [
 78 | 				new MessageEmbed()
 79 | 					.setColor("RED")
 80 | 					.setDescription(`There is no previous song played.`),
 81 | 			],
 82 | 		});
 83 |     }
 84 | 		if (previousSong !== currentSong && previousSong !== nextSong) {
 85 | 			player.queue.splice(0, 0, currentSong)
 86 | 			player.play(previousSong);
 87 | 			return interaction.deferUpdate();
 88 | 		}
 89 | 	}
 90 | 
 91 | 	if (property === "PlayAndPause") {
 92 | 		if (!player || (!player.playing && player.queue.totalSize === 0)) {
 93 | 			const msg = await interaction.channel.send({
 94 |                                ephemeral: true,
 95 | 				embeds: [
 96 | 					new MessageEmbed()
 97 | 						.setColor("RED")
 98 | 						.setDescription("There is no song playing right now."),
 99 | 				],
100 | 			});
101 | 			setTimeout(() => {
102 | 				msg.delete();
103 | 			}, 5000);
104 | 			return interaction.deferUpdate();
105 | 		} else {
106 | 
107 | 			if (player.paused) {
108 | 				player.pause(false);
109 | 			} else {
110 | 				player.pause(true);
111 | 			}
112 | 			client.warn(`Player: ${ player.options.guild } | Successfully ${ player.paused? "paused" : "resumed" } the player`);
113 | 
114 | 			return interaction.update({
115 | 				components: [client.createController(player.options.guild, player)],
116 | 			});
117 | 		}
118 | 	}
119 | 
120 | 	if (property === "Next") {
121 |                 const song = player.queue.current;
122 | 	        const autoQueue = player.get("autoQueue");
123 |                 if (player.queue[0] == undefined && (!autoQueue || autoQueue === false)) {
124 | 		return interaction.reply({
125 |                         ephemeral: true,
126 | 			embeds: [
127 | 				new MessageEmbed()
128 | 					.setColor("RED")
129 | 					.setDescription(`There is nothing after [${ song.title }](${ song.uri }) in the queue.`),
130 | 			],
131 | 		})} else player.stop();
132 | 		return interaction.deferUpdate
133 |     }
134 | 
135 | 	if (property === "Loop") {
136 | 		if (player.trackRepeat) {
137 | 			player.setTrackRepeat(false);
138 | 			player.setQueueRepeat(true);
139 | 		} else if (player.queueRepeat) {
140 | 			player.setQueueRepeat(false);
141 | 		} else {
142 | 			player.setTrackRepeat(true);
143 | 		}
144 | 		client.warn(`Player: ${player.options.guild} | Successfully toggled loop ${player.trackRepeat ? "on" : player.queueRepeat ? "queue on" : "off"} the player`);
145 | 
146 | 		interaction.update({
147 | 			components: [client.createController(player.options.guild, player)],
148 | 		});
149 | 		return;
150 | 	}
151 | 
152 | 	return interaction.reply({
153 | 		ephemeral: true,
154 | 		content: "❌ | **Unknown controller option**",
155 | 	});
156 | };
157 | 


--------------------------------------------------------------------------------
/util/db.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * This only implement global database.
  3 |  * Bugs are expected, contact Shasha on discord or fix it yourself.
  4 |  * TODO: Implement guild database, store each guild data on its own json file.
  5 |  * 
  6 |  * CAUTION: Beware on providing path, all path to json file must be absolute path!!!
  7 |  */
  8 | 
  9 | "use strict";
 10 | 
 11 | const { writeFileSync, readdirSync, unlinkSync } = require("fs");
 12 | const { join } = require("path");
 13 | 
 14 | const _dbName = "db.json";
 15 | const _dbListName = "dbList.json"
 16 | const _globalDbDir = join(__dirname, "..");
 17 | const _globalDbPath = join(_globalDbDir, _dbName);
 18 | const _dbListPath = join(_globalDbDir, _dbListName);
 19 | 
 20 | let _hasGlobalDb = false;
 21 | let _hasDbList = false;
 22 | 
 23 | try {
 24 |     const l = readdirSync(_globalDbDir);
 25 |     _hasGlobalDb = l.includes(_dbName);
 26 |     _hasDbList = l.includes(_dbListName);
 27 | } catch (e) {
 28 |     console.warn("[DB] No global database exist");
 29 | }
 30 | 
 31 | /**
 32 |  * @typedef {object} DbList
 33 |  * @property {string} name - Database name
 34 |  * @property {path} path - Absolute path to json
 35 |  */
 36 | 
 37 | /**
 38 |  * @typedef {object} DbData
 39 |  * @property {string} path - Absolute path to json
 40 |  * @property {object} data - Json to write
 41 |  */
 42 | 
 43 | /**
 44 |  * @type {DbList[]}
 45 |  */
 46 | const _dbList = _hasDbList ? require(_dbListPath) || [] : [];
 47 | 
 48 | /**
 49 | * @type {Map<string, DbData>}
 50 | */
 51 | const _dbs = new Map([
 52 |     [
 53 |         "global",
 54 |         {
 55 |             path: _globalDbPath,
 56 |             data: _hasGlobalDb ? require(_globalDbPath) || {} : {}
 57 |         }
 58 |     ],
 59 | ]);
 60 | 
 61 | for (const v of _dbList) {
 62 |     try {
 63 |         _dbs.set(v.name, {
 64 |             path: v.path,
 65 |             data: require(v.path) || {}
 66 |         });
 67 |     } catch (e) {
 68 |         console.error("[DB] Can't load database '" + v.name + "' in '" + v.path + "'");
 69 |     }
 70 | }
 71 | 
 72 | /**
 73 |  * Idiot checker
 74 |  * @param {string} path 
 75 |  */
 76 | const _validateDbPath = (path) => {
 77 |     if (typeof path !== "string") throw TypeError("path isn't string");
 78 |     if (!path.endsWith(".json")) throw TypeError("path doesn't point to json file");
 79 | }
 80 | 
 81 | /**
 82 |  * Write to global db.
 83 |  * 
 84 |  * @param {string} path - Absolute path to json
 85 |  * @param {object} data - Json to write
 86 |  * 
 87 |  * @returns {boolean}
 88 |  */
 89 | const _write = (path, data) => {
 90 |     _validateDbPath(path);
 91 |     try {
 92 |         // you don't need it to be in human readable format, it will still be valid json
 93 |         writeFileSync(path, JSON.stringify(data));
 94 |         return true;
 95 |     } catch (e) {
 96 |         console.error("[DB] Can't write to '" + path + "', data is lost");
 97 |         return false;
 98 |     }
 99 | };
100 | 
101 | /**
102 |  * Delete database of path
103 |  * @param {string} path - Absolute path to delete
104 |  * @returns {boolean}
105 |  */
106 | const _delete = (path) => {
107 |     _validateDbPath(path);
108 |     try {
109 |         unlinkSync(path);
110 |         return true;
111 |     } catch (e) {
112 |         console.error("[DB] Can't delete '" + path + "'");
113 |         return false;
114 |     }
115 | }
116 | 
117 | /**
118 |  * Write queue
119 |  * @type {DbData[]}
120 |  */
121 | const _wQueue = [];
122 | 
123 | /**
124 |  * Delete queue
125 |  * @type {string[]}
126 |  */
127 | const _dQueue = [];
128 | 
129 | // queue loop
130 | const _run = async () => {
131 |     while (true) {
132 |         await new Promise((r, j) => setTimeout(r, 100));
133 | 
134 |         while (_wQueue.length) {
135 |             const q = _wQueue.shift();
136 |             if (!q) continue;
137 |             _write(q.path, q.data);
138 |         }
139 | 
140 |         while (_dQueue.length) {
141 |             const q = _dQueue.shift();
142 |             if (!q?.length) continue;
143 |             _delete(q);
144 |         }
145 |     }
146 | }
147 | 
148 | _run();
149 | 
150 | const _addDbList = (name, path) => {
151 |     if (!name?.length) throw new TypeError("name undefined");
152 |     _validateDbPath(path);
153 |     _dbList.push({ name, path });
154 |     _wQueue.push({ path: _dbListPath, data: _dbList });
155 | }
156 | 
157 | const _removeDbList = (name) => {
158 |     if (!name?.length) throw new TypeError("name undefined");
159 | 
160 |     for (let i = 0; i < _dbList.length;) {
161 |         if (_dbList[i] && _dbList[i].name === name) {
162 |             _dbList.splice(i, 1);
163 |         }
164 |         else i++;
165 |     }
166 |     _wQueue.push({ path: _dbListPath, data: _dbList });
167 | }
168 | 
169 | /**
170 |  * Get database data
171 |  * @param {string} name - Database name
172 |  * @returns {object} Data
173 |  */
174 | const get = (name) => {
175 |     if (!name?.length) throw new TypeError("name undefined");
176 |     const d = _dbs.get(name);
177 |     if (!d) throw new RangeError("No database with name " + name);
178 |     return d.data;
179 | }
180 | 
181 | /**
182 |  * Create new database with name
183 |  * @param {string} name - Database name
184 |  * @param {string} path - Absolute path to json file
185 |  * @param {object} initialData - Initial value
186 |  * 
187 |  * @returns {boolean}
188 |  */
189 | const create = (name, path, initialData = {}) => {
190 |     if (!name?.length) throw new TypeError("name undefined");
191 |     _validateDbPath(path);
192 |     if (typeof initialData !== "object") throw new TypeError("initialData is not object");
193 |     
194 |     for (const [n,d] of _dbs) {
195 |         if (n === name)
196 |             throw new Error("Database '" + name + "' already exist");
197 |         if (d.path === path) {
198 |             throw new Error("Database in path '" + path + "' already exist with name '" + n + "'");
199 |         }
200 |     }
201 | 
202 |     const d = {
203 |         path: path,
204 |         data: initialData
205 |     };
206 | 
207 |     _wQueue.push(d);
208 |     _addDbList(name, path);
209 |     return !!_dbs.set(name, d);
210 | }
211 | 
212 | /**
213 |  * Set data to associated database name
214 |  * @param {string} name - Database name
215 |  * @param {object} data - Data to set, must be plain javascript object
216 |  * 
217 |  * @returns {boolean}
218 |  */
219 | const set = (name, data) => {
220 |     if (!name?.length) throw new TypeError("name undefined");
221 |     if (!data) throw new TypeError("data undefined");
222 |     if (typeof data !== "object") throw new TypeError("data is not object");
223 | 
224 |     const d = _dbs.get(name);
225 |     if (!d) throw new RangeError("No database with name " + name);
226 |     d.data = data;
227 | 
228 |     _wQueue.push(d);
229 |     return !!_dbs.set(name, d);
230 | }
231 | 
232 | /**
233 |  * Delete database
234 |  * @param {string} name - Database name
235 |  * @returns {boolean}
236 |  */
237 | const remove = (name) => {
238 |     if (!name?.length) throw new TypeError("name undefined");
239 | 
240 |     const d = _dbs.get(name);
241 |     if (!d) throw new RangeError("No database with name " + name);
242 | 
243 |     _dQueue.push(d.path);
244 |     _removeDbList(name);
245 |     return _dbs.delete(name);
246 | }
247 | 
248 | module.exports = {
249 |     get,
250 |     create,
251 |     set,
252 |     remove,
253 | }
254 | 


--------------------------------------------------------------------------------
/util/getChannel.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  *
 3 |  * @param {import("../lib/DiscordMusicBot")} client
 4 |  * @param {import("discord.js").GuildCommandInteraction} interaction
 5 |  * @returns
 6 |  */
 7 | module.exports = async (client, interaction) => {
 8 | 	return new Promise(async (resolve) => {
 9 | 		if (!interaction.member.voice.channel) {
10 | 			await interaction.reply({
11 | 				embeds: [
12 | 					client.ErrorEmbed(
13 | 						"You must be in a voice channel to use this command!",
14 | 					),
15 | 				],
16 | 			});
17 | 			return resolve(false);
18 | 		}
19 | 		if (
20 | 			interaction.guild.members.me.voice.channel &&
21 | 			interaction.member.voice.channel.id !==
22 | 			interaction.guild.members.me.voice.channel.id
23 | 		) {
24 | 			await interaction.reply({
25 | 				embeds: [
26 | 					client.ErrorEmbed(
27 | 						"You must be in the same voice channel as me to use this command!",
28 | 					),
29 | 				],
30 | 			});
31 | 			return resolve(false);
32 | 		}
33 | 		if (!interaction.member.voice.channel.joinable) {
34 | 			await interaction.reply({
35 | 				embeds: [
36 | 					client.ErrorEmbed(
37 | 						"I don't have enough permission to join your voice channel!",
38 | 					),
39 | 				],
40 | 			});
41 | 			return resolve(false);
42 | 		}
43 | 		
44 | 		resolve(interaction.member.voice.channel);
45 | 	});
46 | };
47 | 


--------------------------------------------------------------------------------
/util/getConfig.js:
--------------------------------------------------------------------------------
 1 | const dotenv = require("dotenv").config();
 2 | 
 3 | module.exports = () => {
 4 | 	return new Promise((res, rej) => {
 5 | 		try {
 6 | 			const config = require("../dev-config");
 7 | 			res(config);
 8 | 		} catch {
 9 | 			try {
10 | 				const config = require("../config");
11 | 				res(config);
12 | 			} catch {
13 | 				rej("No config file found.");
14 | 			}
15 | 		}
16 | 	});
17 | };
18 | 


--------------------------------------------------------------------------------
/util/getLavalink.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  *
 3 |  * @param {import("../lib/DiscordMusicBot")} client
 4 |  * @returns {import("erela.js").Node | undefined}
 5 |  */
 6 | module.exports = async (client) => {
 7 |   return new Promise((resolve) => {
 8 |     for (let i = 0; i < client.manager.nodes.size; i++) {
 9 |       client.manager.nodes.forEach((node) => {
10 |         if (node.connected) resolve(node);
11 |       });
12 |     }
13 |     resolve(undefined);
14 |   });
15 | };
16 | 


--------------------------------------------------------------------------------
/util/guildDb.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * ============================ MODULE SAFETY USE ============================
  3 |  * 
  4 |  * Function should only be `getter` and `setter` for each guild data:
  5 |  * 
  6 |  * getter: Call _getOrCreateGuildDb() and return a specific property value if
  7 |  *         exist (not undefined or null), else return a default value.
  8 |  * 
  9 |  * setter: Call _getOrCreateGuildDb() and modify a specific property of the
 10 |  *         returned value and call set() to save.
 11 |  * 
 12 |  * For space management we only need one function to delete the json file
 13 |  * based on database name, the deleteGuildDatabase().
 14 |  * 
 15 |  * We don't need a public createGuildDatabase function as the `getter` and
 16 |  * `setter` should create it if it doesn't exist.
 17 |  */
 18 | 
 19 | "use strict";
 20 | 
 21 | const { readdirSync, mkdirSync } = require("fs");
 22 | const { join } = require("path");
 23 | const { get, set, create, remove } = require("./db");
 24 | 
 25 | const _guildDbDir = join(__dirname,"..",".guild_dbs");
 26 | 
 27 | try {
 28 |     readdirSync(_guildDbDir);
 29 |     } catch (e) {
 30 |     try {
 31 |         mkdirSync(_guildDbDir);
 32 |     } catch (e) {
 33 |         console.error("[ERROR] Can't create guild database folder, module might won't work properly.");
 34 |     }
 35 | }
 36 | 
 37 | const _getGuildDbName = (guild_id) => {
 38 |     return "guild-"+guild_id;
 39 | }
 40 | 
 41 | const _getGuildDbPath = (guild_id) => {
 42 |     return join(_guildDbDir, _getGuildDbName(guild_id)+".json");
 43 | }
 44 | 
 45 | const _getOrCreateGuildDb = (guild_id) => {
 46 |     const dbName = _getGuildDbName(guild_id);
 47 | 
 48 |     let d;
 49 |     try {
 50 |         d = get(dbName);
 51 |     } catch (e) {
 52 |         if (e.message.startsWith("No database with name ")) {
 53 |             create(dbName,_getGuildDbPath(guild_id));
 54 |             d = {};
 55 |         } else {
 56 |             console.error("[ERROR] Unexpected error:");
 57 |             console.error(e);
 58 |         }
 59 |     }
 60 |     return d;
 61 | }
 62 | 
 63 | const _deleteGuildDb = (guild_id) => {
 64 |     const dbName = _getGuildDbName(guild_id);
 65 |     return remove(dbName);
 66 | }
 67 | 
 68 | /**
 69 |  * Wipe and delete all guild's data
 70 |  * @param {string} guild_id 
 71 |  * @returns {boolean}
 72 |  */
 73 | const deleteGuildDatabase = (guild_id) => {
 74 |     return _deleteGuildDb(guild_id);
 75 | }
 76 | 
 77 | /**
 78 |  * Set guild dj-only mode
 79 |  * @param {string} guild_id 
 80 |  * @param {boolean} djOnly 
 81 |  */
 82 | const setDjOnly = (guild_id, djOnly) => {
 83 |     const d = _getOrCreateGuildDb(guild_id);
 84 |     d.djOnly = djOnly;
 85 | 
 86 |     set(_getGuildDbName(guild_id), d);
 87 | }
 88 | 
 89 | /**
 90 |  * Get guild dj-only mode
 91 |  * @param {string} guild_id 
 92 |  * @returns {boolean}
 93 |  */
 94 | const getDjOnly = (guild_id) => {
 95 |     return !!_getOrCreateGuildDb(guild_id)?.djOnly;
 96 | }
 97 | 
 98 | // TODO: Write more stuff here
 99 | 
100 | 
101 | 
102 | module.exports = {
103 |     setDjOnly,
104 |     getDjOnly,
105 |     deleteGuildDatabase,
106 |     // new export here
107 | }
108 | 


--------------------------------------------------------------------------------
/util/loadCommands.js:
--------------------------------------------------------------------------------
 1 | const { join } = require("path");
 2 | const fs = require("fs");
 3 | 
 4 | const LoadCommands = () => {
 5 | 	return new Promise(async (resolve) => {
 6 | 		let slash = await LoadDirectory("slash");
 7 | 		let context = await LoadDirectory("context");
 8 | 		
 9 | 		resolve({ slash, context });
10 | 	});
11 | };
12 | 
13 | const LoadDirectory = (dir) => {
14 | 	return new Promise((resolve) => {
15 | 		let commands = [];
16 | 		let CommandsDir = join(__dirname, "..", "commands", dir);
17 | 		
18 | 		fs.readdir(CommandsDir, (err, files) => {
19 | 			if (err) {
20 | 				throw err;
21 | 			}
22 | 			
23 | 			for (const file of files) {
24 | 				let cmd = require(CommandsDir + "/" + file);
25 | 				if (!cmd || (dir == "context" && !cmd.command)) {
26 | 					return console.log(
27 | 						"Unable to load Command: " +
28 | 						file.split(".")[0] +
29 | 						", File doesn't have either command",
30 | 					);
31 | 				}
32 | 				if (dir == "context") {
33 | 					commands.push(cmd.command);
34 | 				} else {
35 | 					commands.push(cmd);
36 | 				}
37 | 			}
38 | 			;
39 | 			resolve(commands);
40 | 		});
41 | 	});
42 | };
43 | 
44 | module.exports = LoadCommands;
45 | 


--------------------------------------------------------------------------------