discord-giveaways-pro
v1.0.2
Published
A production-grade, high-performance extension of discord-giveaways featuring native button entries, interactive UI components, admin settings modals, write-behind database caching, and rate limit protection.
Maintainers
Readme
discord-giveaways-pro
An optimized, production-grade, and feature-rich extension of Androz2091's popular discord-giveaways library. Built for high-performance Discord bots with large user bases, database persistence, and native button/modal UI components.
🌟 Pro Exclusive Features
Designed as a high-performance upgrade to the original library, discord-giveaways-pro introduces key architectural and interactive UI enhancements:
🎨 Advanced UI & Interactive Components
- 🔘 Native Button Entry: ActionRow button interactions replacing heavy reaction polling.
- 🔀 Hybrid Mode: Seamlessly supports both buttons and reactions simultaneously on the same giveaway, merging entrants without duplicates.
- ⚙️ Admin Settings & Native Modals: Edit active giveaways (duration, winners, prize, image URL, required/excluded roles) dynamically via Discord dropdowns and modals.
- 👥 Paginated Participants Panel: Built-in paginated view of all entrants with a toggle between plaintext tags and user mentions.
- 🗑️ Admin Remove Participant: Interactive
UserSelectMenuBuilderallowing admins to remove specific users from active giveaways. - 🔁 Interactive Admin Reroll Button: Auto-swaps the settings button to a Reroll button when ended, offering multi-winner paginated rerolls directly in Discord.
- 🎨 Fully Customizable Emojis: Configure all 9 UI button and alert emojis globally via manager options.
⚡ Performance & Reliability Upgrades
- 📦 Write-Behind Memory Caching: High-frequency entry changes are queued in RAM and batch-synced to prevent database bottlenecks.
- 💾 Enhanced Database Adapters & Batch Persistence: Replaces slow per-entry synchronous database operations with high-performance async database callbacks (
syncEntrants,fetchActiveGiveawaysEntrants), supporting MySQL, SQLite, PostgreSQL, and MongoDB. - 🚀 Per-Channel Rate Limit Protection: Debounced button updates managed through per-channel queues (~800ms throttle), eliminating HTTP 429 errors.
- ⚡ Cache-First API Optimizations: Eliminates mass guild member fetches. Uses local cache for winner/bonus checks, performing only a single bulk fetch for final winners.
- 🛡️ Custom Middleware Hooks: Add middleware hooks like
beforeJointo run custom bot-side validations (currency checks, role rules, database requirements) before entry.
📦 Installation
npm install discord-giveaways-pro🚀 Quick Start & Configuration
Create a custom subclass extending GiveawaysManager to connect your database adapter, configure default options, and hook custom validation rules.
const { Client, GatewayIntentBits, Events } = require('discord.js');
const { GiveawaysManager } = require('discord-giveaways-pro');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.GuildMembers
]
});
class CustomGiveawaysManager extends GiveawaysManager {
// 1. Retrieve all active and ended giveaways from your database
async getAllGiveaways() {
// Return array of giveaway data objects
return await db.query('SELECT data FROM giveaways');
}
// 2. Save a new giveaway to your database
async saveGiveaway(messageId, giveawayData) {
await db.query('INSERT INTO giveaways (message_id, data) VALUES (?, ?)', [messageId, JSON.stringify(giveawayData)]);
return true;
}
// 3. Update an existing giveaway in your database
async editGiveaway(messageId, giveawayData) {
await db.query('UPDATE giveaways SET data = ? WHERE message_id = ?', [JSON.stringify(giveawayData), messageId]);
return true;
}
// 4. Delete a giveaway from your database
async deleteGiveaway(messageId) {
await db.query('DELETE FROM giveaways WHERE message_id = ?', [messageId]);
return true;
}
}
const manager = new CustomGiveawaysManager(client, {
updateCountdownEvery: 10000,
hasGuildMembersIntent: true,
default: {
botsCanWin: false,
embedColor: '#FFE162',
embedColorEnd: '#CD1818',
reaction: '🎉',
buttonMode: true,
hybridMode: false, // If true, automatically adds reaction emoji alongside buttons
buttonEmoji: '🎉',
participantsEmoji: '👥',
settingsEmoji: '⚙️',
rerollEmoji: '🔁',
removeParticipantEmoji: '🗑️',
prevPageEmoji: '◀️',
nextPageEmoji: '▶️',
successEmoji: '✅',
errorEmoji: '❌'
},
// Database adapter callbacks for high-frequency entrant storage (MySQL, SQLite, etc.)
database: {
async fetchActiveGiveawaysEntrants(messageIds) {
// Preload active entrants into memory cache upon bot startup
// Return array of objects: [{ message_id: '...', user_id: '...' }]
},
async syncEntrants(batch) {
// Process batch queue updates: [{ type: 'insert'|'delete', messageId, userId, enteredAt }]
for (const task of batch) {
if (task.type === 'insert') {
await db.query('INSERT IGNORE INTO giveaway_entries (message_id, user_id) VALUES (?, ?)', [task.messageId, task.userId]);
} else if (task.type === 'delete') {
await db.query('DELETE FROM giveaway_entries WHERE message_id = ? AND user_id = ?', [task.messageId, task.userId]);
}
}
},
async getEntrants(messageId) {
// Retrieve array of user IDs who entered this giveaway
const rows = await db.query('SELECT user_id FROM giveaway_entries WHERE message_id = ?', [messageId]);
return rows.map(r => r.user_id);
}
},
// Middleware hook executed before a user enters a giveaway
async beforeJoin(member, giveaway, interaction) {
// Example: Check if user has enough bot balance or meets custom requirements
// const userBalance = await getUserBalance(member.id);
// if (userBalance < 100) {
// return { pass: false, message: 'You need at least 100 coins to enter this giveaway!' };
// }
return { pass: true };
}
});
client.on(Events.ClientReady, () => {
console.log(`Bot logged in as ${client.user.tag}`);
});
client.login('YOUR_BOT_TOKEN');[!NOTE] Automatic Event Registration: The manager automatically listens to
interactionCreateandrawgateway events on your Discord client. You do NOT need to write any extra event handling boilerplate in yourindex.jsfor giveaway buttons, select menus, or modals!
📖 Code Examples & Usage
🎯 1. Starting a Giveaway
client.on('messageCreate', async (message) => {
if (message.content.startsWith('!gstart')) {
await manager.start(message.channel, {
duration: 60000 * 60, // 1 hour
winnerCount: 2,
prize: 'Nitro Monthly Subscription',
hostedBy: message.author,
thumbnail: message.author.displayAvatarURL(),
image: 'https://i.imgur.com/example.png',
bonusEntries: [
{
// Gives +1 extra ticket if user has VIP role
bonus: (member) => (member.roles.cache.has('VIP_ROLE_ID') ? 1 : null),
cumulative: true
}
],
extraData: {
roleRequirement: ['ROLE_ID_1', 'ROLE_ID_2'], // User must have at least one of these roles
excludedRole: 'BANNED_ROLE_ID' // Users with this role are excluded
}
});
message.reply('🎉 Giveaway started successfully!');
}
});🔄 2. Re-rolling Winners
Re-roll winners programmatically or let admins use the interactive Re-roll Button built into ended giveaway messages.
// Programmatic Reroll
manager.reroll('GIVEAWAY_MESSAGE_ID', {
winnerCount: 1, // Optional: number of new winners to pick
messages: {
congrat: '🎉 New winner(s): {winners}! Congratulations!',
error: 'No valid participants, re-roll cancelled.'
}
}).then(() => {
console.log('Reroll completed!');
}).catch(console.error);✏️ 3. Editing a Giveaway
Edit duration, winner count, prize, or flags dynamically while a giveaway is active.
manager.edit('GIVEAWAY_MESSAGE_ID', {
addTime: 50000, // Add 50 seconds to duration
newWinnerCount: 3,
newPrize: 'Updated Super Prize!',
newImage: 'https://i.imgur.com/new_image.png'
}).then(() => {
console.log('Giveaway updated!');
}).catch(console.error);⏹️ 4. Ending & Deleting Giveaways
// End a giveaway early
manager.end('GIVEAWAY_MESSAGE_ID').then(() => {
console.log('Giveaway ended.');
});
// Delete a giveaway and remove its message
manager.delete('GIVEAWAY_MESSAGE_ID', false).then(() => {
console.log('Giveaway deleted.');
});⏸️ 5. Pausing & Unpausing Giveaways
// Pause giveaway
const giveaway = manager.giveaways.find(g => g.messageId === 'GIVEAWAY_MESSAGE_ID');
await giveaway.pause({
content: '⚠️ **GIVEAWAY PAUSED** ⚠️',
embedColor: '#FFFF00'
});
// Unpause giveaway
await giveaway.unpause();🛠️ In-Bot UI Components Guide
When buttonMode: true is configured, the library attaches interactive components to the giveaway message:
- 🎉 Enter Button (Configurable via
buttonEmoji, default🎉): Allows users to join the giveaway. Clicking adds the user to the entry queue and sends an ephemeral confirmation message containing an interactive Cancel Entry button. - ❌ Cancel Entry Button (Configurable via
errorEmoji, default❌): Attached to ephemeral join confirmation messages. Allows participants to voluntarily withdraw their entry at any time with an instant confirmation message and automatic queue/database synchronization. - 👥 Participants Button (Configurable via
participantsEmoji, default👥): Opens an interactive paginated menu displaying all current entrants (10 per page) with a button to toggle between plaintext usernames and clickable user mentions. - ⚙️ Settings Button (Active Giveaways) (Configurable via
settingsEmoji, default⚙️): Opens a select menu for hosts and administrators to modify base parameters or extra flags (Image URL, Required Roles, Excluded Roles) via native Discord modals. - 🗑️ Remove Participant Button (Configurable via
removeParticipantEmoji, default🗑️): Admins viewing the participants panel can click this button to open aUserSelectMenuBuilderand select up to 10 users to remove from the giveaway in a single operation. - 🔁 Re-roll Button (Ended Giveaways) (Configurable via
rerollEmoji, default🔁): Automatically replaces the settings button when a giveaway ends. Uses an icon-only emoji button to maintain a clean aesthetic. Admins can click it to open a pagination select menu and choose specific winners to re-roll, or re-roll all winners instantly.
📄 License
Distributed under the MIT License. See LICENSE for more information.
