Skip to content

Commit 0677af8

Browse files
committed
feat: giveaway command
1 parent 18efbe0 commit 0677af8

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

src/commands/giveaway.ts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import {
2+
ChatInputCommandInteraction,
3+
SlashCommandBuilder,
4+
EmbedBuilder,
5+
ButtonBuilder,
6+
ButtonStyle,
7+
ActionRowBuilder,
8+
ComponentType,
9+
GuildMember,
10+
MessageFlags,
11+
} from "discord.js";
12+
13+
export const data = new SlashCommandBuilder()
14+
.setName("giveaway")
15+
.setDescription("Initiate a divine distribution of gifts")
16+
.addStringOption((option) =>
17+
option
18+
.setName("prize")
19+
.setDescription("The sacred relic to be bestowed")
20+
.setRequired(true),
21+
)
22+
.addIntegerOption((option) =>
23+
option
24+
.setName("minutes")
25+
.setDescription("Duration of the gathering in minutes")
26+
.setRequired(true)
27+
.setMinValue(1)
28+
.setMaxValue(1440),
29+
)
30+
.addIntegerOption((option) =>
31+
option
32+
.setName("winners")
33+
.setDescription("Number of chosen souls")
34+
.setRequired(false)
35+
.setMinValue(1)
36+
.setMaxValue(50),
37+
);
38+
39+
export async function execute(
40+
interaction: ChatInputCommandInteraction,
41+
executor: GuildMember,
42+
): Promise<void> {
43+
const prize = interaction.options.getString("prize")!;
44+
const durationMinutes = interaction.options.getInteger("minutes")!;
45+
const winnerCount = interaction.options.getInteger("winners") || 1;
46+
const durationMs = durationMinutes * 60 * 1000;
47+
const endTime = new Date(Date.now() + durationMs);
48+
49+
const joinButton = new ButtonBuilder()
50+
.setCustomId("giveaway_join")
51+
.setLabel("Seek Favor")
52+
.setEmoji("🎁")
53+
.setStyle(ButtonStyle.Primary);
54+
55+
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(joinButton);
56+
57+
const embed = new EmbedBuilder()
58+
.setColor("#FFD700")
59+
.setTitle("🎉 GIVEAWAY INITIATED")
60+
.setDescription(
61+
`**The Altershaper has authorized a distribution of wealth!**\n\n**🎁 PRIZE:** ${prize}\n**👑 HOST:** ${executor}\n**🏆 WINNERS:** ${winnerCount}\n**⏰ ENDS:** <t:${Math.floor(endTime.getTime() / 1000)}:R>`,
62+
)
63+
.addFields({
64+
name: "INSTRUCTIONS",
65+
value:
66+
"Press the button below to enter the sacred lottery! (Press again to withdraw)",
67+
inline: false,
68+
})
69+
.setFooter({
70+
text: "May the Altershaper guide fortune to the worthy.",
71+
})
72+
.setTimestamp();
73+
74+
const message = await interaction.reply({
75+
embeds: [embed],
76+
components: [row],
77+
fetchReply: true,
78+
});
79+
80+
const collector = message.createMessageComponentCollector({
81+
componentType: ComponentType.Button,
82+
time: durationMs,
83+
});
84+
85+
const participants = new Set<string>();
86+
87+
collector.on("collect", async (i) => {
88+
if (i.customId === "giveaway_join") {
89+
if (participants.has(i.user.id)) {
90+
participants.delete(i.user.id);
91+
await i.reply({
92+
content: "**THOU HAST WITHDRAWN FROM THE SACRED LOTTERY!**",
93+
flags: MessageFlags.Ephemeral,
94+
});
95+
} else {
96+
participants.add(i.user.id);
97+
await i.reply({
98+
content: "**THOU HAST BEEN REGISTERED!** May fortune favor thee.",
99+
flags: MessageFlags.Ephemeral,
100+
});
101+
}
102+
}
103+
});
104+
105+
collector.on("end", async () => {
106+
const disabledButton = new ButtonBuilder()
107+
.setCustomId("giveaway_join_ended")
108+
.setLabel("Giveaway Ended")
109+
.setStyle(ButtonStyle.Secondary)
110+
.setDisabled(true);
111+
112+
const disabledRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
113+
disabledButton,
114+
);
115+
116+
const participantArray = Array.from(participants);
117+
const winners: string[] = [];
118+
const baseEndedEmbed = new EmbedBuilder()
119+
.setColor(participantArray.length > 0 ? "#00FF00" : "#FF0000")
120+
.setTitle("🎉 GIVEAWAY CONCLUDED")
121+
.setTimestamp();
122+
123+
if (participantArray.length === 0) {
124+
baseEndedEmbed.setDescription(
125+
`**The Altershaper has authorized a distribution of wealth!**\n\n**🎁 PRIZE:** ${prize}\n**👑 HOST:** ${executor}\n**🏆 WINNERS:** ${winnerCount}\n**⏰ ENDED:** <t:${Math.floor(endTime.getTime() / 1000)}:R>\n\n**😔 RESULT:** No souls sought favor.`,
126+
);
127+
128+
await message.edit({
129+
embeds: [baseEndedEmbed],
130+
components: [disabledRow],
131+
});
132+
133+
await message.reply({
134+
content: `**THE GIVEAWAY FOR "${prize}" HAS ENDED!**\nSadly, no one participated.`,
135+
});
136+
} else {
137+
for (let i = 0; i < winnerCount && participantArray.length > 0; i++) {
138+
const randomIndex = Math.floor(Math.random() * participantArray.length);
139+
winners.push(participantArray[randomIndex]);
140+
participantArray.splice(randomIndex, 1);
141+
}
142+
143+
const winnerMentions = winners.map((id) => `<@${id}>`).join(", ");
144+
145+
baseEndedEmbed.setDescription(
146+
`**The Altershaper has authorized a distribution of wealth!**\n\n**🎁 PRIZE:** ${prize}\n**👑 HOST:** ${executor}\n**🏆 WINNERS:** ${winnerCount}\n**⏰ ENDED:** <t:${Math.floor(endTime.getTime() / 1000)}:R>\n\n**🎉 WINNERS:** ${winnerMentions}`,
147+
);
148+
149+
await message.edit({
150+
embeds: [baseEndedEmbed],
151+
components: [disabledRow],
152+
});
153+
154+
await message.reply({
155+
content: `**🎉 THE DIVINE DISTRIBUTION IS COMPLETE!**\n\nCongratulations to the chosen souls: ${winnerMentions}\nThey have won: **${prize}**`,
156+
});
157+
}
158+
});
159+
}

src/utils/commandLoader.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import * as anime from "../commands/anime.js";
2525
import * as ship from "../commands/ship.js";
2626
import * as webhook from "../commands/webhook.js";
2727
import * as family from "../commands/family.js";
28+
import * as giveaway from "../commands/giveaway.js";
2829

2930
export interface Command {
3031
data: any;
@@ -61,6 +62,7 @@ export function loadCommands(): Collection<string, Command> {
6162
ship,
6263
webhook,
6364
family,
65+
giveaway,
6466
];
6567

6668
for (const command of commandModules) {

src/utils/rolePermissions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const COMMAND_PERMISSIONS: Record<string, PermissionLevel> = {
2424
clear: PermissionLevel.MODERATOR,
2525
archives: PermissionLevel.MODERATOR,
2626
slowmode: PermissionLevel.MODERATOR,
27+
giveaway: PermissionLevel.MODERATOR,
2728

2829
// Basic
2930
// everything else basically

0 commit comments

Comments
 (0)