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

ertu-giveaways

v3.5.9

Published

A powerful and customizable giveaway system built for Discord.js v14

Readme

ertu-giveaways

A powerful and customizable giveaway system built for Discord.js v14.

npm version discord.js License: GPL-3.0


Features

  • 🎉 Start, end, pause, unpause, edit, and reroll giveaways
  • 🎁 Drop giveaways (first click wins)
  • 🏆 Bonus entries support
  • 🔒 Exempt members or permissions from entering
  • 🌍 Multi-language support (English, Turkish, French, Spanish, Portuguese, German)
  • 💾 JSON-based persistent storage (easily replaceable with a database)
  • 🎨 Fully customizable container colors and button styles
  • ⏳ Last-chance mode with custom color and message
  • ⏸️ Pause & resume giveaways
  • 📋 Paginated participant list with alphabetical or chronological sorting

Requirements

  • Node.js v16.9.0 or higher
  • discord.js v14

Installation

npm install ertu-giveaways

Note: discord.js must also be installed in your project.

npm install discord.js

Getting Started

Basic Setup

const { Client, GatewayIntentBits } = require('discord.js');
const { GiveawaysManager } = require('ertu-giveaways');

const client = new Client({
    intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.GuildMembers,
    ]
});

const manager = new GiveawaysManager(client, {
    storage: './giveaways.json',
    default: {
        containerColor: '#FF0000',
        containerColorEnd: '#000000',
        buttonEmoji: '🎉',
    }
});

client.giveawaysManager = manager;

client.once('ready', () => {
    console.log(`Logged in as ${client.user.tag}`);
});

client.login('YOUR_BOT_TOKEN');

Manager Options

| Option | Type | Default | Description | |---|---|---|---| | storage | string | ./giveaways.json | Path to the JSON file where giveaways are stored | | forceUpdateEvery | number \| null | null | Interval (ms) to force-check giveaways. Defaults to 10 seconds | | endedGiveawaysLifetime | number \| null | null | How long (ms) to keep ended giveaways in storage before deleting | | language | string | en | Language for giveaway messages. Supported: en, tr, fr, es, pt, de | | default.containerColor | ColorResolvable | #FF0000 | Default container accent color while giveaway is active | | default.containerColorEnd | ColorResolvable | #000000 | Default container accent color when giveaway ends | | default.buttonEmoji | string | 🎉 | Emoji shown on the join button | | default.buttonStyle | ButtonStyle | Secondary | Style of the join button | | default.exemptPermissions | PermissionResolvable[] | [] | Members with these permissions cannot enter giveaways | | default.exemptMembers | Function | () => false | A function to determine if a member should be excluded | | default.lastChance | LastChanceOptions | See below | Last-chance configuration |


Starting a Giveaway

const channel = client.channels.cache.get('CHANNEL_ID');

client.giveawaysManager.start(channel, {
    duration: 60000,        // 60 seconds
    winnerCount: 1,
    prize: 'Discord Nitro',
    hostedBy: interaction.user,
});

Start Options

| Option | Type | Required | Description | |---|---|---|---| | duration | number | ✅ | Duration of the giveaway in milliseconds | | winnerCount | number | ✅ | Number of winners | | prize | string | ✅ | Prize name | | hostedBy | User | ❌ | The user who is hosting the giveaway | | containerColor | ColorResolvable | ❌ | Accent color for the active giveaway container | | containerColorEnd | ColorResolvable | ❌ | Accent color after the giveaway ends | | thumbnail | string | ❌ | URL of a thumbnail image shown in the giveaway | | image | string | ❌ | URL of an image shown in the giveaway | | exemptPermissions | PermissionResolvable[] | ❌ | Permissions that block entry | | exemptMembers | Function | ❌ | Custom function to exclude specific members | | bonusEntries | BonusEntry[] | ❌ | Array of bonus entry rules | | lastChance | LastChanceOptions | ❌ | Last-chance configuration | | pauseOptions | PauseOptions | ❌ | Pause configuration | | isDrop | boolean | ❌ | If true, the first person to click wins immediately | | extraData | any | ❌ | Any extra data you want to store with the giveaway | | allowedMentions | MessageMentionOptions | ❌ | Controls which mentions are allowed in giveaway messages |


Ending a Giveaway

client.giveawaysManager.end('MESSAGE_ID');

// With a custom no-winner message
client.giveawaysManager.end('MESSAGE_ID', 'No valid participants!');

Rerolling a Giveaway

client.giveawaysManager.reroll('MESSAGE_ID', {
    winnerCount: 2,
    messages: {
        congrat: '🎉 New winners: {winners}! You won **{this.prize}**!',
        error: 'No valid participants to reroll!',
        replyWhenNoWinner: true,
    }
});

Editing a Giveaway

client.giveawaysManager.edit('MESSAGE_ID', {
    newPrize: 'Steam Gift Card',
    newWinnerCount: 3,
    addTime: 30000,           // add 30 seconds
    setEndTimestamp: Date.now() + 300000, // or set a specific end time
    newThumbnail: 'https://example.com/image.png',
    newLastChance: { enabled: true, threshold: 5000, containerColor: '#FF0000' },
});

Deleting a Giveaway

// Delete the giveaway and its message
client.giveawaysManager.delete('MESSAGE_ID');

// Delete only from storage (keep the Discord message)
client.giveawaysManager.delete('MESSAGE_ID', true);

Pausing & Resuming

// Pause
client.giveawaysManager.pause('MESSAGE_ID', {
    content: '⏸️ This giveaway is paused.',
    containerColor: '#FFFF00',
});

// Resume
client.giveawaysManager.unpause('MESSAGE_ID');

Bonus Entries

Bonus entries allow specific members to get additional chances to win.

client.giveawaysManager.start(channel, {
    duration: 60000,
    winnerCount: 1,
    prize: 'Nitro',
    bonusEntries: [
        {
            // Members with the MANAGE_MESSAGES permission get +2 entries
            bonus: (member) => member.permissions.has('ManageMessages') ? 2 : 0,
            cumulative: false,
        }
    ]
});

Exempt Members

Prevent specific members from entering a giveaway.

client.giveawaysManager.start(channel, {
    duration: 60000,
    winnerCount: 1,
    prize: 'Nitro',
    exemptMembers: (member) => member.roles.cache.has('ROLE_ID'), // excluded if they have this role
});

Last Chance Mode

When the remaining time drops below the threshold, the container color changes to signal urgency.

client.giveawaysManager.start(channel, {
    duration: 60000,
    winnerCount: 1,
    prize: 'Nitro',
    lastChance: {
        enabled: true,
        threshold: 10000,         // activate when 10 seconds remain
        containerColor: '#FF6600',
        content: '⚠️ Last chance to enter!',
    }
});

Drop Giveaways

The first person to click the button wins immediately.

client.giveawaysManager.start(channel, {
    duration: 60000,
    winnerCount: 1,
    prize: 'Nitro',
    isDrop: true,
});

Events

Listen to giveaway events on the manager.

// Fired when a giveaway ends
manager.on('giveawayEnded', (giveaway, winners) => {
    console.log(`Giveaway ended! Winners: ${winners.map(w => w.user.tag).join(', ')}`);
});

// Fired when a user joins a giveaway
manager.on('giveawayJoined', (giveaway, member, interaction) => {
    console.log(`${member.user.tag} joined the giveaway for ${giveaway.prize}`);
});

// Fired when a user leaves a giveaway
manager.on('giveawayLeaved', (giveaway, member, interaction) => {
    console.log(`${member.user.tag} left the giveaway`);
});

// Fired when a giveaway is rerolled
manager.on('giveawayRerolled', (giveaway, winners) => {
    console.log(`Giveaway rerolled! New winners: ${winners.map(w => w.user.tag).join(', ')}`);
});

// Fired when a giveaway is deleted
manager.on('giveawayDeleted', (giveaway) => {
    console.log(`Giveaway for ${giveaway.prize} was deleted`);
});

Multi-Language Support

Set the language when creating the manager:

const manager = new GiveawaysManager(client, {
    language: 'tr', // Turkish
    default: { ... }
});

Supported languages:

| Code | Language | |---|---| | en | English | | tr | Turkish | | fr | French | | es | Spanish | | pt | Portuguese | | de | German |


Giveaway Properties

Once a giveaway is started, you can access these properties:

| Property | Type | Description | |---|---|---| | prize | string | The prize name | | winnerCount | number | Number of winners | | winnerIds | string[] | IDs of the winners (after end) | | participants | string[] | IDs of all participants | | startAt | number | Start timestamp (ms) | | endAt | number | End timestamp (ms) | | ended | boolean | Whether the giveaway has ended | | remainingTime | number | Milliseconds remaining | | duration | number | Total duration in ms | | messageURL | string | Direct link to the giveaway message | | isDrop | boolean | Whether it's a drop giveaway | | hostedBy | string | Mention string of the host | | extraData | any | Custom data attached to the giveaway |


Links


License

GPL-3.0 © Ertuğrul 'Ertu' Karahanlı