├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
└── workflows
│ └── node.js.yml
├── LICENSE
├── README.md
├── commands
├── create.js
├── drop.js
├── edit.js
├── end.js
├── help.js
├── invite.js
├── list.js
├── ping.js
├── reroll.js
└── start.js
├── config.json
├── events
├── discord
│ ├── interactionCreate.js
│ ├── messageCreate.js
│ └── ready.js
└── giveaways
│ ├── endedGiveawayReactionAdded.js
│ ├── giveawayEnded.js
│ ├── giveawayReactionAdded.js
│ ├── giveawayReactionRemoved.js
│ └── giveawayRerolled.js
├── index.js
├── package.json
├── slash
├── drop.js
├── edit.js
├── end.js
├── help.js
├── invite.js
├── list.js
├── pause.js
├── ping.js
├── reroll.js
├── resume.js
└── start.js
├── storage
└── giveaways.json
└── utils
├── message.js
└── slashsync.js
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at [Discord](https://discord.gg/ARu4hr6hJw).
63 | All complaints will be reviewed and investigated promptly and fairly.
64 |
65 | All community leaders are obligated to respect the privacy and security of the
66 | reporter of any incident.
67 |
68 | ## Enforcement Guidelines
69 |
70 | Community leaders will follow these Community Impact Guidelines in determining
71 | the consequences for any action they deem in violation of this Code of Conduct:
72 |
73 | ### 1. Correction
74 |
75 | **Community Impact**: Use of inappropriate language or other behavior deemed
76 | unprofessional or unwelcome in the community.
77 |
78 | **Consequence**: A private, written warning from community leaders, providing
79 | clarity around the nature of the violation and an explanation of why the
80 | behavior was inappropriate. A public apology may be requested.
81 |
82 | ### 2. Warning
83 |
84 | **Community Impact**: A violation through a single incident or series
85 | of actions.
86 |
87 | **Consequence**: A warning with consequences for continued behavior. No
88 | interaction with the people involved, including unsolicited interaction with
89 | those enforcing the Code of Conduct, for a specified period of time. This
90 | includes avoiding interactions in community spaces as well as external channels
91 | like social media. Violating these terms may lead to a temporary or
92 | permanent ban.
93 |
94 | ### 3. Temporary Ban
95 |
96 | **Community Impact**: A serious violation of community standards, including
97 | sustained inappropriate behavior.
98 |
99 | **Consequence**: A temporary ban from any sort of interaction or public
100 | communication with the community for a specified period of time. No public or
101 | private interaction with the people involved, including unsolicited interaction
102 | with those enforcing the Code of Conduct, is allowed during this period.
103 | Violating these terms may lead to a permanent ban.
104 |
105 | ### 4. Permanent Ban
106 |
107 | **Community Impact**: Demonstrating a pattern of violation of community
108 | standards, including sustained inappropriate behavior, harassment of an
109 | individual, or aggression toward or disparagement of classes of individuals.
110 |
111 | **Consequence**: A permanent ban from any sort of public interaction within
112 | the community.
113 |
114 | ## Attribution
115 |
116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
117 | version 2.0, available at
118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
119 |
120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
121 | enforcement ladder](https://github.com/mozilla/diversity).
122 |
123 | [homepage]: https://www.contributor-covenant.org
124 |
125 | For answers to common questions about this code of conduct, see the FAQ at
126 | https://www.contributor-covenant.org/faq. Translations are available at
127 | https://www.contributor-covenant.org/translations.
128 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Workflow
4 |
5 | 1. Fork and clone this repository.
6 | 2. Create a new branch in your fork based off the **main** branch.
7 | 3. Make your changes.
8 | 4. Commit your changes, and push them.
9 | 5. Submit a Pull Request [here]!
10 |
11 | ## Contributing to the code
12 |
13 | **The issue tracker is only for issue reporting or proposals/suggestions. If you have a question, you can ask the team in our [Discord Server][discord server]**.
14 |
15 | To contribute to this repository, feel free to create a new fork of the repository and
16 | submit a pull request.
17 |
18 | ### Commit Guidelines
19 |
20 | - Follow the [Commit Convention][commit convention].
21 | - Download and use the VSCode Extension [Commitizen](commitizen)
22 |
23 | ### SloshDB Project Concept Guidelines
24 |
25 | There are a number of guidelines considered when reviewing Pull Requests to be merged. _This is by no means an exhaustive list, but here are some things to consider before/while submitting your ideas._
26 |
27 | - Everything in SloshDB Project should be generally useful for the majority of users. Don't let that stop you if you've got a good concept though, as your idea still might be a great addition.
28 | - Everything should follow [OOP paradigms][oop paradigms] and generally rely on behaviour over state where possible. This generally helps methods be predictable, keeps the codebase simple and understandable, reduces code duplication through abstraction, and leads to efficiency and therefore scalability.
29 |
30 |
31 |
32 |
33 |
34 | [vscode]: https://code.visualstudio.com
35 |
36 |
37 | [commit convention]: https://www.conventionalcommits.org/en/v1.0.0/
38 | [discord server]: https://discord.gg/ARu4hr6hJw
39 | [node.js]: https://nodejs.org/en/download/
40 | [here]: https://github.com/ZeroDiscord/Giveaway/pulls
41 | [oop paradigms]: https://en.wikipedia.org/wiki/Object-oriented_programming
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve the bot
4 | title: "[ BUG ]"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Do the '...' command.
16 | 2. Under '....' condition.
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Discord Build [e.g. Ptb 103871 (e0f6e26) Host 1.0.1010 Windows 11 64-Bit ]
35 | - Browser [e.g. stock browser, safari]
36 | - Version [e.g. 22]
37 |
38 | **Additional context**
39 | Add any other context about the problem here.
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this bot
4 | title: "[ Feature Request ] "
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md:
--------------------------------------------------------------------------------
1 | **Please describe the changes this PR makes and why it should be merged:**
2 |
3 |
10 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [16.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v2
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | - run: npm install
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GiveawayBot™
2 | [](https://forthebadge.com)
3 |
4 | ### A Discord Giveaway bot written in Discord.js to create & enjoy Feature rich and Seamless Giveaways within your very own Discord guild!
5 |
6 | ## Links
7 | - ### This Giveaway Bot Was Created by [ZeroSync](https://youtube.com/c/ZeroSync/)
8 | - [Youtube Channel](https://www.youtube.com/c/ZeroSync)
9 | - [Support Server Link](https://discord.gg/ARu4hr6hJw)
10 |
11 | ## Licensed Under
12 | ### Creative Commons Zero v1.0 Universal
13 | [View the license here](https://github.com/ZeroDiscord/Giveaway/blob/master/LICENSE)
14 | #### Copyright 2021 © All Rights are Reserved
15 |
16 | # Contributions
17 |
18 | All contributions are welcomed, it is recommended to create an issue or reply in a comment of an existing issue to let us know what you are working on first, that way we do not overwrite each other.
19 |
20 | - Please read [contributing guide](.github/CONTRIBUTING.md) for details on this project.
21 | - Please respect the [pull request template](.github/PULL_REQUEST_TEMPLATE/pull_request_template.md) while submiting a pull request.
22 |
23 | # Code of Conduct
24 |
25 | Please read [code of conduct](.github/CODE_OF_CONDUCT.md) for details on our code of conduct.
26 |
27 | [](https://forthebadge.com)
28 |
29 | # You can run the bot in just a few steps! Let me show you how:
30 | ## Hosting
31 | > ⚠ This bot needs a [Node.js v16.9+](https://nodejs.org/en/blog/release/v16.9.0/) runtime to function since discord.js version 14 requires said node version to function.
32 |
33 | ### [Host On Repl.it](https://repl.it/github/ZeroDiscord/Giveaway)
34 | ### [Remix On Glitch](https://glitch.com/edit/#!/import/github/ZeroDiscord/Giveaway)
35 |
36 | **Aliter**
37 |
38 | ### Step 1: Install the Dependencies:
39 | Linux
40 | ```sh
41 | wget https://nodejs.org/dist/v16.18.0/node-v16.18.0-linux-x64.tar.xz
42 | unxz node-v16.18.0-linux-x64.tar.xz
43 | tar xvf node-v16.18.0-linux-x64.tar
44 | mv node-v16.18.0-linux-x64 /usr/local/node
45 |
46 | ln /usr/local/node/bin/node /usr/bin
47 | ln /usr/local/node/bin/corepack /usr/bin
48 | ln /usr/local/node/bin/npm /usr/bin
49 |
50 | corepack enable
51 | ```
52 | Windows
53 | ```sh
54 | # https://nodejs.org/en/blog/release/v16.9.1/ get node.js
55 | npm install
56 | ```
57 |
58 | ### Step 2: Obtain a Bot Token From [Here](https://discord.com/developers)
59 | ### Step 3 : Replace the Token in [config.json](https://github.com/ZeroDiscord/Giveaway/blob/master/config.json)
60 | #### That's all! We Are Done! Now Simply host the Bot!
61 |
62 | ### Run with node
63 | ```sh
64 | node index.js
65 | ```
66 | ### Run with pm2
67 | ```sh
68 | npm install -g pm2@latest
69 | pm2 start --name "Giveaway" index.js --watch
70 | ```
71 |
72 | # Features
73 | ## Featuring | Slash Commands
74 | ### Interactive Giveaway Creation
75 | ### Featured ✨ Bonus Entries
76 | ### And Lots More!
77 | - Direct message when the server mentioned for joining is not joined
78 | - Direct message when the server mentioned for joining is joined
79 | - Direct Message When User Reacts on an ended giveaway
80 | - Direct Message User On Removing Reaction
81 | - Direct Message Winner On Winning
82 |
--------------------------------------------------------------------------------
/commands/create.js:
--------------------------------------------------------------------------------
1 | const Discord = require('discord.js'),
2 | { EmbedBuilder } = Discord,
3 | parsec = require('parsec'),
4 | messages = require('../utils/message');
5 |
6 | module.exports.run = async (client, message) => {
7 | // If the member doesn't have enough permissions
8 | if (
9 | !message.member.permissions.has("ManageMessages") &&
10 | !message.member.roles.cache.some(r => r.name === "Giveaways")
11 | ) {
12 | return message.reply(
13 | ":x: You need to have the manage messages permissions to start giveaways."
14 | );
15 | }
16 |
17 | const collector = message.channel.createMessageCollector({
18 | filter: (m) => m.author.id === message.author.id,
19 | time: 60000,
20 | });
21 |
22 | let xembed = new EmbedBuilder()
23 | .setTitle("Oops! Looks Like We Met A Timeout! 🕖")
24 | .setColor("#FF0000")
25 | .setDescription('💥 Snap our luck!\nYou took too much time to decide!\nUse ``create`` again to start a new giveaway!\nTry to respond within **30 seconds** this time!')
26 | .setFooter({
27 | text: `${client.user.username}`,
28 | iconURL: client.user.displayAvatarURL()
29 | })
30 | .setTimestamp()
31 |
32 |
33 | function waitingEmbed(title, desc) {
34 | return message.channel.send({
35 | embeds: [
36 | new EmbedBuilder()
37 | .setAuthor({
38 | name: `${message.author.tag} + ' | Giveaway Setup'`,
39 | iconURL: message.member.displayAvatarURL()
40 | })
41 | .setTitle('Giveaway ' + title)
42 | .setDescription(desc + ' within the next 60 seconds.')
43 | .setFooter({
44 | text: "Type 'cancel' to exit this process.",
45 | iconURL: client.user.displayAvatarURL()
46 | })
47 | .setTimestamp()
48 | .setColor('#2F3136'),
49 | ],
50 |
51 | });
52 | }
53 |
54 | let winnerCount, channel, duration, prize, cancelled;
55 |
56 | await waitingEmbed('Prize', 'Please send the giveaway prize');
57 |
58 | collector.on('collect', async (m) => {
59 | if (cancelled) return;
60 |
61 | async function failed(options, ...cancel) {
62 | if (typeof cancel[0] === 'boolean')
63 | (cancelled = true) && (await m.reply(options));
64 | else {
65 | await m.reply(
66 | options instanceof EmbedBuilder ? { embeds: [options] } : options
67 | );
68 | return await waitingEmbed(...cancel);
69 | }
70 | }
71 |
72 | if (m.content === 'cancel'){
73 | collector.stop()
74 | return await failed('Cancelled Giveaway Creation.', true)
75 | }
76 |
77 | switch (true) {
78 | case !prize: {
79 | if (m.content.length > 256)
80 | return await failed(
81 | 'The prize can not be more than 256 characters.',
82 | 'Prize',
83 | 'Please send the giveaway prize'
84 | );
85 | else {
86 | prize = m.content;
87 | await waitingEmbed('Channel', 'Please send the giveaway channel');
88 | }
89 |
90 | break;
91 | }
92 |
93 | case !channel: {
94 | if (!(_channel = m.mentions.channels.first() || m.guild.channels.cache.get(m.content)))
95 | return await failed(
96 | 'Please send a valid channel / channel ID.',
97 | 'Channel',
98 | 'Please send the giveaway channel'
99 | );
100 | else if (!_channel.isTextBased())
101 | return await failed(
102 | 'The channel must be a text channel.',
103 | 'Channel',
104 | 'Please send the giveaway channel'
105 | );
106 | else {
107 | channel = _channel;
108 | await waitingEmbed(
109 | 'Winner Count',
110 | 'Please send the giveaway winner count.'
111 | );
112 | }
113 |
114 | break;
115 | }
116 |
117 | case !winnerCount: {
118 | if (!(_w = parseInt(m.content)))
119 | return await failed(
120 | 'The number of winners must be an integer.',
121 |
122 | 'Winner Count',
123 | 'Please send the giveaway winner count.'
124 | );
125 | if (_w < 1)
126 | return await failed(
127 | 'Winner count must be more than 1.',
128 | 'Winner Count',
129 | 'Please send the giveaway winner count.'
130 | );
131 | else if (_w > 15)
132 | return await failed(
133 | 'Winner count must be less than 15.',
134 | 'Winner Count',
135 | 'Please send the giveaway winner count.'
136 | );
137 | else {
138 | winnerCount = _w;
139 | await waitingEmbed('Duration', 'Please send the giveaway duration');
140 | }
141 |
142 | break;
143 | }
144 |
145 | case !duration: {
146 | if (!(_d = parsec(m.content).duration))
147 | return await failed(
148 | 'Please provide a valid duration.',
149 | 'Duration',
150 | 'Please send the giveaway duration'
151 | );
152 | if (_d > parsec('21d').duration)
153 | return await failed(
154 | 'Duration must be less than 21 days!',
155 | 'Duration',
156 | 'Please send the giveaway duration'
157 | );
158 | else {
159 | duration = _d;
160 | }
161 |
162 | return client.giveawaysManager.start(channel, {
163 | prize,
164 | duration,
165 | winnerCount,
166 | hostedBy: client.config.hostedBy ? message.author : null,
167 | messages,
168 | });
169 | }
170 | }
171 | });
172 | collector.on('end', (collected, reason) => {
173 | if (reason == 'time') {
174 | message.reply({ embeds: [xembed]})
175 | }
176 | })
177 | };
178 |
--------------------------------------------------------------------------------
/commands/drop.js:
--------------------------------------------------------------------------------
1 | const messages = require("../utils/message");
2 | module.exports.run = async (client, message, args) => {
3 | // If the member doesn't have enough permissions
4 | if (
5 | !message.member.permissions.has("ManageMessages") &&
6 | !message.member.roles.cache.some(r => r.name === "Giveaways")
7 | ) {
8 | return message.reply(
9 | ":x: You need to have the manage messages permissions to start giveaways."
10 | );
11 | }
12 |
13 | // Giveaway channel
14 | let giveawayChannel = message.mentions.channels.first();
15 | // If no channel is mentionned
16 | if (!giveawayChannel) {
17 | return message.reply(":x: You have to mention a valid channel!");
18 | }
19 |
20 | // Number of winners
21 | let giveawayNumberWinners = parseInt(args[1]);
22 | // If the specified number of winners is not a number
23 | if (isNaN(giveawayNumberWinners) || parseInt(giveawayNumberWinners) <= 0) {
24 | return message.reply(
25 | ":x: You have to specify a valid number of winners!"
26 | );
27 | }
28 |
29 | // Giveaway prize
30 | let giveawayPrize = args.slice(2).join(" ");
31 | // If no prize is specified
32 | if (!giveawayPrize) {
33 | return message.reply(":x: You have to specify a valid prize!");
34 | }
35 | // Start the giveaway
36 | await client.giveawaysManager.start(giveawayChannel, {
37 | // The giveaway prize
38 | prize: giveawayPrize,
39 | // The giveaway winner count
40 | winnerCount: parseInt(giveawayNumberWinners),
41 | // Who hosts this giveaway
42 | hostedBy: client.config.hostedBy ? message.author : null,
43 | // specify drop
44 | isDrop: true,
45 | // Messages
46 | messages
47 | });
48 | message.reply(`Giveaway started in ${giveawayChannel}!`);
49 | }
50 |
--------------------------------------------------------------------------------
/commands/edit.js:
--------------------------------------------------------------------------------
1 | module.exports.run = async (client, message) => {
2 | const Discord = require("discord.js");
3 | const ms = require("ms");
4 |
5 | // If the member doesn't have enough permissions
6 | if (
7 | !message.member.permissions.has("ManageMessages") &&
8 | !message.member.roles.cache.some(r => r.name === "Giveaways")
9 | ) {
10 | return message.reply(
11 | ":x: You need to have the manage messages permissions to start giveaways."
12 | );
13 | }
14 |
15 | let time = "";
16 | let winnersCount;
17 | let prize = "";
18 | let giveawayx = "";
19 | let embed = new Discord.EmbedBuilder()
20 | .setTitle("Edit A Giveaway!")
21 | .setColor('#2F3136')
22 | .setFooter({
23 | text: `${client.user.username}`,
24 | iconURL: client.user.displayAvatarURL()
25 | })
26 | .setTimestamp();
27 | const msg = await message.reply({
28 | embeds:
29 | [embed.setDescription(
30 | "Which Giveaway Would You Like To Edit?\nProvide The Giveaway Message's ID\n **Must Reply within 30 seconds!**"
31 | )]
32 | }
33 | );
34 | let xembed = new Discord.EmbedBuilder()
35 | .setTitle("Oops! Looks Like We Met A Timeout! 🕖")
36 | .setColor("#FF0000")
37 | .setDescription('💥 Snap our luck!\nYou took too much time to decide!\nUse ``edit`` again to edit a giveaway!\nTry to respond within **30 seconds** this time!')
38 | .setFooter({
39 | text: `${client.user.username}`,
40 | iconURL: client.user.displayAvatarURL()
41 | })
42 | .setTimestamp();
43 |
44 | const filter = m => m.author.id === message.author.id && !m.author.bot;
45 | const collector = await message.channel.createMessageCollector(filter, {
46 | max: 3,
47 | time: 30000
48 | });
49 |
50 | collector.on("collect", async collect => {
51 |
52 | const response = collect.content;
53 | let gid = response;
54 | // check if the ID is valid
55 |
56 |
57 | await collect.delete()
58 | if (!client.giveawaysManager.giveaways.find((g) => g.messageID === gid)) {
59 | return msg.edit({
60 | embeds: [
61 | embed.setDescription(
62 | "Uh-Oh! Looks like you provided an Invalid Message ID!\n**Try Again?**\n Example: ``677813783523098627``"
63 | )]
64 | }
65 | );
66 | }
67 | else {
68 | collector.stop(
69 | msg.edit({
70 | embeds: [
71 | embed.setDescription(
72 | `Alright! Next, What Would be our new time for the giveaway to be ended \n** Must Reply within 30 seconds!**`
73 | )]
74 | }
75 | )
76 | );
77 | }
78 | const collector2 = await message.channel.createMessageCollector(filter, {
79 | max: 3,
80 | time: 30000
81 | });
82 | collector2.on("collect", async collect2 => {
83 |
84 | let mss = ms(collect2.content);
85 | await collect2.delete()
86 | if (!mss) {
87 | return msg.edit({
88 | embeds: [
89 | embed.setDescription(
90 | "Aw Snap! Looks Like You Provided Me With An Invalid Duration\n**Try Again?**\n Example: ``-10 minutes``,``-10m``,``-10``\n **Note: - (minus) Inidicates you want to reduce the time!**"
91 | )]
92 | }
93 | );
94 | } else {
95 | time = mss;
96 | collector2.stop(
97 | msg.edit({
98 | embeds: [
99 | embed.setDescription(
100 | `Alright! Next, How may winners should I roll for the giveaway now?\n**Must Reply within 30 seconds.**`
101 | )]
102 | }
103 | )
104 | );
105 | }
106 | const collector3 = await message.channel.createMessageCollector(filter, {
107 | max: 3,
108 | time: 30000,
109 | errors: ['time']
110 | });
111 | collector3.on("collect", async collect3 => {
112 |
113 | const response3 = collect3.content.toLowerCase();
114 | await collect3.delete()
115 | if (parseInt(response3) < 1 || isNaN(parseInt(response3))) {
116 | return msg.edit({
117 | embeds: [
118 | embed.setDescription(
119 | "Boi! Winners Must Be A Number or greater than equal to one!\n**Try Again?**\n Example ``1``,``10``, etcetra."
120 | )]
121 | }
122 | );
123 | } else {
124 | winnersCount = parseInt(response3);
125 | collector3.stop(
126 | msg.edit({
127 | embeds: [
128 | embed.setDescription(
129 | `Alright, Generous Human! Next, What should be the new prize for the giveaway?\n**Must Reply within 30 seconds!**`
130 | )]
131 | })
132 | )
133 | }
134 | const collector4 = await message.channel.createMessageCollector(filter, {
135 | max: 3,
136 | time: 30000,
137 | errors: ['time']
138 | });
139 | collector4.on("collect", async collect4 => {
140 |
141 | const response4 = collect4.content.toLowerCase();
142 | prize = response4;
143 | await collect4.delete()
144 | collector4.stop(
145 | console.log(giveawayx),
146 | msg.edit({
147 | embeds: [
148 | embed.setDescription(
149 | `Edited`
150 | )]
151 | }
152 | )
153 | );
154 | client.giveawaysManager.edit(gid, {
155 | newWinnerCount: winnersCount,
156 | newPrize: prize,
157 | addTime: time
158 | })
159 | });
160 | });
161 | });
162 | });
163 | collector.on('end', (collected, reason) => {
164 | if (reason == 'time') {
165 | message.reply({ embeds: [xembed] });
166 | }
167 | })
168 | try {
169 | collector2.on('end', (collected, reason) => {
170 | if (reason == 'time') {
171 |
172 | message.reply({ embeds: [xembed] });
173 | }
174 | });
175 | collector3.on('end', (collected, reason) => {
176 | if (reason == 'time') {
177 | message.reply({ embeds: [xembed] });
178 |
179 | }
180 | })
181 | collector4.on('end', (collected, reason) => {
182 | if (reason == 'time') {
183 |
184 | message.reply({ embeds: [xembed] });
185 | }
186 | })
187 | } catch (e) { }
188 | }
189 |
--------------------------------------------------------------------------------
/commands/end.js:
--------------------------------------------------------------------------------
1 | exports.run = async (client, message, args) => {
2 |
3 | // If the member doesn't have enough permissions
4 | if(!message.member.permissions.has('ManageMessages') && !message.member.roles.cache.some((r) => r.name === "Giveaways")){
5 | return message.reply(':x: You need to have the manage messages permissions to reroll giveaways.');
6 | }
7 |
8 | // If no message ID or giveaway name is specified
9 | if(!args[0]){
10 | return message.reply(':x: You have to specify a valid message ID!');
11 | }
12 |
13 | // try to found the giveaway with prize then with ID
14 | let giveaway =
15 | // Search with giveaway prize
16 | client.giveawaysManager.giveaways.find((g) => g.prize === args.join(' ')) ||
17 | // Search with giveaway ID
18 | client.giveawaysManager.giveaways.find((g) => g.messageId == args[0]);
19 |
20 | // If no giveaway was found
21 | if(!giveaway){
22 | return message.reply('Unable to find a giveaway for `'+ args.join(' ') + '`.');
23 | }
24 |
25 | // Edit the giveaway
26 | client.giveawaysManager.end(giveaway.messageId)
27 | // Success message
28 | .then(() => {
29 | // Success message
30 | message.reply('Giveaway Ended.');
31 | }).catch((e) => {
32 | message.reply({
33 | content: e
34 | });
35 | })
36 |
37 | };
38 |
--------------------------------------------------------------------------------
/commands/help.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder, ActionRowBuilder, SelectMenuBuilder, ComponentType } = require("discord.js");
2 | const config = require('../config.json');
3 |
4 | module.exports.run = async (client, message, args) => {
5 |
6 | const embed = new EmbedBuilder()
7 | .setTitle(`Commands of ${client.user.username}`)
8 | .setColor('#2F3136')
9 | .setDescription('**Please Select a category to view all its commands**')
10 | .addFields({ name: `Links:`, value: `- [Youtube Channel](https://youtube.com/c/Zerosync)\n- [Discord Server](https://discord.gg/ARu4hr6hJw)\n- [GitHub](https://github.com/ZeroDiscord/Giveaway)`, inline: true })
11 | .setTimestamp()
12 | .setFooter({
13 | text: `Requested by ${message.author.username} | ` + config.copyright,
14 | iconURL: message.author.displayAvatarURL()
15 | });
16 |
17 | const giveaway = new EmbedBuilder()
18 | .setTitle("Categories » Giveaway")
19 | .setColor('#2F3136')
20 | .setDescription("```yaml\nHere are the giveaway commands:```")
21 | .addFields(
22 | { name: 'Create / Start' , value: `Start a giveaway in your guild!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
23 | { name: 'Drop' , value: `Start a drop giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
24 | { name: 'Edit' , value: `Edit an already running giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
25 | { name: 'End' , value: `End an already running giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
26 | { name: 'List' , value: `List all the giveaways running within this guild!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
27 | { name: 'Pause' , value: `Pause an already running giveaway!\n > **Type: __\`slash\`__**`, inline: true },
28 | { name: 'Reroll' , value: `Reroll an ended giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
29 | { name: 'Resume' , value: `Resume a paused giveaway!\n > **Type: __\`slash\`__**`, inline: true },
30 | )
31 | .setTimestamp()
32 | .setFooter({
33 | text: `Requested by ${message.author.username} | ` + config.copyright,
34 | iconURL: message.author.displayAvatarURL()
35 | });
36 |
37 | const general = new EmbedBuilder()
38 | .setTitle("Categories » General")
39 | .setColor('#2F3136')
40 | .setDescription("```yaml\nHere are the general bot commands:```")
41 | .addFields(
42 | { name: 'Help' , value: `Shows all available commands to this bot!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
43 | { name: 'Invite' , value: `Get the bot's invite link!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
44 | { name: 'Ping' , value: `Check the bot's websocket latency!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
45 | )
46 | .setTimestamp()
47 | .setFooter({
48 | text: `Requested by ${message.author.username} | ` + config.copyright,
49 | iconURL: message.author.displayAvatarURL()
50 | });
51 |
52 | const components = (state) => [
53 | new ActionRowBuilder().addComponents(
54 | new SelectMenuBuilder()
55 | .setCustomId("help-menu")
56 | .setPlaceholder("Please Select a Category")
57 | .setDisabled(state)
58 | .addOptions([{
59 | label: `Giveaways`,
60 | value: `giveaway`,
61 | description: `View all the giveaway based commands!`,
62 | emoji: `🎉`
63 | },
64 | {
65 | label: `General`,
66 | value: `general`,
67 | description: `View all the general bot commands!`,
68 | emoji: `⚙`
69 | }
70 | ])
71 | ),
72 | ];
73 |
74 | const initialMessage = await message.reply({ embeds: [embed], components: components(false) });
75 |
76 | const filter = (interaction) => interaction.user.id === message.author.id;
77 |
78 | const collector = message.channel.createMessageComponentCollector(
79 | {
80 | filter,
81 | componentType: ComponentType.SelectMenu,
82 | idle: 300000,
83 | dispose: true,
84 | });
85 |
86 | collector.on('collect', (interaction) => {
87 | if (interaction.values[0] === "giveaway") {
88 | interaction.update({ embeds: [giveaway], components: components(false) }).catch((e) => {});
89 | } else if (interaction.values[0] === "general") {
90 | interaction.update({ embeds: [general], components: components(false) }).catch((e) => {});
91 | }
92 | });
93 | collector.on("end", (collected, reason) => {
94 | if (reason == "time") {
95 | initialMessage.edit({
96 | content: "Collector Destroyed, Try Again!",
97 | components: [],
98 | });
99 | }
100 | });
101 | }
102 |
--------------------------------------------------------------------------------
/commands/invite.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
2 | const config = require('../config.json');
3 |
4 | module.exports.run = async (client, message, args) => {
5 | const row = new ActionRowBuilder()
6 | .addComponents(
7 | new ButtonBuilder()
8 | .setLabel(`Invite ${client.user.username}`)
9 | .setStyle(ButtonStyle.Link)
10 | .setURL(`https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=applications.commands%20bot`),
11 | new ButtonBuilder()
12 | .setLabel('Support Server')
13 | .setStyle(ButtonStyle.Link)
14 | .setURL("https://discord.gg/ARu4hr6hJw"),
15 | )
16 | let invite = new EmbedBuilder()
17 | .setAuthor({
18 | name: `Invite ${client.user.username}`,
19 | iconURL: client.user.displayAvatarURL()
20 | })
21 | .setTitle("Invite & Support Link!")
22 | .setDescription(`Invite ${client.user} to your server today & enjoy seamless giveaways with advanced features!`)
23 | .setColor('#2F3136')
24 | .setTimestamp()
25 | .setFooter({
26 | text: `Requested by ${message.author.username} | ` + config.copyright,
27 | iconURL: message.author.displayAvatarURL()
28 | });
29 | message.reply({ embeds: [invite], components: [row]});
30 | }
31 |
--------------------------------------------------------------------------------
/commands/list.js:
--------------------------------------------------------------------------------
1 | const Discord = require('discord.js');
2 | const config = require('../config.json');
3 |
4 | module.exports.run = async (client, message, args) => {
5 | const select = new Discord.SelectMenuBuilder().setCustomId("select").setPlaceholder("Choose a type of giveaway to view!").addOptions([
6 | {
7 | label: '🎉 Normal Giveaways',
8 | description: 'Check the normal giveaways currently running in your server!',
9 | value: 'normal',
10 | },
11 | ])
12 | const row = new Discord.ActionRowBuilder().addComponents([select])
13 | let giveaways = client.giveawaysManager.giveaways.filter(g => g.guildId === `${message.guild.id}` && !g.ended);
14 | if (!giveaways.some(e => e.messageId)) {
15 | return message.reply('💥 No Giveaways To Be Displayed')
16 | }
17 | const msg = await message.reply({ embeds: [new Discord.EmbedBuilder().setDescription("Choose an option in the select menu to get started!").setColor("#2F3136").setTimestamp()], components: [row] })
18 | let embed = new Discord.EmbedBuilder()
19 | .setTitle("Currently Active Giveaways")
20 | .setColor("#2F3136")
21 | .setFooter({
22 | text: `${client.user.username}`,
23 | iconURL: client.user.displayAvatarURL()
24 | })
25 | .setTimestamp()
26 |
27 | const filter = x => x.customId == "select" && x.user.id == message.author.id
28 | const collector = await message.channel.createMessageComponentCollector({ filter, time: 60000, max: 1 })
29 | collector.on("collect", async (i) => {
30 | i.update({ components: [] });
31 | const val = i.values[0]
32 | if (val == "normal") {
33 | await Promise.all(giveaways.map(async (x) => {
34 | embed.addFields({ name:
35 | `Normal Giveaway:`, value: `**Prize:** **[${x.prize}](https://discord.com/channels/${x.guildId}/${x.channelId}/${x.messageId})\nStarted:** ()\n**Ends:** ()`
36 | });
37 | }));
38 | msg.edit({ embeds: [embed] })
39 | }
40 |
41 | })
42 | collector.on("end",(collected, reason) => {
43 | if(reason == "time")
44 | msg.edit({ content: "👀 Collector Destroyed, Try Again!", components: [] })
45 | })
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/commands/ping.js:
--------------------------------------------------------------------------------
1 | const Discord = require('discord.js');
2 | const config = require('../config.json');
3 | module.exports.run = async (client, message, args) => {
4 | let m = await message.reply("Sending request to websocket...")
5 | let pong = new Discord.EmbedBuilder()
6 | .setAuthor({
7 | name: `🏓 Pong!`,
8 | iconURL: message.author.displayAvatarURL()
9 | })
10 | .setTitle("Client's Ping")
11 | .setColor('#2F3136')
12 | .setTimestamp()
13 |
14 | .addFields([
15 | { name: '**Latency**', value: `\`${Date.now() - message.createdTimestamp}ms\`` },
16 | { name: '**API Latency**', value: `\`${Math.round(client.ws.ping)}ms\`` },
17 | ])
18 | .setFooter({
19 | text: `Requested by ${message.author.tag}`,
20 | iconURL: message.author.displayAvatarURL()
21 | });
22 |
23 | m.delete()
24 | message.reply({ content: " ", embeds: [pong] })
25 | }
26 |
--------------------------------------------------------------------------------
/commands/reroll.js:
--------------------------------------------------------------------------------
1 | const ms = require('ms');
2 | module.exports.run = async (client, message, args) => {
3 |
4 | // If the member doesn't have enough permissions
5 | if(!message.member.permissions.has('ManageMessages') && !message.member.roles.cache.some((r) => r.name === "Giveaways")){
6 | return message.reply(':x: You need to have the manage messages permissions to reroll giveaways.');
7 | }
8 |
9 | // If no message ID or giveaway name is specified
10 | if(!args[0]){
11 | return message.reply(':x: You have to specify a valid message ID!');
12 | }
13 |
14 | // try to found the giveaway with prize then with ID
15 | let giveaway =
16 | // Search with giveaway prize
17 | client.giveawaysManager.giveaways.find((g) => g.prize === args.join(' ')) ||
18 | // Search with giveaway ID
19 | client.giveawaysManager.giveaways.find((g) => g.messageID === args[0]);
20 |
21 | // If no giveaway was found
22 | if(!giveaway){
23 | return message.reply('Unable to find a giveaway for `'+ args.join(' ') +'`.');
24 | }
25 |
26 | // Reroll the giveaway
27 | client.giveawaysManager.reroll(giveaway.messageID)
28 | .then(() => {
29 | // Success message
30 | message.reply('Giveaway rerolled!');
31 | })
32 | .catch((e) => {
33 | if(e.startsWith(`Giveaway with message ID ${giveaway.messageID} is not ended.`)){
34 | message.reply('This giveaway is not ended!');
35 | } else {
36 | console.error(e);
37 | message.reply(e);
38 | }
39 | });
40 |
41 | };
42 |
--------------------------------------------------------------------------------
/commands/start.js:
--------------------------------------------------------------------------------
1 | const ms = require("ms");
2 | const messages = require("../utils/message");
3 | module.exports.run = async (client, message, args) => {
4 | // If the member doesn't have enough permissions
5 | if (
6 | !message.member.permissions.has("ManageMessages") &&
7 | !message.member.roles.cache.some(r => r.name === "Giveaways")
8 | ) {
9 | return message.reply(
10 | ":x: You need to have the manage messages permissions to start giveaways."
11 | );
12 | }
13 |
14 | // Giveaway channel
15 | let giveawayChannel = message.mentions.channels.first();
16 | // If no channel is mentionned
17 | if (!giveawayChannel) {
18 | return message.reply(":x: You have to mention a valid channel!");
19 | }
20 |
21 | // Giveaway duration
22 | let giveawayDuration = args[1];
23 | // If the duration isn't valid
24 | if (!giveawayDuration || isNaN(ms(giveawayDuration))) {
25 | return message.reply(":x: You have to specify a valid duration!");
26 | }
27 |
28 | // Number of winners
29 | let giveawayNumberWinners = parseInt(args[2]);
30 | // If the specified number of winners is not a number
31 | if (isNaN(giveawayNumberWinners) || parseInt(giveawayNumberWinners) <= 0) {
32 | return message.reply(
33 | ":x: You have to specify a valid number of winners!"
34 | );
35 | }
36 |
37 | // Giveaway prize
38 | let giveawayPrize = args.slice(3).join(" ");
39 | // If no prize is specified
40 | if (!giveawayPrize) {
41 | return message.reply(":x: You have to specify a valid prize!");
42 | }
43 | // Start the giveaway
44 | await client.giveawaysManager.start(giveawayChannel, {
45 | // The giveaway duration
46 | duration: ms(giveawayDuration),
47 | // The giveaway prize
48 | prize: giveawayPrize,
49 | // The giveaway winner count
50 | winnerCount: parseInt(giveawayNumberWinners),
51 | // Who hosts this giveaway
52 | hostedBy: client.config.hostedBy ? message.author : null,
53 | // Messages
54 | messages
55 | });
56 | message.reply(`Giveaway started in ${giveawayChannel}!`);
57 | }
58 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": "BotTokenHere",
3 | "prefix": "PrefixHere",
4 | "everyoneMention": false,
5 | "copyright": "GiveawayBot™ v3 By ZeroSync",
6 | "hostedBy": true
7 | }
8 |
--------------------------------------------------------------------------------
/events/discord/interactionCreate.js:
--------------------------------------------------------------------------------
1 | module.exports = (client, interaction) => {
2 | // Check if our interaction is a slash command
3 | if (interaction.isCommand()) {
4 |
5 | // Get the command from our slash command collection
6 | const command = client.interactions.get(interaction.commandName);
7 |
8 | // If command does not exist return an error message
9 | if (!command) return interaction.reply({
10 | content: "Something Went Wrong | Perhaps command not registered?",
11 | ephemeral: true
12 | });
13 |
14 | command.run(client, interaction);
15 | }
16 | }
--------------------------------------------------------------------------------
/events/discord/messageCreate.js:
--------------------------------------------------------------------------------
1 | module.exports = (client, message) => {
2 | // return if author is a bot
3 | if (message.author.bot) return;
4 |
5 | // return if message does not match prefix (in command)
6 | if (message.content.indexOf(client.config.prefix) !== 0) return;
7 |
8 | // Defining what are arguments and commands
9 | const args = message.content.slice(client.config.prefix.length).trim().split(/ +/g);
10 | const command = args.shift().toLowerCase();
11 |
12 | // Get the command data from the client.commands Enmap
13 | const cmd = client.commands.get(command);
14 |
15 | // If command does not exist return
16 | if (!cmd) return;
17 |
18 | // Run the command
19 | cmd.run(client, message, args);
20 | };
21 |
--------------------------------------------------------------------------------
/events/discord/ready.js:
--------------------------------------------------------------------------------
1 | const register = require('../../utils/slashsync');
2 | const { ActivityType } = require('discord.js');
3 |
4 | module.exports = async (client) => {
5 | await register(client, client.register_arr.map((command) => ({
6 | name: command.name,
7 | description: command.description,
8 | options: command.options,
9 | type: '1'
10 | })), {
11 | debug: true
12 | });
13 | // Register slash commands - ( If you are one of those people who read the codes I highly suggest ignoring this because I am very bad at what I am doing, thanks LMAO )
14 | console.log(`[ / | Slash Command ] - ✅ Loaded all slash commands!`)
15 | let invite = `https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=applications.commands%20bot`;
16 | console.log(`[STATUS] ${client.user.tag} is now online!\n[INFO] Bot by ZeroSync https://www.youtube.com/c/ZeroSync\n[Invite Link] ${invite}`);
17 | client.user.setPresence({
18 | activities: [{ name: `ZeroSync on YouTube`, type: ActivityType.Watching }],
19 | status: 'online',
20 | });
21 |
22 | };
23 |
--------------------------------------------------------------------------------
/events/giveaways/endedGiveawayReactionAdded.js:
--------------------------------------------------------------------------------
1 | const Discord = require('discord.js');
2 | module.exports = {
3 | async execute(giveaway, member, reaction) {
4 | reaction.users.remove(member.user);
5 | member.send({
6 | embeds: [
7 | new Discord.EmbedBuilder()
8 | .setTitle(`Giveaway ended already!`)
9 | .setColor('#b50505')
10 | .setDescription(
11 | `Hey ${member.user} **[[This Giveaway]](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})** that you reacted has already ended :sob:\nBe quick next time!`
12 | )
13 | .setTimestamp(),
14 | ],
15 | })
16 | .catch((e) => {});
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/events/giveaways/giveawayEnded.js:
--------------------------------------------------------------------------------
1 | const Discord = require("discord.js")
2 | module.exports = {
3 | async execute(giveaway, winners) {
4 | winners.forEach((member) => {
5 | member.send({
6 | embeds: [new Discord.EmbedBuilder()
7 | .setTitle(`🎁 Let's goo!`)
8 | .setColor("#2F3136")
9 | .setDescription(`Hello there ${member.user}\n I heard that you have won **[[This Giveaway]](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})**\n Good Job On Winning **${giveaway.prize}!**\nDirect Message the host to claim your prize!!`)
10 | .setTimestamp()
11 | .setFooter({
12 | text: `${member.user.username}`,
13 | iconURL: member.user.displayAvatarURL()
14 | })
15 | ]
16 | }).catch(e => {})
17 | });
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/events/giveaways/giveawayReactionAdded.js:
--------------------------------------------------------------------------------
1 | const Discord = require("discord.js")
2 | module.exports = {
3 | async execute(giveaway, reactor, messageReaction) {
4 | let approved = new Discord.EmbedBuilder()
5 | .setTimestamp()
6 | .setColor("#2F3136")
7 | .setTitle("Entry Approved! | You have a chance to win!!")
8 | .setDescription(
9 | `Your entry to [This Giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId}) has been approved!`
10 | )
11 | .setFooter({ text: "Subscribe to ZeroSync on YT!" })
12 | .setTimestamp()
13 | let denied = new Discord.EmbedBuilder()
14 | .setTimestamp()
15 | .setColor("#2F3136")
16 | .setTitle(":x: Entry Denied | Database Entry Not Found & Returned!")
17 | .setDescription(
18 | `Your entry to [This Giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId}) has been denied, please review the requirements to the giveaway properly.`
19 | )
20 | .setFooter({ text: "Subscribe to ZeroSync on YT!" })
21 |
22 | let client = messageReaction.message.client
23 | if (reactor.user.bot) return;
24 | if(giveaway.extraData) {
25 | if (giveaway.extraData.server !== "null") {
26 | try {
27 | await client.guilds.cache.get(giveaway.extraData.server).members.fetch(reactor.id)
28 | return reactor.send({
29 | embeds: [approved]
30 | });
31 | } catch(e) {
32 | messageReaction.users.remove(reactor.user);
33 | return reactor.send({
34 | embeds: [denied]
35 | }).catch(e => {})
36 | }
37 | }
38 | if (giveaway.extraData.role !== "null" && !reactor.roles.cache.get(giveaway.extraData.role)){
39 | messageReaction.users.remove(reactor.user);
40 | return reactor.send({
41 | embeds: [denied]
42 | }).catch(e => {})
43 | }
44 |
45 | return reactor.send({
46 | embeds: [approved]
47 | }).catch(e => {})
48 | } else {
49 | return reactor.send({
50 | embeds: [approved]
51 | }).catch(e => {})
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/events/giveaways/giveawayReactionRemoved.js:
--------------------------------------------------------------------------------
1 | const Discord = require("discord.js")
2 | module.exports = {
3 | async execute(giveaway, member) {
4 | return member.send({
5 | embeds: [new Discord.EmbedBuilder()
6 | .setTimestamp()
7 | .setTitle('❓ Hold Up Did You Just Remove a Reaction From A Giveaway?')
8 | .setColor("#2F3136")
9 | .setDescription(
10 | `Your entry to [This Giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId}) was recorded but you un-reacted, since you don't need **${giveaway.prize}** I would have to choose someone else 😭`
11 | )
12 | .setFooter({ text: "Think It was a mistake? Go react again!" })
13 | ]
14 | }).catch(e => {})
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/events/giveaways/giveawayRerolled.js:
--------------------------------------------------------------------------------
1 | const Discord = require("discord.js")
2 | module.exports = {
3 | async execute(giveaway, winners) {
4 | winners.forEach((member) => {
5 | member.send({
6 | embeds: [new Discord.EmbedBuilder()
7 | .setTitle(`🎁 Let's goo! We Have A New Winner`)
8 | .setColor("#2F3136")
9 | .setDescription(`Hello there ${member.user}\n I heard that the host rerolled and you have won **[[This Giveaway]](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})**\n Good Job On Winning **${giveaway.prize}!**\nDirect Message the host to claim your prize!!`)
10 | .setTimestamp()
11 | .setFooter({
12 | text: `${member.user.username}`,
13 | iconURL: member.user.displayAvatarURL()
14 | })
15 | ]
16 | }).catch(e => {})
17 | });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const Discord = require("discord.js")
2 | const { Client, GatewayIntentBits, Partials } = require("discord.js");
3 | const client = new Client({
4 | partials: [
5 | Partials.Message, // for message
6 | Partials.Channel, // for text channel
7 | Partials.GuildMember, // for guild member
8 | Partials.Reaction, // for message reaction
9 | ],
10 | intents: [
11 | GatewayIntentBits.Guilds, // for guild related things
12 | GatewayIntentBits.GuildInvites, // for guild invite managing
13 | GatewayIntentBits.GuildMessages, // for guild messages things
14 | GatewayIntentBits.GuildMessageReactions, // for message reactions things
15 | GatewayIntentBits.MessageContent, // enable if you need message content things
16 | ],
17 | });
18 | const fs = require("fs");
19 | const config = require("./config.json");
20 | client.config = config;
21 |
22 | // Initialise discord giveaways
23 | const { GiveawaysManager } = require("discord-giveaways");
24 | client.giveawaysManager = new GiveawaysManager(client, {
25 | storage: "./storage/giveaways.json",
26 | default: {
27 | botsCanWin: false,
28 | embedColor: "#2F3136",
29 | reaction: "🎉",
30 | lastChance: {
31 | enabled: true,
32 | content: `🛑 **Last chance to enter** 🛑`,
33 | threshold: 5000,
34 | embedColor: '#FF0000'
35 | }
36 | }
37 | });
38 | //Coded by ZeroSync on yt
39 |
40 | /* Load all events (discord based) */
41 |
42 |
43 | fs.readdir("./events/discord", (_err, files) => {
44 | files.forEach(file => {
45 | if (!file.endsWith(".js")) return;
46 | const event = require(`./events/discord/${file}`);
47 | let eventName = file.split(".")[0];
48 | console.log(`[Event] ✅ Loaded: ${eventName}`);
49 | client.on(eventName, event.bind(null, client));
50 | delete require.cache[require.resolve(`./events/discord/${file}`)];
51 | });
52 | });
53 |
54 | /* Load all events (giveaways based) */
55 |
56 |
57 | fs.readdir("./events/giveaways", (_err, files) => {
58 | files.forEach((file) => {
59 | if (!file.endsWith(".js")) return;
60 | const event = require(`./events/giveaways/${file}`);
61 | let eventName = file.split(".")[0];
62 | console.log(`[Event] 🎉 Loaded: ${eventName}`);
63 | client.giveawaysManager.on(eventName, (...file) => event.execute(...file, client)), delete require.cache[require.resolve(`./events/giveaways/${file}`)];
64 | })
65 | })
66 |
67 | // Let commands be a new collection ( message commands )
68 | client.commands = new Discord.Collection();
69 | /* Load all commands */
70 | fs.readdir("./commands/", (_err, files) => {
71 | files.forEach(file => {
72 | if (!file.endsWith(".js")) return;
73 | let props = require(`./commands/${file}`);
74 | let commandName = file.split(".")[0];
75 | client.commands.set(commandName, {
76 | name: commandName,
77 | ...props
78 | });
79 | console.log(`[Command] ✅ Loaded: ${commandName}`);
80 | });
81 | });
82 |
83 | // let interactions be a new collection ( slash commands )
84 | client.interactions = new Discord.Collection();
85 | // creating an empty array for registering slash commands
86 | client.register_arr = []
87 | /* Load all slash commands */
88 | fs.readdir("./slash/", (_err, files) => {
89 | files.forEach(file => {
90 | if (!file.endsWith(".js")) return;
91 | let props = require(`./slash/${file}`);
92 | let commandName = file.split(".")[0];
93 | client.interactions.set(commandName, {
94 | name: commandName,
95 | ...props
96 | });
97 | client.register_arr.push(props)
98 | });
99 | });
100 |
101 |
102 | // Login through the client
103 | client.login(config.token);
104 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "GiveawayBot",
3 | "version": "3.0.0",
4 | "description": "Giveaway bot created with discord-giveaways package in discord.js v14",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "author": "Zero",
10 | "license": "CC-Zero",
11 | "keywords": [
12 | "discord",
13 | "giveaways",
14 | "giveaway",
15 | "bot",
16 | "discord.js",
17 | "discordjs"
18 | ],
19 | "dependencies": {
20 | "discord-giveaways": "^6.0.1",
21 | "discord.js": "^14.6.0",
22 | "ms": "^3.0.0-canary.1",
23 | "parsec": "^2.0.2"
24 | },
25 | "engines": {
26 | "node": "16.x"
27 | },
28 | "repository": {
29 | "url": "https://github.com/ZeroDiscord/Giveaway"
30 | },
31 | "devDependencies": {
32 | "node": "^16.9.1"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/slash/drop.js:
--------------------------------------------------------------------------------
1 | const messages = require("../utils/message");
2 | const { ApplicationCommandOptionType } = require('discord.js');
3 |
4 | module.exports = {
5 | name: 'drop',
6 | description: 'Create a drop giveaway',
7 | options: [
8 | {
9 | name: 'winners',
10 | description: 'How many winners the giveaway should have',
11 | type: ApplicationCommandOptionType.Integer,
12 | required: true
13 | },
14 | {
15 | name: 'prize',
16 | description: 'What the prize of the giveaway should be',
17 | type: ApplicationCommandOptionType.String,
18 | required: true
19 | },
20 | {
21 | name: 'channel',
22 | description: 'The channel to start the giveaway in',
23 | type: ApplicationCommandOptionType.Channel,
24 | required: true
25 | }
26 | ],
27 |
28 | run: async (client, interaction) => {
29 |
30 | // If the member doesn't have enough permissions
31 | if(!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")){
32 | return interaction.reply({
33 | content: ':x: You need to have the manage messages permissions to start giveaways.',
34 | ephemeral: true
35 | });
36 | }
37 |
38 | const giveawayChannel = interaction.options.getChannel('channel');
39 | const giveawayWinnerCount = interaction.options.getInteger('winners');
40 | const giveawayPrize = interaction.options.getString('prize');
41 |
42 | if (!giveawayChannel.isTextBased()) {
43 | return interaction.reply({
44 | content: ':x: Please select a text channel!',
45 | ephemeral: true
46 | });
47 | }
48 | if (giveawayWinnerCount < 1) {
49 | return interaction.reply({
50 | content: ':x: Please select a valid winner count! greater or equal to one.',
51 | })
52 | }
53 |
54 | // Start the giveaway
55 | client.giveawaysManager.start(giveawayChannel, {
56 | // The number of winners for this drop
57 | winnerCount: giveawayWinnerCount,
58 | // The prize of the giveaway
59 | prize: giveawayPrize,
60 | // Who hosts this giveaway
61 | hostedBy: client.config.hostedBy ? interaction.user : null,
62 | // specify drop
63 | isDrop: true,
64 | // Messages
65 | messages
66 | });
67 |
68 | interaction.reply(`Giveaway started in ${giveawayChannel}!`);
69 |
70 | }
71 | };
72 |
--------------------------------------------------------------------------------
/slash/edit.js:
--------------------------------------------------------------------------------
1 | const { ApplicationCommandOptionType } = require('discord.js');
2 | const ms = require("ms");
3 |
4 | module.exports = {
5 | name: 'edit',
6 | description: '🎉 Edit a giveaway',
7 |
8 | options: [
9 | {
10 | name: 'giveaway',
11 | description: 'The giveaway to end (message ID)',
12 | type: ApplicationCommandOptionType.String,
13 | required: true
14 | },
15 | {
16 | name: 'duration',
17 | description: 'Setting time of mentioned giveaway. Eg. 1h sets the current giveaway to end after an hour!',
18 | type: ApplicationCommandOptionType.String,
19 | required: true
20 | },
21 | {
22 | name: 'winners',
23 | description: 'How many winners the giveaway should have',
24 | type: ApplicationCommandOptionType.Integer,
25 | required: true
26 | },
27 | {
28 | name: 'prize',
29 | description: 'What the prize of the giveaway should be',
30 | type: ApplicationCommandOptionType.String,
31 | required: true
32 | }
33 | ],
34 |
35 | run: async (client, interaction) => {
36 |
37 | // If the member doesn't have enough permissions
38 | if (!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")) {
39 | return interaction.reply({
40 | content: ':x: You need to have the manage messages permissions to start giveaways.',
41 | ephemeral: true
42 | });
43 | }
44 | const gid = interaction.options.getString('giveaway');
45 | const time = interaction.options.getString('duration');
46 | const winnersCount = interaction.options.getInteger('winners');
47 | const prize = interaction.options.getString('prize');
48 | let duration;
49 | if (time.startsWith("-")) {
50 | duration = -ms(time.substring(1));
51 | } else {
52 | duration = ms(time);
53 | }
54 |
55 | if (isNaN(duration)) {
56 | return interaction.reply({
57 | content: ":x: Please select a valid duration!",
58 | ephemeral: true,
59 | });
60 | }
61 | await interaction.deferReply({
62 | ephemeral: true
63 | })
64 | // Edit the giveaway
65 | try {
66 | await client.giveawaysManager.edit(gid, {
67 | newWinnerCount: winnersCount,
68 | newPrize: prize,
69 | addTime: time
70 | })
71 | } catch (e) {
72 | return interaction.editReply({
73 | content:
74 | `No giveaway found with the given message ID: \`${gid}\``,
75 | ephemeral: true
76 | });
77 | }
78 | interaction.editReply({
79 | content:
80 | `This giveaway has now been edited!`,
81 | ephemeral: true
82 | });
83 | }
84 |
85 | };
86 |
--------------------------------------------------------------------------------
/slash/end.js:
--------------------------------------------------------------------------------
1 | const { ApplicationCommandOptionType } = require('discord.js');
2 |
3 | module.exports = {
4 | name: "end",
5 | description: '🎉 End an already running giveaway',
6 |
7 | options: [
8 | {
9 | name: 'giveaway',
10 | description: 'The giveaway to end (message ID or giveaway prize)',
11 | type: ApplicationCommandOptionType.String,
12 | required: true
13 | }
14 | ],
15 |
16 | run: async (client, interaction) => {
17 |
18 | // If the member doesn't have enough permissions
19 | if (!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")) {
20 | return interaction.reply({
21 | content: ':x: You need to have the manage messages permissions to end giveaways.',
22 | ephemeral: true
23 | });
24 | }
25 |
26 | const query = interaction.options.getString('giveaway');
27 |
28 | // fetching the giveaway with message Id or prize
29 | const giveaway =
30 | // Search with giveaway prize
31 | client.giveawaysManager.giveaways.find((g) => g.prize === query && g.guildId === interaction.guild.id) ||
32 | // Search with giveaway Id
33 | client.giveawaysManager.giveaways.find((g) => g.messageId === query && g.guildId === interaction.guild.id);
34 |
35 | // If no giveaway was found with the corresponding input
36 | if (!giveaway) {
37 | return interaction.reply({
38 | content: 'Unable to find a giveaway for `' + query + '`.',
39 | ephemeral: true
40 | });
41 | }
42 |
43 | if (giveaway.ended) {
44 | return interaction.reply({
45 | content: 'This giveaway has already ended!',
46 | ephemeral: true
47 | });
48 | }
49 |
50 | // Edit the giveaway
51 | client.giveawaysManager.end(giveaway.messageId)
52 | // Success message
53 | .then(() => {
54 | // Success message
55 | interaction.reply(`**[This Giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})** Has Now Ended!`);
56 | })
57 | .catch((e) => {
58 | interaction.reply({
59 | content: e,
60 | ephemeral: true
61 | });
62 | });
63 |
64 | }
65 | };
66 |
--------------------------------------------------------------------------------
/slash/help.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder, ActionRowBuilder, SelectMenuBuilder, ComponentType } = require('discord.js');
2 | const config = require('../config.json');
3 |
4 | module.exports = {
5 | name: 'help',
6 | description: '📜 View all the commands available to the bot!',
7 | run: async (client, interaction) => {
8 | const embed = new EmbedBuilder()
9 | .setTitle(`Commands of ${client.user.username}`)
10 | .setColor('#2F3136')
11 | .setDescription('**Please Select a category to view all its commands**')
12 | .addFields({ name: `Links:`, value: `- [Youtube Channel](https://youtube.com/c/Zerosync)\n- [Discord Server](https://discord.gg/ARu4hr6hJw)\n- [GitHub](https://github.com/ZeroDiscord/Giveaway)`, inline: true })
13 |
14 | .setTimestamp()
15 | .setFooter({
16 | text: `Requested by ${interaction.user.username} | ` + config.copyright,
17 | iconURL: interaction.user.displayAvatarURL()
18 | });
19 |
20 | const giveaway = new EmbedBuilder()
21 | .setTitle("Categories » Giveaway")
22 | .setColor('#2F3136')
23 | .setDescription("```yaml\nHere are the giveaway commands:```")
24 | .addFields(
25 | { name: 'Create / Start', value: `Start a giveaway in your guild!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
26 | { name: 'Drop', value: `Start a drop giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
27 | { name: 'Edit', value: `Edit an already running giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
28 | { name: 'End', value: `End an already running giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
29 | { name: 'List', value: `List all the giveaways running within this guild!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
30 | { name: 'Pause', value: `Pause an already running giveaway!\n > **Type: __\`slash\`__**`, inline: true },
31 | { name: 'Reroll', value: `Reroll an ended giveaway!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
32 | { name: 'Resume', value: `Resume a paused giveaway!\n > **Type: __\`slash\`__**`, inline: true },
33 | )
34 | .setTimestamp()
35 | .setFooter({
36 | text: `Requested by ${interaction.user.username} | ` + config.copyright,
37 | iconURL: interaction.user.displayAvatarURL()
38 | });
39 |
40 | const general = new EmbedBuilder()
41 | .setTitle("Categories » General")
42 | .setColor('#2F3136')
43 | .setDescription("```yaml\nHere are the general bot commands:```")
44 | .addFields(
45 | { name: 'Help', value: `Shows all available commands to this bot!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
46 | { name: 'Invite', value: `Get the bot's invite link!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
47 | { name: 'Ping', value: `Check the bot's websocket latency!\n > **Types: __\`slash\` / \`message\`__**`, inline: true },
48 | )
49 | .setTimestamp()
50 | .setFooter({
51 | text: `Requested by ${interaction.user.username} | ` + config.copyright,
52 | iconURL: interaction.user.displayAvatarURL()
53 | });
54 |
55 | const components = (state) => [
56 | new ActionRowBuilder().addComponents(
57 | new SelectMenuBuilder()
58 | .setCustomId("help-menu")
59 | .setPlaceholder("Please Select a Category")
60 | .setDisabled(state)
61 | .addOptions([{
62 | label: `Giveaways`,
63 | value: `giveaway`,
64 | description: `View all the giveaway based commands!`,
65 | emoji: `🎉`
66 | },
67 | {
68 | label: `General`,
69 | value: `general`,
70 | description: `View all the general bot commands!`,
71 | emoji: `⚙`
72 | }
73 | ])
74 | ),
75 | ];
76 |
77 | const initialMessage = await interaction.reply({ embeds: [embed], components: components(false) });
78 |
79 | const filter = (interaction) => interaction.user.id === interaction.member.id;
80 |
81 | const collector = interaction.channel.createMessageComponentCollector(
82 | {
83 | filter,
84 | componentType: ComponentType.SelectMenu,
85 | idle: 300000,
86 | dispose: true,
87 | });
88 |
89 | collector.on('collect', (interaction) => {
90 | if (interaction.values[0] === "giveaway") {
91 | interaction.update({ embeds: [giveaway], components: components(false) }).catch((e) => { });
92 | } else if (interaction.values[0] === "general") {
93 | interaction.update({ embeds: [general], components: components(false) }).catch((e) => { });
94 | }
95 | });
96 | collector.on('end', (collected, reason) => {
97 | if (reason == "time") {
98 | initialMessage.edit({
99 | content: "Collector Destroyed, Try Again!",
100 | components: [],
101 | });
102 | }
103 | })
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/slash/invite.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
2 | const config = require('../config.json');
3 |
4 | module.exports = {
5 | name: 'invite',
6 | description: '➕ Invite the bot to your server!',
7 | run: async (client, interaction) => {
8 | const row = new ActionRowBuilder()
9 | .addComponents(
10 | new ButtonBuilder()
11 | .setLabel(`Invite ${client.user.username}`)
12 | .setStyle(ButtonStyle.Link)
13 | .setURL(`https://discord.com/api/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=applications.commands%20bot`),
14 | new ButtonBuilder()
15 | .setLabel('Support Server')
16 | .setStyle(ButtonStyle.Link)
17 | .setURL("https://discord.gg/ARu4hr6hJw"),
18 | )
19 | let invite = new EmbedBuilder()
20 | .setAuthor({
21 | name: `Invite ${client.user.username}`,
22 | iconURL: client.user.displayAvatarURL()
23 | })
24 | .setTitle("Invite & Support Link!")
25 | .setDescription(`Invite ${client.user} to your server today & enjoy seamless giveaways with advvanced features!`)
26 | .setColor('#2F3136')
27 | .setTimestamp()
28 | .setFooter({
29 | text: `Requested by ${interaction.user.username} | ` + config.copyright,
30 | iconURL: interaction.user.displayAvatarURL()
31 | })
32 |
33 | interaction.reply({ embeds: [invite], components: [row]});
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/slash/list.js:
--------------------------------------------------------------------------------
1 | const Discord = require('discord.js');
2 | const config = require('../config.json');
3 |
4 | module.exports = {
5 | name: 'list',
6 | description: '🎉 List all the active giveaways for this server.',
7 | run: async (client, interaction) => {
8 | const select = new Discord.SelectMenuBuilder()
9 | .setCustomId('select')
10 | .setPlaceholder('Choose a type of giveaway to view!')
11 | .addOptions([
12 | {
13 | label: '🎉 Normal Giveaways',
14 | description: 'Check the giveaways currently running in your server!',
15 | value: 'normal',
16 | },
17 | ]);
18 | const row = new Discord.ActionRowBuilder().addComponents([select]);
19 | let giveaways = client.giveawaysManager.giveaways.filter(
20 | (g) => g.guildId === `${interaction.guild.id}` && !g.ended
21 | );
22 | if (!giveaways.some((e) => e.messageId)) {
23 | return interaction.reply('💥 No Giveaways To Be Displayed');
24 | }
25 | const msg = await interaction.channel.send({
26 | embeds: [
27 | new Discord.EmbedBuilder()
28 | .setDescription('Choose an option in the select menu to get started!')
29 | .setColor('#f542ec')
30 | .setTimestamp(),
31 | ],
32 | components: [row],
33 | });
34 | let embed = new Discord.EmbedBuilder()
35 | .setTitle('Currently Active Giveaways')
36 | .setColor('#f58142')
37 | .setFooter({
38 | text: `Requested by ${interaction.user.username} | ` + config.copyright,
39 | iconURL: interaction.user.displayAvatarURL()
40 | })
41 | .setTimestamp();
42 | const filter = (x) =>
43 | x.customId == 'select' && x.user.id == interaction.member.id;
44 | const collector = await interaction.channel.createMessageComponentCollector(
45 | { filter, time: 60000, max: 1 }
46 | );
47 | await interaction.deferReply();
48 | collector.on('collect', async (i) => {
49 | const val = i.values[0];
50 | if (val == 'normal') {
51 | await Promise.all(
52 | giveaways.map(async (x) => {
53 | embed.addFields({ name:
54 | `Normal Giveaway:`, value: `**Prize:** **[${x.prize}](https://discord.com/channels/${x.guildId}/${x.channelId}/${x.messageId})\nStarted:** ()\n**Ends:** ()`
55 | });
56 | })
57 | );
58 | msg.delete();
59 | interaction.editReply({ embeds: [embed], components: [] });
60 | }
61 | });
62 | collector.on('end', (collected, reason) => {
63 | if (reason == 'time') {
64 | interaction.editReply({
65 | content: 'Collector Destroyed, Try Again!',
66 | components: [],
67 | });
68 | }
69 | });
70 | },
71 | };
--------------------------------------------------------------------------------
/slash/pause.js:
--------------------------------------------------------------------------------
1 | const { ApplicationCommandOptionType } = require('discord.js');
2 |
3 | module.exports = {
4 | name: "pause",
5 | description: '⏸ Pause a giveaway',
6 |
7 | options: [
8 | {
9 | name: 'giveaway',
10 | description: 'The giveaway to pause (message ID or giveaway prize)',
11 | type: ApplicationCommandOptionType.String,
12 | required: true
13 | }
14 | ],
15 |
16 | run: async (client, interaction) => {
17 |
18 | // If the member doesn't have enough permissions
19 | if (!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")) {
20 | return interaction.reply({
21 | content: ':x: You need to have the manage messages permissions to pause giveaways.',
22 | ephemeral: true
23 | });
24 | }
25 |
26 | const query = interaction.options.getString('giveaway');
27 |
28 | // try to find the giveaway with prize alternatively with ID
29 | const giveaway =
30 | // Search with giveaway prize
31 | client.giveawaysManager.giveaways.find((g) => g.prize === query && g.guildId === interaction.guild.id) ||
32 | // Search with giveaway ID
33 | client.giveawaysManager.giveaways.find((g) => g.messageId === query && g.guildId === interaction.guild.id);
34 |
35 | // If no giveaway was found
36 | if (!giveaway) {
37 | return interaction.reply({
38 | content: 'Unable to find a giveaway for `' + query + '`.',
39 | ephemeral: true
40 | });
41 | }
42 |
43 | if (giveaway.pauseOptions.isPaused) {
44 | return interaction.reply({
45 | content: `**[This giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})** is already paused.`,
46 | ephemeral: true
47 | });
48 | }
49 |
50 | // Edit the giveaway
51 | client.giveawaysManager.pause(giveaway.messageId)
52 | // Success message
53 | .then(() => {
54 | // Success message
55 | interaction.reply(`**[This giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})** has now been paused!`);
56 | })
57 | .catch((e) => {
58 | interaction.reply({
59 | content: e,
60 | ephemeral: true
61 | });
62 | });
63 |
64 | }
65 | };
66 |
--------------------------------------------------------------------------------
/slash/ping.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder } = require('discord.js');
2 |
3 | module.exports = {
4 | name: 'ping',
5 | description: '🏓Check my ping!',
6 | run: async (client, interaction) => {
7 | let pembed = new EmbedBuilder()
8 | .setColor('#2F3136')
9 | .setTitle('Client Ping')
10 | .addFields({ name: '**Latency**',
11 | value: `\`${Date.now() - interaction.createdTimestamp}ms\``
12 | })
13 | .addFields({ name: '**API Latency**',
14 | value: `\`${Math.round(client.ws.ping)}ms\``
15 | })
16 | .setTimestamp()
17 | .setFooter({
18 | text: `${interaction.user.username}`,
19 | iconURL: interaction.user.displayAvatarURL()
20 | })
21 | interaction.reply({
22 | embeds: [pembed]
23 | });
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/slash/reroll.js:
--------------------------------------------------------------------------------
1 | const { ApplicationCommandOptionType } = require('discord.js');
2 |
3 | module.exports = {
4 | name: "reroll",
5 | description: '🎉 Reroll a giveaway',
6 |
7 | options: [
8 | {
9 | name: 'giveaway',
10 | description: 'The giveaway to reroll (message ID or prize)',
11 | type: ApplicationCommandOptionType.String,
12 | required: true
13 | }
14 | ],
15 |
16 | run: async (client, interaction) => {
17 |
18 | // If the member doesn't have enough permissions
19 | if (!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")) {
20 | return interaction.reply({
21 | content: ':x: You need to have the manage messages permission to reroll giveaways.',
22 | ephemeral: true
23 | });
24 | }
25 |
26 | const query = interaction.options.getString('giveaway');
27 |
28 | // try to find the giveaway with the provided prize OR with the ID
29 | const giveaway =
30 | // Search with giveaway prize
31 | client.giveawaysManager.giveaways.find((g) => g.prize === query && g.guildId === interaction.guild.id) ||
32 | // Search with giveaway ID
33 | client.giveawaysManager.giveaways.find((g) => g.messageId === query && g.guildId === interaction.guild.id);
34 |
35 | // If no giveaway was found
36 | if (!giveaway) {
37 | return interaction.reply({
38 | content: 'Unable to find a giveaway for `' + query + '`.',
39 | ephemeral: true
40 | });
41 | }
42 |
43 | if (!giveaway.ended) {
44 | return interaction.reply({
45 | content: `[This Giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId}) has not been ended yet`,
46 | ephemeral: true
47 | });
48 | }
49 |
50 | // Reroll the giveaway
51 | client.giveawaysManager.reroll(giveaway.messageId)
52 | .then(() => {
53 | // Success message
54 | interaction.reply(`Rerolled **[this giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})!**`);
55 | })
56 | .catch((e) => {
57 | interaction.reply({
58 | content: e,
59 | ephemeral: true
60 | });
61 | });
62 |
63 | }
64 | };
65 |
--------------------------------------------------------------------------------
/slash/resume.js:
--------------------------------------------------------------------------------
1 | const { ApplicationCommandOptionType } = require('discord.js');
2 |
3 | module.exports = {
4 | name: "resume",
5 | description: '▶ Resume a paused giveaway',
6 |
7 | options: [
8 | {
9 | name: 'giveaway',
10 | description: 'The giveaway to resume (message ID or giveaway prize)',
11 | type: ApplicationCommandOptionType.String,
12 | required: true
13 | }
14 | ],
15 |
16 | run: async (client, interaction) => {
17 |
18 | // If the member doesn't have enough permissions
19 | if (!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")) {
20 | return interaction.reply({
21 | content: ':x: You need to have the manage messages permissions to pause giveaways.',
22 | ephemeral: true
23 | });
24 | }
25 |
26 | const query = interaction.options.getString('giveaway');
27 |
28 | // try to find the giveaway with prize alternatively with ID
29 | const giveaway =
30 | // Search with giveaway prize
31 | client.giveawaysManager.giveaways.find((g) => g.prize === query && g.guildId === interaction.guild.id) ||
32 | // Search with giveaway ID
33 | client.giveawaysManager.giveaways.find((g) => g.messageId === query && g.guildId === interaction.guild.id);
34 |
35 | // If no giveaway was found
36 | if (!giveaway) {
37 | return interaction.reply({
38 | content: 'Unable to find a giveaway for `' + query + '`.',
39 | ephemeral: true
40 | });
41 | }
42 |
43 | if (!giveaway.pauseOptions.isPaused) {
44 | return interaction.reply({
45 | content: `**[This giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})** is not paused!`,
46 | ephemeral: true
47 | });
48 | }
49 |
50 | // Edit the giveaway
51 | client.giveawaysManager.unpause(giveaway.messageId)
52 | // Success message
53 | .then(() => {
54 | // Success message
55 | interaction.reply(`**[This giveaway](https://discord.com/channels/${giveaway.guildId}/${giveaway.channelId}/${giveaway.messageId})** has been successfully resumed!`);
56 | })
57 | .catch((e) => {
58 | interaction.reply({
59 | content: e,
60 | ephemeral: true
61 | });
62 | });
63 |
64 | }
65 | };
66 |
--------------------------------------------------------------------------------
/slash/start.js:
--------------------------------------------------------------------------------
1 | const Discord = require("discord.js")
2 | const { ApplicationCommandOptionType } = require("discord.js");
3 | const messages = require("../utils/message");
4 | const ms = require("ms")
5 | module.exports = {
6 | name: 'start',
7 | description: '🎉 Start a giveaway',
8 |
9 | options: [
10 | {
11 | name: 'duration',
12 | description: 'How long the giveaway should last for. Example values: 1m, 1h, 1d',
13 | type: ApplicationCommandOptionType.String,
14 | required: true
15 | },
16 | {
17 | name: 'winners',
18 | description: 'How many winners the giveaway should have',
19 | type: ApplicationCommandOptionType.Integer,
20 | required: true
21 | },
22 | {
23 | name: 'prize',
24 | description: 'What the prize of the giveaway should be',
25 | type: ApplicationCommandOptionType.String,
26 | required: true
27 | },
28 | {
29 | name: 'channel',
30 | description: 'The channel to start the giveaway in',
31 | type: ApplicationCommandOptionType.Channel,
32 | required: true
33 | },
34 | {
35 | name: 'bonusrole',
36 | description: 'Role which would recieve bonus entries',
37 | type: ApplicationCommandOptionType.Role,
38 | required: false
39 | },
40 | {
41 | name: 'bonusamount',
42 | description: 'The amount of bonus entries the role will recieve',
43 | type: ApplicationCommandOptionType.Integer,
44 | required: false
45 | },
46 | {
47 | name: 'invite',
48 | description: 'Invite of the server you want to add as giveaway joining requirement',
49 | type: ApplicationCommandOptionType.String,
50 | required: false
51 | },
52 | {
53 | name: 'role',
54 | description: 'Role you want to add as giveaway joining requirement',
55 | type: ApplicationCommandOptionType.Role,
56 | required: false
57 | },
58 | ],
59 |
60 | run: async (client, interaction) => {
61 |
62 | // If the member doesn't have enough permissions
63 | if (!interaction.member.permissions.has('ManageMessages') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")) {
64 | return interaction.reply({
65 | content: ':x: You need to have the manage messages permissions to start giveaways.',
66 | ephemeral: true
67 | });
68 | }
69 |
70 | const giveawayChannel = interaction.options.getChannel('channel');
71 | const giveawayDuration = interaction.options.getString('duration');
72 | const giveawayWinnerCount = interaction.options.getInteger('winners');
73 | const giveawayPrize = interaction.options.getString('prize');
74 |
75 | if (!giveawayChannel.isTextBased()) {
76 | return interaction.reply({
77 | content: ':x: Please select a text channel!',
78 | ephemeral: true
79 | });
80 | }
81 | if(isNaN(ms(giveawayDuration))) {
82 | return interaction.reply({
83 | content: ':x: Please select a valid duration!',
84 | ephemeral: true
85 | });
86 | }
87 | if (giveawayWinnerCount < 1) {
88 | return interaction.reply({
89 | content: ':x: Please select a valid winner count! greater or equal to one.',
90 | })
91 | }
92 |
93 | const bonusRole = interaction.options.getRole('bonusrole')
94 | const bonusEntries = interaction.options.getInteger('bonusamount')
95 | let rolereq = interaction.options.getRole('role')
96 | let invite = interaction.options.getString('invite')
97 |
98 | if (bonusRole) {
99 | if (!bonusEntries) {
100 | return interaction.reply({
101 | content: `:x: You must specify how many bonus entries would ${bonusRole} recieve!`,
102 | ephemeral: true
103 | });
104 | }
105 | }
106 |
107 |
108 | await interaction.deferReply({ ephemeral: true })
109 | let reqinvite;
110 | if (invite) {
111 | let invitex = await client.fetchInvite(invite)
112 | let client_is_in_server = client.guilds.cache.get(
113 | invitex.guild.id
114 | )
115 | reqinvite = invitex
116 | if (!client_is_in_server) {
117 | const gaEmbed = {
118 | author: {
119 | name: client.user.username,
120 | iconURL: client.user.displayAvatarURL()
121 | },
122 | title: "Server Check!",
123 | url: "https://youtube.com/c/ZeroSync",
124 | description:
125 | "Woah woah woah! I see a new server! are you sure I am in that? You need to invite me there to set that as a requirement! 😳",
126 | timestamp: new Date(),
127 | footer: {
128 | iconURL: client.user.displayAvatarURL(),
129 | text: "Server Check"
130 | }
131 | }
132 | return interaction.editReply({ embeds: [gaEmbed]})
133 | }
134 | }
135 |
136 | if (rolereq && !invite) {
137 | messages.inviteToParticipate = `**React with 🎉 to participate!**\n>>> - Only members having ${rolereq} are allowed to participate in this giveaway!`
138 | }
139 | if (rolereq && invite) {
140 | messages.inviteToParticipate = `**React with 🎉 to participate!**\n>>> - Only members having ${rolereq} are allowed to participate in this giveaway!\n- Members are required to join [this server](${invite}) to participate in this giveaway!`
141 | }
142 | if (!rolereq && invite) {
143 | messages.inviteToParticipate = `**React with 🎉 to participate!**\n>>> - Members are required to join [this server](${invite}) to participate in this giveaway!`
144 | }
145 |
146 |
147 | // start giveaway
148 | client.giveawaysManager.start(giveawayChannel, {
149 | // The giveaway duration
150 | duration: ms(giveawayDuration),
151 | // The giveaway prize
152 | prize: giveawayPrize,
153 | // The giveaway winner count
154 | winnerCount: parseInt(giveawayWinnerCount),
155 | // Hosted by
156 | hostedBy: client.config.hostedBy ? interaction.user : null,
157 | // BonusEntries If Provided
158 | bonusEntries: [
159 | {
160 | // Members who have the role which is assigned to "rolename" get the amount of bonus entries which are assigned to "BonusEntries"
161 | bonus: new Function('member', `return member.roles.cache.some((r) => r.name === \'${bonusRole ?.name}\') ? ${bonusEntries} : null`),
162 | cumulative: false
163 | }
164 | ],
165 | // Messages
166 | messages,
167 | extraData: {
168 | server: reqinvite == null ? "null" : reqinvite.guild.id,
169 | role: rolereq == null ? "null" : rolereq.id,
170 | }
171 | });
172 | interaction.editReply({
173 | content:
174 | `Giveaway started in ${giveawayChannel}!`,
175 | ephemeral: true
176 | })
177 |
178 | if (bonusRole) {
179 | let giveaway = new Discord.EmbedBuilder()
180 | .setAuthor({ name: `Bonus Entries Alert!` })
181 | .setDescription(
182 | `**${bonusRole}** Has **${bonusEntries}** Extra Entries in this giveaway!`
183 | )
184 | .setColor("#2F3136")
185 | .setTimestamp();
186 | giveawayChannel.send({ embeds: [giveaway] });
187 | }
188 |
189 | }
190 |
191 | };
192 |
--------------------------------------------------------------------------------
/storage/giveaways.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/utils/message.js:
--------------------------------------------------------------------------------
1 | const config = require('../config.json');
2 | module.exports = {
3 | giveaway:
4 | (config.everyoneMention ? "@everyone\n\n" : "") +
5 | "🎉 **GIVEAWAY** 🎉",
6 | giveawayEnded:
7 | (config.everyoneMention ? "@everyone\n\n" : "") +
8 | "🎉 **GIVEAWAY ENDED** 🎉",
9 | drawing: `Ends: **{timestamp}**`,
10 | inviteToParticipate: `React with 🎉 to participate!`,
11 | winMessage: "Congratulations, {winners}! You won **{this.prize}**!",
12 | embedFooter: "{this.winnerCount} winner(s)",
13 | noWinner: "Giveaway cancelled, no valid participations.",
14 | hostedBy: "Hosted by: {this.hostedBy}",
15 | winners: "winner(s)",
16 | endedAt: "Ended at"
17 | }
18 |
--------------------------------------------------------------------------------
/utils/slashsync.js:
--------------------------------------------------------------------------------
1 | const Discord = require('discord.js');
2 |
3 | module.exports = async (client, commands, options = {
4 | debug: false,
5 | guildId: null
6 | }) => {
7 |
8 | const log = (message) => options.debug && console.log(message);
9 |
10 | const ready = client.readyAt ? Promise.resolve() : new Promise(resolve => client.once('ready', resolve));
11 | await ready;
12 | const currentCommands = await client.application.commands.fetch(options.guildId && { guildId: options.guildId });
13 |
14 | log(`Synchronizing commands...`);
15 | log(`Currently ${currentCommands.size} commands are registered to the bot.`);
16 |
17 | const newCommands = commands.filter((command) => !currentCommands.some((c) => c.name === command.name));
18 | for (let newCommand of newCommands) {
19 | await client.application.commands.create(newCommand, options.guildId);
20 | }
21 |
22 | log(`Created ${newCommands.length} commands!`);
23 |
24 | const deletedCommands = currentCommands.filter((command) => !commands.some((c) => c.name === command.name)).toJSON();
25 | for (let deletedCommand of deletedCommands) {
26 | await deletedCommand.delete();
27 | }
28 |
29 | log(`Deleted ${deletedCommands.length} commands!`);
30 |
31 | const updatedCommands = commands.filter((command) => currentCommands.some((c) => c.name === command.name));
32 | let updatedCommandCount = 0;
33 | for (let updatedCommand of updatedCommands) {
34 | const newCommand = updatedCommand;
35 | const previousCommand = currentCommands.find((c) => c.name === updatedCommand.name);
36 | let modified = false;
37 | if (previousCommand.description !== newCommand.description) modified = true;
38 | if (!Discord.ApplicationCommand.optionsEqual(previousCommand.options ?? [], newCommand.options ?? [])) modified = true;
39 | if (modified) {
40 | await previousCommand.edit(newCommand);
41 | updatedCommandCount++;
42 | }
43 | }
44 |
45 | log(`Updated ${updatedCommandCount} commands!`);
46 |
47 | log(`Commands synchronized!`);
48 |
49 | return {
50 | currentCommandCount: currentCommands.size,
51 | newCommandCount: newCommands.length,
52 | deletedCommandCount: deletedCommands.length,
53 | updatedCommandCount
54 | };
55 |
56 | };
--------------------------------------------------------------------------------