Launch Week Day 1: Socket for Jira Is Now Available.Learn More
Socket
Book a DemoSign in
Socket

djs-builder

Package Overview
Dependencies
Maintainers
2
Versions
118
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

djs-builder

🎉 Full-featured Discord.js utilities: CreateComponents, CreateModal, Dashboard, Logging & more! 🥏

latest
npmnpm
Version
0.7.20
Version published
Weekly downloads
70
84.21%
Maintainers
2
Weekly downloads
 
Created
Source

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.

Discord Banner

📑 Table of Contents

  • 🎯 Starter – Initialize your bot with commands, events, presence, and more.
  • ⚙️ Functions – Utilities like CreateRow, CreateBar, Wait, and GetUser.
  • Commands & Events – Easy setup with cooldowns, permissions, logging, and anti-crash.
  • 🌐 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 🗑️.

Starter Usage ⚙️
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
    dbName: "yourDatabaseName", // 📁 Optional: Specify database name (default: "test")
  },

  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: {
    clientSecret: "YOUR_DISCORD_CLIENT_SECRET", // 🔐 Discord Application Client Secret
    callbackURL: "http://localhost:3000/auth/discord/callback", // 🔗 OAuth2 Callback URL (OPTINAL)
    sessionSecret: "your-super-secret-key", // 🔒 Session encryption secret
    port: 3000, // 🌍 Dashboard port (OPTINAL / 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 1Game 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.
  • Specify the database name using the dbName option. If not specified, MongoDB uses "test" as the default database name.

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 🔵

🔵 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
    },
    {           
      style: 5,
      label: "link",
      emoji: "🔗" 
      url : "https://discord.gg/z9GpYsYF" // for url button
    }
  ],

  // 🔹 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" }, // 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
  • url → requiier for like button (style : 5)

🔹 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 🧾

🧾 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
CreateModal 🔳

🔳 CreateModal – Easily create Discord Modals with Text Inputs, Menus, Files, and Labels ✨

CreateModal is a powerful utility to build Discord Modals. It supports:

  • Text Inputs 📝
  • Select Menus 🎯 (string, role, user, channel)
  • File Uploads 📎
  • Text Displays 🏷️

📌 Example Usage:

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

const modal = CreateModal({
  id: "myModal",
  title: "User Information",
  components: [
    {
      type: "textInput",
      components: [
        {
          label: "Your Name",
          id: "name",
          style: 1, // 1: Short, 2: Paragraph
          placeholder: "Enter your name",
          required: true,
          minLength: 2,
          maxLength: 50,
        },
      ],
    },
    {
      type: "menu",
      components: {
        type: "string",
        options: {
          id: "favoriteColor",
          placeholder: "Choose your favorite color",
          min: 1,
          max: 1,
          data: [
            {
              label: "Red",
              value: "red",
              description: "A bold color",
              emoji: "🔴",
              default: false,
            },
            {
              label: "Blue",
              value: "blue",
              description: "A calm color",
              emoji: "🔵",
            },
          ],
          label: "label",
          value: "value",
          description: "description",
          emoji: "emoji",
          disabled: false,
        },
      },
    },
    {
      type: "file",
      components: {
        id: "avatar",
        label: "Upload Avatar",
        description: "Optional avatar image",
      },
    },
    {
      type: "text",
      components: {
        content: "Thank you for your input!",
      },
    },
  ],
});

// Show the modal
await interaction.showModal(modal);

📔 Examples for Each Component Type:

🔹 Text Input Example:

{
  type: "textInput",
  components: [
    {
      label: "Your Age",
      id: "age",
      style: 1, // Short input
      placeholder: "Enter your age",
      required: true,
      minLength: 1,
      maxLength: 3,
      value: "18", // Pre-filled
    },
  ],
},

🔹 Menu Example:

{
  type: "menu",
  components: {
    type: "string",
    options: {
      id: "country",
      mine_label: "Choose Your Country", // Label displayed above the menu
      mine_description: "Please select your country from the list below", // Optional description
      placeholder: "Select your country",
      min: 1,
      max: 1,
      data: [
        {
          name: "USA",
          id: "usa",
          desc: "United States",
          icon: "🇺🇸",
          default: true, // This option is pre-selected
        },
        {
          name: "Canada",
          id: "canada",
          desc: "Canada",
          icon: "🇨🇦",
        },
      ],
      label: "name", // Maps to 'name' field in data
      value: "id",   // Maps to 'id' field in data
      description: "desc", // Maps to 'desc' field
      emoji: "icon", // Maps to 'icon' field
      disabled: false,
    },
  },
},

🔹 File Example:

{
  type: "file",
  components: {
    id: "document",
    label: "Upload Document",
    description: "Upload a PDF or image",
  },
},

🔹 Label Example:

{
  type: "text",
  components: {
    content: "Please fill out the form above.",
  },
},

�📖 Explanation

🔹 Text Input

  • label → Label for the input field
  • id → customId for the input
  • style → 1: Short, 2: Paragraph
  • placeholder → Placeholder text
  • required → true/false
  • minLength / maxLength → Min/Max characters
  • value → Pre-filled value

🔹 Menu

Same as CreateRow select menus.

  • type"string" | "user" | "role" | "channel"

  • id → customId for menu

  • mine_label → Label displayed above the menu component (defaults to "Select an option")

  • mine_description → Description text displayed below the menu label (optional)

  • placeholder → Text shown before selection

  • min / max → Min/Max selectable values

  • data → Options array (for string select only)

    • label → Visible text (maps to the field specified in label key)
    • value → Internal value (maps to the field specified in value key)
    • description → Short description (maps to the field specified in description key)
    • emoji → Option emoji (maps to the field specified in emoji key)
    • default → Pre-selected option (true/false in data array)
  • label → Key in data to use as label (e.g., "name")

  • value → Key in data to use as value (e.g., "id")

  • description → Key in data to use as description (e.g., "desc")

  • emoji → Key in data to use as emoji (e.g., "icon")

  • disabled → Disable menu completely

  • defaultValues → Pre-selected user/role/channel options (for non-string menus)

  • channelTypes → Restrict selectable channel types (for channel menu)

🔹 File

  • id → customId for the file upload
  • label → Label
  • description → Description

🔹 text

  • content → Text to display

🔹 Notes

  • Supports multiple components in one modal
  • Fully customizable with Discord.js ModalBuilder
CreateComponents 🧩

🧩 CreateComponents – Build Advanced Discord UI Components with Containers, Sections & Media ✨

CreateComponents is a powerful utility to build Discord's new UI components. It supports:

  • Text Displays 📝
  • Separators
  • Media Galleries 🖼️
  • File Attachments 📎
  • Buttons 🔘
  • Select Menus 🎯 (string, role, user, channel)
  • Sections with Accessories 📦 (Thumbnails & Buttons)
  • Containers 📦 (Group all components together)

📌 Example Usage (Array Mode):

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

const components = await CreateComponents("array", [
  {
    type: "text",
    content: "Welcome to our server! 🎉",
  },
  {
    type: "separator",
    divider: true,
    spacing: 1, // 1: Small, 2: Large
  },
  {
    type: "media",
    links: [
      "https://example.com/image1.png",
      {
        url: "https://example.com/image2.png",
        description: "A cool image",
        spoiler: true,
      },
    ],
  },
  {
    type: "button",
    components: [
      {
        id: "btn_1",
        style: 1, // 1: Primary, 2: Secondary, 3: Success, 4: Danger, 5: Link
        label: "Click Me!",
        emoji: "🚀",
      },
      {
        id: "btn_2",
        style: 3,
        label: "Confirm",
      },
    ],
  },
]);

// Send the components
await channel.send({ 
   flags: 32768,
   components : components
 });

📌 Example Usage (Container Mode):

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

const components = await CreateComponents(true, [
  {
    type: "text",
    content: "# 📢 Server Announcement\nWelcome everyone!",
  },
  {
    type: "separator",
    divider: true,
  },
  {
    type: "color",
    color: "#ff0000",
  },
  {
    type: "section",
    content: "Check out our latest updates and news!",
    accessory: {
      type: "thumbnail",
      url: "https://example.com/thumbnail.png",
      description: "News Image",
    },
  },
  {
    type: "section",
    content: "**Click below to get your roles!**",
    accessory: {
      type: "button",
      id: "get_roles",
      style: 1,
      label: "Get Roles",
      emoji: "🎭",
    },
  },
  {
    type: "media",
    links: ["https://example.com/banner.png"],
  },
  {
    type: "button",
    components: [
      { id: "rules", style: 2, label: "📜 Rules", emoji: "📜" },
      { id: "help", style: 2, label: "❓ Help", emoji: "❓" },
      { style: 5, label: "🌐 Website", url: "https://example.com" },
    ],
  },
]);

// Send with container
await channel.send({ components, flags: 32768 }); // flags for components v2

📜 Examples for Each Component Type:

🔹 Text Component Examples

📌 Simple Text:

{
  type: "text",
  content: "Hello World! 👋",
}

📌 Text with Markdown:

{
  type: "text",
  content: "# 📢 Announcement\n**Important:** Server maintenance tonight!",
}

📌 Text with Multiple Lines:

{
  type: "text",
  content: `## 🎮 Game Stats
  
**Player:** Ahmed
**Level:** 50
**XP:** 12,500 / 15,000
**Rank:** Diamond 💎`,
}

📌 Text with Emojis & Formatting:

{
  type: "text",
  content: ">>> 💡 **Tip:** Use `/help` to see all commands!\n\n*This message will auto-delete in 30 seconds*",
}
➖ Separator Component Examples

➖ Separator – Add spacing and dividers between components

The separator component allows you to add visual breaks between other components with customizable spacing and divider lines.

📖 Separator Options

OptionTypeDefaultDescription
typestringMust be "separator"
dividerbooleanfalseShow a horizontal dividing line
spacingnumber1Spacing size: 1 (Small) or 2 (Large)

📌 Simple Divider Line:

{
  type: "separator",
  divider: true,
}

Result: A thin horizontal line appears between components.

📌 Separator without Line (Spacing Only):

{
  type: "separator",
  divider: false,
  spacing: 2, // Large spacing
}

Result: Empty space without any visible line - useful for visual grouping.

📌 Small Spacing with Divider:

{
  type: "separator",
  divider: true,
  spacing: 1, // Small spacing (default)
}

Result: A divider line with minimal padding above and below.

📌 Large Spacing with Divider:

{
  type: "separator",
  divider: true,
  spacing: 2, // Large spacing
}

Result: A divider line with more padding - creates stronger visual separation.

💡 When to Use Each Option:

Scenariodividerspacing
Separate major sectionstrue2
Separate sub-sectionstrue1
Group related items visuallyfalse1
Create breathing room between contentfalse2
Minimal separationfalse1
🖼️ Media Gallery Examples

🖼️ Media Gallery – Display images in a beautiful gallery format

The media component creates an image gallery that can display one or multiple images. Each image can be a simple URL string or a detailed object with additional options.

📌 Format 1: Simple String URLs

The simplest way - just pass image URLs as strings:

{
  type: "media",
  links: ["https://example.com/image.png"],
}

📌 Format 2: Object with Full Options

For more control, use objects with url, description, and spoiler:

{
  type: "media",
  links: [
    {
      url: "https://example.com/image.png",
      description: "A beautiful sunset",
      spoiler: false,
    },
  ],
}

📌 Single Image with Spoiler:

{
  type: "media",
  links: [
    {
      url: "https://example.com/spoiler.png",
      description: "⚠️ Spoiler Alert!",
      spoiler: true,
    },
  ],
}

📌 Multiple Images (Simple Strings):

{
  type: "media",
  links: [
    "https://example.com/image1.png",
    "https://example.com/image2.png",
    "https://example.com/image3.png",
    "https://example.com/image4.png",
  ],
}

Result: A 2x2 gallery grid of images.

📌 Mixed Format (Strings + Objects):

You can mix simple strings with detailed objects in the same array:

{
  type: "media",
  links: [
    // Simple string - just the URL
    "https://example.com/public-image.png",
    
    // Object with description
    {
      url: "https://example.com/special-image.png",
      description: "Limited Edition Art",
    },
    
    // Object with spoiler
    {
      url: "https://example.com/secret-image.png",
      description: "Secret Content",
      spoiler: true,
    },
    
    // Another simple string
    "https://example.com/another-image.png",
  ],
}
{
  type: "media",
  links: [
    {
      url: "https://example.com/meme1.png",
      description: "Funny meme #1",
      spoiler: false,
    },
    {
      url: "https://example.com/meme2.png",
      description: "Funny meme #2",
      spoiler: false,
    },
    {
      url: "https://example.com/nsfw-meme.png",
      description: "⚠️ Slightly inappropriate",
      spoiler: true, // Hidden behind blur
    },
  ],
}

💡 Tips for Media Galleries:

Images CountDisplay Layout
1 imageFull width single image
2 imagesSide by side
3 images1 large + 2 small
4+ imagesGrid layout
  • Use description for accessibility and context
  • Use spoiler: true for sensitive/spoiler content
  • Mix formats freely - strings for quick images, objects for detailed ones
  • Images are displayed in the order provided
📎 File Component Examples

📌 Simple File Attachment:

{
  type: "file",
  url: "attachment://document.pdf",
}

📌 Full Example with File:

const { CreateComponents } = require("djs-builder");
const { AttachmentBuilder } = require("discord.js");

const file = new AttachmentBuilder("./myfile.txt", { name: "myfile.txt" });

const components = await CreateComponents("container", [
  {
    type: "text",
    content: "📄 **Here is your requested file:**",
  },
  {
    type: "file",
    url: "attachment://myfile.txt",
  },
]);

await channel.send({ components, files: [file], flags: 32768 });
🔘 Button Component Examples

📌 Button with Emoji:

{
  type: "button",
  components: [
    {
      id: "like_btn",
      style: 3, // Success (Green)
      label: "Like",
      emoji: "👍",
    },
  ],
}

📌 Multiple Buttons in Row:

{
  type: "button",
  components: [
    { id: "btn_yes", style: 3, label: "Yes", emoji: "✅" },
    { id: "btn_no", style: 4, label: "No", emoji: "❌" },
    { id: "btn_maybe", style: 2, label: "Maybe", emoji: "🤔" },
  ],
}

📌 All Button Styles:

{
  type: "button",
  components: [
    { id: "primary", style: 1, label: "Primary", emoji: "🔵" },    // Blue
    { id: "secondary", style: 2, label: "Secondary", emoji: "⚪" }, // Gray
    { id: "success", style: 3, label: "Success", emoji: "🟢" },    // Green
    { id: "danger", style: 4, label: "Danger", emoji: "🔴" },      // Red
    { style: 5, label: "Link", emoji: "🔗", url: "https://discord.com" }, // Link
  ],
}
🎯 Menu Component Examples

📌 String Select Menu (Basic):

{
  type: "menu",
  components: {
    type: "string",
    options: {
      id: "color_select",
      placeholder: "🎨 Choose a color",
      data: [
        { name: "Red", id: "red", icon: "🔴" },
        { name: "Blue", id: "blue", icon: "🔵" },
        { name: "Green", id: "green", icon: "🟢" },
      ],
      label: "name",
      value: "id",
      emoji: "icon",
    },
  },
}

📌 String Select with Description:

{
  type: "menu",
  components: {
    type: "string",
    options: {
      id: "role_select",
      placeholder: "🎭 Select your role",
      data: [
        { name: "Gamer", id: "gamer", desc: "For gaming enthusiasts", icon: "🎮" },
        { name: "Artist", id: "artist", desc: "For creative people", icon: "🎨" },
        { name: "Developer", id: "dev", desc: "For coders & programmers", icon: "💻" },
        { name: "Music Lover", id: "music", desc: "For music fans", icon: "🎵" },
      ],
      label: "name",
      value: "id",
      description: "desc",
      emoji: "icon",
    },
  },
}

📌 String Select with Min/Max:

{
  type: "menu",
  components: {
    type: "string",
    options: {
      id: "games_select",
      placeholder: "🎮 Select your favorite games (2-4)",
      min: 2,
      max: 4,
      data: [
        { name: "Minecraft", id: "mc", icon: "⛏️" },
        { name: "Fortnite", id: "fn", icon: "🔫" },
        { name: "Valorant", id: "val", icon: "🎯" },
        { name: "League of Legends", id: "lol", icon: "⚔️" },
        { name: "Rocket League", id: "rl", icon: "🚗" },
      ],
      label: "name",
      value: "id",
      emoji: "icon",
    },
  },
}

📌 User Select (Multiple):

{
  type: "menu",
  components: {
    type: "user",
    options: {
      id: "users_select",
      placeholder: "👥 Select users to invite (1-5)",
      min: 1,
      max: 5,
    },
  },
}

📌 Role Select Menu:

{
  type: "menu",
  components: {
    type: "role",
    options: {
      id: "role_select",
      placeholder: "🎭 Select a role",
      min: 1,
      max: 1,
    },
  },
}

📌 Channel Select Menu:

{
  type: "menu",
  components: {
    type: "channel",
    options: {
      id: "channel_select",
      placeholder: "📢 Select a channel",
      min: 1,
      max: 1,
    },
  },
}

📌 Channel Select with Type Filter:

const { ChannelType } = require("discord.js");

{
  type: "menu",
  components: {
    type: "channel",
    options: {
      id: "text_channel_select",
      placeholder: "💬 Select a text channel",
      channelTypes: [ChannelType.GuildText, ChannelType.GuildAnnouncement],
    },
  },
}

📌 Menu with Default Values (User/Role/Channel):

{
  type: "menu",
  components: {
    type: "user",
    options: {
      id: "user_select_default",
      placeholder: "👤 Select users",
      min: 1,
      max: 3,
      defaultValues: [
        { id: "123456789012345678", type: "user" },
        { id: "987654321098765432", type: "user" },
      ],
    },
  },
}
📦 Section Component Examples

📌 Section with Thumbnail (Product):

{
  type: "section",
  content: "🛒 **iPhone 15 Pro**\n💰 Price: $999\n⭐ Rating: 4.8/5\n📦 In Stock: Yes",
  accessory: {
    type: "thumbnail",
    url: "https://example.com/iphone.png",
    description: "iPhone 15 Pro Image",
  },
}

📌 Section with Button :

{
  type: "section",
  content: "🎁 **Daily Reward**\nClick to claim your daily reward!\n💎 **+100 Coins**",
  accessory: {
    type: "button",
    id: "claim_daily",
    style: 3, 
    label: "Claim",
    emoji: "🎁",
  },
}
🎨 Color Component Examples

🎨 Color – Set an accent color for the container (Requires Container Mode)

You can add an accent color line to your container. The color can be an integer or a hexadecimal string (#ff0000).

📌 Container with Color:

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

const components = await CreateComponents(true, [
  {
    type: "text",
    content: "Welcome to our server! 🎉",
  },
  {
    type: "color",
    color: "#ff0000", // You can use Hex strings like "#ff0000" or an integer like 0xff0000
  }
]);

await interaction.reply({ components, flags: 32768 });

📖 Component Types Explanation

🔹 Text

  • type"text"
  • content → The text content to display (supports Markdown)

🔹 Separator

  • type"separator"
  • divider → Show a dividing line (true/false)
  • spacing → Spacing size (1: Small, 2: Large)
  • type"media"
  • links → Array of image URLs or objects with:
    • url → Image URL
    • description → Alt text for the image
    • spoiler → Hide behind spoiler (true/false)

🔹 File

  • type"file"
  • url → File URL or attachment reference
  • spoiler → Hide behind spoiler (true/false)

🔹 Button

  • type"button"
  • components → Array of button objects:
    • id → customId (required for non-link buttons)
    • style → 1: Primary, 2: Secondary, 3: Success, 4: Danger, 5: Link
    • label → Button text
    • emoji → Button emoji

🔹 Color

  • type"color"
  • color → An Accent Color (Hex string like "#ff0000" or number)
    • disabled → Disable button (true/false)
    • url → URL for link buttons (style: 5)

🔹 Menu

  • type"menu"
  • components → Object containing:
    • type"string" | "user" | "role" | "channel"
    • options → Menu configuration:
      • id → customId
      • placeholder → Placeholder text
      • min / max → Min/Max selectable values
      • data → Options array (for string select)
      • label → Key in data for label
      • value → Key in data for value
      • emoji → Key in data for emoji
      • description → Key in data for description
      • channelTypes → Channel types filter (for channel menu)
      • defaultValues → Pre-selected values

🔹 Section

  • type"section"
  • content → Text content for the section
  • accessory → Side component:
    • Button Accessory:
      • type"button"
      • id → customId
      • style → Button style
      • label → Button text
      • emoji → Button emoji
      • url → URL (for link buttons)
    • Thumbnail Accessory:
      • type"thumbnail"
      • url → Image URL
      • description → Image description

🔹 Mode Differences

Feature"array" Mode"container" Mode
Return TypeArray of componentsSingle ContainerBuilder
UsageStandard messagesComponents V2 messages
Sections Support
GroupingIndividual componentsAll grouped in container
Flags Required✅ (flags: 32768)

🔹 Notes

  • Use "container" mode for Discord's Components V2 (newer UI)
  • Use "array" mode for standard component arrays
  • Sections can have either a button or thumbnail as accessory, not both
  • Media galleries support multiple images in a single component
  • All components are fully customizable 🎨
Wait ⏰

⏰ 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 👤

👤 GetUser – Fetch a GuildMember easily from a message ✨

GetUser helps to detect a target member in multiple ways:

  • Mention (@User)
  • User ID (123456789012345678)
  • 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
Logging System 🛡️

🛡️ Logging System – Track Everything in Your Server

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!

Note 1: Using database: true requires a MongoDB connection. | Note 2: You can import the Log model for direct database access 💾.

📦 Module Exports

const { log, Log } = require("djs-builder");
  • log(client, options) → Start the logging system with your configuration 🚀.
  • Log → The Mongoose model for direct database access and custom modifications 💾.

📋 Simple Example

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

module.exports = {
  name: "clientReady",
  async run(client) {
    const logData = [
      {
        guildId: "999888777666555444",
        channelId: "444555666777888999",
      },

          {
        guildId: "999888777666555444",
        channelId: "444555666777888999",
      },
    ];

    // Start logging with custom data
    await log(client, {
      Data: logData, // 📊 Your configurations
    });

    console.log("✅ Logging system started with custom data!");
  },
};

✨ 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.
  • 👤 Members – Join, leave, kick, ban, and unban events.
  • 🚨 Audit Log Integration – Fetches the executor (who did what).
  • 🎨 Beautiful Embeds – Every log is shown in a clean, styled embed with timestamps.
  • 🗄️ Caching System – Fast performance with built-in data caching.

📊 Database Schema

The logging system uses the following data structure:

{
  guildId: String,      // 🏠 Server ID (required)
  channelId: String,    // 📢 Default log channel ID
  channels: Object,     // 📂 Custom channels per event type (optional)
  colors: Object,       // 🎨 Custom colors per event type (optional)
  disable: Array,       // 🚫 Array of disabled event types (optional)
}
📋 Supported Event Types

📋 Supported Event Types

Event TypeDescription
messageDeleteMessage deleted 📝
messageUpdateMessage edited ✏️
channelCreateChannel created 📁
channelDeleteChannel deleted 🗑️
channelUpdateChannel updated ⚙️
guildMemberAddMember joined 🎉
guildMemberRemoveMember left/kicked 🚪
guildBanAddMember banned 🔨
guildBanRemoveMember unbanned 🤗
roleCreateRole created 🏅
roleDeleteRole deleted ❌
roleUpdateRole updated 🔄
guildMemberUpdateMember roles changed 👤
voiceStateUpdateVoice channel activity 🎤
inviteCreateInvite created 🔗
emojiCreateEmoji added 😀
emojiDeleteEmoji removed 🚫
emojiUpdateEmoji updated 🔄
stickerCreateSticker added ✨
stickerDeleteSticker removed 🗑️
stickerUpdateSticker updated 🌀
⚡ Method Using Database (Recommended) 🗄️

🗄️ Using MongoDB Database

This method stores log configuration in MongoDB, allowing dynamic management via commands.

⚡ Setup in clientReady Event:

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

module.exports = {
  name: "clientReady",
  async run(client) {
    // Start logging with database mode
    await log(client, {
      database: true, // 🗄️ Uses MongoDB to store/fetch config
    });

    console.log("✅ Logging system started with database mode!");
  },
};

💡 How It Works

  • ✅ The system automatically fetches log configuration for each guild from MongoDB.
  • 🛠️ You can manage settings via slash commands (see management command below).
  • 🎨 Supports per-guild customization for channels, colors, and disabled events.
  • 📖 Important: If you use database mode, see the Log Management Command below to edit data.
⚡ Method Using Custom Data Array 📋

📋 Using Custom Data Array

This method uses a predefined array of configurations – perfect for simple setups or testing.

⚡ Setup in clientReady Event:

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

module.exports = {
  name: "clientReady",
  async run(client) {
    // Define your log configurations
    const logData = [
      {
        guildId: "123456789012345678", // 🏠 Server ID
        channelId: "987654321098765432", // 📢 Default log channel
        channels: {
          // 📂 Custom channels (optional)
          messageDelete: "111111111111111111",
          voiceStateUpdate: "222222222222222222",
        },
        colors: {
          // 🎨 Custom colors (optional)
          messageDelete: "DarkRed",
          channelCreate: "DarkGreen",
        },
        disable: ["inviteCreate"], // 🚫 Disabled events (optional)
      },
      // Add more guild configurations...
      {
        guildId: "999888777666555444",
        channelId: "444555666777888999",
      },
    ];

    // Start logging with custom data
    await log(client, {
      database: false, // 📋 Uses custom array
      Data: logData, // 📊 Your configurations
    });

    console.log("✅ Logging system started with custom data!");
  },
};

🎨 Supported Colors

Default, White, Aqua, Green, Blue, Yellow, Purple, LuminousVividPink, Fuchsia, Gold, Orange, Red, Grey, Navy, DarkAqua, DarkGreen, DarkBlue, DarkPurple, DarkVividPink, DarkGold, DarkOrange, DarkRed, DarkGrey, DarkerGrey, LightGrey, DarkNavy, Blurple, Greyple, DarkButNotBlack, NotQuiteBlack, Random, or any Hex Color like #FF5733.

🌐 Multi-Guild Support

💡 Tip: You can add configurations for multiple guilds in the same array. Each guild can have its own unique settings for channels, colors, and disabled events!

🔧 Direct Database Access with Log Model 💾

💾 Using the Log Model Directly

You can import the Log Mongoose model to create, update, or delete log configurations programmatically.

📥 Import the Model:

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

➕ Create New Configuration:

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

// Create a new log configuration for a guild
const newConfig = await Log.create({
  guildId: "123456789012345678",
  channelId: "987654321098765432",
  channels: {
    messageDelete: "111111111111111111",
  },
  colors: {
    messageDelete: "Red",
  },
  disable: [],
});

console.log("✅ Log configuration created!", newConfig);

✏️ Update Existing Configuration:

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

// Update log channel
await Log.findOneAndUpdate(
  { guildId: "123456789012345678" },
  { channelId: "NEW_CHANNEL_ID" },
  { upsert: true } // Create if doesn't exist
);

// Add event to disable list
await Log.findOneAndUpdate(
  { guildId: "123456789012345678" },
  { $push: { disable: "voiceStateUpdate" } }
);

// Remove event from disable list
await Log.findOneAndUpdate(
  { guildId: "123456789012345678" },
  { $pull: { disable: "voiceStateUpdate" } }
);

// Update specific channel for an event
await Log.findOneAndUpdate(
  { guildId: "123456789012345678" },
  { $set: { "channels.messageDelete": "NEW_CHANNEL_ID" } }
);

// Update specific color for an event
await Log.findOneAndUpdate(
  { guildId: "123456789012345678" },
  { $set: { "colors.messageDelete": "DarkRed" } }
);

🗑️ Delete Configuration:

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

await Log.findOneAndDelete({ guildId: "123456789012345678" });
console.log("🗑️ Log configuration deleted!");

📊 Fetch Configuration:

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

const config = await Log.findOne({ guildId: "123456789012345678" });
if (config) {
  console.log("📢 Log Channel:", config.channelId);
  console.log("📂 Custom Channels:", config.channels);
  console.log("🎨 Custom Colors:", config.colors);
  console.log("🚫 Disabled Events:", config.disable);
}
🌐 Dashboard Integration – Manage Logs from Web Panel 🖥️

🖥️ Web Dashboard for Log Management

When you use database mode (database: true), you can manage the logging system directly from the djs-builder Dashboard! 🎛️

✨ Dashboard Features for Logs

FeatureDescription
📢 Default ChannelSet the main channel for all logs
📂 Custom ChannelsAssign specific channels for each event type
🎨 Custom ColorsChoose embed colors for each event type
🔄 Toggle EventsEnable/Disable specific event types
📊 StatisticsView enabled/disabled events count
🗑️ ResetClear all log settings with one click

🚀 How to Access

  • Navigate to your dashboard (e.g., http://localhost:3000)
  • Log in with Discord OAuth2
  • Select a server
  • Click on "سجلات المراقبة" (Logs) in the sidebar

⚠️ Important Notes

When database: true:

  • ✅ Full editing capabilities from dashboard
  • ✅ Changes are saved automatically to MongoDB
  • ✅ Real-time updates

When database: false (Custom Data Array):

  • ⚠️ Dashboard shows read-only mode
  • ⚠️ A warning banner appears at the top
  • ⚠️ You must edit settings in your code
🎮 Slash Command for Log Management 🛠️

🛠️ Complete Log Management Slash Command

This command allows server administrators to manage the logging system via Discord.

⚠️ Important: This command requires database: true mode to work properly.

const { Log, getLogConfigData } = require("djs-builder");
const {
  SlashCommandBuilder,
  PermissionFlagsBits,
  EmbedBuilder,
  ChannelType,
} = require("discord.js");

// All supported event types (21 events)
const EVENT_TYPES = [
  { name: "Message Delete", value: "messageDelete" },
  { name: "Message Update", value: "messageUpdate" },
  { name: "Channel Create", value: "channelCreate" },
  { name: "Channel Delete", value: "channelDelete" },
  { name: "Channel Update", value: "channelUpdate" },
  { name: "Member Join", value: "guildMemberAdd" },
  { name: "Member Leave", value: "guildMemberRemove" },
  { name: "Member Ban", value: "guildBanAdd" },
  { name: "Member Unban", value: "guildBanRemove" },
  { name: "Role Create", value: "roleCreate" },
  { name: "Role Delete", value: "roleDelete" },
  { name: "Role Update", value: "roleUpdate" },
  { name: "Member Role Update", value: "guildMemberUpdate" },
  { name: "Voice State", value: "voiceStateUpdate" },
  { name: "Invite Create", value: "inviteCreate" },
  { name: "Emoji Create", value: "emojiCreate" },
  { name: "Emoji Delete", value: "emojiDelete" },
  { name: "Emoji Update", value: "emojiUpdate" },
  { name: "Sticker Create", value: "stickerCreate" },
  { name: "Sticker Delete", value: "stickerDelete" },
  { name: "Sticker Update", value: "stickerUpdate" },
];

// Helper function to clear cache after updates
function clearLogCache(guildId) {
  const logData = getLogConfigData();
  if (logData.clearCache) logData.clearCache(guildId);
}

module.exports = {
  data: new SlashCommandBuilder()
    .setName("logs")
    .setDescription("🛡️ Manage the server logging system")
    .setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
    .addSubcommand((sub) =>
      sub
        .setName("setup")
        .setDescription("📢 Set up the default log channel")
        .addChannelOption((opt) =>
          opt
            .setName("channel")
            .setDescription("The channel to send logs to")
            .addChannelTypes(ChannelType.GuildText)
            .setRequired(true)
        )
    )
    .addSubcommand((sub) =>
      sub
        .setName("channel")
        .setDescription("📂 Set a specific channel for an event type")
        .addStringOption((opt) =>
          opt
            .setName("event")
            .setDescription("The event type")
            .setRequired(true)
            .addChoices(...EVENT_TYPES)
        )
        .addChannelOption((opt) =>
          opt
            .setName("channel")
            .setDescription("The channel for this event")
            .addChannelTypes(ChannelType.GuildText)
            .setRequired(true)
        )
    )
    .addSubcommand((sub) =>
      sub
        .setName("color")
        .setDescription("🎨 Set a custom color for an event type")
        .addStringOption((opt) =>
          opt
            .setName("event")
            .setDescription("The event type")
            .setRequired(true)
            .addChoices(...EVENT_TYPES)
        )
        .addStringOption((opt) =>
          opt
            .setName("color")
            .setDescription("Color name or hex code (e.g., Red, #FF5733)")
            .setRequired(true)
        )
    )
    .addSubcommand((sub) =>
      sub
        .setName("toggle")
        .setDescription("🔄 Enable or disable an event type")
        .addStringOption((opt) =>
          opt
            .setName("event")
            .setDescription("The event type")
            .setRequired(true)
            .addChoices(...EVENT_TYPES)
        )
        .addStringOption((opt) =>
          opt
            .setName("action")
            .setDescription("Enable or disable")
            .setRequired(true)
            .addChoices(
              { name: "Enable", value: "enable" },
              { name: "Disable", value: "disable" }
            )
        )
    )
    .addSubcommand((sub) =>
      sub.setName("view").setDescription("📊 View current log configuration")
    )
    .addSubcommand((sub) =>
      sub
        .setName("reset")
        .setDescription("🗑️ Reset all log settings for this server")
    ),

  async run(client, interaction) {
    await interaction.deferReply({ ephemeral: true });

    const subcommand = interaction.options.getSubcommand();
    const guildId = interaction.guild.id;

    // ═══════════════════════════════════════════════════════
    // 📢 SETUP - Set default log channel
    // ═══════════════════════════════════════════════════════
    if (subcommand === "setup") {
      const channel = interaction.options.getChannel("channel");

      await Log.findOneAndUpdate(
        { guildId },
        { guildId, channelId: channel.id },
        { upsert: true, new: true }
      );

      clearLogCache(guildId); // Clear cache to apply changes immediately

      const embed = new EmbedBuilder()
        .setTitle("✅ Logging System Setup")
        .setDescription(`Logs will now be sent to ${channel}`)
        .setColor("Green")
        .setTimestamp();

      return interaction.editReply({ embeds: [embed] });
    }

    // ═══════════════════════════════════════════════════════
    // 📂 CHANNEL - Set specific channel for event
    // ═══════════════════════════════════════════════════════
    if (subcommand === "channel") {
      const event = interaction.options.getString("event");
      const channel = interaction.options.getChannel("channel");

      await Log.findOneAndUpdate(
        { guildId },
        { $set: { [`channels.${event}`]: channel.id } },
        { upsert: true }
      );

      clearLogCache(guildId);

      const eventName =
        EVENT_TYPES.find((e) => e.value === event)?.name || event;
      const embed = new EmbedBuilder()
        .setTitle("📂 Event Channel Updated")
        .setDescription(`**${eventName}** logs will now be sent to ${channel}`)
        .setColor("Blue")
        .setTimestamp();

      return interaction.editReply({ embeds: [embed] });
    }

    // ═══════════════════════════════════════════════════════
    // 🎨 COLOR - Set custom color for event
    // ═══════════════════════════════════════════════════════
    if (subcommand === "color") {
      const event = interaction.options.getString("event");
      const color = interaction.options.getString("color");

      await Log.findOneAndUpdate(
        { guildId },
        { $set: { [`colors.${event}`]: color } },
        { upsert: true }
      );

      clearLogCache(guildId);

      const eventName =
        EVENT_TYPES.find((e) => e.value === event)?.name || event;
      
      // Try to use the color, fallback to Blue if invalid
      let embedColor;
      try {
        embedColor = color;
      } catch {
        embedColor = "Blue";
      }

      const embed = new EmbedBuilder()
        .setTitle("🎨 Event Color Updated")
        .setDescription(
          `**${eventName}** embeds will now use color: \`${color}\``
        )
        .setColor(embedColor)
        .setTimestamp();

      return interaction.editReply({ embeds: [embed] });
    }

    // ═══════════════════════════════════════════════════════
    // 🔄 TOGGLE - Enable/Disable event
    // ═══════════════════════════════════════════════════════
    if (subcommand === "toggle") {
      const event = interaction.options.getString("event");
      const action = interaction.options.getString("action");

      if (action === "disable") {
        await Log.findOneAndUpdate(
          { guildId },
          { $addToSet: { disable: event } },
          { upsert: true }
        );
      } else {
        await Log.findOneAndUpdate(
          { guildId },
          { $pull: { disable: event } },
          { upsert: true }
        );
      }

      clearLogCache(guildId);

      const eventName =
        EVENT_TYPES.find((e) => e.value === event)?.name || event;
      const embed = new EmbedBuilder()
        .setTitle(
          action === "disable" ? "🚫 Event Disabled" : "✅ Event Enabled"
        )
        .setDescription(`**${eventName}** logging has been ${action}d`)
        .setColor(action === "disable" ? "Red" : "Green")
        .setTimestamp();

      return interaction.editReply({ embeds: [embed] });
    }

    // ═══════════════════════════════════════════════════════
    // 📊 VIEW - Show current configuration
    // ═══════════════════════════════════════════════════════
    if (subcommand === "view") {
      const config = await Log.findOne({ guildId });

      if (!config) {
        return interaction.editReply({
          content:
            "❌ No logging configuration found for this server. Use `/logs setup` first!",
        });
      }

      const channelsList = config.channels
        ? Object.entries(config.channels)
            .map(([k, v]) => {
              const eventName = EVENT_TYPES.find((e) => e.value === k)?.name || k;
              return `• **${eventName}**: <#${v}>`;
            })
            .join("\n") || "None"
        : "None";

      const colorsList = config.colors
        ? Object.entries(config.colors)
            .map(([k, v]) => {
              const eventName = EVENT_TYPES.find((e) => e.value === k)?.name || k;
              return `• **${eventName}**: \`${v}\``;
            })
            .join("\n") || "None"
        : "None";

      const disabledList =
        config.disable?.length > 0
          ? config.disable.map((e) => {
              const eventName = EVENT_TYPES.find((ev) => ev.value === e)?.name || e;
              return `• ${eventName}`;
            }).join("\n")
          : "None";

      const enabledCount = EVENT_TYPES.length - (config.disable?.length || 0);

      const embed = new EmbedBuilder()
        .setTitle("📊 Log Configuration")
        .setDescription(`**${enabledCount}/${EVENT_TYPES.length}** events are enabled`)
        .setColor("Blue")
        .addFields(
          {
            name: "📢 Default Channel",
            value: config.channelId ? `<#${config.channelId}>` : "Not set",
            inline: true,
          },
          {
            name: "📂 Custom Channels",
            value: channelsList.slice(0, 1024) || "None",
            inline: false,
          },
          {
            name: "🎨 Custom Colors",
            value: colorsList.slice(0, 1024) || "None",
            inline: false,
          },
          {
            name: "🚫 Disabled Events",
            value: disabledList.slice(0, 1024) || "None",
            inline: false,
          }
        )
        .setFooter({ text: `Guild ID: ${guildId}` })
        .setTimestamp();

      return interaction.editReply({ embeds: [embed] });
    }

    // ═══════════════════════════════════════════════════════
    // 🗑️ RESET - Delete all configuration
    // ═══════════════════════════════════════════════════════
    if (subcommand === "reset") {
      await Log.findOneAndDelete({ guildId });
      
      clearLogCache(guildId);

      const embed = new EmbedBuilder()
        .setTitle("🗑️ Configuration Reset")
        .setDescription(
          "All logging settings have been deleted for this server."
        )
        .setColor("Red")
        .setTimestamp();

      return interaction.editReply({ embeds: [embed] });
    }
  },
};

💡 Tips & Notes

  • 🔄 Caching: The system caches guild configurations for better performance.
  • 🔐 Permissions: Make sure your bot has View Audit Log permission for full functionality.
  • 📢 Invite Tracking: Uses discord-inviter package for accurate invite tracking.
  • 🎨 Default Colors: Each event type has sensible default colors if not customized.
  • 🚫 Disabled Events: Events in the disable array will be completely ignored.
  • 📂 Channel Fallback: If no specific channel is set for an event, it uses channelId.
  • 💾 Database Mode: Recommended for multi-server bots with dynamic configuration needs.
  • 🌐 Dashboard Integration: When using database mode, you can manage logs via the web dashboard!
Level System 🏆

🏆 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 🎉

🎉 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

🎬 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

⚠️ Important NotesRead 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

📆 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

🔄 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

📜 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

⏸️ 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

🗑️ 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

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

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

📊 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

💡 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: ✨🚀

📝 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 🚫

🚫 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 🔄

🔄 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:

⚡ Commands & Events Options

⚡ 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, lightweight web-based control panel for your Discord bot. It provides a beautiful interface to manage your bot, servers, levels, giveaways, and more! 🎛️🚀

Features:

  • 🔐 Discord OAuth2 Login – Secure authentication with Discord.
  • 📊 Bot Statistics – View real-time bot stats (servers, users, commands, uptime).
  • 🏠 Server Management – Manage servers where you have admin permissions.
  • 📜 Commands Viewer – Browse all slash and prefix commands.
  • 🏆 Level System Management – View leaderboards, manage user XP and levels.
  • 🎉 Giveaway Control – Pause, resume, end, reroll, or delete giveaways.
  • 🦾 Logging System Control – Manage log channels, colors, and event toggles.
  • 🚫 Blacklist Management – Add/remove users, roles, or channels from blacklist.
  • 🎨 Modern UI – Beautiful, responsive design with dark mode support.

Note: This module requires express, express-session, passport, and passport-discord packages. | Note 2: You must have a DATABASE connection for full functionality.

💡 Tip: You can use Dashboard with the starter function (see Starter) or as a standalone function!

Dashboard with Starter ⚙️

When using with starter, simply add the dashboard option to your starter configuration:

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

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

const starterOptions = {
  bot: {
    token: "YOUR_BOT_TOKEN",
    ownerId: "YOUR_USER_ID",
  },
  terminal: true,

  // 🌐 Dashboard Configuration
  dashboard: {
    clientSecret: "YOUR_DISCORD_CLIENT_SECRET", // 🔐 Discord Application Client Secret
    callbackURL: "http://localhost:3000/auth/discord/callback", // 🔗 OAuth2 Callback URL (OPTINAL)
    sessionSecret: "your-super-secret-key", // 🔒 Session encryption secret
    port: 3000, // 🌍 Dashboard port (OPTINAL / default: 3000)
  },

  // ... other options (Status, database, anticrash, etc.)
};

await starter(client, starterOptions);
Dashboard Standalone Usage 🔧

You can use the Dashboard as a standalone function without the starter! This is useful if you already have your own bot setup or want more control.

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

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

// 🔑 Login your bot first
client.login("YOUR_BOT_TOKEN");

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

  // 🌐 Start the Dashboard
  dashboard(client, {
    clientSecret: "YOUR_DISCORD_CLIENT_SECRET", // 🔐 Discord Application Client Secret
    callbackURL: "http://localhost:3000/auth/discord/callback", // 🔗 OAuth2 Callback URL (OPTINAL)
    sessionSecret: "your-super-secret-key", // 🔒 Session encryption secret
    port: 3000, // 🌍 Dashboard port (OPTINAL / default: 3000)
  });
});

💡 Tip: When using standalone, make sure your bot is logged in before starting the dashboard!

Dashboard Routes & API 📡

📌 Dashboard Routes

RouteDescription
/🏠 Homepage with bot stats
/login🔐 Login page
/dashboard📊 Server selection
/dashboard/:guildId🏠 Server overview
/dashboard/:guildId/commands📜 Commands list
/dashboard/:guildId/levels🏆 Level leaderboard
/dashboard/:guildId/giveaways🎉 Giveaway management
/dashboard/:guildId/logs🛡️ Logging system management

📡 API Endpoints

The dashboard exposes several API endpoints for dynamic data:

// 📊 Get bot statistics
GET /api/stats

// 🏠 Get server statistics
GET /api/:guildId/stats

// 👤 Get user level data
GET /api/:guildId/user/:userId

// 🔍 Search members
GET /api/:guildId/members/search?q=query

// 🏆 Level management
POST /api/:guildId/level/update   // Update user level
POST /api/:guildId/level/add      // Add XP to user
POST /api/:guildId/level/reset    // Reset user level

// 🎉 Giveaway actions
POST /api/:guildId/giveaway/pause
POST /api/:guildId/giveaway/resume
POST /api/:guildId/giveaway/end
POST /api/:guildId/giveaway/reroll
POST /api/:guildId/giveaway/delete

// �️ Logging system management
POST /api/:guildId/logs/channel    // Set default log channel
POST /api/:guildId/logs/toggle     // Enable/disable event type
POST /api/:guildId/logs/event      // Update event settings (channel, color)
POST /api/:guildId/logs/reset      // Reset all log settings

// �🚫 Blacklist management
POST /api/guild/:guildId/blacklist    // Add to blacklist
DELETE /api/guild/:guildId/blacklist  // Remove from blacklist
Dashboard Options 🔧

🔧 Configuration Options

OptionTypeRequiredDescription
clientSecretstring🔐 Your Discord Application Client Secret
callbackURLstring🔗 OAuth2 redirect URL (must match Discord settings)
sessionSecretstring🔒 Secret key for session encryption
portnumber🌍 Port to run the dashboard (default: 3000)

📁 View Templates

The dashboard uses EJS templates located in the views folder:

FileDescription
index.ejs🏠 Homepage
login.ejs🔐 Login page
dashboard.ejs📊 Server selection
guild.ejs🏠 Server overview
commands.ejs📜 Commands list
levels.ejs🏆 Level management
giveaways.ejs🎉 Giveaway management
logs.ejs🛡️ Logging system management
404.ejs❌ Not found page
Discord Developer Portal Setup 🛠️

🛠️ How to Get Client ID & Secret

  • Go to Discord Developer Portal 🌐
  • Create a new application or select existing one 📱
  • Go to OAuth2General 🔐
  • Copy the Client ID and Client Secret 📋
  • Add your Redirect URL (e.g., http://localhost:3000/auth/discord/callback) ✅
  • Make sure to add the URL to Redirects list 📝

💡 Tips & Notes

  • 🔒 Keep secrets safe – Never expose clientSecret or sessionSecret publicly.
  • 🌐 Production URL – Update callbackURL when deploying to production.
  • 🗄️ Database Required – Level and Giveaway features require MongoDB connection.
  • 🎨 Customizable – Edit EJS templates to customize the look and feel.
  • 🔄 Real-time Stats – Bot statistics update automatically.
  • 🛡️ Secure – Only admins can manage their servers.

💌 Support

We welcome contributions! If you have any suggestions, bug reports, or feature requests, feel free to reach out to us on Discord.

💬 Contact Me: <@679036538511687711>

🌐 Join our Discord:

Discord Banner

Keywords

discord.js

FAQs

Package last updated on 21 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts