import { ActionRowBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, ChatInputCommandInteraction, EmbedBuilder, } from 'discord.js'; import { GiveawaySession } from './types.js'; import { DEFAULT_REQUIRE_ALL, DEFAULT_WINNER_COUNT } from './constants.js'; import { getSession, saveSession } from './utils.js'; /** * Handles the start of the giveaway builder. * @param interaction The interaction object from the command or button click. */ export async function startGiveawayBuilder( interaction: ChatInputCommandInteraction | ButtonInteraction, ): Promise { await interaction.deferReply({ flags: ['Ephemeral'] }); const session: GiveawaySession = { step: 1, winnerCount: DEFAULT_WINNER_COUNT, requirements: { requireAll: DEFAULT_REQUIRE_ALL, }, }; await saveSession(interaction.user.id, session); await showBuilderStep(interaction, session); } /** * Handles the display of the current step in the giveaway builder. * @param interaction The interaction object from the command or button click. * @param session The current giveaway session. */ export async function showBuilderStep( interaction: any, session: GiveawaySession, ): Promise { if (!interaction.isCommand() && interaction.responded) { return; } try { let embed: EmbedBuilder; const components: ActionRowBuilder[] = []; switch (session.step) { case 1: embed = createStep1Embed(session); components.push(createStep1Buttons(session)); break; case 2: embed = createStep2Embed(session); components.push(...createStep2Buttons(session)); break; case 3: embed = createStep3Embed(session); components.push(...createStep3Buttons(session)); break; case 4: embed = createStep4Embed(session); components.push(...createStep4Buttons()); break; case 5: embed = createStep5Embed(session); components.push(...createStep5Buttons()); break; default: embed = new EmbedBuilder() .setTitle('🎉 Giveaway Creation') .setDescription('Setting up your giveaway...') .setColor(0x3498db); } if (interaction.replied || interaction.deferred) { await interaction.editReply({ embeds: [embed], components }); } else { await interaction.update({ embeds: [embed], components }); } } catch (error) { console.error('Error in showBuilderStep:', error); if (!interaction.replied) { try { await interaction.reply({ content: 'There was an error updating the giveaway builder.', flags: ['Ephemeral'], }); } catch (replyError) { console.error('Failed to send error reply:', replyError); } } } } /** * Handles the next step in the giveaway builder. * @param interaction The interaction object from the button click. */ export async function nextBuilderStep( interaction: ButtonInteraction, ): Promise { const session = await getSession(interaction.user.id); if (!session) { await interaction.reply({ content: 'Your giveaway session has expired. Please start over.', flags: ['Ephemeral'], }); return; } if (session.step === 1) { if (!session.prize || !session.endTime) { await interaction.reply({ content: 'Please set both prize and duration before continuing.', flags: ['Ephemeral'], }); return; } if (!(session.endTime instanceof Date)) { await interaction.reply({ content: 'Invalid duration setting. Please set the duration again.', flags: ['Ephemeral'], }); return; } } session.step = Math.min(session.step + 1, 5); await saveSession(interaction.user.id, session); await showBuilderStep(interaction, session); } /** * Handles the previous step in the giveaway builder. * @param interaction The interaction object from the button click. */ export async function previousBuilderStep( interaction: ButtonInteraction, ): Promise { const session = await getSession(interaction.user.id); if (!session) { await interaction.reply({ content: 'Your giveaway session has expired. Please start over.', flags: ['Ephemeral'], }); return; } session.step = Math.max(session.step - 1, 1); await saveSession(interaction.user.id, session); await showBuilderStep(interaction, session); } function createStep1Embed(session: GiveawaySession): EmbedBuilder { const endTimeValue = session.endTime instanceof Date ? `${session.duration} (ends )` : 'Not set'; return new EmbedBuilder() .setTitle(' Giveaway Creation - Step 1/5') .setDescription('Set the basic details for your giveaway.') .setColor(0x3498db) .addFields([ { name: 'Prize', value: session.prize || 'Not set', inline: true }, { name: 'Duration', value: endTimeValue, inline: true }, { name: 'Winners', value: session.winnerCount.toString(), inline: true }, ]); } function createStep1Buttons( session: GiveawaySession, ): ActionRowBuilder { return new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_set_prize') .setLabel('Set Prize') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId('giveaway_set_duration') .setLabel('Set Duration') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId('giveaway_set_winners') .setLabel('Set Winners') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId('giveaway_next') .setLabel('Next Step') .setStyle(ButtonStyle.Success) .setDisabled(!session.prize || !session.endTime), ); } function createStep2Embed(session: GiveawaySession): EmbedBuilder { const requirementsList = []; if (session.requirements?.level) { requirementsList.push(`• Level ${session.requirements.level}+`); } if (session.requirements?.roleId) { requirementsList.push(`• Role <@&${session.requirements.roleId}>`); } if (session.requirements?.messageCount) { requirementsList.push(`• ${session.requirements.messageCount}+ messages`); } const requirementsText = requirementsList.length ? `${session.requirements.requireAll ? 'ALL requirements must be met' : 'ANY ONE requirement must be met'}\n${requirementsList.join('\n')}` : 'No requirements set'; return new EmbedBuilder() .setTitle('🎉 Giveaway Creation - Step 2/5') .setDescription('Set entry requirements for your giveaway (optional).') .setColor(0x3498db) .addFields([ { name: 'Prize', value: session.prize || 'Not set' }, { name: 'Requirements', value: requirementsText }, ]); } function createStep2Buttons( session: GiveawaySession, ): ActionRowBuilder[] { return [ new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_set_requirements') .setLabel('Set Requirements') .setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId('giveaway_toggle_logic') .setLabel( session.requirements.requireAll ? 'Require ANY' : 'Require ALL', ) .setStyle(ButtonStyle.Secondary), ), new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_previous') .setLabel('Previous Step') .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId('giveaway_next') .setLabel('Next Step') .setStyle(ButtonStyle.Success), ), ]; } function createStep3Embed(session: GiveawaySession): EmbedBuilder { const embed = new EmbedBuilder() .setTitle('🎉 Giveaway Creation - Step 3/5') .setDescription('Select Giveaway Channel (optional).') .setColor(0x3498db) .addFields([ { name: 'Channel', value: session.channelId ? `<#${session.channelId}>` : 'Current Channel', }, ]); return embed; } function createStep3Buttons( session: GiveawaySession, ): ActionRowBuilder[] { return [ new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_set_channel') .setLabel(session.channelId ? 'Change Channel' : 'Set Channel') .setStyle(ButtonStyle.Primary), ), new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_previous') .setLabel('Previous Step') .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId('giveaway_next') .setLabel('Next Step') .setStyle(ButtonStyle.Success), ), ]; } function createStep4Embed(session: GiveawaySession): EmbedBuilder { const bonusEntries = session.bonusEntries || {}; const rolesText = bonusEntries.roles?.map((r) => `<@&${r.id}>: +${r.entries}`).join('\n') || 'None'; const levelsText = bonusEntries.levels ?.map((l) => `Level ${l.threshold}+: +${l.entries}`) .join('\n') || 'None'; const messagesText = bonusEntries.messages ?.map((m) => `${m.threshold}+ messages: +${m.entries}`) .join('\n') || 'None'; return new EmbedBuilder() .setTitle('🎉 Giveaway Creation - Step 4/5') .setDescription('Configure bonus entries for your giveaway.') .setColor(0x3498db) .addFields([ { name: 'Role Bonuses', value: rolesText, inline: true }, { name: 'Level Bonuses', value: levelsText, inline: true }, { name: 'Message Bonuses', value: messagesText, inline: true }, ]); } function createStep4Buttons(): ActionRowBuilder[] { return [ new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_bonus_entries') .setLabel('Set Bonus Entries') .setStyle(ButtonStyle.Primary), ), new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_previous') .setLabel('Previous Step') .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId('giveaway_next') .setLabel('Next Step') .setStyle(ButtonStyle.Success), ), ]; } function createStep5Embed(session: GiveawaySession): EmbedBuilder { return new EmbedBuilder() .setTitle('🎉 Giveaway Creation - Step 5/5') .setDescription('Finalize your giveaway settings.') .setColor(0x3498db) .addFields([ { name: 'Role to Ping', value: session.pingRoleId ? `<@&${session.pingRoleId}>` : 'None', }, ]); } function createStep5Buttons(): ActionRowBuilder[] { return [ new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_set_ping_role') .setLabel('Set Ping Role') .setStyle(ButtonStyle.Primary), ), new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId('giveaway_previous') .setLabel('Previous Step') .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId('giveaway_publish') .setLabel('Create Giveaway') .setStyle(ButtonStyle.Success), ), ]; }