mirror of
https://github.com/ahmadk953/poixpixel-discord-bot.git
synced 2025-05-10 10:43:06 +00:00
feat: add giveaway system
Signed-off-by: Ahmad <103906421+ahmadk953@users.noreply.github.com>
This commit is contained in:
parent
e898a9238d
commit
d9d5f087e7
23 changed files with 2811 additions and 168 deletions
375
src/util/giveaways/builder.ts
Normal file
375
src/util/giveaways/builder.ts
Normal file
|
@ -0,0 +1,375 @@
|
|||
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<void> {
|
||||
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<void> {
|
||||
if (!interaction.isCommand() && interaction.responded) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let embed: EmbedBuilder;
|
||||
const components: ActionRowBuilder<ButtonBuilder>[] = [];
|
||||
|
||||
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<void> {
|
||||
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<void> {
|
||||
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 <t:${Math.floor(session.endTime.getTime() / 1000)}:R>)`
|
||||
: '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<ButtonBuilder> {
|
||||
return new ActionRowBuilder<ButtonBuilder>().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<ButtonBuilder>[] {
|
||||
return [
|
||||
new ActionRowBuilder<ButtonBuilder>().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<ButtonBuilder>().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<ButtonBuilder>[] {
|
||||
return [
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('giveaway_set_channel')
|
||||
.setLabel(session.channelId ? 'Change Channel' : 'Set Channel')
|
||||
.setStyle(ButtonStyle.Primary),
|
||||
),
|
||||
new ActionRowBuilder<ButtonBuilder>().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<ButtonBuilder>[] {
|
||||
return [
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('giveaway_bonus_entries')
|
||||
.setLabel('Set Bonus Entries')
|
||||
.setStyle(ButtonStyle.Primary),
|
||||
),
|
||||
new ActionRowBuilder<ButtonBuilder>().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<ButtonBuilder>[] {
|
||||
return [
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('giveaway_set_ping_role')
|
||||
.setLabel('Set Ping Role')
|
||||
.setStyle(ButtonStyle.Primary),
|
||||
),
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('giveaway_previous')
|
||||
.setLabel('Previous Step')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('giveaway_publish')
|
||||
.setLabel('Create Giveaway')
|
||||
.setStyle(ButtonStyle.Success),
|
||||
),
|
||||
];
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue