const { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType, Events } = require('discord.js'); module.exports = { name: 'giveaway', aliases: '', description: 'Create giveaway for SkyHikerMC', cooldown : 5, permissions : { bot : '', user: '', }, mode: { developerOnly : false, ownerGuildOnly : false, }, category: "Staff Ultility", async run(bot, interaction, args, timecooldown, prefix) { const giveawayChannel = interaction.mentions.channels.first(); const giveawayRole = interaction.mentions.roles.first(); const giveawayWinnerAmount = parseInt(args[2]); const giveawayCountDown = parseInt(args[3]); const giveawayArgs = args.slice(4).join(" "); const managementRole = bot.config.settings.management; const memberRole = bot.config.settings.member; // Required user have management Role if (!interaction.member.roles.cache.has(managementRole)) { setTimeout(() => { interaction.channel.messages.delete(interaction); }, 1000); const noPermEmbed = new EmbedBuilder() .setColor(bot.config.embed.error) .setTitle(`<:alert:861479518811979786> No Permission to Access`) .setDescription("You do not have the necessary permissions to use this command.") .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); return interaction.channel.send({ embeds: [noPermEmbed] }) .then((message) => { setTimeout(() => { message.delete(); }, 7000); }); } // Required command to have the correct length if (args.length < 5) { setTimeout(() => { interaction.channel.messages.delete(interaction); }, 3000); const instructionEmbed = new EmbedBuilder() .setColor(bot.config.embed.instrucColor) .setTitle("<:info:875347263558389800> Command Info") .setDescription("Please follow the command:\n\n`-giveaway `.") .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); return interaction.channel.send({ embeds: [instructionEmbed] }) .then((message) => { setTimeout(() => { message.delete(); }, 7000); }); } // Required args[2] to be a positive number if (isNaN(giveawayWinnerAmount) || giveawayWinnerAmount <= 0) { setTimeout(() => { interaction.channel.messages.delete(interaction); }, 3000); const notWinnerEmbed = new EmbedBuilder() .setColor(bot.config.embed.error) .setTitle("<:alert:861479518811979786> Invalid Winners Specified") .setDescription("Please specify the number of winners you would like to have in `-giveaway `.") .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); return interaction.channel.send({ embeds: [notWinnerEmbed] }) .then((message) => { setTimeout(() => { message.delete(); }, 7000); }); } // Required args[3] to be a positive number if (isNaN(giveawayCountDown) || giveawayCountDown <= 0) { setTimeout(() => { interaction.channel.messages.delete(interaction); }, 3000); const notNumberEmbed = new EmbedBuilder() .setColor(bot.config.embed.error) .setTitle("<:alert:861479518811979786> Invalid Time Specified") .setDescription("Please specify the time (in minutes) in `-giveaway `.") .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); return interaction.channel.send({ embeds: [notNumberEmbed] }) .then((message) => { setTimeout(() => { message.delete(); }, 7000); }); } // Check if the mentioned channel and role exist if (!giveawayChannel || !giveawayRole) { setTimeout(() => { interaction.channel.messages.delete(interaction); }, 3000); const invalidMentionEmbed = new EmbedBuilder() .setColor(bot.config.embed.error) .setTitle("<:alert:861479518811979786> Invalid Channel or Role Mentioned") .setDescription("Please mention a valid channel and role in `-giveaway `.") .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); return interaction.channel.send({ embeds: [invalidMentionEmbed] }) .then((message) => { setTimeout(() => { message.delete(); }, 7000); }); } // giveaway Embed (Processing)_________________________________________________________________ let startTime = giveawayCountDown * 60; // change giveaway CountDown to d/h/m/s const hoursStart = Math.floor(startTime / 3600); const minutesStart = Math.floor((startTime % 3600) / 60); const secondsStart = startTime % 60; const startTimeString = `${hoursStart} h ${minutesStart} m`; // Buttons const giveawayJoinButton = new ButtonBuilder() .setStyle(ButtonStyle.Primary) .setLabel('JOIN') .setCustomId('giveaway_join') .setEmoji('๐ŸŽ‰'); const giveawayQuitButton = new ButtonBuilder() .setStyle(ButtonStyle.Secondary) .setLabel('QUIT') .setCustomId('giveaway_quit') .setEmoji('๐Ÿšซ'); const giveawayParticipantButton = new ButtonBuilder() .setStyle(ButtonStyle.Secondary) .setLabel('JOINERS') .setCustomId('giveaway_participant') .setEmoji('๐Ÿฅท'); const giveawayActionRow = new ActionRowBuilder().addComponents(giveawayJoinButton, giveawayQuitButton, giveawayParticipantButton); // Check if the user has the management role if (interaction.member.roles.cache.has(managementRole.id)) { // User has the management role, so you can add the participant button giveawayActionRow.addComponent(giveawayParticipantButton); } // Interaction join button let giveawayEntriesAmount = 0; const enteredUsers = new Set(); // Set to store entered user IDs // giveaway Starting Embed const giveawayEmbed = new EmbedBuilder() .setAuthor({ name: `${interaction.author.username}`, iconURL: `${interaction.author.displayAvatarURL()}` }) .setColor(bot.config.embed.purpleColor) .setTitle("ยป ๐ŸŽ‰ ๐—š๐—œ๐—ฉ๐—˜๐—”๐—ช๐—”๐—ฌ ๐—ฆ๐—ง๐—”๐—ฅ๐—ง๐—˜๐——") .setDescription(`\n${giveawayArgs}\n\n`) .addFields( { name: ' ', value: `\n\n๐Ÿ’Ž \n` + '**```js\n' + `๐Ÿ“ฅ แด‡ษดแด›ส€ษชแด‡๊œฑ : ${giveawayEntriesAmount} \n` + `๐Ÿ† แดกษชษดษดแด‡ส€๊œฑ: ${giveawayWinnerAmount}\n` + `๐ŸŽ Giveaway Started! Ends in ${startTimeString} !\n` + '```**' } ) .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); let giveawayMessage; giveawayChannel.send({ content: `<@&854302713609584671> & ${giveawayRole}, React the button to participate the giveaway!`, embeds: [giveawayEmbed], components: [giveawayActionRow] }) .then(message => { giveawayMessage = message; setTimeout(() => { interaction.channel.messages.delete(interaction); }, 2000); // Record Embed ID to console console.log('Giveaway Created:', giveawayMessage.id); }); // Start the countdown timer && Update the embed every second______________________________________________________________________ let timeLeft = giveawayCountDown * 60; const countdownTimer = setInterval(async () => { try { timeLeft -= 1; // Subtract 1 second // Change giveaway CountDown to h/m/s const hoursLeft = Math.floor(timeLeft / 3600); const minutesLeft = Math.floor((timeLeft % 3600) / 60); const secondsLeft = timeLeft % 60; // Update the giveaway embed with the remaining time const remainingTimeString = `${hoursLeft} h ${minutesLeft} m ${secondsLeft} s`; // Clone the original giveaway embed and update the time field const updatedGiveawayEmbed = EmbedBuilder.from(giveawayEmbed).spliceFields(0, 1, { name: ' ', value: `\n\n๐Ÿ’Ž \n` + '**```js\n' + `๐Ÿ“ฅ แด‡ษดแด›ส€ษชแด‡๊œฑ : ${giveawayEntriesAmount} \n` + `๐Ÿ† แดกษชษดษดแด‡ส€๊œฑ: ${giveawayWinnerAmount}\n` + `โŒ›๏ธ Giveaway End in ${remainingTimeString} !\n` + '```**' }); // Edit the giveaway message with the updated giveaway embed const updatedGiveawayMessage = await giveawayChannel.messages.fetch(giveawayMessage.id); await updatedGiveawayMessage.edit({ content: `<@&854302713609584671> & ${giveawayRole}, React the button to participate the giveaway!`, embeds: [updatedGiveawayEmbed], components: [giveawayActionRow] }); if (timeLeft <= 0) { clearInterval(countdownTimer); // Ending giveaway section && Pick the users____________________________________________________________________________________________ // Define the enteredUsersArray variable const enteredUsersArray = Array.from(enteredUsers); // Convert the set to an array // Randomly pick winners within the giveawayEntriesAmount (can be 1 or more winners) const giveawayWinnerTags = []; for (let i = 0; i < giveawayWinnerAmount; i++) { if (enteredUsersArray.length === 0) { console.log(`No users to be selected as winners`); break; // No more users left to select as winners } const randomIndex = Math.floor(Math.random() * enteredUsersArray.length); const winnerId = enteredUsersArray.splice(randomIndex, 1)[0]; try { const winnerMember = await interaction.guild.members.fetch(winnerId); console.log(winnerMember.user.id); // Log the winnerMember object to the console if (winnerMember && winnerMember.user.id) { const winnerTag = winnerMember.toString(); // Get the user mention giveawayWinnerTags.push(winnerTag); // Store the winner's tag } else { console.log(`Invalid member object returned for ID ${winnerId}`); } } catch (error) { console.log(`Failed to fetch member with ID ${winnerId}`); } } // There are winners if (giveawayWinnerTags.length > 0) { const giveawayWinnerTag = giveawayWinnerTags.join('\n'); // Giveaway End Embed const giveawayEndEmbed = new EmbedBuilder() .setAuthor({ name: `${interaction.author.username}`, iconURL: `${interaction.author.displayAvatarURL()}` }) .setColor(bot.config.embed.success) .setTitle("ยป ๐ŸŽŠ ๐—š๐—œ๐—ฉ๐—˜๐—”๐—ช๐—”๐—ฌ ๐—˜๐—ก๐——๐—˜๐——") .setDescription(`\n${giveawayArgs}\n\n`) .addFields({ name: ' ', value: `\n ๐Ÿ† **แดกษชษดษดแด‡ส€๊œฑ:**\n` + `${giveawayWinnerTag}` + `\n` }, { name: ' ', value: '\n\n๐ŸŽ‰\n**```arm\n' + 'GIVEAWAY FINISHED!\n' + '```**' }) .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); // Update the giveaway embed with the final results const finalGiveawayMessage = await giveawayChannel.messages.fetch(giveawayMessage.id); await finalGiveawayMessage.edit({ content: `<@&854302713609584671> & ${giveawayRole}, **GIVEAWAY ENDED**, Thanks for joining.`, embeds: [giveawayEndEmbed], components: [] }); // Send an additional message announcing the winners await giveawayChannel.send(`Thank you to everyone who participated! \nCongratulations to our winner(s):\n${giveawayWinnerTag}`); } else { // Create an embed to display the no winners message const noWinnersEmbed = new EmbedBuilder() .setColor(bot.config.embed.error) .setTitle("ยป ๐ŸŽŠ ๐—š๐—œ๐—ฉ๐—˜๐—”๐—ช๐—”๐—ฌ ๐—˜๐—ก๐——๐—˜๐——") .setDescription(`\n${giveawayArgs}\n\n`) .addFields({ name: ' ', value: `\n๐ŸŽ‰ **No Winners**\n` + `๐Ÿ˜ข Unfortunately, no winner in this giveaway event! ` + `Look forward to seeing you in the next giveaway!` }, { name: ' ', value: '\n\n**```arm\n' + 'GIVEAWAY FINISHED!\n' + '```**' }) .setFooter({ text: bot.config.embed.footer, iconURL: bot.config.embed.serverIcon }) .setTimestamp(); // Update the giveaway embed with the no winners message const finalGiveawayMessage = await giveawayChannel.messages.fetch(giveawayMessage.id); await finalGiveawayMessage.edit({ content: `<@&854302713609584671> & ${giveawayRole}, **GIVEAWAY ENDED**`, embeds: [noWinnersEmbed], components: [] }); } } } catch (error) { console.log(error); clearInterval(countdownTimer); } }, 1000); // Update the countdown every 1000 milliseconds (1 second) // Create a collector for button interactions const collector = interaction.message.createMessageComponentCollector({ componentType: ComponentType.Button, time: giveawayCountDown * 1000 }); collector.on('collect', async (i) => { if (i.user.id === interaction.user.id) { i.reply(`${i.user.id} clicked on the ${i.customId} button.`); } else { i.reply({ content: `You do not have the permission to react this button.`, ephemeral: true }).then((responseMessage) => { setTimeout(() => { responseMessage.delete(); }, 2500); // Delete the response after 2.5 seconds }); } }); collector.on('end', (collected) => { console.log(`Collected ${collected.size} interactions.`); }); } }