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

testsss-akazaz-lib

v0.6.74

Published

๐ŸŽ‰ NEW: Dashboard System - Web Control Panel! ๐ŸŒ

Readme

Example Image

Typing SVG

Welcome to the ultimate Discord Bot Utilities package! ๐Ÿฅ Boost your Discord bot development with ease, speed, and all-in-one features.


๐Ÿ“‘ Table of Contents

  1. ๐ŸŽฏ Starter โ€“ Initialize your bot with commands, events, presence, and more.
  2. โš™๏ธ Functions โ€“ Utilities like CreateRow, CreateBar, Wait, and GetUser.
  3. โšก Commands & Events โ€“ Easy setup with cooldowns, permissions, logging, and anti-crash.
  4. ๐ŸŒ Dashboard โ€“ Web-based control panel for managing your bot.

๐ŸŽฏ STARTER

The starter function is the ultimate initializer for your Discord bot ๐Ÿค–. It handles everything from logging in, loading commands/events, setting the bot presence, anti-crash protection, logging commands usage, and even checking for library updates.

Features:

  • ๐Ÿ› ๏ธ One-line loader for both Slash & Prefix commands ๐Ÿ”ฉ
  • ๐Ÿ“œ Comprehensive terminal info display (commands, events, bot stats) ๐Ÿ“Š
  • ๐Ÿงฐ Event handler loader in one line ๐ŸŽ‰
  • โš ๏ธ Anti-crash system with automatic webhook reporting ๐Ÿ“ƒ
  • ๐Ÿ”‹ MongoDB connection support ๐Ÿ“ฅ
  • ๐Ÿ“‘ Command logger for both Slash and Prefix commands ๐Ÿงญ
  • ๐Ÿ’ก Supports custom prefixes per guild and cooldowns โณ
  • ๐Ÿ”ผ Automatic update checker for djs-builder ๐Ÿ“ฆ

๐Ÿ’ก Tip: Any option you donโ€™t want, just remove it ๐Ÿ—‘๏ธ.


const { starter } = require("djs-builder");
const { Client, GatewayIntentBits } = require("discord.js");

const client = new Client({
  intents: Object.keys(GatewayIntentBits).map((a) => GatewayIntentBits[a]),
});

// Define starter options
const starterOptions = {
  bot: {
    token: "YOUR_BOT_TOKEN", // ๐Ÿ”‘ Discord bot token
    ownerId: "YOUR_USER_ID", // ๐Ÿ‘ค Bot owner ID
  },
  terminal: true, // ๐Ÿ–ฅ๏ธ Show bot info in terminal

  Status: {
    status: "online", // โœ… Presence state: online, dnd, idle, offline
    activities: ["Game 1", "Game 2"], // ๐ŸŽฎ Multiple activities
    type: 0, // ๐ŸŽญ Activity type: (0=PLAYING, 1=STREAMING, 2=LISTENING, 3=WATCHING)
    time: 60000, // โฑ๏ธ Rotate activities every X ms
    url: "https://twitch.tv/example", // ๐ŸŒ Twitch URL (only required for streaming)
  },

  database: {
    url: "mongodb://localhost:27017", // ๐Ÿ’พ MongoDB connection
  },

  anticrash: {
    url: "https://your.crash.webhook.url", // ๐Ÿšจ Webhook for crash reports
    mention_id: "YOUR_USER_ID", // ๐Ÿ“ฃ Optional: mention user on crash
  },

  // ๐ŸŒ Dashboard Configuration (Full docs at the end of this page)
  dashboard: {
    clientID: "YOUR_DISCORD_CLIENT_ID",        // ๐Ÿ”‘ Discord Application Client ID
    clientSecret: "YOUR_DISCORD_CLIENT_SECRET", // ๐Ÿ” Discord Application Client Secret
    callbackURL: "http://localhost:3000/auth/discord/callback", // ๐Ÿ”— OAuth2 Callback URL
    sessionSecret: "your-super-secret-key",    // ๐Ÿ”’ Session encryption secret
    port: 3000,                                 // ๐ŸŒ Dashboard port (default: 3000)
  },
};

// Start the bot
await starter(client, starterOptions);

๐Ÿ“Œ How Starter Works

1๏ธโƒฃ Bot Login & Status

  • Authenticates the bot using your token ๐Ÿ”‘.
  • Supports multiple rotating activities (e.g., Game 1 โ†’ Game 2 โ†’ โ€ฆ) โฑ๏ธ.
  • Works with all Discord activity types: PLAYING, STREAMING, LISTENING, WATCHING ๐ŸŽญ.
  • Twitch URL is supported for streaming mode ๐ŸŒ.

2๏ธโƒฃ Database Connection

  • Connects automatically to MongoDB ๐Ÿ’พ.
  • Useful for bots with persistent data storage.

3๏ธโƒฃ Anticrash System

  • Catches and logs:

    • unhandledRejection
    • uncaughtException
    • uncaughtExceptionMonitor
    • unhandledRejectionMonitor
    • warning โš ๏ธ
  • Sends error reports to a webhook ๐Ÿšจ.

  • Optionally pings the owner ๐Ÿ“ฃ.

4๏ธโƒฃ Terminal Info

  • Displays colorful and structured information ๐Ÿ–ฅ๏ธ:

    • Bot name, user, guild, and channel counts ๐Ÿ“Š.
    • Owner ID ๐Ÿ‘ค.
    • Database connection status ๐Ÿ’พ.
    • Uptime โณ.
  • Powered by cli-table3 + chalk for a professional CLI look ๐ŸŽจ.

5๏ธโƒฃ Auto Update Checker

  • Monitors new versions of djs-builder automatically ๐Ÿ”„.
  • Sends update notifications to the webhook ๐ŸŽ‰.

6๏ธโƒฃ Bot Files Information

  • Access detailed stats about loaded files directly from the client:

    • Number of prefix commands โšก.
    • Number of slash commands โš”๏ธ.
    • Number of events ๐ŸŽ‰.
  • Available via client.files, useful for debugging or terminal display ๐Ÿ› ๏ธ.

7๏ธโƒฃ Dashboard (Web Control Panel)

  • Launches a modern web-based control panel for your bot ๐ŸŒ.
  • Supports Discord OAuth2 authentication ๐Ÿ”.
  • Manage servers, levels, giveaways, and blacklists from the browser ๐ŸŽ›๏ธ.

๐Ÿ“– Full Dashboard documentation available at the end of this page โ†’ ๐ŸŒ DASHBOARD


๐Ÿ’ก Tips

  • Flexible: Delete any section you donโ€™t need (anticrash, database, etc.) ๐Ÿ—‘๏ธ.
  • Multi-Status: Add as many activities as you want and let them rotate ๐ŸŽฎ.
  • Safe by Default: Anticrash system ensures your bot wonโ€™t go down easily ๐Ÿ›ก๏ธ.
  • Always Up-to-Date: Automatic update checker keeps your bot running on the latest version โฌ†๏ธ.
  • Transparent: Quickly check how many files your bot has loaded anytime ๐Ÿ“Š.


โš™๏ธ functions

  • Easiest โœจ / Fastest โšก /Clear ๐Ÿงต

Than the discord.js


๐Ÿ”ต CreateRow โ€“ Easily create Discord Action Rows with Buttons & Select Menus โœจ

CreateRow is a powerful utility to build Discord Action Rows. It supports:

  • Buttons โœ…
  • Select Menus ๐ŸŽฏ (string, role, user, channel)
  • Advanced options like defaultValues and channelTypes.

๐Ÿ“Œ Example Usage:

const { CreateRow } = require("djs-builder");

const actionRow = new CreateRow([
  //// For each new row, use [] for buttons or {} for a select menu

  // ๐Ÿ”น Row #1: Buttons
  [
    {
      id: "button1", // customId for the button
      style: 1, // Button styles: Primary(1), Secondary(2), Success(3), Danger(4), Link(5)
      label: "Primary Button", // Text shown on button
      emoji: "๐Ÿ˜ƒ", // Emoji displayed
      disabled: false, // true = disabled
    },
    {
      id: "button2",
      style: 2,
      emoji: "๐Ÿš€",
      disabled: true, // Button is disabled
    },
  ],

  // ๐Ÿ”น Row #2: Select Menu
  {
    type: "string", // Options: "string" | "role" | "user" | "channel"
    options: {
      id: "menu1", // customId for the select menu
      placeholder: "Select an option",
      min: 1, // Minimum selection
      max: 2, // Maximum selection

      // ๐Ÿ”ธ Data for string select only
      data: [
        {
          name: "Option 1",
          id: "opt1",
          about: "First option",
          icon: "๐ŸŒŸ",
          default: true,
        },
        { name: "Option 2", id: "opt2", about: "Second option", icon: "๐Ÿš€" },
        { name: "Option 3", id: "opt3", about: "Third option", icon: "๐Ÿ”—" },
      ],

      // ๐Ÿ”ธ Map keys
      label: "name", // Which field is the label
      value: "id", // Which field is the value
      description: "about", // Description for each option
      emoji: "icon", // Emoji for each option

      // ๐Ÿ”ธ Extra options
      disabled: false, // Disable the entire menu
      defaultValues: [
        // For role/user/channel menus
        { id: "123456789012345678", type: "user" }, // Pre-selected
      ],
      channelTypes: [0, 2], // Only for ChannelSelectMenu (0 = Text, 2 = Voice)
    },
  },
]);

๐Ÿ“– Explanation

๐Ÿ”น Buttons

  • id โ†’ customId for the button
  • style โ†’ 1: Primary, 2: Secondary, 3: Success, 4: Danger, 5: Link
  • label โ†’ Button text
  • emoji โ†’ Displayed emoji
  • disabled โ†’ true = button is unclickable

๐Ÿ”น Select Menus

  • type โ†’ "string" | "user" | "role" | "channel"

  • id โ†’ customId for menu

  • placeholder โ†’ Text shown before selection

  • min / max โ†’ Min/Max selectable values

  • data โ†’ Options array (for string select only)

    • label โ†’ Visible text
    • value โ†’ Internal value
    • description โ†’ Short description
    • emoji โ†’ Option emoji
    • default โ†’ Pre-selected option
  • disabled โ†’ Disable menu completely

  • defaultValues โ†’ Pre-selected user/role/channel options

  • channelTypes โ†’ Restrict selectable channel types


๐Ÿงพ CreateBar โ€“ Text-based Progress Bar for Discord โœจ

CreateBar allows you to display a customizable progress bar with optional percentages and partial symbols. Perfect for showing progress, loading, or stats in messages.


๐Ÿ“Œ Basic Example:

const { CreateBar } = require("djs-builder");

const bar = new CreateBar(7, 10, {
  length: 20, // Total length of the bar
  fill: "๐Ÿ’š", // Filled portion
  empty: "๐Ÿ–ค", // Empty portion
  partialChar: "๐Ÿ’›", // Partial fill
  showPercent: true, // Show percentage
  left: "โฐ", // Left bracket
  right: "โฑ", // Right bracket
});

console.log(bar);
// Output: โฐ๐Ÿ’š๐Ÿ’š๐Ÿ’š๐Ÿ’š๐Ÿ’š๐Ÿ’š๐Ÿ’š๐Ÿ’›๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–ค๐Ÿ–คโฑ 70%

๐Ÿ“Œ Another Example with different symbols:

console.log(
  CreateBar(3.7, 5, {
    fill: "๐ŸŸฆ",
    empty: "โฌ›",
    partialChar: "๐ŸŸจ",
    length: 10,
    left: "โฐ",
    right: "โฑ",
    showPercent: true,
  })
);

// Output: โฐ๐ŸŸฆ๐ŸŸฆ๐ŸŸฆ๐ŸŸจโฌ›โฌ›โฌ›โฌ›โฌ›โฌ›โฑ 74%

๐Ÿ“Œ Minimalist example without percentage:

console.log(
  CreateBar(4, 8, {
    fill: "๐Ÿ”ต",
    empty: "โšช",
    showPercent: false,
  })
);

// Output: ๐Ÿ”ต๐Ÿ”ต๐Ÿ”ต๐Ÿ”ตโšชโšชโšชโšช

๐Ÿ“Œ Fun Emoji example:

console.log(
  CreateBar(6, 10, {
    length: 12,
    fill: "๐Ÿ”ฅ",
    empty: "โ„๏ธ",
    partialChar: "๐ŸŒŸ",
    showPercent: true,
    left: "ยซ",
    right: "ยป",
  })
);

// Output: ยซ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ๐ŸŒŸโ„๏ธโ„๏ธโ„๏ธโ„๏ธยป 60%

๐Ÿ”น Options Summary

  • length โ†’ Total number of symbols
  • fill โ†’ Symbol for filled portion
  • empty โ†’ Symbol for empty portion
  • partialChar โ†’ Symbol for partial fill (e.g., half-filled)
  • showPercent โ†’ Show percentage at the end
  • left / right โ†’ Brackets or edges for the bar

๐Ÿ”น Notes

  • Supports fractional values for partial fill
  • Fully customizable with any emoji or character ๐ŸŽจ
  • Great for progress, stats, experience bars, or loading indicators


โฐ Wait โ€“ Await messages, buttons, select menus or modals easily โœจ

Wait is a replacement for traditional collectors. It supports:

  • Awaiting messages ๐Ÿ“
  • Awaiting interactions (buttons / select menus) ๐ŸŽ›๏ธ
  • Awaiting modal submissions ๐Ÿ“‹
  • Filtering by user and timeout

๐Ÿ“Œ Example Usage:

const { Wait } = require("djs-builder");

const response = await Wait({
  context: message, // Message or Interaction object
  userId: message.author.id, // Optional: filter by user
  type: "both", // "message" | "interaction" | "both"
  time: 30000, // Time in ms
  message_Wait: message, // Required if waiting for buttons/selects
});

if (!response) return console.log("โฑ๏ธ Timeout!");
console.log("โœ… Collected:", response);

๐Ÿ”น Options

  • context โ†’ The message or interaction context
  • userId โ†’ Only collect from this user (optional)
  • type โ†’ "message" | "interaction" | "both"
  • time โ†’ Timeout in milliseconds
  • message_Wait โ†’ Message containing buttons/select menus (for interaction/both type)

๐Ÿ”น Notes

  • Supports automatic cleanup of collectors after completion
  • Can return Message, Interaction, or ModalSubmitInteraction

๐Ÿ‘ค GetUser โ€“ Fetch a GuildMember easily from a message โœจ

GetUser helps to detect a target member in multiple ways:

  1. Mention (@User)
  2. User ID (123456789012345678)
  3. Reply to another message

๐Ÿ“Œ Example Usage:

const { GetUser } = require("djs-builder");

const data = await GetUser(message);

if (!data) return message.reply("โŒ Could not find the user.");

const member = data.user; // GuildMember object
const args = data.args; // Remaining arguments
const reason = args.join(" ") || "No reason provided";

await member.ban({ reason });
message.reply(`๐Ÿšซ ${member.user.tag} was banned for: ${reason}`);

๐Ÿ”น Returns

{
  user: <GuildMember>,  // Targeted member
  args: [ "arg1", "arg2" ] // Remaining message arguments
}

๐Ÿ”น Detection Methods

  • Mention: !ban @Ahmed Spamming
  • User ID: !ban 123456789012345678 Spamming
  • Reply: Reply to user's message with !ban

๐Ÿ”น Notes

  • Automatically handles missing users
  • Returns null if user not found
  • Works in any text channel of the guild

The Logging System is a powerful feature that keeps track of almost everything happening inside your Discord server ๐Ÿ”.
From messages ๐Ÿ“ to channels ๐Ÿ“‚, roles ๐ŸŽญ, invites ๐Ÿ”—, and even voice state changes ๐ŸŽ™๏ธ โ€“ nothing goes unnoticed!


โœจ Features

  • ๐Ÿ“ Messages โ€“ Deleted & edited messages are logged with details.
  • ๐Ÿ“‚ Channels โ€“ Creation, deletion, and updates are tracked.
  • ๐ŸŽญ Roles โ€“ Created, deleted, and updated roles, including member role changes.
  • ๐ŸŽ™๏ธ Voice State โ€“ Joins, leaves, and moves between channels.
  • ๐Ÿ”— Invites โ€“ Created invites & usage tracking.
  • ๐Ÿ˜€ Emojis & Stickers โ€“ Added, removed, or updated.
  • ๐Ÿšจ Audit Log Integration โ€“ Fetches the executor (who did what).
  • ๐ŸŽจ Beautiful Embeds โ€“ Every log is shown in a clean, styled embed with timestamps.

โš™๏ธ Setup Example

Using the log function is very simple โšก.
Just place this code inside an event (like clientReady) to start logging:

const { log } = require("djs-builder");

module.exports = {
  name: "clientReady",
  async run(client) {
    await log(
      client,
      "GUILD_ID", // ๐Ÿ  Guild ID (server)
      "CHANNEL_ID" // ๐Ÿ“ข Channel ID for logs
    );
  },
};

๐Ÿ’ก How It Works

  • โœ… Pass your Client, Guild ID, and Log Channel ID.
  • ๐Ÿ”” Instantly starts tracking events and sending them to the log channel.
  • ๐Ÿงฐ No extra setup required โ€“ plug and play!


๐Ÿ† Level System โ€“ XP, Levels & Leaderboard

The Level System module lets you track user experience points (XP) in text ๐Ÿ’ฌ and voice ๐ŸŽ™๏ธ, handle level-ups โฌ†๏ธ, and display leaderboards ๐Ÿ…. Perfect for gamifying your Discord server! ๐ŸŽฎโœจ

Note: To use this module, you MUST have DATABASE conection. | Note 2: You can get all data by requiring the Level module.


๐Ÿ“ฆ Module Exports

const { addXP, UserLevel, leaderboard } = require("djs-builder");
  • addXP(userId, guildId, options) โ†’ Adds XP for a user and handles level-ups ๐ŸŽฒ.
  • UserLevel(userId, guildId) โ†’ Fetch a user's XP and level ๐Ÿ‘ค.
  • leaderboard(guildId, type, limit) โ†’ Get top users ๐Ÿ….

๐ŸŽฒ addXP โ€“ Add Experience Points

Adds XP to a user and automatically handles level-ups.

const result = await addXP("USER_ID", "GUILD_ID", {
  type: "text", // "text" ๐Ÿ’ฌ | "voice" ๐ŸŽ™๏ธ
  minXP: 5, // Minimum random XP ๐ŸŸข
  maxXP: 15, // Maximum random XP ๐Ÿ”ต
  amount_add: 10, // Optional: fixed XP ๐Ÿ’Ž
  level_add: 1, // Optional: direct level boost โฌ†๏ธ
});

console.log(result);
/* Example output:
{
  newLevel: 3,
  oldLevel: 2,
  totalXP: 250,
  leveledUp: true
}
*/

๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ UserLevel โ€“ Fetch User Data

Fetch a user's text XP, voice XP, total XP, and current level.

const data = await UserLevel("USER_ID", "GUILD_ID");
console.log(data);
/* Example output:
{
  text: 120 ๐Ÿ’ฌ,
  voice: 50 ๐ŸŽ™๏ธ,
  totalXP: 170 โญ,
  level: 2 โฌ†๏ธ
}
*/

Returns default values if the user is not found.


๐Ÿ… Leaderboard โ€“ Top Users

Get a sorted list of users based on XP or level.

const topUsers = await leaderboard("GUILD_ID", "totalXP", 5);
console.log(topUsers);
/* Example output:
[
  { userId: "123", totalXP: 500, level: 5 },
  { userId: "456", totalXP: 400, level: 4 },
  ...
]
*/

Parameters:

  • guildId โ†’ Server ID ๐Ÿ 
  • type โ†’ "totalXP", "text" ๐Ÿ’ฌ, "voice" ๐ŸŽ™๏ธ, or "level" โฌ†๏ธ. Default = "totalXP"
  • limit โ†’ Number of top users to return ๐Ÿ”ข. Default = 10

โšก Practical Example: messageCreate Event

const { addXP, UserLevel, leaderboard } = require("djs-builder");

module.exports = {
  name: "messageCreate",
  run: async (msg, client) => {
    if (msg.author.bot) return;

    // ๐ŸŽฒ Add XP on every message
    const result = await addXP(msg.author.id, msg.guild.id, {
      type: "text",
      minXP: 5,
      maxXP: 15,
    });

    // ๐ŸŽ‰ Level-up notification
    if (result.leveledUp) {
      msg.channel.send(`๐ŸŽŠ ${msg.author} new level **${result.newLevel}** โฌ†๏ธ`);
    }

    // ๐Ÿ“Š Check your rank
    if (msg.content === "!rank") {
      const data = await UserLevel(msg.author.id, msg.guild.id);
      msg.reply(`๐Ÿ“ˆ **level ** ${data.level} โฌ†๏ธ โ€“ **XP:** ${data.totalXP} โญ`);
    }

    // ๐Ÿ… Display top users
    if (msg.content === "!top") {
      const lb = await leaderboard(msg.guild.id, "totalXP", 5);
      msg.reply(
        lb
          .map(
            (u, i) =>
              `#${i + 1} <@${u.userId}> โ€“ Lv.${u.level} โฌ†๏ธ (${u.totalXP} โญ)`
          )
          .join("\n")
      );
    }
  },
};

๐Ÿ’ก Notes & Tips

  • ๐Ÿ’ฌ Text XP โ€“ Add XP for messages automatically.
  • ๐ŸŽ™๏ธ Voice XP โ€“ Add XP for voice activity.
  • โฌ†๏ธ Level Up โ€“ Trigger notifications when leveling up.
  • ๐Ÿ… Leaderboard โ€“ Display the top users in server using embeds for better look.
  • ๐ŸŽฎ Gamify your server easily with XP rewards, mini-games, and custom commands.


๐ŸŽ‰ Giveaway System โ€“ All-in-One Contest Management ๐Ÿ†โœจ

This module provides a robust and feature-rich suite of functions to effortlessly launch, monitor, manage, and conclude Giveaways on your Discord server. It fully supports both Reactions and Buttons for entry, featuring advanced controls like pausing, resuming, and rerolling winners. It is highly recommended to read the Important Notes section below. ๐Ÿšจ

Note: To use this module, you MUST have DATABASE conection. | Note 2: You can get all data by requiring the giveaway module.


๐Ÿ“ฆ Module Exports

const {
  Gstart,
  Gcheck,
  Greroll,
  Glist,
  Gpause,
  Gresume,
  Gdelete,
  GaddUser,
  GremoveUser,
  GaddTime,
  GremoveTime,
  Gdata,
} = require("djs-builder");

๐ŸŽฌ Gstart โ€“ Launch a Brand New Giveaway! ๐Ÿš€

This is the primary function to kick off a new giveaway. It handles creating the Discord message and persists all necessary data in the database (MongoDB). This function offers deep customization for embeds and entry methods. ๐ŸŽจ

โš™๏ธ Essential Options:

  • context: The Message or Interaction object that triggered the command.
  • endTime: The duration until the giveaway ends (in milliseconds โฑ๏ธ).
  • winers: The number of winners for the contest. ๐Ÿ…
  • channelId: The ID of the channel where the giveaway message will be posted. ๐Ÿ“ข
  • embed / endEmbed: Options to fully customize the starting message and the final end message.
  • reaction: To specify the entry method (button or reaction). ๐Ÿ–ฑ๏ธ

โšก Simple Usage Example (Basic Requirements Only):

This example provides a fast and minimalist way to start a giveaway with default embed colors, a simple title, and the default reaction entry type (if the reaction object is omitted or set to reaction).

const { Gstart } = require("djs-builder");

module.exports = {
  name: "gstart",
  description: "Starts a new simple giveaway.",
  run: async (client, message, args) => {
    // โฐ Giveaway ends in 1 hour
    const oneHour = 60 * 60 * 1000;
    const channelId = message.channel.id;

    await Gstart({
      context: message,
      endTime: oneHour,
      winers: 1,
      channelId: channelId,

      embed: {
        title: "๐ŸŽ‰ Simple Test Giveaway",
        description: "React to enter! Prize: Discord Nitro.",
      },
    });
    message.reply("๐ŸŽ‰ Simple Giveaway started successfully!");
  },
};

๐Ÿ’ก Tip: This example provides a fast and minimalist way to start a giveaway with the default reaction type (emoji reaction).


โšก Full Customization Example (Demonstrating all options):

This example showcases all available configuration options for deep customization of the embeds and the button entry method.

const { Gstart } = require("djs-builder");
const { EmbedBuilder } = require("discord.js");

module.exports = {
ย  name: "gstart",
ย  description: "Starts a new highly-customized giveaway.",
ย  run: async (client, message, args) => {
ย  ย  // โฐ Giveaway ends in 48 hours
    const twoDays = Date.now() + (48 * 60 * 60 * 1000);
    const channelId = 'YOUR_GIVEAWAY_CHANNEL_ID'; // ๐Ÿ“ข Target Channel

ย  ย  await Gstart({
ย  ย  ย  context: message,
ย  ย  ย  endTime: twoDays,
ย  ย  ย  winers: 5, // 5 lucky winners! ๐Ÿ†
ย  ย  ย  channelId: channelId,

      // ๐ŸŽจ Customization for the STARTING EMBED
ย  ย  ย  embed: {

            custom: new EmbedBuilder().setTitle("Raw Embed"),// ๐Ÿ’ก You can use 'custom' to pass a raw Discord.js EmbedBuilder JSON
ย  ย  ย  ย  ย  ย  title: "๐ŸŽ‰ **HUGE SERVER BOOST GIVEAWAY!**",
ย  ย  ย  ย  ย  ย  description: "Click the button below to enter for a chance to win a free server boost!",
ย  ย  ย  ย  ย  ย  color: "Blue", // Any valid Discord color
            image: "https://yourimage.com/banner.png", // image URL
ย  ย  ย  ย  ย  ย  thumbnail: message.guild.iconURL(),
ย  ย  ย  },

      // ๐Ÿ›‘ Customization for the ENDED EMBED
ย  ย  ย  endEmbed: {
            custom: new EmbedBuilder().setTitle("Raw Embed"),// ๐Ÿ’ก You can use 'custom' to pass a raw Discord.js EmbedBuilder JSON
ย  ย  ย  ย  ย  ย  title: "๐Ÿ›‘ Giveaway Has Concluded!",
ย  ย  ย  ย  ย  ย  description: "Congratulations to the winners! Check the message below.",
ย  ย  ย  ย  ย  ย  color: "Green",
ย  ย  ย  ย  ย  ย  // Eimage and Ethumbnail can also be set here
ย  ย  ย  },

      // ๐Ÿ–ฑ๏ธ Button Entry Method
ย  ย  ย  reaction: {
ย  ย  ย  ย  type: "button", // Use 'reaction' for an emoji reaction
ย  ย  ย  ย  emoji: "โœ…", // The emoji displayed on the button
ย  ย  ย  ย  label: "Enter Giveaway!", // The text label
ย  ย  ย  ย  style: 3, // Button style: Primary(1), Secondary(2), Success(3), Danger(4)
ย  ย  ย  ย  id: "djs-builder-giveaway", // Custom ID for the button
ย  ย  ย  },

      // ๐Ÿ”’ Requirements (Optional)
      requirements: {
        requiredRoles: ["123456789012345678"], // ๐Ÿ›ก๏ธ User MUST have this role to join (Button Only)
      },
ย  ย  });
ย  ย  message.reply("๐ŸŽ‰ Giveaway started successfully!");
ย  },
};

๐Ÿ’ก Flexibility Tip: You can safely remove any option you do not wish to use (e.g., remove endEmbed entirely if you are happy with the default end message structure). ๐Ÿ—‘๏ธ


โš ๏ธ Important Notes โ€“ Read Before Use! ๐Ÿšจ

This module requires specific setup steps to function correctly. Pay attention to the following crucial points:

  • 1. The Gcheck Requirement (Monitor):

    • The Gcheck(client) function is responsible for monitoring and ending the giveaways once their time runs out.
    • Failure to call Gcheck(client) when your bot starts (e.g., in the clientReady event) will result in giveaways never ending automatically! ๐Ÿ›‘
  • 2. Button Entry vs. InteractionCreate:

    • If you set reaction.type to button in Gstart, the system will create the button but will NOT automatically register the participants.
    • You MUST manually implement a listener for the interactionCreate event and use the GaddUser function to register the user entry when the button is clicked. ๐Ÿ–ฑ๏ธ
    • See the GaddUser section for a detailed code example.
  • 3. Requirements (Roles):

    • The requiredRoles feature currently works ONLY with the Button entry method (reaction.type: "button").
    • When using GaddUser, you must pass the guild object as the third argument for the role check to work.

๐Ÿ“† Gcheck โ€“ Monitor and End Time-Expired Giveaways ๐Ÿ”Ž

This function runs periodically (every 10 seconds โฑ๏ธ) to check all active, non-paused giveaways. It automatically concludes contests that have passed their endTime. This function must be called once when the bot starts up. ๐Ÿš€

โšก Simple Example (in your clientReady event):

const { Gcheck } = require("djs-builder");

module.exports = {
  name: "clientReady",
  once: true,
  run: (client) => {
    console.log(`๐Ÿค– Bot is ready and monitoring giveaways...`);
    Gcheck(client); // ๐Ÿ‘ˆ Start the continuous check loop!
  },
};

๐Ÿ”„ Greroll โ€“ Redraw Winners for an Ended Contest ๐ŸŽฒ

Used to redraw one or more new winners for a completed giveaway. It sends a new announcement message with the updated winners.

โš™๏ธ Options:

  • client: The Discord Client object. ๐Ÿค–
  • messageId: The Message ID of the giveaway post. ๐Ÿ†”

โšก Simple Example:

const { Greroll } = require("djs-builder");

module.exports = {
  name: "reroll",
  description: "Redraws winners for an ended giveaway.",
  Permissions: ["MANAGE_MESSAGES"], // ๐Ÿ›ก๏ธ Permission check
  run: async (client, message, args) => {
    const id = args[0];
    if (!id)
      return message.reply(
        "โŒ **Error:** Please provide the Giveaway Message ID."
      );
    const result = await Greroll(client, id);
    if (result?.error) {
      return message.reply(`โš ๏ธ **Reroll Failed:** ${result.error}`);
    } else {
      message.reply(`โœ… **Success!** Reroll initiated for giveaway \`${id}\`.`);
    }
  },
};

๐Ÿ“œ Glist โ€“ Retrieve List of Giveaways ๐Ÿ“‹

Fetches a list of all saved giveaways based on their current status.

โš™๏ธ Options:

  • type: Can be ended, active, paused, or all. ๐ŸŒ

โšก Simple Example:

const { Glist } = require("djs-builder");

// Retrieve all currently active (non-paused, non-ended) giveaways
const activeGiveaways = await Glist("active");

if (activeGiveaways.length > 0) {
  console.log(`โœ… Found ${activeGiveaways.length} Active Giveaways.`); // ... You can format and display this list to the user
} else {
  console.log("No active giveaways found.");
}

โธ๏ธ Gpause & โ–ถ๏ธ Gresume โ€“ Stop and Continue Contests ๐Ÿ›‘

Allows you to temporarily halt an ongoing giveaway, preserving the remaining time, and then resume it later. The message embed is automatically updated to reflect the pause/resume status.

โš™๏ธ Options:

  • client: The Discord Client object. ๐Ÿค–
  • messageId: The Message ID of the giveaway post. ๐Ÿ†”

โšก Simple Example (for a management command):

const { Gpause, Gresume } = require("djs-builder");

module.exports = {
  name: "g_manage",
  description: "Pause or resume a giveaway.",
  devOnly: true, // ๐Ÿ‘‘ Developer only access
  run: async (client, message, args) => {
    const [action, id] = args;
    if (!action || !id) return message.reply("โš ๏ธ Missing action or ID.");

    if (action === "pause") {
      const result = await Gpause(client, id);
      if (result?.error) return message.reply(`โŒ ${result.error}`);
      message.reply("โธ๏ธ Giveaway successfully **paused**!");
    } else if (action === "resume") {
      const result = await Gresume(client, id);
      if (result?.error) return message.reply(`โŒ ${result.error}`);
      message.reply("โ–ถ๏ธ Giveaway successfully **resumed**!");
    }
  },
};

๐Ÿ—‘๏ธ Gdelete โ€“ Permanently Remove Giveaway Data ๐Ÿ—„๏ธ

Deletes the giveaway record entirely from the database. Note: This does not delete the Discord message itself, only the data.

โš™๏ธ Options:

  • messageId: The Message ID of the giveaway post. ๐Ÿ†”

โšก Simple Example:

const { Gdelete } = require("djs-builder");

module.exports = {
  name: "gdelete",
  run: async (client, message, args) => {
    const messageId = args[0];
    const result = await Gdelete(messageId);
    if (result?.delete) {
      message.reply(
        `โœ… **Success:** Giveaway data for \`${messageId}\` has been permanently deleted.`
      );
    } else {
      message.reply(
        `โŒ **Error:** ${result?.error || "Could not delete giveaway data."}`
      );
    }
  },
};

โž• GaddUser & โž– GremoveUser โ€“ Manual Participant Control ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘

These functions allow you to manually add or remove user IDs from the participant list. This is crucial when setting the giveaway reaction type to button.

โš™๏ธ Options:

  • messageId: The Message ID of the giveaway. ๐Ÿ†”
  • userId: The ID of the user to add/remove. ๐Ÿ‘ค

๐Ÿ’ก InteractionCreate Setup (for Button Entry) ๐Ÿ–ฑ๏ธ

When using the button type, you MUST implement the following listener to handle join/leave actions dynamically.

โšก Example Code for Dynamic Join/Leave in interactionCreate Event:

const { GaddUser, GremoveUser, CreateRow } = require("djs-builder");

module.exports = {
  name: "interactionCreate",
  run: async (interaction) => {
    // 1. Handle Join/Entry Button Click
    if (interaction.customId === "djs-builder-giveaway") {
      // โš ๏ธ Note: Pass 'interaction.guild' as the 3rd argument to enable Role Requirements check!
      const result = await GaddUser(
        interaction.message.id,
        interaction.user.id,
        interaction.guild 
      );

      if (result?.error) {
        // Handles both "User Already Joined" and "Missing Roles" errors
        if (result.error === "โŒ User Already Joined") {
           // ... (Leave button logic)
           const row = await CreateRow([
            [
              {
                label: "Leave Giveaway",
                id: "djs-builder-giveaway-leave-" + interaction.message.id, // Dynamic ID
                style: 4, // Danger Red
              },
            ],
          ]);
          return interaction.reply({
            content: "โš ๏ธ You have **already joined** this giveaway! You can **leave** by clicking the button below.",
            components: row,
            flags: 64, 
          });
        }
        
        // Show error (e.g. Missing Role)
        return interaction.reply({
            content: result.error,
            flags: 64
        });
      }

      await interaction.reply({
        content: "๐ŸŽ‰ **Success!** Your entry has been registered!",
        flags: 64,
      });
    }


    // 2. Handle Leave Button Click (Dynamically Generated ID)
    if (
      interaction.customId &&
      interaction.customId.startsWith("djs-builder-giveaway-leave-")
    ) {
      const id = interaction.customId.split("-")[4]; // Extract message ID
      const result = await GremoveUser(id, interaction.user.id);

      // Note: The error message should ideally reflect the module's logic (User Not Joined)
      if (result?.error && result.error.includes("User Not Joined")) {
        return interaction.reply({
          content: "โš ๏ธ You have **already left** this giveaway!",
          flags: 64,
        });
      }

      await interaction.reply({
        content: "๐ŸŽ‰ **Success!** Your entry has been removed!",
        flags: 64,
      });
    }
  },
};

โž• GaddTime & โž– GremoveTime โ€“ Adjust the End Time ๐Ÿ•ฐ๏ธ

Allows you to extend or shorten the duration of an active giveaway.

โš™๏ธ Options:

  • messageId: The Message ID of the giveaway. ๐Ÿ†”
  • client: The Discord Client object. ๐Ÿค–
  • time: The amount of time (in milliseconds โณ) to add or remove.

โšก Simple Example (Extending the Giveaway):

const { GaddTime } = require("djs-builder");

// Extend the contest by 30 minutes (1,800,000 MS)
const halfHour = 30 * 60 * 1000;
const result = await GaddTime(messageId, client, halfHour);

if (result?.error) {
  console.log(`Error adding time: ${result.error}`);
} else {
  console.log("Time successfully added!");
}

๐Ÿ“Š Gdata โ€“ Retrieve Giveaway Data ๐Ÿ’พ

Fetches all saved data for a specific giveaway message.

โš™๏ธ Options:

  • messageId: The Message ID of the giveaway. ๐Ÿ†”

โšก Simple Example:

const { Gdata } = require("djs-builder");

const giveawayData = await Gdata(messageId);

if (giveawayData) {
  console.log(`Giveaway hosted by: <@${giveawayData.hoster}>`);
  console.log(`Current status: ${giveawayData.ended ? "Ended" : "Active"}`);
} else {
  console.log("No data found for this message ID.");
}

๐Ÿ“Š Gdata(messageId) โ€“ Returned Object Structure

  • Returns an object like:
{
ย  "_id": "651234567890abcdef12345678", // ๐Ÿ”‘ MongoDB unique ID
ย  "ended": false, // ๐Ÿ’ก Current status of the giveaway
ย  "guildId": "123456789012345678", // ๐Ÿ  Server ID
ย  "channelId": "876543210987654321", // ๐Ÿ“ข Channel ID
ย  "messageId": "987654321098765432", // ๐Ÿ’ฌ Message ID of the giveaway post
ย  "hoster": "112233445566778899", // ๐Ÿ‘ค User ID of the giveaway host
"prize" : "code" // ๐ŸŽ prize
ย  "winnerCount": 1, // ๐Ÿ”ข Number of winners set
ย  "winners": [], // ๐Ÿ† Array of user IDs who won (empty if not ended)
ย  "paused": false, // โธ๏ธ True if the giveaway is paused
ย  "pausedTime": [], // โฑ๏ธ Array of saved remaining times (used for resume)
ย  "endTime": "1730995200000", // ๐Ÿ“… Timestamp (MS) of when the giveaway should end
ย  "endType": "Time End", // ๐Ÿ“ How the giveaway ended (e.g., Time End, Reroll: 1 time(s))
ย  "reaction": "button", // ๐Ÿ–ฑ๏ธ Entry method: "reaction" or "button"
ย  "users": ["111111111111111111", "222222222222222222"], // ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ Array of user IDs who entered (used for button type)
ย  "endEmbed": {
ย  ย  "title": "๐Ÿ›‘ Giveaway Has Concluded!",
ย  ย  "description": "Congratulations to the winners!",
ย  ย  "color": 3066993, // Green color code
ย  ย  "fields": [
ย  ย  ย  {
ย  ย  ย  ย  "name": "๐ŸŽ‰ Giveaway",
ย  ย  ย  ย  "value": "- Winner(s): 1\n- Time : <t:1730995200:R>\n- Hosted By : <@112233445566778899>",
ย  ย  ย  ย  "inline": true
ย  ย  ย  }
ย  ย  ]
ย  },
ย  "__v": 0 // ๐ŸŒ Mongoose version key (internal)
}

๐Ÿ’ก General Code Example โ€“ The All-in-One /giveaway Command ๐Ÿ› ๏ธ

This complete example demonstrates how to integrate all functions of the Giveaway System into a single Discord Slash Command, /giveaway, using subcommands for clean management.

const {
  Gstart,
  Greroll,
  Glist,
  Gpause,
  Gresume,
  Gdelete,
  GaddUser,
  GremoveUser,
  GaddTime,
  GremoveTime,
  Gdata,
} = require("djs-builder");
const {
  SlashCommandBuilder,
  EmbedBuilder,
  AttachmentBuilder,
  PermissionFlagsBits,
} = require("discord.js");
const ms = require("ms");

module.exports = {
  data: new SlashCommandBuilder()
    .setName("giveaway")
    .setDescription("Comprehensive giveaway system management")
    .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)

    // ๐ŸŽฌ /giveaway start
    .addSubcommand((subcommand) =>
      subcommand
        .setName("start")
        .setDescription("Starts a new giveaway")
        .addStringOption((option) =>
          option
            .setName("prize")
            .setDescription("The prize for the winner(s)")
            .setRequired(true)
        )
        .addStringOption((option) =>
          option
            .setName("time")
            .setDescription("Duration (e.g., 1h, 30m, 7d)")
            .setRequired(true)
        )
        .addStringOption((option) =>
          option
            .setName("winner")
            .setDescription("Number of winners (e.g., 1, 3)")
            .setRequired(true)
        )
        .addChannelOption((option) =>
          option
            .setName("channel")
            .setDescription("The channel to post the giveaway in")
        )
        .addStringOption((option) =>
          option
            .setName("description")
            .setDescription("Custom description for the giveaway embed")
        )
        .addStringOption((option) =>
          option
            .setName("image")
            .setDescription("Image URL for the embed banner")
        )
        .addStringOption((option) =>
          option
            .setName("thumbnail")
            .setDescription("Thumbnail URL for the embed")
        )
    )

    // ๐Ÿ”„ Management Commands: /giveaway reroll, pause, resume, delete, data
    .addSubcommand((subcommand) =>
      subcommand
        .setName("reroll")
        .setDescription("Redraw winners for an ended giveaway")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
    )
    .addSubcommand((subcommand) =>
      subcommand
        .setName("pause")
        .setDescription("Pause an active giveaway")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
    )
    .addSubcommand((subcommand) =>
      subcommand
        .setName("resume")
        .setDescription("Resume a paused giveaway")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
    )
    .addSubcommand((subcommand) =>
      subcommand
        .setName("delete")
        .setDescription("Delete a giveaway's data")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
    )
    .addSubcommand((subcommand) =>
      subcommand
        .setName("data")
        .setDescription("Get raw data for a giveaway")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
    )

    // ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ User Entry Management: /giveaway adduser, removeuser
    .addSubcommand((subcommand) =>
      subcommand
        .setName("adduser")
        .setDescription("Manually add user to giveaway (button entry only)")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
        .addUserOption((option) =>
          option.setName("user").setDescription("User to add").setRequired(true)
        )
    )
    .addSubcommand((subcommand) =>
      subcommand
        .setName("removeuser")
        .setDescription(
          "Manually remove user from giveaway (button entry only)"
        )
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
        .addUserOption((option) =>
          option
            .setName("user")
            .setDescription("User to remove")
            .setRequired(true)
        )
    )

    // โฑ๏ธ Time Modification: /giveaway addtime, removetime
    .addSubcommand((subcommand) =>
      subcommand
        .setName("addtime")
        .setDescription("Add time to an active giveaway")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
        .addStringOption((option) =>
          option
            .setName("time")
            .setDescription("Time to add (e.g., 30m, 1h)")
            .setRequired(true)
        )
    )
    .addSubcommand((subcommand) =>
      subcommand
        .setName("removetime")
        .setDescription("Remove time from an active giveaway")
        .addStringOption((option) =>
          option
            .setName("id")
            .setDescription("Giveaway message ID")
            .setRequired(true)
        )
        .addStringOption((option) =>
          option
            .setName("time")
            .setDescription("Time to remove (e.g., 10m)")
            .setRequired(true)
        )
    )
    // ๐Ÿ“œ giveaways list
    .addSubcommand((subcommand) =>
      subcommand
        .setName("list")
        .setDescription("List giveaways")
        .addStringOption((option) =>
          option
            .setName("type")
            .setDescription("Type of giveaways to list")
            .setRequired(true)
            .addChoices(
              { name: "Ended", value: "ended" },
              { name: "Active", value: "active" },
              { name: "Paused", value: "paused" },
              { name: "All", value: "all" }
            )
        )
    ),

  // โš™๏ธ Command Execution
  run: async (interaction, client) => {
    await interaction.deferReply({ flags: 64 }); // Acknowledge command immediately

    const command = interaction.options.getSubcommand();
    const id = interaction.options.getString("id");

    if (command === "start") {
      // ๐Ÿš€ START LOGIC
      const channel =
        interaction.options.getChannel("channel") || interaction.channel;
      const description = interaction.options.getString("description");
      const image = interaction.options.getString("image");
      const thumbnail = interaction.options.getString("thumbnail");
      const timeString = interaction.options.getString("time");
      const winnerCount = parseInt(interaction.options.getString("winner"));
      const prize = interaction.options.getString("prize");

      const durationMs = ms(timeString);
      const endTimeMs = Date.now() + durationMs;

      if (!durationMs) {
        return interaction.editReply(
          "โŒ **Error:** Invalid time format provided (e.g., 1h, 30m)."
        );
      }
      if (isNaN(winnerCount) || winnerCount < 1) {
        return interaction.editReply(
          "โŒ **Error:** Winner count must be a number greater than 0."
        );
      }

      const giveaway = await Gstart({
        context: interaction,
        channelId: channel.id,
        winers: winnerCount,
        endTime: endTimeMs,
        prize : prize,
        embed: {
          title: `๐ŸŽ‰ ${prize} Giveaway!`,
          image: image,
          thumbnail: thumbnail,
          description: description,
        },
        reaction: {
          type: "button",
          label: "Enter Giveaway",
          style: 1,
          emoji: "๐ŸŽ‰",
        },
      });

      if (giveaway?.error) {
        return interaction.editReply(`โŒ **Error:** ${giveaway.error}`);
      }

      return interaction.editReply(
        `โœ… **Success!** Giveaway for **${prize}** has been started in ${channel}!`
      );
    } else if (command === "list") {
      // ๐Ÿ“œ LIST LOGIC
      const type = interaction.options.getString("type");
      const giveaways = await Glist(type);
      const MAX_EMBED_LENGTH = 4000;
      const MAX_MESSAGE_LENGTH = 2000;

      if (!giveaways || giveaways.length === 0) {
        return interaction.editReply(`โŒ No **${type}** giveaways found.`);
      }

      const listContent = giveaways
        .map(
          (g, i) =>
            `**#${i + 1}** | ID: \`${g.messageId}\` | Hoster: <@${
              g.hoster
            }> | End: <t:${Math.floor(g.endTime / 1000)}:R>`
        )
        .join("\n");

      const title = `๐Ÿ“œ **${type.toUpperCase()} Giveaways:** (${
        giveaways.length
      } found)`;

      if (listContent.length <= MAX_MESSAGE_LENGTH) {
        // Output as a simple message (Under 2000 chars)
        return interaction.editReply({
          content: `${title}\n\n${listContent}`,
        });
      } else if (listContent.length <= MAX_EMBED_LENGTH) {
        // Output as an Embed (Over 2000, under 4000)
        const embed = new EmbedBuilder()
          .setTitle(title.replace(/\*\*/g, ""))
          .setDescription(listContent)
          .setColor("Blue");

        return interaction.editReply({
          content: `Too many results for a single message, displaying via Embed.`,
          embeds: [embed],
        });
      } else {
        // Output as an attachment/JSON file (Over 4000 chars)
        const jsonFile = new AttachmentBuilder(
          Buffer.from(JSON.stringify(giveaways, null, 2)),
          { name: `${type}_giveaways_list.json` }
        );

        return interaction.editReply({
          content: `โš ๏ธ **Warning:** The list is too long! Sending ${giveaways.length} results in a JSON file.`,
          files: [jsonFile],
        });
      }
    } else if (command === "pause") {
      // โธ๏ธ PAUSE LOGIC
      const result = await Gpause(client, id);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `โธ๏ธ Giveaway \`${id}\` paused successfully!`
      );
    } else if (command === "resume") {
      // โ–ถ๏ธ RESUME LOGIC
      const result = await Gresume(client, id);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `โ–ถ๏ธ Giveaway \`${id}\` resumed successfully!`
      );
    } else if (command === "reroll") {
      // ๐Ÿ”„ REROLL LOGIC
      const result = await Greroll(client, id);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `๐Ÿ”„ Reroll command sent for giveaway \`${id}\`! Check the channel for the new winner.`
      );
    } else if (command === "delete") {
      // ๐Ÿ—‘๏ธ DELETE LOGIC
      const result = await Gdelete(id);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `๐Ÿ—‘๏ธ Giveaway data for \`${id}\` deleted successfully!`
      );

      // โž• USER MANAGEMENT LOGIC
    } else if (command === "adduser") {
      const user = interaction.options.getUser("user");
      const result = await GaddUser(id, user.id);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `โž• User **${user.tag}** added to giveaway \`${id}\`.`
      );
    } else if (command === "removeuser") {
      // โž– REMOVE USER LOGIC
      const user = interaction.options.getUser("user");
      const result = await GremoveUser(id, user.id);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `โž– User **${user.tag}** removed from giveaway \`${id}\`.`
      );

      // โณ TIME MANAGEMENT LOGIC
    } else if (command === "addtime") {
      const timeString = interaction.options.getString("time");
      const timeMs = ms(timeString);

      if (!timeMs) {
        return interaction.editReply(
          "โŒ **Error:** Invalid time format provided (e.g., 1h, 30m)."
        );
      }

      const result = await GaddTime(id, client, timeMs);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `โฑ๏ธ Added ${timeString} to giveaway \`${id}\`.`
      );
    } else if (command === "removetime") {
      // โช REMOVE TIME LOGIC
      const timeString = interaction.options.getString("time");
      const timeMs = ms(timeString);

      if (!timeMs) {
        return interaction.editReply(
          "โŒ **Error:** Invalid time format provided (e.g., 1h, 30m)."
        );
      }

      const result = await GremoveTime(id, client, timeMs);
      if (result?.error)
        return interaction.editReply(`โŒ **Error:** ${result.error}`);
      return interaction.editReply(
        `โฑ๏ธ Removed ${timeString} from giveaway \`${id}\`.`
      );

      // ๐Ÿ“Š DATA LOGIC
    } else if (command === "data") {
      const id = interaction.options.getString("id");
      console.log(id);
      const data = await Gdata(id);
      if (data?.error)
        return interaction.editReply(`โŒ **Error:** ${data.error}`);

      const giveawayEmbed = new EmbedBuilder()
        .setTitle(`๐Ÿ“Š Giveaway Data: ${id}`)
        .setColor(data.ended ? 0xff0000 : data.paused ? 0xffc0cb : 0x00ff00)
        .setDescription(
          `**Jump to Message ๐Ÿ”— ** : ${
            data.messageId
              ? `https://discord.com/channels/${data.guildId}/${data.channelId}/${data.messageId}`
              : "N/A"
          }`
        );

      giveawayEmbed.addFields(
        {
          name: "โœจ Status",
          value: data.ended
            ? "ENDED ๐Ÿ›‘"
            : data.paused
            ? "PAUSED โธ๏ธ"
            : "ACTIVE โœ…",
          inline: true,
        },
        {
          name: "๐Ÿ† Winners",
          value: data.winwesNumber.toString(),
          inline: true,
        },
        {
          name: "๐Ÿ™‹โ€โ™‚๏ธ Hoster",
          value: `<@${data.hoster}>`,
          inline: true,
        },

        {
          name: "โณ Ends In",
          value: `<t:${Math.floor(data.endTime / 1000)}:R>`,
          inline: true,
        },
        {
          name: "๐ŸŽŸ๏ธ Entries",
          value:
            data.reaction === "button"
              ? data.users.length.toString()
              : "N/A (Reaction Type)",
          inline: true,
        },
        {
          name: "๐Ÿ–ฑ๏ธ Entry Type",
          value: data.reaction.toUpperCase(),
          inline: true,
        }
      );

      if (data.ended && data.winers.length > 0) {
        giveawayEmbed.addFields({
          name: "๐Ÿ… Past Winners",
          value: data.winers.map((u) => `<@${u}>`).join(", ") || "N/A",
          inline: false,
        });
      }

      return interaction.editReply({
        embeds: [giveawayEmbed],
        content: `Data retrieved for giveaway \`${id}\`.`,
      });
    }
  },
};

๐Ÿ“ Final Improvements and Notes: โœจ๐Ÿš€

  • 1. Time Parsing via ms: โฐโณ

    • for time inputs (e.g., 1h, 30m) to be correctly converted into milliseconds.
  • 2. List Command Robustness: ๐Ÿ“œ๐Ÿ›ก๏ธ

    • The list command logic handles large result sets automatically to avoid the Discord 2000-character limit error:
      • Under 2000 characters: Sent as a direct, formatted message.
      • Between 2000 and 4000 characters: Sent inside an EmbedBuilder.
      • Over 4000 characters: Sent as a JSON attachment, preventing API errors.
  • 3. Function Call Integrity: โš™๏ธโœ…

    • The client object is correctly passed to GaddTime and GremoveTime (id, client, timeMs), meeting the module's requirements for management functions.
  • 4. Enhanced User Experience (UX): ๐ŸŽฏ๐ŸŒŸ

    • The channel option uses .addChannelOption() for a native Discord selector.
    • Validation is included for winnerCount and time formats. ๐Ÿšซ
  • 5. Private and Immediate Feedback: ๐Ÿ”’๐Ÿ’ฌ

    • Uses interaction.deferReply({ flags: 64 }) (ephemeral/private) and interaction.editReply() for fast, clean, and non-intrusive status updates.


๐Ÿšซ Blacklist System โ€“ Restrict Access to Commands

The Blacklist System allows you to block specific users, roles, or channels from using your bot's commands. This is useful for moderation and preventing abuse.

Note 1: To use this module, you MUST have DATABASE connection. | Note 2: You can get all data by requiring the Blacklist module.


๐Ÿ“ฆ Module Exports

const {
  isBlacklisted,
  addToBlacklist,
  removeFromBlacklist,
  getBlacklist,
  Blacklist,
} = require("djs-builder");
  • isBlacklisted(guildId, type, id) โ†’ Check if a target is blacklisted ๐Ÿ”.
  • addToBlacklist(guildId, type, id) โ†’ Add a target to the blacklist โž•.
  • removeFromBlacklist(guildId, type, id) โ†’ Remove a target from the blacklist โž–.
  • getBlacklist(guildId, type) โ†’ Get all blacklisted items (optionally filtered by type) ๐Ÿ“œ.
  • Blacklist โ†’ The Mongoose model for direct DB access ๐Ÿ’พ.

๐Ÿ” isBlacklisted โ€“ Check Status

Checks if a user, role, or channel is blacklisted.

const isBlocked = await isBlacklisted("GUILD_ID", "user", "USER_ID");
if (isBlocked) {
  console.log("User is blacklisted! ๐Ÿšซ");
}

โž• addToBlacklist โ€“ Block a Target

Adds a user, role, or channel to the blacklist.

await addToBlacklist("GUILD_ID", "channel", "CHANNEL_ID");
console.log("Channel blacklisted successfully! ๐Ÿ”’");

โž– removeFromBlacklist โ€“ Unblock a Target

Removes a user, role, or channel from the blacklist.

await removeFromBlacklist("GUILD_ID", "role", "ROLE_ID");
console.log("Role unblacklisted! ๐Ÿ”“");

๐Ÿ“œ getBlacklist โ€“ List Blacklisted Items

Returns an array of blacklisted items for a guild. You can optionally filter by type (user, role, channel).

Parameters:

  • guildId (String): The ID of the guild.
  • type (String, optional): The type to filter by (user, role, channel).

Example:

// Get all blacklisted items
const allBlacklisted = await getBlacklist("GUILD_ID");
console.log(allBlacklisted);

// Get only blacklisted users
const blacklistedUsers = await getBlacklist("GUILD_ID", "user");
console.log(blacklistedUsers);
/*
Output:
[
  { guild: 'GUILD_ID', type: 'user', id: 'USER_ID_1', ... },
  { guild: 'GUILD_ID', type: 'user', id: 'USER_ID_2', ... }
]
*/

โšก Practical Example: Blacklist Command

You can create a slash command to manage the blacklist easily.

const { addToBlacklist, removeFromBlacklist, isBlacklisted, getBlacklist } = require("djs-builder");
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require("discord.js");

module.exports = {
  data: new SlashCommandBuilder()
    .setName("blacklist")
    .setDescription("Manage the blacklist")
    .setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
    .addSubcommand(sub =>
      sub.setName("add").setDescription("Add to blacklist")
        .addUserOption(opt => opt.setName("user").setDescription("User to blacklist"))
        .addRoleOption(opt => opt.setName("role").setDescription("Role to blacklist"))
        .addChannelOption(opt => opt.setName("channel").setDescription("Channel to blacklist"))
    )
    .addSubcommand(sub =>
      sub.setName("remove").setDescription("Remove from blacklist")
        .addUserOption(opt => opt.setName("user").setDescription("User to remove"))
        .addRoleOption(opt => opt.setName("role").setDescription("Role to remove"))
        .addChannelOption(opt => opt.setName("channel").setDescription("Channel to remove"))
    )
    .addSubcommand(sub =>
      sub.setName("check").setDescription("Check if a target is blacklisted")
        .addUserOption(opt => opt.setName("user").setDescription("User to check"))
        .addRoleOption(opt => opt.setName("role").setDescription("Role to check"))
        .addChannelOption(opt => opt.setName("channel").setDescription("Channel to check"))
    )
    .addSubcommand(sub =>
      sub.setName("list").setDescription("List all blacklisted items")
        .addStringOption(opt => opt.setName("type").setDescription("Filter by type").addChoices({ name: "User", value: "user" }, { name: "Role", value: "role" }, { name: "Channel", value: "channel" } , { name: "All", value: "all" }))
    ),
  async run(interaction) {
    const sub = interaction.options.getSubcommand();
    const user = interaction.options.getUser("user");
    const role = interaction.options.getRole("role");
    const channel = interaction.options.getChannel("channel");
    const guildId = interaction.guild.id;

    if (sub === "list") {
        const type = interaction.options.getString("type");
        if (type !== "all") {
        const list = await getBlacklist(guildId, type);

        if (!list.length) return interaction.reply({ content: "โœ… No blacklisted items found.", flags : 64 });

        const embed = new EmbedBuilder()
            .setTitle("๐Ÿšซ Blacklist")
            .setColor("Red")
            .setDescription(list.map(item => `โ€ข **${item.type.toUpperCase()}**: <${item.type === 'channel' ? '#' : item.type === 'role' ? '@&' : '@'}${item.id}> (\`${item.id}\`)`).join("\n").slice(0, 4000));

        return interaction.reply({ embeds: [embed], flags : 64 });
        } else {
        const list = await getBlacklist(guildId);
        if (!list.length) return interaction.reply({ content: "โœ… No blacklisted items found.", flags : 64 });

        const embeds = [];
        const roles = list.filter(i => i.type === "role");
        const users = list.filter(i => i.type === "user");
        const channels = list.filter(i => i.type === "channel");

        if (users.length) {
            const userEmbed = new EmbedBuilder()
            .setTitle("๐Ÿšซ Blacklisted Users")
            .setColor("Red")
            .setDescription(users.map(item => `โ€ข <@${item.id}> (\`${item.id}\`)`).join("\n").slice(0, 4000));
            embeds.push(userEmbed);
        }
        if( roles.length) {
            const roleEmbed = new EmbedBuilder()
            .setTitle("๐Ÿšซ Blacklisted Roles")
            .setColor("Red")
            .setDescription(roles.map(item => `โ€ข <@&${item.id}> (\`${item.id}\`)`).join("\n").slice(0, 4000));
            embeds.push(roleEmbed);
        }
        if( channels.length) {
            const channelEmbed = new EmbedBuilder()
            .setTitle("๐Ÿšซ Blacklisted Channels")
            .setColor("Red")
            .setDescription(channels.map(item => `โ€ข <#${item.id}> (\`${item.id}\`)`).join("\n").slice(0, 4000));
            embeds.push(channelEmbed);
        }
        return interaction.reply({ embeds, flags : 64 });
    }
  }
  
    if (!user && !role && !channel) {
      return interaction.reply({ content: "โš ๏ธ You must provide at least one option (User, Role, or Channel).", flags : 64 });
    }
    const target = user || role || channel;
    const type = user ? "user" : role ? "role" : "channel";
    const id = target.id;

    if (sub === "add") {
      const success = await addToBlacklist(guildId, type, id);
      if (success) interaction.reply(`โœ… Added **${type}** ${target} to blacklist.`);
      else interaction.reply(`โš ๏ธ **${type}** ${target} is already blacklisted.`);
    } else if (sub === "remove") {
      const success = await removeFromBlacklist(guildId, type, id);
      if (success) interaction.reply(`โœ… Removed **${type}** ${target} from blacklist.`);
      else interaction.reply(`โš ๏ธ **${type}** ${target} is not blacklisted.`);
    } else if (sub === "check") {
      const isBlocked = await isBlacklisted(guildId, type, id);
      if (isBlocked) interaction.reply(`๐Ÿšซ **${type}** ${target} is currently **blacklisted**.`);
      else interaction.reply(`โœ… **${type}** ${target} is **not** blacklisted.`);
    }
  }
};

๐Ÿ”„ Hot Reloading โ€“ Update your bot without restarting!

djs-builder allows you to reload your commands and events instantly while the bot is running. This is perfect for rapid development and fixing bugs on the fly.


๐Ÿ“Œ Features

  • Prefix Commands: Reloads all prefix commands from disk.
  • Slash Commands: Reloads slash command logic (internal cache).
  • Events: Reloads event listeners (removes old ones, adds new ones).
  • All: Reloads everything at once.

๐Ÿ“Œ Example Usage (Slash Command)

const { reload } = require("djs-builder");
const { SlashCommandBuilder } = require("discord.js");

module.exports = {
  data: new SlashCommandBuilder()
    .setName("reload")
    .setDescription("Reload bot commands/events")
    .addStringOption((option) =>
      option
        .setName("type")
        .setDescription("What to reload?")
        .setRequired(true)
        .addChoices(
          { name: "Prefix Commands", value: "prefix" },
          { name: "Slash Commands", value: "slash" },
          { name: "Events", value: "events" },
          { name: "All", value: "all" }
        )
    ),
  async run(interaction, client) {
    const type = interaction.options.getString("type");

    try {
      await interaction.deferReply();
      await reload(client, type);
      await interaction.editReply(`โœ… Successfully reloaded **${type}**! ๐Ÿš€`);
    } catch (error) {
      console.error(error);
      await interaction.editReply(
        "โŒ Failed to reload. Check console for errors."
      );
    }
  },
};

๐Ÿ’ก Notes

  • Slash Commands: Reloading only updates the code execution. If you change the command name, description, or options, you still need to restart the bot to update Discord's API.
  • Events: Old event listeners are automatically removed before adding new ones to prevent duplicates.

โšก Commands & Events

๐Ÿ’ก Commands & Events made easy! With djs-builder, handling commands and events is smooth, fast, and fully customizable. You get built-in features like:

  • โš ๏ธ Anti-crash protection โ€“ your bot wonโ€™t crash unexpectedly.
  • โณ Cooldowns โ€“ prevent spam and control usage.
  • ๐Ÿ›ก๏ธ Permissions & owner/dev only checks โ€“ secure your commands.
  • โšก Fast Use & custom prefixes โ€“ run commands easily across guilds.
  • ๐Ÿ“ Logging โ€“ track every command usage for prefix or slash.

Below are all the available properties you can use for your commands and events:


โšก COMMAND OPTIONS (Prefix & Slash)


name: 'string',                // ๐Ÿท๏ธ Command name (required)
aliases: ['string'],           // ๐Ÿ” Alternative names (Prefix only)
description: 'string',         // โœ๏ธ Short description of the command
cooldown: 5,                   // โณ Cooldown in seconds before reusing the command
Permissions: ['ADMINISTRATOR'],// ๐Ÿ›ก๏ธ Required permissions to execute the command
ownerOnly: true,               // ๐Ÿ‘‘ Only server owner can use this command
devOnly: true,                 // ๐Ÿ› ๏ธ Only bot developer can use this command
guildOnly: true,               // ๐Ÿ  Command works only in servers
dmOnly: true,                  // โœ‰๏ธ Command works only in DMs
fastUse: true,                 // โšก Allow command to be executed without prefix (Prefix only)
Blacklist: true,               // ๐Ÿšซ Check if user/role/channel is blacklisted
run: Function,                 // ๐Ÿƒโ€โ™‚๏ธ Main function to run the command
execute: Function,             // ๐Ÿƒโ€โ™‚๏ธ Alternative function to run the command

โšก EVENT OPTIONS


name: 'string',                // ๐Ÿท๏ธ Event name (e.g., messageCreate, guildMemberAdd)
once: true,                    // ๐ŸŽฏ If true, the event will be executed only once
run: Function,                 // ๐Ÿƒโ€โ™‚๏ธ Function to run when the event triggers
execute: Function              // ๐Ÿƒโ€โ™‚๏ธ Alternative function to run the event

๐Ÿ’ก Tip: Use these properties to fully control command behavior, access, and event handling.


๐ŸŒ DASHBOARD

The Dashboard System is a modern,