const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, AttachmentBuilder, PermissionFlagsBits } = require('discord.js'); const ms = require('ms'); const path = require('path'); const config = require('../config.json'); module.exports = { data: new SlashCommandBuilder() .setName('contest') .setDescription('Start a contest countdown.') .addStringOption(option => option.setName('time') .setDescription('Set the contest duration (e.g., 1d, 1h, 1m)') .setRequired(true)) .addStringOption(option => option.setName('competitionmessagelink') .setDescription('Send the competition message link!') .setRequired(true)) .setDefaultMemberPermissions(PermissionFlagsBits.Administrator), async execute(interaction) { const time = interaction.options.getString('time'); const competitionLink = interaction.options.getString('competitionmessagelink'); const duration = ms(time); if (!duration) { return interaction.reply({ content: 'Invalid time format. Please use a valid format (e.g., 1d, 1h, 1m).', ephemeral: true }); } const contestEndTimestamp = Math.floor(Date.now() / 1000) + Math.floor(duration / 1000); const logoPath = path.join(__dirname, '../petloverslogo.png'); const logoAttachment = new AttachmentBuilder(logoPath); // Embed setup const embed = new EmbedBuilder() .setColor(0x1E90FF) .setTitle('🕒 Contest Countdown') .setDescription(`This contest has been started [here](${competitionLink}). To attend, please press the attend button and link us your GitHub repository there.`) .addFields( { name: 'End Time', value: `` }, { name: 'Status', value: 'Ongoing', inline: true }, { name: 'Participants', value: '0', inline: true } ) .setFooter({ text: 'Pet Lovers Development Team', iconURL: 'attachment://petloverslogo.png' }) .setTimestamp(); const row = new ActionRowBuilder() .addComponents( new ButtonBuilder() .setCustomId('attend') .setLabel('Attend') .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId('unattend') .setLabel('Withdraw') .setStyle(ButtonStyle.Danger) ); const contestChannel = interaction.client.channels.cache.get(config.contestChannelId); const message = await contestChannel.send({ embeds: [embed], components: [row], files: [logoAttachment] }); let participants = new Set(); const filter = i => ['attend', 'unattend'].includes(i.customId); const collector = message.createMessageComponentCollector({ filter, time: duration }); collector.on('collect', async i => { if (i.customId === 'attend') { if (participants.has(i.user.id)) { return i.reply({ content: 'You are already attending!', ephemeral: true }); } participants.add(i.user.id); const updatedEmbed = EmbedBuilder.from(embed) .setFields( { name: 'End Time', value: `` }, { name: 'Status', value: 'Ongoing', inline: true }, { name: 'Participants', value: `${participants.size}`, inline: true } ); await message.edit({ embeds: [updatedEmbed] }); i.reply({ content: 'You have successfully joined the contest!', ephemeral: true }); } else if (i.customId === 'unattend') { if (!participants.has(i.user.id)) { return i.reply({ content: 'You are not currently attending.', ephemeral: true }); } participants.delete(i.user.id); const updatedEmbed = EmbedBuilder.from(embed) .setFields( { name: 'End Time', value: `` }, { name: 'Status', value: 'Ongoing', inline: true }, { name: 'Participants', value: `${participants.size}`, inline: true } ); await message.edit({ embeds: [updatedEmbed] }); i.reply({ content: 'You have withdrawn from the contest.', ephemeral: true }); } }); collector.on('end', async () => { const endedEmbed = EmbedBuilder.from(embed) .setFields( { name: 'End Time', value: `` }, { name: 'Status', value: 'Ended', inline: true }, { name: 'Participants', value: `${participants.size}`, inline: true } ) .setColor(0xFF0000); await message.edit({ embeds: [endedEmbed], components: [] }); contestChannel.send(`🏁 The contest has ended with ${participants.size} participants!`); }); await interaction.reply({ content: 'Contest started!', ephemeral: true }); } };