From 75c6d307decdc0184623380d2cbbdcb641c70373 Mon Sep 17 00:00:00 2001 From: EmotionChild Date: Sat, 9 Oct 2021 12:58:57 +1300 Subject: [PATCH] Yes --- .gitignore | 4 ++ AVAILABLE_COMMANDS.md | 9 +++ Database Examples/index4mongo.js | 106 +++++++++++++++++++++++++++++++ commands/drop.js | 65 +++++++++++++++++++ commands/end.js | 55 ++++++++++++++++ commands/reroll.js | 62 ++++++++++++++++++ commands/start.js | 75 ++++++++++++++++++++++ config.example.json | 5 ++ events/interactionCreate.js | 14 ++++ events/ready.js | 12 ++++ index.js | 83 ++++++++++++++++++++++++ package.json | 35 ++++++++++ utils/messages.js | 15 +++++ 13 files changed, 540 insertions(+) create mode 100644 .gitignore create mode 100644 AVAILABLE_COMMANDS.md create mode 100644 Database Examples/index4mongo.js create mode 100644 commands/drop.js create mode 100644 commands/end.js create mode 100644 commands/reroll.js create mode 100644 commands/start.js create mode 100644 config.example.json create mode 100644 events/interactionCreate.js create mode 100644 events/ready.js create mode 100644 index.js create mode 100644 package.json create mode 100644 utils/messages.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5d9f0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +package-lock.json +giveaways.js +config.json \ No newline at end of file diff --git a/AVAILABLE_COMMANDS.md b/AVAILABLE_COMMANDS.md new file mode 100644 index 0000000..3aa386e --- /dev/null +++ b/AVAILABLE_COMMANDS.md @@ -0,0 +1,9 @@ +## Giveaway Commands +1. /start [channel-name] [Time] [winners] [Prize] +2. /reroll [giveawayid] +3. /end [prize name] + +## Examples +1. /start #giveaway 5m 1 Testing +2. /end Testing +3. /reroll Testing \ No newline at end of file diff --git a/Database Examples/index4mongo.js b/Database Examples/index4mongo.js new file mode 100644 index 0000000..ff2295e --- /dev/null +++ b/Database Examples/index4mongo.js @@ -0,0 +1,106 @@ +const fs = require('fs'); + +const Discord = require('discord.js'); +const client = new Discord.Client({ + intents: [ + Discord.Intents.FLAGS.GUILDS, + Discord.Intents.FLAGS.GUILD_MESSAGE_REACTIONS + ] +}); + +const config = require('./config.json'); +client.config = config; + +// Load quickmongo +const { Database } = require('quickmongo'); +const db = new Database(`${config.mongo_url}`); + +// check the DB when it's ready +db.once('ready', async () => { + if (!Array.isArray(await db.get('giveaways')) === null) await db.set('giveaways', []); + // start the manager only if the db got checked to prevent an error + client.giveawaysManager._init(); + console.log('SUCCESS!'); +}); + +// Init discord giveaways +const { GiveawaysManager } = require('discord-giveaways'); +class GiveawayManagerWithOwnDatabase extends GiveawaysManager { + // This funtion is called when the manager need to get all the giveaways stored in the database + async getAllGiveaways() { + return await db.get('giveaways'); + } + + // This function is called when a giveaway needs to be saved in the database (when a giveaway is created or when a giveaway is edited). + async saveGiveaway(messageID, giveawayData) { + // Add the nw one + await db.push('giveaways', giveawayData); + // Can't forget to return something. + return true; + } + + async editGiveaway(messageID, giveawayData) { + // Gets all the current giveaways + const giveaways = await db.get('giveaways'); + // Remove the old giveaway from the current giveaways ID + const newGiveawaysArray = giveaways.filter((giveaway) => giveaway.messageID !== messageID); + // Push the new giveaway to the array + newGiveawaysArray.push(giveawayData); + // Save the updated array + await db.set('giveaways', newGiveawaysArray); + // Can't forget to return something. + return true; + } + + async deleteGiveaway(messageID) { + // Gets all the current giveaways + const data = await db.get('giveaways'); + // Remove the giveaway from the array + const newGiveawaysArray = data.filter((giveaway) => giveaway.messageID !== messageID); + // Save the updated array + await db.set('giveaways', newGiveawaysArray); + // Can't forget to return something. + return true; + } +} + +// Create a new instance of your new class +const manager = new GiveawayManagerWithOwnDatabase(client, { + updateCountdownEvery: 10000, + default: { + botsCanWin: false, + embedColor: "#FF0000", + embedColorEnd: '#000000', + reaction: "🎉" + } +}, false) + +client.giveawaysManager = manager; + +/* Load all events */ +fs.readdir("./events/", (_err, files) => { + files.forEach((file) => { + if (!file.endsWith(".js")) return; + const event = require(`./events/${file}`); + let eventName = file.split(".")[0]; + console.log(`👌 Event loaded: ${eventName}`); + client.on(eventName, event.bind(null, client)); + delete require.cache[require.resolve(`./events/${file}`)]; + }); +}); + +client.commands = new Discord.Collection(); + +/* Load all commands */ +fs.readdir("./commands", (_err, files) => { + files.forEach((file) => { + if (!file.endsWith(".js")) return; + let props = require(`./commands/${file}`); + let commandName = file.split(".")[0]; + client.commands.set(commandName, props); + console.log(`👌 Command loaded: ${commandName}`); + }); +}); + +// Login +client.login(config.token); \ No newline at end of file diff --git a/commands/drop.js b/commands/drop.js new file mode 100644 index 0000000..9657ee3 --- /dev/null +++ b/commands/drop.js @@ -0,0 +1,65 @@ +const messages = require("../utils/messages"); + +module.exports = { + + description: 'Create a drop giveaway', + + options: [ + { + name: 'Winners', + description: 'How many winners the giveaway should have', + type: 'INTEGER', + required: true + }, + { + name: 'prize', + description: 'What the prize of the giveaway should be', + type: 'STRING', + required: true + }, + { + name: 'channel', + description: 'The channel to start the giveaway in', + type: 'CHANNEL', + required: true + } + ], + + run: async (client, interaction) => { + + // If the member doesn't have enough permissions + if(!interaction.member.permissions.has('MANAGE_MESSAGES') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")){ + return interaction.reply({ + content: ':x: You need to have the manage messages permissions to start giveaways.', + ephemeral: true + }); + } + + const giveawayChannel = interaction.options.getChannel('channel'); + const giveawayWinnerCount = interaction.options.getInteger('winners'); + const giveawayPrize = interaction.options.getString('prize'); + + if(!giveawayChannel.isText()) { + return interaction.reply({ + content: ':x: Selected channel; is not text-based.', + ephemeral: true + }); + } + + // Start the giveaway + client.giveawaysManager.start(giveawayChannel, { + // The number of winners for this frop + winnerCount: giveawayWinnerCount, + // The prize of the giveaway + prize: giveawayPrize, + // Who hosts this giveaway + hostedBy: client.config.hostedBy ? interaction.user : null, + // specify drop + isDrop: true, + // Messages + messages + }); + + interaction.reply(`Giveaway started in ${giveawayChannel}!`); + } +} \ No newline at end of file diff --git a/commands/end.js b/commands/end.js new file mode 100644 index 0000000..8fb0069 --- /dev/null +++ b/commands/end.js @@ -0,0 +1,55 @@ +const ms = require('ms'); +const messages = require('../utils/messages'); + +module.exports = { + description: 'End a Giveaway', + options: [ + { + name: 'giveaway', + description: 'The giveaway to end (message ID or Giveaway prize.)', + type: 'STRING', + required: true + } + ], + + run: async (client, interaction) => { + // If the member does not have enough permissions + if(!interaction.member.permission.has('MANAGE_MESSAGES') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")){ + return interaction.reply({ + content: `:x: You need to have the manage messages permissions ot end giveaways.`, + ephemeral: true + }); + } + + const query = interaction.options.getString('giveaway'); + + // try to find the giveaway with prize then with ID + const giveaway = + // Search with giveaway prize + client.giveawaysManager.giveaways.find((g) => g.prize == query && g.guildId === messages.guild.id) || + // Search with giveaway ID + client.giveawaysManager.giveaways.find((g) => g.messageID === query && g.guildId === messages.guild.id); + + //if no giveaway was found + if(!giveaway){ + return interaction.reply({ + content: 'Unable to find a giveaway for `'+ query +'`.', + ephemeral: true + }); + } + + // Edit the giveaway + client.giveawaysManager.end(giveaway.messageId) + // Success message + .then(() => { + // Success message + interaction.reply('Giveaway Ended!') + }) + .catch((e) => { + interaction.reply({ + content: e, + ephemeral: true + }) + }); + } +}; \ No newline at end of file diff --git a/commands/reroll.js b/commands/reroll.js new file mode 100644 index 0000000..b69d23d --- /dev/null +++ b/commands/reroll.js @@ -0,0 +1,62 @@ +module.exports = { + + description: 'Reroll a giveaway', + + options: [ + { + name: 'giveaway', + description: 'The giveaway to reroll (message ID or prize)', + type: 'STRING', + required: true + } + ], + + run: async (client, interaction) => { + + // If the member doesn't have enough permissions + if(!interaction.member.permissions.has('MANAGE_MESSAGES') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")){ + return interaction.reply({ + content: ':x: You need to have the manage messages permissions to reroll giveaways.', + ephemeral: true + }); + } + + const query = interaction.options.getString('giveaway'); + + // try to found the giveaway with prize then with ID + const giveaway = + // Search with giveaway prize + client.giveawaysManager.giveaways.find((g) => g.prize === query && g.guildId === interaction.guild.id) || + // Search with giveaway ID + client.giveawaysManager.giveaways.find((g) => g.messageId === query && g.guildId === interaction.guild.id); + + // If no giveaway was found + if (!giveaway) { + return interaction.reply({ + content: 'Unable to find a giveaway for `'+ query +'`.', + ephemeral: true + }); + } + + if (!giveaway.ended) { + return interaction.reply({ + content: 'The giveaway is not ended yet.', + ephemeral: true + }); + } + + // Reroll the giveaway + client.giveawaysManager.reroll(giveaway.messageId) + .then(() => { + // Success message + interaction.reply('Giveaway rerolled!'); + }) + .catch((e) => { + interaction.reply({ + content: e, + ephemeral: true + }); + }); + + } +} \ No newline at end of file diff --git a/commands/start.js b/commands/start.js new file mode 100644 index 0000000..c48a19e --- /dev/null +++ b/commands/start.js @@ -0,0 +1,75 @@ +const ms = require('ms'); +const messages = require("../utils/messages"); + +module.exports = { + + description: 'Start a giveaway', + + options: [ + { + name: 'duration', + description: 'How long the giveaway should last for. Example values: 1m, 1h, 1d', + type: 'STRING', + required: true + }, + { + name: 'winners', + description: 'How many winners the giveaway should have', + type: 'INTEGER', + required: true + }, + { + name: 'prize', + description: 'What the prize of the giveaway should be', + type: 'STRING', + required: true + }, + { + name: 'channel', + description: 'The channel to start the giveaway in', + type: 'CHANNEL', + required: true + } + ], + + run: async (client, interaction) => { + + // If the member doesn't have enough permissions + if(!interaction.member.permissions.has('MANAGE_MESSAGES') && !interaction.member.roles.cache.some((r) => r.name === "Giveaways")){ + return interaction.reply({ + content: ':x: You need to have the manage messages permissions to start giveaways.', + ephemeral: true + }); + } + + const giveawayChannel = interaction.options.getChannel('channel'); + const giveawayDuration = interaction.options.getString('duration'); + const giveawayWinnerCount = interaction.options.getInteger('winners'); + const giveawayPrize = interaction.options.getString('prize'); + + if(!giveawayChannel.isText()) { + return interaction.reply({ + content: ':x: Selected channel is not text-based.', + ephemeral: true + }); + } + + // Start the giveaway + client.giveawaysManager.start(giveawayChannel, { + // The giveaway duration + duration: ms(giveawayDuration), + // The giveaway prize + prize: giveawayPrize, + // The giveaway winner count + winnerCount: giveawayWinnerCount, + // Who hosts this giveaway + hostedBy: client.config.hostedBy ? interaction.user : null, + // Messages + messages + }); + + interaction.reply(`Giveaway started in ${giveawayChannel}!`); + + } + +}; \ No newline at end of file diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..1c36f8c --- /dev/null +++ b/config.example.json @@ -0,0 +1,5 @@ +{ + "token": "Discord Bot Token", + "everyoneMention": false, + "hostedBy": true +} \ No newline at end of file diff --git a/events/interactionCreate.js b/events/interactionCreate.js new file mode 100644 index 0000000..2fd253b --- /dev/null +++ b/events/interactionCreate.js @@ -0,0 +1,14 @@ +module.exports = (client, interaction) => { + + if (!interaction.isCommand()) return; + + const command = client.commands.get(interaction.commandName); + + if (!command) return void interaction.reply({ + content: `Command \`${interaction.commandName}\` not found.`, + ephemeral: true + }); + + command.run(client, interaction); + +}; \ No newline at end of file diff --git a/events/ready.js b/events/ready.js new file mode 100644 index 0000000..73763ee --- /dev/null +++ b/events/ready.js @@ -0,0 +1,12 @@ +module.exports = (client) => { + console.log( + `Ready to server in ${client.channels.cache.size} channels on ${client.guilds.cache.size} servers, for a total of ${client.users.cache.size} users.` + ); + + const activities = [`Giveaways in ${client.guilds.cache.size} guilds`,"g!help",`over ${client.users.cache.size} users!`]; + setInterval(() => { + let activity = activities[Math.floor(Math.random() * activities.length)]; + client.user.setActivity(activity, { type: "WATCHING" }); + }, 20000); + +}; \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..2cf78e2 --- /dev/null +++ b/index.js @@ -0,0 +1,83 @@ +const fs = require('fs'); + +const Discord = require('discord.js'); +const client = new Discord.Client({ + intents: [ + Discord.Intents.FLAGS.GUILDS, + Discord.Intents.FLAGS.GUILD_MEMBERS, + Discord.Intents.FLAGS.GUILD_MESSAGE_REACTIONS + ] +}); + +const config = require('./config.json'); +client.config = config; + +const synchronizeSlashCommands = require('discord-sync-commands'); + +// Init discord giveaways +const { GiveawaysManager } = require('discord-giveaways'); +client.giveawaysManager = new GiveawaysManager(client, { + storage: "./giveaways.json", + default: { + botsCanWin: false, + embedColor: "#FF0000", + reaction: "🎉", + lastChance: { + enabled: true, + content: '⚠️ **LAST CHANCE TO ENTER !** ⚠️', + threshold: 5000, + embedColor: '#FF0000' + } + } +}); +// We now have a client.giveawaysManager property to manage our giveaways! + +client.giveawaysManager.on("giveawayReactionAdded", (giveaway, member, reaction) => { + console.log(`${member.user.tag} entered giveaway #${giveaway.messageId} (${reaction.emoji.name})`); +}); + +client.giveawaysManager.on("giveawayReactionRemoved", (giveaway, member, reaction) => { + console.log(`${member.user.tag} unreact to giveaway #${giveaway.messageId} (${reaction.emoji.name})`); +}); + +client.giveawaysManager.on("giveawayEnded", (giveaway, winners) => { + console.log(`Giveaway #${giveaway.messageId} ended! Winners: ${winners.map((member) => member.user.username).join(', ')}`); +}); + +/* Load all commands */ +client.commands = new Discord.Collection(); +fs.readdir("./commands/", (_err, files) => { + files.forEach((file) => { + if (!file.endsWith(".js")) return; + let props = require(`./commands/${file}`); + let commandName = file.split(".")[0]; + client.commands.set(commandName, { + name: commandName, + ...props + }); + console.log(`👌 Command loaded: ${commandName}`); + }); + synchronizeSlashCommands(client, client.commands.map((c) => ({ + name: c.name, + description: c.description, + options: c.options, + type: 'CHAT_INPUT' + })), { + debug: true + }); +}); + +/* Load all events */ +fs.readdir("./events/", (_err, files) => { + files.forEach((file) => { + if (!file.endsWith(".js")) return; + const event = require(`./events/${file}`); + let eventName = file.split(".")[0]; + console.log(`👌 Event loaded: ${eventName}`); + client.on(eventName, event.bind(null, client)); + delete require.cache[require.resolve(`./events/${file}`)]; + }); +}); + +// Login +client.login(config.token); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..0d54604 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "giveaway-child", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node index.js", + "dev": "nodemon index.js" + }, + "engines": { + "node": "16.x" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/EmotionChild/Giveaway-Child.git" + }, + "author": "EmotionChild", + "license": "GPL-3.0-or-later", + "bugs": { + "url": "https://github.com/EmotionChild/Giveaway-Child/issues" + }, + "homepage": "https://github.com/EmotionChild/Giveaway-Child#readme", + "dependencies": { + "beautify": "^0.0.8", + "discord-giveaways": "^5.0.1", + "discord-sync-commands": "^0.3.0", + "discord.js": "^13.2.0", + "ms": "^2.1.3", + "quickdb": "^1.0.5", + "quickmongo": "^4.0.0" + }, + "devDependencies": { + "nodemon": "^2.0.13" + } +} diff --git a/utils/messages.js b/utils/messages.js new file mode 100644 index 0000000..61719c7 --- /dev/null +++ b/utils/messages.js @@ -0,0 +1,15 @@ +const config = require('../config.json'); + +module.exports = { + giveaway: (config.everyoneMention ? "@everyone\n\n" : "")+"🎉🎉 **GIVEAWAY** 🎉🎉", + giveawayEnded: (config.everyoneMention ? "@everyone\n\n" : "")+"🎉🎉 **GIVEAWAY ENDED** 🎉🎉", + inviteToParticipate: "React with 🎉 to participate!", + dropMessage: "Be the first to react with 🎉 !", + drawing: 'Drawing: {timestamp}', + winMessage: "Congratulations, {winners}! You won **{this.prize}**!", + embedFooter: "Giveaways", + noWinner: "Giveaway cancelled, no valid participations.", + hostedBy: "Hosted by: {this.hostedBy}", + winners: "winner(s)", + endedAt: "Ended at" +}; \ No newline at end of file