npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2026 – Pkg Stats / Ryan Hefner

@hedystia/discord

v2.1.0

Published

A modern, type-safe Discord client library for Bun and Node.js

Readme

@hedystia/discord

A modern, type-safe Discord client library built with TypeScript and designed for Bun/Node.js runtime.

Installation

bun add @hedystia/discord
# or
npm install @hedystia/discord
# or
yarn add @hedystia/discord

Quick Start

import Client, { Intents } from "@hedystia/discord";

const client = new Client({
  token: process.env.DISCORD_TOKEN!,
  intents: Intents.allNonPrivileged,
});

client.on("ready", () => {
  console.log(`Logged in as ${client.me?.username}!`);
});

await client.login();

Table of Contents


Client Configuration

Basic Setup

import Client, { Intents, ActivityType } from "@hedystia/discord";

const client = new Client({
  token: "YOUR_BOT_TOKEN",
  
  // Gateway intents (required for events)
  intents: Intents.guilds | Intents.guildMessages | Intents.messageContent,
  
  // Bot presence
  presence: {
    activities: [
      { name: "your game", type: ActivityType.Playing },
      { name: "music", type: ActivityType.Listening },
    ],
    status: "online", // online, idle, dnd, invisible
  },
  
  // Sharding
  shardsCount: "auto", // or a specific number like 4
  
  // Cache configuration
  cache: {
    guilds: { maxSize: 1000, ttl: 3600000 },
    users: { maxSize: 10000, ttl: 1800000, dynamicTTL: true },
    channels: { maxSize: 5000 },
    members: { maxSize: 50000 },
  },
});

await client.login();

Intents

import { Intents } from "@hedystia/discord";

// Individual intents
Intents.guilds;
Intents.guildMembers;
Intents.guildBans;
Intents.guildEmojisAndStickers;
Intents.guildIntegrations;
Intents.guildWebhooks;
Intents.guildInvites;
Intents.guildVoiceStates;
Intents.guildPresences;
Intents.guildMessages;
Intents.guildMessageReactions;
Intents.guildMessageTyping;
Intents.directMessages;
Intents.directMessageReactions;
Intents.directMessageTyping;
Intents.messageContent;
Intents.guildScheduledEvents;
Intents.autoModerationConfiguration;
Intents.autoModerationExecution;
Intents.guildMessagePolls;
Intents.directMessagePolls;

// Presets
Intents.allNonPrivileged; // All except privileged intents
Intents.allPrivileged; // guildMembers, guildPresences, messageContent
Intents.all; // Every intent

Client Properties

// Managers
client.users;      // UserManager
client.channels;   // ChannelManager
client.guilds;     // GuildManager
client.members;    // MemberManager
client.roles;      // RoleManager
client.emojis;     // EmojiManager
client.stickers;   // StickerManager
client.bans;       // GuildBanManager
client.invites;    // InviteManager
client.webhooks;   // WebhookManager
client.voice;      // VoiceManager

// REST API
client.rest;       // REST client

// Bot info
client.me;         // Current bot user (APIUser)
client.readyAt;    // Date when client became ready
client.uptime;     // Uptime in milliseconds
client.ping;       // WebSocket ping
client.isReady;    // Whether client is ready
client.ws;         // WebSocket options

Events

The client extends Node.js EventEmitter with full TypeScript support.

Core Events

// Ready event
client.on("ready", (data) => {
  console.log(`Logged in as ${client.me?.username}#${client.me?.discriminator}`);
  console.log(`Serving ${client.guilds.cache.size} guilds`);
});

// Resumed event (after reconnection)
client.on("resumed", () => {
  console.log("Session resumed");
});

// Interaction received
client.on("interactionCreate", (interaction) => {
  // Handle interactions (commands, buttons, select menus)
});

Message Events

import type { MessageStructureInstance } from "@hedystia/discord";

client.on("messageCreate", (message: MessageStructureInstance) => {
  if (message.author.bot) return;
  
  if (message.content === "!ping") {
    message.reply("Pong!");
  }
});

client.on("messageUpdate", (oldMessage, newMessage) => {
  console.log("Message edited");
});

client.on("messageDelete", (message) => {
  console.log("Message deleted:", message.content);
});

Guild Events

client.on("guildCreate", (guild) => {
  console.log(`Joined guild: ${guild.name}`);
});

client.on("guildDelete", (guild) => {
  console.log(`Left guild: ${guild.name}`);
});

client.on("guildMemberAdd", (member) => {
  console.log(`New member: ${member.displayName}`);
});

client.on("guildMemberRemove", (member) => {
  console.log(`Member left: ${member.displayName}`);
});

client.on("guildRoleCreate", (role) => {
  console.log(`Role created: ${role.name}`);
});

Channel Events

client.on("channelCreate", (channel) => {
  console.log("Channel created:", channel.name);
});

client.on("channelDelete", (channel) => {
  console.log("Channel deleted:", channel.name);
});

client.on("threadCreate", (thread) => {
  console.log("Thread created:", thread.name);
});

Voice Events

client.on("voiceStateUpdate", (oldState, newState) => {
  console.log("Voice state changed");
});

client.on("voiceServerUpdate", (data) => {
  console.log("Voice server updated");
});

All Available Events

  • ready, resumed
  • interactionCreate
  • messageCreate, messageUpdate, messageDelete, messageDeleteBulk
  • messageReactionAdd, messageReactionRemove, messageReactionRemoveAll, messageReactionRemoveEmoji
  • guildCreate, guildDelete, guildUpdate, guildUnavailable
  • guildMemberAdd, guildMemberRemove, guildMemberUpdate
  • guildBanAdd, guildBanRemove
  • guildRoleCreate, guildRoleDelete, guildRoleUpdate
  • guildEmojisUpdate, guildStickersUpdate
  • guildIntegrationsUpdate, guildScheduledEventCreate, guildScheduledEventDelete, guildScheduledEventUpdate, guildScheduledEventUserAdd, guildScheduledEventUserRemove
  • channelCreate, channelDelete, channelUpdate, channelPinsUpdate
  • threadCreate, threadDelete, threadUpdate, threadListSync, threadMemberUpdate, threadMembersUpdate
  • voiceStateUpdate, voiceServerUpdate, voiceChannelEffectSend
  • presenceUpdate, typingStart
  • webhooksUpdate, integrationCreate, integrationDelete, integrationUpdate
  • inviteCreate, inviteDelete
  • autoModerationActionExecution, autoModerationRuleCreate, autoModerationRuleDelete, autoModerationRuleUpdate
  • entitlementCreate, entitlementDelete, entitlementUpdate
  • subscriptionCreate, subscriptionUpdate
  • stageInstanceCreate, stageInstanceDelete, stageInstanceUpdate
  • guildSoundboardSoundCreate, guildSoundboardSoundDelete, guildSoundboardSoundUpdate, soundboardSound
  • userInstall, passiveSubscriptionUpdate

Slash Commands

Registering Commands

import { SlashCommandBuilder } from "@hedystia/discord";

// Wait for client to be ready
client.on("ready", async () => {
  // Register global commands (takes up to 1 hour to propagate)
  await client.registerCommands([
    new SlashCommandBuilder()
      .setName("ping")
      .setDescription("Replies with Pong!"),
    
    new SlashCommandBuilder()
      .setName("echo")
      .setDescription("Repeats your message")
      .addStringOption((option) =>
        option
          .setName("text")
          .setDescription("The text to repeat")
          .setRequired(true)
          .setMinLength(1)
          .setMaxLength(2000)
      ),
  ]);
  
  // Register guild-specific commands (instant, for testing)
  await client.registerGuildCommands("GUILD_ID", [
    new SlashCommandBuilder()
      .setName("test")
      .setDescription("Test command for this guild"),
  ]);
});

Command Options

new SlashCommandBuilder()
  .setName("command")
  .setDescription("A command with all option types")
  .setDefaultMemberPermissions(0n) // Permission bits (0 = no one)
  .setDMPermission(false)          // Disable in DMs
  .setNSFW(true)                   // Mark as NSFW
  
  // Text options
  .addStringOption((option) =>
    option
      .setName("text")
      .setDescription("Some text")
      .setRequired(true)
      .setMinLength(1)
      .setMaxLength(100)
      .addChoice("Option 1", "value1")
      .addChoice("Option 2", "value2")
  )
  
  // Number options
  .addIntegerOption((option) =>
    option
      .setName("number")
      .setDescription("An integer")
      .setMinValue(0)
      .setMaxValue(100)
  )
  .addNumberOption((option) =>
    option
      .setName("decimal")
      .setDescription("A decimal number")
  )
  
  // Boolean option
  .addBooleanOption((option) =>
    option.setName("toggle").setDescription("Toggle something")
  )
  
  // Discord objects
  .addUserOption((option) =>
    option.setName("user").setDescription("Target user")
  )
  .addChannelOption((option) =>
    option.setName("channel").setDescription("Target channel")
  )
  .addRoleOption((option) =>
    option.setName("role").setDescription("Target role")
  )
  
  // Mentionable (users or roles)
  .addMentionableOption((option) =>
    option.setName("mention").setDescription("Mention someone")
  )
  
  // File attachment
  .addAttachmentOption((option) =>
    option.setName("file").setDescription("Upload a file")
  )
  
  // Subcommands
  .addSubcommand((sub) =>
    sub
      .setName("subcommand")
      .setDescription("A subcommand")
      .addStringOption((opt) => opt.setName("arg").setDescription("Argument"))
  )
  
  // Subcommand groups
  .addSubcommandGroup((group) =>
    group
      .setName("group")
      .setDescription("A group of subcommands")
      .addSubcommand((sub) =>
        sub.setName("nested").setDescription("Nested subcommand")
      )
  );

Handling Interactions

client.on("interactionCreate", async (interaction) => {
  // Application commands (slash commands)
  if (interaction.type === 2) {
    const { name, options } = interaction.data;

    if (name === "ping") {
      // Reply to interaction (initial response)
      await interaction.reply({
        content: "Pong! 🏓",
      });
    }

    if (name === "echo") {
      const text = options?.[0]?.value;
      
      // Defer first (shows "thinking..." state)
      await interaction.defer();
      
      // Then edit the original response
      await interaction.editOriginal({ content: text });
    }
    
    if (name === "secret") {
      // Ephemeral response (only visible to the user)
      await interaction.reply({
        content: "This is secret!",
        flags: 64, // ephemeral flag
      });
    }
  }

  // Component interactions (buttons, select menus)
  if (interaction.type === 3) {
    const { custom_id } = interaction.data;

    if (custom_id === "my_button") {
      // Reply to button click (ephemeral)
      await interaction.reply({
        content: "Button clicked!",
        flags: 64,
      });
    }
  }
  
  // Using other interaction methods
  if (needsFollowUp) {
    // Send additional messages after initial response
    await interaction.sendFollowUp({
      content: "Follow-up message",
      ephemeral: true,
    });
  }
  
  // Delete original response
  await interaction.deleteOriginal();
});

Voice Module

Joining Voice Channels

import { AudioPlayer } from "@hedystia/discord";

// Join a voice channel
const connection = await client.voice.joinVoiceChannel({
  guildId: "GUILD_ID",
  channelId: "VOICE_CHANNEL_ID",
  selfMute: false,
  selfDeaf: false,
  selfVideo: false,
});

console.log("Joined voice channel!");

Playing Audio

// Create audio player
const player = new AudioPlayer();
player.setConnection(connection);

// Play audio file (requires ffmpeg)
player.play("path/to/audio.mp3", {
  volume: 0.5,    // 0.0 to 1.0
  loop: false,    // Loop the audio
  seek: 0,        // Start at seconds
});

// Play from URL
player.play("https://example.com/audio.mp3", {
  volume: 0.8,
});

Player Controls

// Playback control
player.pause();
player.resume();
player.stop();

// Volume control
player.setVolume(0.5);  // 50%
player.getVolume();     // Get current volume

// Seek
player.seek(30);        // Jump to 30 seconds
player.getCurrentTime(); // Get current position

// Events
player.on("start", () => console.log("Started playing"));
player.on("finish", () => console.log("Finished playing"));
player.on("error", (err) => console.error("Player error:", err));
player.on("debug", (info) => console.log("Debug:", info));

Connection Events

connection.on("ready", (data) => {
  console.log("Voice connection ready", data);
});

connection.on("error", (error) => {
  console.error("Voice connection error:", error);
});

connection.on("close", () => {
  console.log("Voice connection closed");
});

connection.on("stateChange", (oldState, newState) => {
  console.log("State changed:", oldState, newState);
});

Leaving Voice Channels

// Leave specific guild
await client.voice.leaveVoiceChannel("GUILD_ID");

// Leave all voice channels
for (const guildId of client.voice.connections.keys()) {
  await client.voice.leaveVoiceChannel(guildId);
}

Builders

Builders are fluent interfaces for constructing Discord objects. They provide method chaining for easy configuration.

EmbedBuilder

import { EmbedBuilder } from "@hedystia/discord";

const embed = new EmbedBuilder()
  .setTitle("My Embed")
  .setDescription("This is the description")
  .setURL("https://example.com")
  .setColor("#FF0000")      // Hex or number: 0xFF0000
  .setTimestamp()            // Current time
  .setFooter({
    text: "Footer text",
    iconURL: "https://example.com/icon.png",
  })
  .setThumbnail("https://example.com/thumbnail.png")
  .setImage("https://example.com/image.png")
  .setAuthor({
    name: "Author Name",
    url: "https://example.com",
    iconURL: "https://example.com/author.png",
  })
  .addField({
    name: "Field 1",
    value: "Field value",
    inline: true,
  })
  .addFields(
    { name: "Field 2", value: "Value 2" },
    { name: "Field 3", value: "Value 3", inline: true }
  );

// Send with a channel (assuming you have a channel object)
channel.send({
  content: "Check this out!",
  embeds: [embed],
});

// Or with interaction response
interaction.reply({
  embeds: [embed],
});

ButtonBuilder

import { ButtonBuilder, ButtonStyle } from "@hedystia/discord";

// Primary button
const primary = new ButtonBuilder()
  .setLabel("Click Me")
  .setCustomId("my_button")
  .setStyle(ButtonStyle.Primary);

// Preset styles
ButtonBuilder.primary()
  .setLabel("Primary")
  .setCustomId("primary");

ButtonBuilder.secondary()
  .setLabel("Secondary")
  .setCustomId("secondary");

ButtonBuilder.success()
  .setLabel("Success")
  .setCustomId("success");

ButtonBuilder.danger()
  .setLabel("Danger")
  .setCustomId("danger");

// Link button (opens URL)
ButtonBuilder.link()
  .setLabel("Visit Site")
  .setURL("https://example.com");

// With emoji
button.setEmoji("👍");
// or
button.setEmoji({ id: "emoji_id", name: "emoji", animated: false });

// Disabled state
button.setDisabled(true);

ActionRowBuilder

import { ActionRowBuilder } from "@hedystia/discord";

const row = new ActionRowBuilder()
  .addComponents(
    ButtonBuilder.primary().setLabel("Button 1").setCustomId("btn1"),
    ButtonBuilder.secondary().setLabel("Button 2").setCustomId("btn2")
  );

// Or set all components at once
row.setComponents(button1, button2, button3);

// Send with message (builders are auto-converted)
channel.send({
  content: "Click a button!",
  components: [row],
});

Select Menu Builders

import {
  ChannelSelectBuilder,
  MentionableSelectBuilder,
  RoleSelectBuilder,
  UserSelectBuilder,
} from "@hedystia/discord";

// Channel select
const channelSelect = new ChannelSelectBuilder()
  .setCustomId("channel_select")
  .setPlaceholder("Select a channel")
  .setMaxValues(3)
  .setMinValues(1);

// User select
const userSelect = new UserSelectBuilder()
  .setCustomId("user_select")
  .setPlaceholder("Select a user")
  .setMaxValues(5);

// Role select
const roleSelect = new RoleSelectBuilder()
  .setCustomId("role_select")
  .setPlaceholder("Select a role");

// Mentionable select (users or roles)
const mentionableSelect = new MentionableSelectBuilder()
  .setCustomId("mentionable_select")
  .setPlaceholder("Select someone");

Other Component Builders

import {
  ContainerBuilder,
  SectionBuilder,
  SeparatorBuilder,
  TextDisplayBuilder,
  MediaGalleryBuilder,
  ThumbnailBuilder,
} from "@hedystia/discord";

// Text display (markdown)
const textDisplay = new TextDisplayBuilder()
  .setContent("# Hello\nThis is **markdown** content");

// Separator
const separator = new SeparatorBuilder()
  .setSpacing(1); // or 2, 3

// Section with components
const section = new SectionBuilder()
  .addComponents(textDisplay, button);

// Container for grouping
const container = new ContainerBuilder()
  .addComponents(section, separator);

// Media gallery
const gallery = new MediaGalleryBuilder()
  .addItems({
    url: "https://example.com/image.jpg",
    description: "Image description",
  });

// Thumbnail
const thumbnail = new ThumbnailBuilder()
  .setURL("https://example.com/thumb.jpg")
  .setHeight(100)
  .setWidth(100);

Collectors

Collectors allow you to collect and handle multiple events over a period of time.

MessageCollector

import { MessageCollector } from "@hedystia/discord";

const collector = new MessageCollector(client, {
  time: 60000,           // 1 minute
  max: 10,               // Max 10 messages
  maxProcessing: 5,      // Max 5 per second
  filter: (msg) => msg.author.id === "USER_ID",
  channelId: "CHANNEL_ID",
  guildId: "GUILD_ID",
  userId: "USER_ID",
  dispose: true,         // Remove from cache on dispose
  resetTimer: true,      // Reset timer on each collect
});

collector.on("collect", (message) => {
  console.log("Collected:", message.content);
});

collector.on("ignore", (message) => {
  console.log("Ignored:", message.content);
});

collector.on("dispose", (message) => {
  console.log("Disposed:", message.content);
});

collector.on("end", (collected, reason) => {
  console.log(`Ended: ${reason}`);
  console.log(`Collected ${collected.size} messages`);
  
  // Iterate over collected messages
  for (const [id, msg] of collected.entries()) {
    console.log(id, msg.content);
  }
});

// Stop manually
collector.stop("user");

InteractionCollector (Buttons & Select Menus)

import { InteractionCollector } from "@hedystia/discord";

// Button collector
const buttonCollector = InteractionCollector.createButtonCollector(
  client,
  "my_button_id",  // Custom ID to filter
  {
    time: 30000,
    max: 5,
    channelId: "CHANNEL_ID",
    guildId: "GUILD_ID",
    userId: "USER_ID",
  }
);

buttonCollector.on("collect", async (interaction) => {
  console.log("Button clicked by:", interaction.user?.id);

  // Acknowledge interaction
  await interaction.reply({
    content: "Button clicked!",
  });
});

// Select menu collector
const selectCollector = InteractionCollector.createSelectMenuCollector(
  client,
  "select_id",
  { time: 60000 }
);

selectCollector.on("collect", (interaction) => {
  const values = interaction.data.values;
  console.log("Selected:", values);
});

// Message component collector (all interactions on a message)
const messageCollector = InteractionCollector.createMessageCollector(
  client,
  "MESSAGE_ID",
  { time: 120000 }
);

ReactionCollector

import { ReactionCollector } from "@hedystia/discord";

const collector = new ReactionCollector(client, {
  messageId: "MESSAGE_ID",
  channelId: "CHANNEL_ID",
  guildId: "GUILD_ID",
  userId: "USER_ID",
  emoji: "👍",           // Filter by emoji
  maxReactions: 10,      // Max total reactions
  time: 60000,
});

collector.on("collect", (reaction, userId) => {
  console.log(`Reaction ${reaction.emoji.name} by ${userId}`);
});

collector.on("end", (collected, reason) => {
  console.log(`Total unique reactions: ${collector.uniqueReactions}`);
  console.log(`Total reactions: ${collector.totalReactions}`);
});

// Static helpers
ReactionCollector.createMessageCollector(client, "MESSAGE_ID");
ReactionCollector.createEmojiCollector(client, "👍");

Collector Options Reference

| Option | Type | Description | |--------|------|-------------| | time | number | Time in milliseconds before stopping | | max | number | Maximum number of items to collect | | maxProcessing | number | Maximum items per second | | filter | function | Filter function (item, ...args) => boolean | | dispose | boolean | Whether to dispose items when removed | | resetTimer | boolean | Whether to reset timer on each collect | | messageId | string | Filter by message ID | | channelId | string | Filter by channel ID | | guildId | string | Filter by guild ID | | userId | string | Filter by user ID | | emoji | string | Filter by emoji (ReactionCollector) | | customId | string | Filter by custom ID (InteractionCollector) | | componentType | number | Filter by component type |


Managers

Managers handle caching and fetching of Discord objects.

UserManager

// Fetch a user
const user = await client.users.fetch("USER_ID");

// Access cache
const cached = client.users.cache.get("USER_ID");

// Iterate cache
for (const [id, user] of client.users.cache.entries()) {
  console.log(id, user.username);
}

GuildManager

// Fetch a guild
const guild = await client.guilds.fetch("GUILD_ID");

// Access cache
const cached = client.guilds.cache.get("GUILD_ID");

// Get all guilds
const allGuilds = await client.guilds.fetchAll();

ChannelManager

// Fetch a channel
const channel = await client.channels.fetch("CHANNEL_ID");

// Access cache
const cached = client.channels.cache.get("CHANNEL_ID");

MemberManager

// Fetch a member
const member = await client.members.fetch("GUILD_ID", "USER_ID");

// Access cache
const cached = client.members.cache.get("USER_ID");

// Get all members in a guild
const guildMembers = await client.members.fetchGuildMembers("GUILD_ID");

RoleManager

// Fetch a role
const role = await client.roles.fetch("GUILD_ID", "ROLE_ID");

// Access cache
const cached = client.roles.cache.get("ROLE_ID");

EmojiManager

// Fetch all emojis from a guild
const emojis = await client.emojis.fetch("GUILD_ID");

// Fetch single emoji
const emoji = await client.emojis.fetchOne("GUILD_ID", "EMOJI_ID");

// Access cache
const cached = client.emojis.cache.get("EMOJI_ID");

Cache Configuration

const client = new Client({
  cache: {
    managers: {
      users: {
        enabled: true,
        maxSize: 10000,
        ttl: 1800000,        // 30 minutes
        dynamicTTL: true,    // Extend TTL on access
      },
      guilds: {
        enabled: true,
        maxSize: 1000,
      },
      channels: {
        enabled: true,
        maxSize: 5000,
      },
    },
  },
});

Structures

Structures are rich wrappers around Discord API data.

InteractionStructure

// Properties
interaction.id;                 // Interaction ID
interaction.type;               // Interaction type
interaction.token;              // Interaction token
interaction.data;               // Interaction data
interaction.message;            // Message (for component interactions)
interaction.user;               // User who triggered
interaction.member;             // Member (in guilds)
interaction.customId;           // Custom ID (for components)
interaction.userId;             // User ID who triggered
interaction.isButton;           // Is from a button
interaction.isSelectMenu;       // Is from a select menu
interaction.isMessageComponent; // Is a message component

// Methods - Response handling
await interaction.reply({               // Initial response
  content: "Hello!",
  embeds: [embed],
  components: [row],
  flags: 64,            // ephemeral (optional)
});

await interaction.defer(ephemeral);     // "thinking..." state
await interaction.editOriginal({        // Edit original response
  content: "Edited!",
  embeds: [newEmbed],
});
await interaction.deleteOriginal();     // Delete original response
await interaction.sendFollowUp({        // Additional message
  content: "Follow-up",
  ephemeral: true,
});

// Methods - Collectors
interaction.createMessageComponentCollector(options);
interaction.createButtonCollector(customId, options);
interaction.createSelectMenuCollector(customId, options);

MessageStructure

// Properties
message.mention;                    // Message URL
message.author;                     // Author info
message.isFromCurrentUser;          // Boolean
message.isCrossposted;              // Boolean
message.isAutomaticModerationAction;
message.createdTimestamp;           // Unix timestamp
message.createdAt;                  // Date object
message.editedTimestamp;
message.editedAt;
message.isEditable;
message.isDeletable;
message.isPinnable;
message.hasEmbeds;
message.hasAttachments;
message.hasReactions;
message.mentionsEveryone;

// Check mentions
message.mentionsRole("ROLE_ID");
message.mentionsUser("USER_ID");
message.mentionsChannel("CHANNEL_ID");

// Actions
await message.react("👍");
await message.reply("Reply content");
await message.edit("New content");
await message.delete();
await message.pin();
await message.unpin();
await message.fetch();              // Refetch from API
await message.crosspost();          // For news channels
await message.startThread("Name", 1440); // 1440 = 24h

// Create collectors on message
message.createReactionCollector({ time: 60000 });
message.createButtonCollector("button_id", { time: 30000 });
message.createSelectMenuCollector("select_id");
message.createMessageComponentCollector({ time: 60000 });

GuildStructure

// URLs
guild.iconURL({ size: 1024, extension: "png" });
guild.bannerURL({ size: 1024 });
guild.splashURL();
guild.discoverySplashURL();

// Timestamps
guild.createdTimestamp;
guild.createdAt;

// Cache access
guild.members;      // Members array from cache
guild.channels;     // Channels array from cache
guild.roles;        // Roles array from cache

// Properties
guild.isOwner;      // Is current user the owner?
guild.available;    // Is guild available?
guild.isLarge;      // Is guild large (>250 members)?

// Comparison
guild.equals(otherGuild);

MemberStructure

// Mentions
member.mention;             // <@user_id>
member.nicknameMention;     // <@!user_id> or null

// Display name
member.displayName;         // Nickname or username

// Avatar
member.avatarURL({ size: 1024, extension: "png" });
member.bannerURL({ size: 1024 });

// Timestamps
member.joinedTimestamp;
member.joinedAt;
member.premiumSinceTimestamp;
member.premiumSince;
member.communicationDisabledUntilTimestamp;
member.communicationDisabledUntil;

// Properties
member.isCommunicationDisabled;
member.isPremium;           // Is boosting
member.isPending;           // Pending verification

// Role checks
member.hasRole("ROLE_ID");
member.hasAnyRole("ROLE1", "ROLE2");
member.hasAllRoles("ROLE1", "ROLE2");

RoleStructure

// Mention
role.mention;               // <@&role_id> or @everyone

// Color
role.hexColor;              // #RRGGBB

// Timestamp
role.createdTimestamp;
role.createdAt;

// Properties
role.isEveryone;            // Is @everyone
role.isBotRole;             // Has bot_id tag
role.isIntegrationRole;     // Has integration_id tag
role.isPremiumSubscriberRole;
role.isAvailableForPurchase;
role.isGuildLinkedRole;

UserStructure

// Mention
user.mention;               // <@user_id>

// Avatar
user.avatarURL({ size: 1024, extension: "png", forceStatic: false });
user.defaultAvatarURL;      // Default Discord avatar

// Banner
user.bannerURL({ size: 1024 });

// Properties
user.isBot;
user.isSystem;
user.displayName;           // Global name or username

// Timestamps
user.createdTimestamp;
user.createdAt;

REST API

Note: For most use cases, prefer using the high-level methods from Structures (like channel.send(), message.edit(), interaction.reply()). Use the REST API directly only when you need low-level control or when a method isn't available in the structures.

Making Requests

import REST from "@hedystia/discord";

const rest = new REST({
  token: "Bot YOUR_TOKEN",
  version: 10,
  restRequestTimeout: 10000,
});

// GET request
const user = await rest.get("/users/@me");

// POST request
const message = await rest.post("/channels/CHANNEL_ID/messages", {
  body: {
    content: "Hello!",
    embeds: [embed],
  },
});

// PUT request (update)
await rest.put("/channels/CHANNEL_ID/messages/MSG_ID", {
  body: { content: "Updated content" },
});

// PATCH request (partial update)
await rest.patch("/users/@me", {
  body: { username: "New Name" },
});

// DELETE request
await rest.delete("/channels/CHANNEL_ID/messages/MSG_ID");

Request Options

await rest.get("/users/USER_ID", {
  headers: {
    "X-Custom-Header": "value",
  },
  query: {
    with_counts: "true",
  },
  reason: "Audit log reason",
  auth: true,  // Include auth header
});

Routes Helper

import { Routes } from "@hedystia/discord";

// User routes
Routes.user(userId);
Routes.channel(channelId);

// Guild routes
Routes.guild(guildId);
Routes.guildChannels(guildId);
Routes.guildMembers(guildId);
Routes.guildMember(guildId, userId);
Routes.guildRoles(guildId);
Routes.guildRole(guildId, roleId);
Routes.guildBans(guildId);
Routes.guildBan(guildId, userId);
Routes.guildEmojis(guildId);
Routes.guildEmoji(guildId, emojiId);

// Command routes
Routes.applicationCommands(applicationId);
Routes.applicationCommand(applicationId, commandId);
Routes.applicationGuildCommands(applicationId, guildId);
Routes.applicationGuildCommand(applicationId, guildId, commandId);

// Interaction routes
Routes.interactionCallback(interactionId, token);

// Other routes
Routes.gatewayBot();
Routes.webhook(webhookId, token);
Routes.webhookMessage(webhookId, token, messageId);

Rate Limit Handling

// Automatic rate limit handling
const info = rest.getRateLimitInfo("GET", "/channels/CHANNEL_ID/messages");
console.log(info.remaining, info.resetAfter);

// Check if rate limited
const isLimited = rest.isRateLimited("POST", "/channels/CHANNEL_ID/messages");
const timeUntilReset = rest.getTimeUntilReset("GET", "/users/@me");

CDN Helper

import { CDN, ImageFormat } from "@hedystia/discord";

// Avatar
const avatarUrl = CDN.avatar(userId, hash, {
  size: 1024,
  format: ImageFormat.PNG,
});

// Guild icons
const iconUrl = CDN.guildIcon(guildId, hash);
const bannerUrl = CDN.guildBanner(guildId, hash);
const splashUrl = CDN.splash(guildId, hash);

// Emoji
const emojiUrl = CDN.emoji(emojiId, { animated: true });

// App icons
const appIconUrl = CDN.appIcon(applicationId, assetId);
const appCoverImageUrl = CDN.appCoverImage(applicationId, assetId);

// All URLs include https://cdn.discordapp.com prefix

Utilities

Cache

import { Cache } from "@hedystia/discord";

const cache = new Cache({
  maxSize: 100,
  ttl: 300000,           // 5 minutes
  dynamicTTL: true,      // Extend on access
  cleanupInterval: 60000,
});

// Basic operations
cache.set("key", "value");
cache.get("key");
cache.has("key");
cache.delete("key");
cache.clear();

// With per-item options
cache.set("key", "value", {
  ttl: 60000,           // Override TTL
  keepForever: true,    // Never expire
});

// Stats
const stats = cache.getStats();
console.log(`Hit ratio: ${(stats.hitRatio * 100).toFixed(2)}%`);
console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}`);

Collection

import { Collection } from "@hedystia/discord";

const collection = new Collection();

// Map-like operations
collection.set("key", "value");
collection.get("key");
collection.has("key");
collection.delete("key");
collection.clear();
collection.size;

// Array-like methods
collection.first();           // First value
collection.first(5);          // First 5 values
collection.last();            // Last value
collection.at(0);             // Value at index
collection.find((v, k) => v === "value");
collection.filter((v, k) => k.startsWith("prefix"));
collection.map((v, k) => v.toUpperCase());
collection.some((v, k) => v === "test");
collection.every((v, k) => v !== null);
collection.forEach((v, k) => console.log(k, v));

// Reduce
const sum = collection.reduce((acc, val) => acc + val, 0);

// Sorting
collection.sort((a, b) => a - b);
const sorted = collection.sorted((a, b) => a - b);

// Utilities
collection.clone();
collection.random();
collection.randomKey();
collection.equals(otherCollection);
collection.isEmpty();

// Set operations
const union = collection.union(other);
const intersection = collection.intersection(other);
const difference = collection.difference(other);
const symmetricDiff = collection.symmetricDifference(other);

// Math
collection.sum();
collection.average();
collection.min();
collection.max();

Complete Example

import Client, {
  Intents,
  EmbedBuilder,
  ButtonBuilder,
  ActionRowBuilder,
  SlashCommandBuilder,
  ActivityType,
} from "@hedystia/discord";

const client = new Client({
  token: process.env.DISCORD_TOKEN!,
  intents: Intents.guilds | Intents.guildMessages | Intents.messageContent,
  presence: {
    activities: [{ name: "Discord", type: ActivityType.Playing }],
    status: "online",
  },
});

client.on("ready", async () => {
  console.log(`✅ Logged in as ${client.me?.username}`);

  // Register commands
  await client.registerCommands([
    new SlashCommandBuilder()
      .setName("ping")
      .setDescription("Check bot latency"),
  ]);
});

client.on("interactionCreate", async (interaction) => {
  if (interaction.type !== 2) return; // Not a command
  
  if (interaction.data.name === "ping") {
    const embed = new EmbedBuilder()
      .setTitle("🏓 Pong!")
      .setDescription(`Latency: ${client.ping}ms`)
      .setColor(0x00FF00)
      .setTimestamp();
    
    const button = ButtonBuilder.primary()
      .setLabel("Refresh")
      .setCustomId("refresh_ping");
    
    const row = new ActionRowBuilder().addComponents(button);
    
    // Reply to interaction using InteractionStructure methods
    await interaction.reply({
      embeds: [embed],
      components: [row],
    });
    
    // Handle button clicks
    const collector = interaction.createButtonCollector("refresh_ping", { 
      time: 60000 
    });
    
    collector.on("collect", async (btnInteraction) => {
      embed.setDescription(`Latency: ${client.ping}ms (refreshed)`);
      
      // Edit the original response
      await btnInteraction.editOriginal({
        embeds: [embed],
      });
      
      // Acknowledge the button click (ephemeral)
      await btnInteraction.defer(true);
    });
  }
});

client.on("messageCreate", (message) => {
  if (message.author.bot) return;
  
  if (message.content === "!hello") {
    message.reply("Hello there! 👋");
  }
});

await client.login();

License

MIT