Discord Giveaways is a powerful Node.js module that allows you to easily create giveaways!
npm install --save discord-giveaways
You can read this example bot on GitHub: discord-giveaways-bot
Required Discord Intents: Guilds
and GuildMessageReactions
.
Optional Discord Privileged Intent for better performance: GuildMembers
.
const Discord = require('discord.js');
const client = new Discord.Client({
intents: [
Discord.IntentsBitField.Flags.Guilds,
Discord.IntentsBitField.Flags.GuildMessageReactions,
Discord.IntentsBitField.Flags.GuildMembers // Optional, for better performance
]
});
// Requires Manager from discord-giveaways
const { GiveawaysManager } = require('discord-giveaways');
const manager = new GiveawaysManager(client, {
storage: './giveaways.json',
default: {
botsCanWin: false,
embedColor: '#FF0000',
embedColorEnd: '#000000',
reaction: 'π'
}
});
// We now have a giveawaysManager property to access the manager everywhere!
client.giveawaysManager = manager;
client.on('ready', () => {
console.log('Bot is ready!');
});
client.login(process.env.DISCORD_BOT_TOKEN);
After that, giveaways that are not yet completed will start to be updated again and new giveaways can be started. You can pass an options object to customize the giveaways. Here is a list of them:
client.on('interactionCreate', (interaction) => {
const ms = require('ms');
if (interaction.isChatInputCommand() && interaction.commandName === 'start') {
// /start 2d 1 Awesome prize!
// Will create a giveaway with a duration of two days, with one winner and the prize will be "Awesome prize!"
const duration = interaction.options.getString('duration');
const winnerCount = interaction.options.getInteger('winners');
const prize = interaction.options.getString('prize');
client.giveawaysManager
.start(interaction.channel, {
duration: ms(duration),
winnerCount,
prize
})
.then((data) => {
console.log(data); // {...} (messageId, end date and more)
});
// And the giveaway has started!
}
});
This allows you to start a new giveaway. Once the start()
function is called, the giveaway starts, and you only have to observe the result, the package does the rest!
The command examples below (reroll, edit delete, end) can be executed on any server your bot is a member of if a person has the prize
or the messageId
of a giveaway. To prevent abuse we recommend to check if the prize
or the messageId
that was provided by the command user is for a giveaway on the same server, if it is not, then cancel the command execution.
const query = interaction.options.getString('query');
const giveaway =
// Search with giveaway prize
client.giveawaysManager.giveaways.find((g) => g.guildId === interaction.guildId && g.prize === query) ||
// Search with messageId
client.giveawaysManager.giveaways.find((g) => g.guildId === interaction.guildId && g.messageId === query);
// If no giveaway was found
if (!giveaway) return interaction.reply(`Unable to find a giveaway for \`${query}\`.`);
client.on('interactionCreate', (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'reroll') {
const messageId = interaction.options.getString('message_id');
client.giveawaysManager
.reroll(messageId)
.then(() => {
interaction.reply('Success! Giveaway rerolled!');
})
.catch((err) => {
interaction.reply(`An error has occurred, please check and try again.\n\`${err}\``);
});
}
});
client.on('interactionCreate', (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'edit') {
const messageId = interaction.options.getString('message_id');
client.giveawaysManager
.edit(messageId, {
addTime: 5000,
newWinnerCount: 3,
newPrize: 'New Prize!'
})
.then(() => {
interaction.reply('Success! Giveaway updated!');
})
.catch((err) => {
interaction.reply(`An error has occurred, please check and try again.\n\`${err}\``);
});
}
});
Date.now() + 60000
).Note: to reduce giveaway duration, define addTime
with a negative number! For example addTime: -5000
will reduce giveaway duration by 5 seconds!
client.on('interactionCreate', (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'delete') {
const messageId = interaction.options.getString('message_id');
client.giveawaysManager
.delete(messageId)
.then(() => {
interaction.reply('Success! Giveaway deleted!');
})
.catch((err) => {
interaction.reply(`An error has occurred, please check and try again.\n\`${err}\``);
});
}
});
β οΈ Note: when you use the delete function, the giveaway data and the message of the giveaway are deleted (by default). You cannot restore a giveaway once you have deleted it!
client.on('interactionCreate', (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'end') {
const messageId = interaction.options.getString('message_id');
client.giveawaysManager
.end(messageId)
.then(() => {
interaction.reply('Success! Giveaway ended!');
})
.catch((err) => {
interaction.reply(`An error has occurred, please check and try again.\n\`${err}\``);
});
}
});
client.on('interactionCreate', (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'pause') {
const messageId = interaction.options.getString('message_id');
client.giveawaysManager
.pause(messageId)
.then(() => {
interaction.reply('Success! Giveaway paused!');
})
.catch((err) => {
interaction.reply(`An error has occurred, please check and try again.\n\`${err}\``);
});
}
});
GiveawayMessages#drawing
in the paused embed, when there is no unpauseAfter
.β οΈ Note: the pause function overwrites/edits the pauseOptions object property of a giveaway!
client.on('interactionCreate', (interaction) => {
if (interaction.isChatInputCommand() && interaction.commandName === 'unpause') {
const messageId = interaction.options.getString('message_id');
client.giveawaysManager
.unpause(messageId)
.then(() => {
interaction.reply('Success! Giveaway unpaused!');
})
.catch((err) => {
interaction.reply(`An error has occurred, please check and try again.\n\`${err}\``);
});
}
});
// A list of all the giveaways
const allGiveaways = client.giveawaysManager.giveaways; // [ {Giveaway}, {Giveaway} ]
// A list of all the giveaways on the server with Id "1909282092"
const onServer = client.giveawaysManager.giveaways.filter((g) => g.guildId === '1909282092');
// A list of the current active giveaways (not ended)
const notEnded = client.giveawaysManager.giveaways.filter((g) => !g.ended);
Function to filter members. If true is returned, the member will not be able to win the giveaway.
client.giveawaysManager.start(interaction.channel, {
duration: 60000,
winnerCount: 1,
prize: 'Free Steam Key',
// Only members who have the "Nitro Boost" role are able to win
exemptMembers: (member, giveaway) => !member.roles.cache.some((r) => r.name === 'Nitro Boost')
});
Note (only for proficients): if you want to use values of global variables inside of the function without using giveaway.extraData
, you can use the Function constructor:
const roleName = 'Nitro Boost';
client.giveawaysManager.start(interaction.channel, {
duration: 60000,
winnerCount: 1,
prize: 'Free Steam Key',
// Only members who have the the role which is assigned to "roleName" are able to win
exemptMembers: new Function(
'member',
'giveaway',
`return !member.roles.cache.some((r) => r.name === '${roleName}')`
)
});
β Note
this
, instead of the giveaway
parameter, inside of the function string to access anything of the giveaway instance.this.extraData
, or this.client
.giveaway.extraData
for storing variables.client.giveawaysManager.start(interaction.channel, {
duration: 60000,
winnerCount: 1,
prize: 'Discord Nitro!',
lastChance: {
enabled: true,
content: 'β οΈ **LAST CHANCE TO ENTER !** β οΈ',
threshold: 10_000,
embedColor: '#FF0000'
}
});
client.giveawaysManager.start(interaction.channel, {
duration: 60000,
winnerCount: 1,
prize: 'Discord Nitro!',
pauseOptions: {
isPaused: true,
content: 'β οΈ **THIS GIVEAWAY IS PAUSED !** β οΈ',
unpauseAfter: null,
embedColor: '#FFFF00',
infiniteDurationText: '`NEVER`'
}
});
GiveawayMessages#drawing
in the paused embed, when there is no unpauseAfter
.client.giveawaysManager.start(interaction.channel, {
duration: 60000,
winnerCount: 1,
prize: 'Free Steam Key',
bonusEntries: [
{
// Members who have the "Nitro Boost" role get 2 bonus entries
bonus: (member, giveaway) => (member.roles.cache.some((r) => r.name === 'Nitro Boost') ? 2 : null),
cumulative: false
}
]
});
Note (only for proficients): if you want to use values of global variables inside of the function without using giveaway.extraData
, you can use the Function constructor.
Look at the exemptMembers section for more information on that.
Options are available for the following messages:
GiveawayStartOptions#GiveawayMessages#winMessage
, GiveawayRerollOptions#messages#congrat
, GiveawayRerollOptions#messages#error
and client.giveawaysManager.end(messageId, noWinnerMessage)
.
You can access giveaway properties in all embed or component properties that are a string.
The format, including all currently available options, looks like this:
message: {
content: '',
embed: new Discord.EmbedBuilder(),
components: [new Discord.ActionRowBuilder()],
replyToGiveaway: true
}
β Note: When sending a component, content
or embed
is required.
You can access any giveaway property inside of giveaway messages with the format: {this.<property>}
.
For example:
winMessage: 'Congratulations, {winners}! You won **{this.prize}**!\n{this.messageURL}'
Also, you can write JavaScript code inside of {}
.
For example:
winMessage: 'Congratulations, {winners}! You won **{this.prize.toUpperCase()}**!\n{this.messageURL}'
If you want to fill in strings that are not messages of a giveaway, or just custom embeds, then you can use giveaway.fillInString(string)
for strings, giveaway.fillInEmbed(embed)
for embeds and giveaway.fillInComponents(embed)
for components.
You can also pass a messages
parameter for the start()
function, if you want to translate the giveaway texts:
iconURL
can be set.For example:
const duration = interaction.options.getString('duration');
const winnerCount = interaction.options.getInteger('winners');
const prize = interaction.options.getString('prize');
client.giveawaysManager.start(interaction.channel, {
duration: ms(duration),
winnerCount,
prize,
messages: {
giveaway: 'ππ **GIVEAWAY** ππ',
giveawayEnded: 'ππ **GIVEAWAY ENDED** ππ',
title: '{this.prize}',
drawing: 'Drawing: {timestamp}',
dropMessage: 'Be the first to react with π !',
inviteToParticipate: 'React with π to participate!',
winMessage: 'Congratulations, {winners}! You won **{this.prize}**!\n{this.messageURL}',
embedFooter: '{this.winnerCount} winner(s)',
noWinner: 'Giveaway cancelled, no valid participations.',
hostedBy: 'Hosted by: {this.hostedBy}',
winners: 'Winner(s):',
endedAt: 'Ended at'
}
});
You can access giveaway properties in all these messages.
And for the reroll()
function:
client.giveawaysManager.reroll(messageId, {
messages: {
congrat: ':tada: New winner(s): {winners}! Congratulations, you won **{this.prize}**!\n{this.messageURL}',
error: 'No valid participations, no new winner(s) can be chosen!'
}
});
You can access giveaway properties in these messages.
Message options are available in these messages.
You can use your custom database to save giveaways, instead of the json files (the "database" by default for discord-giveaways
).
For this, you will need to extend the GiveawaysManager
class, and replace some methods with your custom ones.
There are 4 methods you will need to replace:
getAllGiveaways
: this method returns an array of stored giveaways.saveGiveaway
: this method stores a new giveaway in the database.editGiveaway
: this method edits a giveaway already stored in the database.deleteGiveaway
: this method deletes a giveaway from the database (permanently).β οΈ All the methods should be asynchronous to return a promise!
SQL examples
NoSQL examples
mongoose
example instead