Skip to content
Fully Developed Version - v4.0

Giveaway Command

Current features

  • Giveaway Start
  • Giveaway End(on timeout)
  • Can have more than 1 winners
  • Giveaway End(on command)
  • Storing giveaways in DB
  • Giveaway Reroll
  • Giveaway List(active and ended)
  • Giveaway Info
  • Giveaway Cancel
  • DM winner

Installations needed

NPM Package Install
npm install aoi.js
npm install @akarui/aoi.parser
npm install discord.js
npm install sqlite3

DJS Code

Let’s get started!

const { parseDuration } = require('/parseDuration.js');
const { addGiveaway, addUserToGiveawayDB, fetchParticipantsFromDB, deleteGiveaway, getGiveawayDetails, removeWinnersFromDB, getAllGiveawaysInfo, fetchParticipantsAll, getActiveGiveaways, getEndedGiveaways, deletePermanentDB } = require('/giveawayDB.js');
const { EmbedBuilder } = require('discord.js');
//Start the giveaway
async function startGiveaway(prize, duration, message, winnersCount, host) {
const em = new EmbedBuilder()
.setTitle('Giveaway!')
.setDescription(`**Giveaway Prize:** ${prize}\n**Winner(s):** ${winnersCount}\n**Host:** <@${host}>\n**Ends on:** <t:${Math.floor((Date.now() + parseDuration(duration))/1000)}:f> (<t:${Math.floor((Date.now() + parseDuration(duration))/1000)}:R>)\n\nReact 🎉 to join!`)
.setColor(0xFFA500)
const giveawayMessage = await message.channel.send({ embeds: [em] });
giveawayMessage.react('🎉');
const endTime = Date.now() + parseDuration(duration);
const guildId = message.guild.id;
const channelId = message.channel.id;
const hoster = host;
addGiveaway(giveawayMessage.id, guildId, channelId, prize, endTime, winnersCount, hoster)
// Create a reaction collector for the giveaway message
const filter = (reaction, user) => reaction.emoji.name === '🎉' && !user.bot;
const collector = giveawayMessage.createReactionCollector({ filter, time: parseDuration(duration) });
collector.on('collect', (reaction, user) => {
addUserToGiveawayDB(giveawayMessage.id, user.id)
});
collector.on('end', async (collected, reason) => {
try {
const details = await getGiveawayDetails(giveawayMessage.id);
if (!details) {
return
}
if (!giveawayMessage) {
return message.channel.send('Giveaway Message not found!')
}
const participantsArray = await fetchParticipantsFromDB(giveawayMessage.id);
const embed = new EmbedBuilder()
.setTitle('Giveaway Ended!')
.setDescription(`**Giveaway Prize:** ${prize}\n**Winner(s):** No eligible participants.`)
.setColor(0xFFA500);
if (participantsArray.length === 0) {
await giveawayMessage.edit({ embeds: [embed] });
await deleteGiveaway(giveawayMessage.id);
return
}
const winnersCount = details.winnerCount;
const winners = [];
const win = [];
//dm embed
const e = new EmbedBuilder()
.setTitle('Congratulations!')
.setDescription(`You have won the giveaway for **${details.prize}!**\nThis needs your immediate attention as **a rerolled prize will never come back!** | [Giveaway Link](https://discord.com/channels/${details.guildId}/${details.channelId}/${giveawayMessage.id})`)
.setColor(0xFFC0CB)
// Winner selection
for (let i = 0; i < winnersCount; i++) {
if (participantsArray.length === 0) {
break; // No more participants to select as winners
}
const randomIndex = Math.floor(Math.random() * participantsArray.length);
const winner = participantsArray.splice(randomIndex, 1)[0];
winners.push(`<@${winner}>`);
win.push(`${winner}`)
}
const embedWin = new EmbedBuilder()
.setTitle('Giveaway Ended!')
.setDescription(`**Giveaway Prize:** ${prize}\n**Winner(s):** ${winners.join(', ')}`)
.setColor(0xFFA500);
if (winners.length > 0) {
await removeWinnersFromDB(giveawayMessage.id, win);
await giveawayMessage.edit({ embeds: [embedWin] });
await giveawayMessage.reply(`**Congratulations to ${winners.join(', ')} for winning!**`);
win.forEach((u) => {
const user = message.guild.members.cache.get(u);
if (user) {
try {
user.send({ embeds: [e] });
} catch(er) {
console.error(er);
}
}
});
} else {
await giveawayMessage.edit({ embeds: [embed] });
}
await deleteGiveaway(giveawayMessage.id);
return
} catch (error) {
console.error("Error handling giveaway", error);
return message.channel.send('An error occurred while handling the giveaway.');
}
});
//timeout to end giveaway
setTimeout(() => {
collector.stop();
}, parseDuration(duration));
}
//giveaway force stop
async function stopGiveaway(giveawayId, message) {
try {
const details = await getGiveawayDetails(giveawayId);
if (!details) {
return message.channel.send('Giveaway already ended / not found!');
}
const guild = message.guild;
const giveawayChannel = guild.channels.cache.get(details.channelId);
const giveawayMessage = await giveawayChannel.messages.fetch(giveawayId);
if (!giveawayMessage) {
return message.channel.send('Giveaway Message not found!')
}
const participantsArray = await fetchParticipantsFromDB(giveawayId);
const embed = new EmbedBuilder()
.setTitle('Giveaway Ended!')
.setDescription(`**Giveaway Prize:** ${details.prize}\n**Winner(s):** No eligible participants.`)
.setColor(0xFFA500);
if (participantsArray.length === 0) {
await giveawayMessage.edit({ embeds: [embed] });
await deleteGiveaway(giveawayId);
return message.channel.send('Giveaway manually ended.');
}
const winnersCount = details.winnerCount;
const winners = [];
const win = [];
//dm embed
const e = new EmbedBuilder()
.setTitle('Congratulations!')
.setDescription(`You have won the giveaway for **${details.prize}!**\nThis needs your immediate attention as **a rerolled prize will never come back!** | [Giveaway Link](https://discord.com/channels/${details.guildId}/${details.channelId}/${giveawayId})`)
.setColor(0xFFC0CB)
// Winner selection
for (let i = 0; i < winnersCount; i++) {
if (participantsArray.length === 0) {
break;
}
const randomIndex = Math.floor(Math.random() * participantsArray.length);
const winner = participantsArray.splice(randomIndex, 1)[0];
winners.push(`<@${winner}>`);
win.push(`${winner}`)
}
const embedWin = new EmbedBuilder()
.setTitle('Giveaway Ended!')
.setDescription(`**Giveaway Prize:** ${details.prize}\n**Winner(s):** ${winners.join(', ')}`)
.setColor(0xFFA500);
if (winners.length > 0) {
await removeWinnersFromDB(giveawayMessage.id, win);
await giveawayMessage.edit({ embeds: [embedWin] });
await giveawayMessage.reply(`**Congratulations to ${winners.join(', ')} for winning!**`);
win.forEach((u) => {
const user = guild.members.cache.get(u);
if (user) {
try {
user.send({ embeds: [e] });
} catch(er) {
console.error(er)
}
}
});
} else {
await giveawayMessage.edit({ embeds: [embed] });
}
await deleteGiveaway(giveawayId);
return message.channel.send('Giveaway manually ended.');
} catch (error) {
console.error("Error handling giveaway", error);
return message.channel.send('An error occurred while handling the giveaway.');
}
}
//giveaway reroll
async function rerollGiveaway(giveawayId, winnersCount, message) {
try {
const details = await getAllGiveawaysInfo(giveawayId);
if (!details) {
return message.channel.send('Giveaway not found!');
}
const guild = message.guild;
const giveawayChannel = guild.channels.cache.get(details.channelId);
const giveawayMessage = await giveawayChannel.messages.fetch(giveawayId);
if (Date.now() < details.endTime) {
return message.channel.send('Giveaway not ended!')
}
if (!giveawayMessage) {
return message.channel.send('Giveaway Message not found!')
}
if (winnersCount > details.winnerCount) {
return message.channel
.send('You cannot reroll a giveaway to have more winners than specified in the original!')
}
const participantsArray = await fetchParticipantsAll(giveawayId);
const embed = new EmbedBuilder()
.setTitle('Giveaway Ended!')
.setDescription(`**Giveaway Prize:** ${details.prize}\n**Winner(s):** No eligible participants.`)
.setColor(0xFFA500);
if (participantsArray.length === 0) {
return message.channel.send('No more participants to reroll.');
}
const winners = [];
const win = [];
//dm embed
const e = new EmbedBuilder()
.setTitle('Congratulations!')
.setDescription(`You have won the giveaway for **${details.prize}!**\nThis needs your immediate attention as **a rerolled prize will never come back!** | [Giveaway Link](https://discord.com/channels/${details.guildId}/${details.channelId}/${giveawayId})`)
.setColor(0xFFC0CB)
// Winner selection
for (let i = 0; i < winnersCount; i++) {
if (participantsArray.length === 0) {
break;
}
const randomIndex = Math.floor(Math.random() * participantsArray.length);
const winner = participantsArray.splice(randomIndex, 1)[0];
winners.push(`<@${winner}>`);
win.push(`${winner}`)
}
const embedWin = new EmbedBuilder()
.setTitle('Giveaway Ended!')
.setDescription(`**Giveaway Prize:** ${details.prize}\n**Winner(s):** ${winners.join(', ')}`)
.setColor(0xFFA500);
if (winners.length > 0) {
await removeWinnersFromDB(giveawayMessage.id, win);
await giveawayMessage.edit({ embeds: [embedWin] });
await giveawayMessage.reply(`**Congratulations to ${winners.join(', ')} for winning!**`);
win.forEach((u) => {
const user = guild.members.cache.get(u);
if (user) {
try {
user.send({ embeds: [e] });
} catch(er) {
console.error(er)
}
}
});
} else {
await giveawayMessage.edit({ embeds: [embed] });
}
return message.channel.send('Giveaway rerolled.');
} catch (error) {
console.error("Error handling giveaway", error);
return message.channel.send('An error occurred while handling the giveaway.');
}
}
//giveaway list embed
async function createGiveawayEmbed(giveaways, title) {
const w = [];
let i = 0;
for (const giveaway of giveaways) {
i +=1;
const endTime = new Date(giveaway.endTime);
w.push(`\n\n${i} | ${giveaway.id} | ${giveaway.prize} | <t:${Math.floor(endTime/1000)}:f>`)
}
const em = new EmbedBuilder()
.setTitle(title)
.setColor(0x800080)
.setDescription(`** S No. | ID | Prize | End Time |**${w}`)
return em;
}
//giveaway list function
async function giveawaysList(message) {
const guildId = message.guild.id;
const currentTime = Date.now();
const activeGiveaways = await getActiveGiveaways(guildId);
const endedGiveaways = await getEndedGiveaways(guildId, currentTime);
if (activeGiveaways.length > 0) {
const em = await createGiveawayEmbed(activeGiveaways, 'Active Giveaways');
message.channel.send({ embeds: [em] });
} else {
message.channel.send('No active giveaways!');
}
if (endedGiveaways.length > 0) {
const emb = await createGiveawayEmbed(endedGiveaways, 'Ended Giveaways');
message.channel.send({ embeds: [emb] });
} else {
message.channel.send('No ended giveaways');
}
}
// function to cancel an ongoing giveaway
async function cancelGiveaway(giveawayId, reason, message) {
const details = await getGiveawayDetails(giveawayId);
if (!details) {
return message.channel.send('Giveaway already ended / not found')
}
const guild = message.guild;
const giveawayChannel = guild.channels.cache.get(details.channelId);
const giveawayMessage = await giveawayChannel.messages.fetch(giveawayId);
if (!giveawayMessage) {
return message.channel.send('Giveaway message not found!')
}
const embed = new EmbedBuilder()
.setTitle('Giveaway Cancelled!')
.setDescription(`This giveaway has unfortunately been called off.\n\n**Reason:** ${reason}`)
.setColor(0x610C04)
await deletePermanentDB(giveawayId);
await deleteGiveaway(giveawayId);
giveawayMessage.edit({ embeds: [embed] });
message.channel.send('Giveaway cancelled!');
}
// function to get giveaway status
async function giveawayStatus(giveawayId) {
const details = await getAllGiveawaysInfo(giveawayId);
if (!details) {
return "Giveaway not found or cancelled!";
}
if (Date.now() < details.endTime) {
return "Active";
} else {
return "Inactive";
}
}
// function to get info of a giveaway.
async function giveawayInfo(giveawayId, message) {
const details = await getAllGiveawaysInfo(giveawayId);
if (!details) {
return message.channel.send("Invalid / cancelled giveaway!");
}
const endTime = new Date(details.endTime);
const status = await giveawayStatus(giveawayId);
const embed = new EmbedBuilder()
.setTitle('Giveaway Info')
.setDescription('Here are some insights of the giveaway:')
.addFields(
{name: 'ID', value: `${giveawayId}`},
{name: 'Prize', value: `${details.prize}`},
{name: 'Status', value: `${status}`},
{name: 'Host', value: `<@${details.hoster}>`},
{name: 'Channel', value: `<#${details.channelId}>`},
{name: 'End Time', value: `<t:${Math.floor(endTime/1000)}:f>`},
)
.setColor(0x964B00);
await message.channel.send({ embeds: [embed] });
}
module.exports = {
startGiveaway,
stopGiveaway,
rerollGiveaway,
giveawaysList,
cancelGiveaway,
giveawayInfo,
};

Put the above codes in a folder other than the commands folder where the normal aoi codes are stored. The above is a fully functional discord.js code and no need to be stored on aoi folder as we will simply import it to our giveaway commands!

Explanation

This is for those people who want to understand how the DB works but, can’t do so. So, I will briefly explain all tables.

Primarily, there are 4 tables in the giveaways.db namely - giveaways, participants, giveawaysAll, participantsAll. The first 2 DBs are temporary DBs that is, once the giveaway is ended, the two tables will be refreshed making it empty. The other two tables are permanent and pseudo-permanent respectively. In giveawaysAll, the entry isn’t deleted unless we manually do so. In participantsAll, the entry will be deleted depending on the winner. If a winner is called out, his/her names will be deleted but, others stay there. These two DBs are for list, reroll, etc functions.

I have added comments in the functions so, I think it will make you understand each of its uses. In depth, it’s JavaScript so, it’s better to leave the in-depth explanation as it’s unwanted.

AOI Code

Now, all the DJS codes needed for giveaway is done. Let’s move on for the real code that is, AOI one.

module.exports = [{
name: "giveaway start",
aliases: ["gstart", "giveaway-start"],
code: `
$djsEval[(async () => {
const { startGiveaway } = require('/giveawayManager.js');
const prize = "$messageSlice[2]";
const duration = '$message[1]';
const winnersCount = "$message[2]";
const host = '$authorID';
startGiveaway(prize, duration, message, winnersCount, host);
})();
]
$onlyIf[$message[2]>0;{newEmbed:{description:Enter a number greater than 0.}{color:Red}}]
$onlyIf[$isNumber[$message[1]]==false;{newEmbed:{description:Enter time in parsable format like \`1m/1h/1d\`.}{color:Red}}]
$onlyIf[$parseTime[$message[1]]!=-1;{newEmbed:{description:Enter a valid parsable time.}{color:Red}}]
$onlyIf[$messageSlice[2]!=;{newEmbed:{description:Enter a prize for the giveaway.}{color:Red}}]
$onlyIf[$isNumber[$message[2]]==true;{newEmbed:{description:Enter a valid numberical value for number of winners.}{color:Red}}]
$onlyIf[$message[2]!=;{newEmbed:{description:Enter number of winners.}{color:Red}}]
$onlyIf[$message[1]!=;{newEmbed:{description:Enter a valid time.}{color:Red}}]
$onlyPerms[manageguild;{newEmbed:{description:You do not have \`MANAGE_GUILD\` permission.}{color:Red}}]
$suppressErrors[Something went wrong ]
`
}]

Syntax

OptionTypeExample
TimeParsable Time1d/1w/1m/1h
WinnersNumberAny natural number
PrizeStringAny string

Syntax: giveaway start {time} {winners} {prize}