npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

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 UserSelectMenuBuilder allowing 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 beforeJoin to 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 interactionCreate and raw gateway events on your Discord client. You do NOT need to write any extra event handling boilerplate in your index.js for 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:

  1. 🎉 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.
  2. ❌ 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.
  3. 👥 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.
  4. ⚙️ 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.
  5. 🗑️ Remove Participant Button (Configurable via removeParticipantEmoji, default 🗑️): Admins viewing the participants panel can click this button to open a UserSelectMenuBuilder and select up to 10 users to remove from the giveaway in a single operation.
  6. 🔁 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.