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-lottery-private

v1.1.4

Published

A complete framework to facilitate the creation of lotteries using discord.js

Downloads

326

Readme

🎰 Discord Lottery Private

npm version npm downloads License Tests Code Quality Node.js Version

A powerful and feature-rich Node.js module for creating and managing lotteries in Discord using discord.js v14+.


🎯 Features

  • 🎰 Easy-to-use lottery system for Discord bots
  • 🎨 Customizable embeds and messages
  • ⏰ Automatic lottery management
  • 🏆 Winner selection with reaction-based entries
  • 💾 Persistent storage (JSON-based)
  • 🎨 Custom database support
  • 🌍 Multi-language support
  • 📊 Event-driven architecture
  • ⚠️ Last Chance announcements - Automatic @everyone pings before lottery ends
  • 🧹 Memory management - Proper cleanup with destroy() method
  • Input validation - Comprehensive parameter validation
  • 🔒 Production ready - 45 tests, 100% passing
  • ⚙️ Highly Customizable - Configure prizes, duration, winners, and more
  • 🚀 Powerful Features - Start, edit, reroll, end, delete, and pause lotteries
  • 💥 Event System - Listen to lottery events (ended, rerolled, deleted, etc.)
  • ⚠️ Last Chance Alerts - Automatic warnings before lottery ends
  • 🕸️ Shard Support - Works seamlessly with sharded bots
  • 🎨 Custom Embeds - Full embed customization
  • 🧪 Well Tested - Comprehensive unit test coverage

📦 Installation

npm install discord-lottery-private

Requirements:

  • Node.js >= 16.9.0
  • discord.js >= 14.0.0

🚀 Quick Start

Basic Setup

const { Client, GatewayIntentBits } = require('discord.js');
const { LotteriesManager } = require('discord-lottery-private');

const client = new Client({
	intents: [
		GatewayIntentBits.Guilds,
		GatewayIntentBits.GuildMessageReactions,
		GatewayIntentBits.GuildMembers // Optional, for better performance
	]
});

// Initialize the lottery manager
const lotteryManager = new LotteriesManager(client, {
	storage: './lotteries.json',
	default: {
		embedColor: '#FF0000',
		embedColorEnd: '#000000',
		reaction: '🎰'
	}
});

// Make it accessible everywhere
client.lotteryManager = lotteryManager;

client.on('ready', () => {
	console.log(`${client.user.tag} is ready!`);
});

client.login('YOUR_BOT_TOKEN');

Start a Lottery

client.on('interactionCreate', async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	if (interaction.commandName === 'lottery') {
		const duration = interaction.options.getInteger('duration'); // in hours
		const prize = interaction.options.getString('prize');

		await client.lotteryManager.start(interaction.channel, {
			duration: duration * 60 * 60 * 1000, // Convert hours to ms
			winnerCount: 1,
			prize: prize,
			hostedBy: interaction.user,
			lastChance: {
				enabled: true,
				content: '⚠️ **LAST CHANCE TO ENTER!** ⚠️',
				threshold: 60 * 60 * 1000, // 1 hour before end
				embedColor: '#FFA500'
			}
		});

		await interaction.reply('🎰 Lottery started!');
	}
});

📖 Documentation

Manager Options

new LotteriesManager(client, {
	storage: './lotteries.json', // or false for no storage
	forceUpdateEvery: 15000, // Update interval in ms
	endedLotteriesLifetime: 30000, // How long to keep ended lotteries
	default: {
		embedColor: '#FF0000',
		embedColorEnd: '#000000',
		reaction: '🎰',
		botsCanWin: false
	}
});

Start a Lottery

lotteryManager.start(channel, {
	duration: 86400000, // 24 hours in ms
	winnerCount: 1,
	prize: '10,000 coins',
	hostedBy: user,

	// Optional: Last chance warning
	lastChance: {
		enabled: true,
		content: '⚠️ **LAST CHANCE TO ENTER!** ⚠️',
		threshold: 3600000, // 1 hour before end
		embedColor: '#FFA500'
	},

	// Optional: Custom messages
	messages: {
		lottery: '🎰🎰 **LOTTERY** 🎰🎰',
		lotteryEnded: '🎰🎰 **LOTTERY ENDED** 🎰🎰',
		inviteToParticipate: 'React with 🎰 to enter!',
		winMessage: 'Congratulations {winners}! You won **{this.prize}**!',
		embedFooter: '{this.winnerCount} winner(s)',
		noWinner: 'Lottery cancelled, no valid participants.',
		winners: 'Winner(s):',
		endedAt: 'Ended at'
	}
});

Edit a Lottery

lotteryManager.edit(messageId, {
	newWinnerCount: 3,
	newPrize: 'New Prize!',
	addTime: 3600000 // Add 1 hour
	// or setEndTimestamp: Date.now() + 7200000 // End in 2 hours
});

End a Lottery

lotteryManager.end(messageId);

Reroll a Lottery

lotteryManager.reroll(messageId, {
	winnerCount: 2,
	messages: {
		congrat: '🎉 New winner(s): {winners}!',
		error: 'No valid participants!'
	}
});

Delete a Lottery

lotteryManager.delete(messageId, {
	doNotDeleteMessage: false // Set to true to keep the message
});

Pause/Unpause a Lottery

// Pause
lotteryManager.pause(messageId, {
	content: '⚠️ **LOTTERY PAUSED** ⚠️',
	unpauseAfter: 3600000, // Auto-unpause after 1 hour
	embedColor: '#FFFF00'
});

// Unpause
lotteryManager.unpause(messageId);

🎯 Events

Listen to lottery events:

// Lottery ended
lotteryManager.on('lotteryEnded', (lottery, winners) => {
	console.log(`Lottery ${lottery.messageId} ended!`);
	console.log(`Winners:`, winners);
});

// Last chance triggered (NEW!)
lotteryManager.on('lotteryLastChance', (lottery) => {
	console.log(`⚠️ Last chance for lottery ${lottery.messageId}!`);

	// Send a public announcement
	const channel = lottery.message?.channel;
	if (channel) {
		channel.send(`@everyone 🎰 **LAST CHANCE!** The lottery ends in ${lottery.lastChance.threshold / 60000} minutes! React now to enter!`);
	}
});

// Lottery rerolled
lotteryManager.on('lotteryRerolled', (lottery, winners) => {
	console.log(`Lottery ${lottery.messageId} rerolled!`);
});

// Lottery deleted
lotteryManager.on('lotteryDeleted', (lottery) => {
	console.log(`Lottery ${lottery.messageId} deleted!`);
});

// Reaction added
lotteryManager.on('lotteryReactionAdded', (lottery, user) => {
	console.log(`${user.tag} entered lottery ${lottery.messageId}`);
});

// Reaction removed
lotteryManager.on('lotteryReactionRemoved', (lottery, user) => {
	console.log(`${user.tag} left lottery ${lottery.messageId}`);
});

Available Events

| Event | Parameters | Description | | ------------------------ | -------------------- | -------------------------------- | | lotteryEnded | (lottery, winners) | Lottery has ended | | lotteryLastChance | (lottery) | Last chance period triggered | | lotteryRerolled | (lottery, winners) | Lottery was rerolled | | lotteryDeleted | (lottery) | Lottery was deleted | | lotteryReactionAdded | (lottery, user) | User entered lottery | | lotteryReactionRemoved | (lottery, user) | User left lottery |


⚠️ Last Chance Feature

The "Last Chance" feature automatically updates the lottery embed with a warning message when approaching the end time and emits an event for custom announcements:

lotteryManager.start(channel, {
	duration: 86400000, // 24 hours
	prize: 'Amazing Prize',
	lastChance: {
		enabled: true,
		content: '⚠️ **LAST CHANCE TO ENTER!** ⚠️',
		threshold: 3600000, // Show warning 1 hour before end
		embedColor: '#FFA500' // Orange color for urgency
	}
});

// Listen for last chance event to send custom announcements
lotteryManager.on('lotteryLastChance', async (lottery) => {
	const channel = lottery.message?.channel;
	if (channel) {
		// Send a public announcement
		await channel.send({
			content: '@everyone',
			embeds: [
				{
					title: '⚠️ LAST CHANCE! ⚠️',
					description: `The **${lottery.prize}** lottery ends soon!\nReact to the message above to enter!`,
					color: 0xffa500,
					footer: { text: 'Hurry up!' }
				}
			]
		});
	}
});

How it works:

  • When remainingTime <= threshold, the embed automatically updates
  • Shows the custom warning message
  • Changes embed color to grab attention
  • Emits lotteryLastChance event for custom announcements
  • Increases participation in the final hours!

Use Cases:

  • Send @everyone ping when last chance triggers
  • Post announcement in a separate channel
  • Log to your database
  • Send DMs to participants
  • Update external systems

🎨 Custom Database

Use your own database instead of JSON files:

class CustomLotteriesManager extends LotteriesManager {
	// Get all lotteries from database
	async getAllLotteries() {
		return await db.lotteries.findMany();
	}

	// Save a new lottery
	async saveLottery(messageId, lotteryData) {
		await db.lotteries.create({
			data: { messageId, ...lotteryData }
		});
	}

	// Edit a lottery
	async editLottery(messageId, lotteryData) {
		await db.lotteries.update({
			where: { messageId },
			data: lotteryData
		});
	}

	// Delete a lottery
	async deleteLottery(messageId) {
		await db.lotteries.delete({
			where: { messageId }
		});
	}
}

🌍 Translations

Customize all messages:

lotteryManager.start(channel, {
	duration: 86400000,
	prize: 'Prize',
	messages: {
		lottery: '🎰 **LOTERIE** 🎰',
		lotteryEnded: '🎰 **LOTERIE TERMINÉE** 🎰',
		inviteToParticipate: 'Réagissez avec 🎰 pour participer!',
		winMessage: 'Félicitations {winners}! Vous avez gagné **{this.prize}**!',
		embedFooter: '{this.winnerCount} gagnant(s)',
		noWinner: 'Loterie annulée, aucun participant valide.',
		winners: 'Gagnant(s):',
		endedAt: 'Terminé à',
		hostedBy: 'Organisé par: {this.hostedBy}'
	}
});

🧪 Testing

Run the test suite:

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Generate coverage report
npm run test:coverage

🛠️ Development

# Clone the repository
git clone https://gitlab.com/bbnetnl/m4t3/npm-packages/discord-lottery.git
cd discord-lottery

# Install dependencies
npm install

# Run linter
npm run lint

# Fix linting issues
npm run lint:fix

# Run tests
npm test

📝 Changelog

See CHANGELOG.md for version history.


🤝 Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

Commit Format:

feat: add new feature      # Minor version bump
fix: bug fix               # Patch version bump
feat!: breaking change     # Major version bump

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


🔗 Links

  • npm: https://www.npmjs.com/package/discord-lottery-private
  • GitLab: https://gitlab.com/bbnetnl/m4t3/npm-packages/discord-lottery
  • Issues: https://gitlab.com/bbnetnl/m4t3/npm-packages/discord-lottery/issues
  • discord.js: https://discord.js.org

💡 Examples

Complete Bot Example

const { Client, GatewayIntentBits } = require('discord.js');
const { LotteriesManager } = require('discord-lottery-private');

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

client.lotteryManager = new LotteriesManager(client, {
	storage: './lotteries.json',
	default: {
		embedColor: '#FF0000',
		embedColorEnd: '#000000',
		reaction: '🎰'
	}
});

// Start lottery command
client.on('interactionCreate', async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	if (interaction.commandName === 'start-lottery') {
		await client.lotteryManager.start(interaction.channel, {
			duration: 24 * 60 * 60 * 1000, // 24 hours
			winnerCount: 1,
			prize: '10,000 Coins',
			hostedBy: interaction.user,
			lastChance: {
				enabled: true,
				content: '⚠️ **LAST CHANCE!** ⚠️',
				threshold: 60 * 60 * 1000,
				embedColor: '#FFA500'
			}
		});

		await interaction.reply('🎰 Lottery started!');
	}

	if (interaction.commandName === 'end-lottery') {
		const messageId = interaction.options.getString('message_id');
		await client.lotteryManager.end(messageId);
		await interaction.reply('🎰 Lottery ended!');
	}

	if (interaction.commandName === 'reroll-lottery') {
		const messageId = interaction.options.getString('message_id');
		await client.lotteryManager.reroll(messageId);
		await interaction.reply('🎰 Lottery rerolled!');
	}
});

// Listen to events
client.lotteryManager.on('lotteryEnded', (lottery, winners) => {
	console.log(`Lottery ended: ${lottery.prize}`);
	console.log(`Winners:`, winners.map((w) => w.tag).join(', '));
});

client.login('YOUR_BOT_TOKEN');

🎉 Credits

Created and maintained by universenl

Based on the original discord-giveaways package with lottery-specific enhancements.


Made with ❤️ for the Discord community