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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@axrxvm/bdjs

v0.0.4

Published

The ultimate Discord.js wrapper - makes Discord bot development effortless

Readme

BetterDiscordJS

The ultimate Discord.js wrapper that makes Discord bot development effortless for beginners and infinitely powerful for experts.

npm version Downloads License Discord

✨ Features

  • Zero Configuration to Start: Working bot in 5 lines of code
  • Automatic Everything: Intent detection, command registration, interaction routing
  • Universal Compatibility: Single-file, modular, TS, JS, ESM, CommonJS - all supported
  • Unified Context System: Single interface for all interactions (slash commands, buttons, modals, etc.)
  • Advanced Command System: Slash commands, prefix commands, context menus, subcommands
  • Built-in Middleware: Permissions, cooldowns, rate limiting, validation
  • Plugin Ecosystem: Database, logging, analytics, i18n, dashboard
  • Production Ready: Clustering, sharding, health monitoring, graceful shutdown
  • Developer Friendly: Hot reload, debug mode, TypeScript support

🚀 Quick Start

Create a New Bot (Recommended)

Use our interactive CLI to create a fully configured bot in seconds:

npx @axrxvm/bdjs create my-bot

Choose from:

  • JavaScript or TypeScript
  • Minimal, Basic, or Advanced templates
  • npm, yarn, or pnpm

See CLI Documentation | View Examples

Manual Installation

npm install @axrxvm/bdjs discord.js

Your First Bot

import { BetterBot } from '@axrxvm/bdjs';

const bot = new BetterBot({
  token: 'YOUR_BOT_TOKEN'
});

bot.command('ping', (ctx) => {
  ctx.reply(`Pong! ${ctx.bot.ping}ms`);
});

bot.start();

That's it! You now have a working Discord bot with:

  • ✅ Automatic intent detection
  • ✅ Slash command support
  • ✅ Error handling
  • ✅ Rate limiting
  • ✅ And much more!

📚 Documentation

Creating Commands

Simple Command

bot.command('hello', (ctx) => {
  ctx.reply(`Hello, ${ctx.user.username}!`);
});

Advanced Command with Options

bot.command('ban', {
  description: 'Ban a member from the server',
  slash: true,
  permissions: ['BAN_MEMBERS'],
  guildOnly: true,
  
  options: [
    {
      name: 'user',
      type: 'USER',
      description: 'User to ban',
      required: true
    },
    {
      name: 'reason',
      type: 'STRING',
      description: 'Reason for ban'
    }
  ],
  
  execute: async (ctx) => {
    const user = ctx.get('user');
    const reason = ctx.get('reason') || 'No reason provided';
    
    await ctx.guild.members.ban(user, { reason });
    await ctx.reply(`Banned ${user.tag} for: ${reason}`);
  }
});

Command with Subcommands

bot.command('config', {
  description: 'Configure bot settings',
  slash: true,
  
  subcommands: {
    prefix: {
      description: 'Change bot prefix',
      options: [
        { name: 'new_prefix', type: 'STRING', required: true }
      ],
      execute: async (ctx) => {
        const prefix = ctx.get('new_prefix');
        await ctx.reply(`Prefix changed to: ${prefix}`);
      }
    },
    
    logs: {
      description: 'Set log channel',
      options: [
        { name: 'channel', type: 'CHANNEL', required: true }
      ],
      execute: async (ctx) => {
        const channel = ctx.get('channel');
        await ctx.reply(`Logs will be sent to ${channel}`);
      }
    }
  }
});

Events

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

bot.on('guildMemberAdd', (member) => {
  member.guild.systemChannel?.send(`Welcome ${member}!`);
});

bot.on('messageCreate', async (message) => {
  if (message.author.bot) return;
  // Handle message...
});

Components

Buttons

bot.button('delete', async (ctx) => {
  await ctx.message.delete();
  await ctx.reply({ content: 'Deleted!', ephemeral: true });
});

// Dynamic buttons with parameters
bot.button('role_{roleId}', async (ctx) => {
  const roleId = ctx.params.roleId;
  await ctx.member.roles.add(roleId);
  await ctx.reply({ content: 'Role added!', ephemeral: true });
});

Select Menus

bot.selectMenu('color', async (ctx) => {
  const color = ctx.values[0];
  await ctx.reply(`You selected ${color}!`);
});

Modals

bot.modal('feedback', async (ctx) => {
  const rating = ctx.fields.rating;
  const comments = ctx.fields.comments;
  await ctx.reply('Thanks for your feedback!');
});

Builders

Embeds

import { EmbedBuilder } from '@axrxvm/bdjs';

const embed = new EmbedBuilder()
  .setTitle('Hello World')
  .setDescription('This is an embed')
  .setColor('BLUE')
  .addField('Field 1', 'Value 1')
  .setFooter({ text: 'Footer text' })
  .setTimestamp();

await ctx.reply({ embeds: [embed] });

Buttons

import { ButtonBuilder, ActionRow } from '@axrxvm/bdjs';

const button = new ButtonBuilder()
  .setCustomId('my-button')
  .setLabel('Click Me')
  .setStyle('PRIMARY')
  .setEmoji('👍');

const row = new ActionRow().addComponents(button);

await ctx.reply({
  content: 'Click the button!',
  components: [row]
});

Middleware

import { permissions, cooldown } from '@axrxvm/bdjs/middleware';

// Global middleware
bot.use(permissions({
  user: ['MANAGE_MESSAGES'],
  message: 'You need Manage Messages permission!'
}));

bot.use(cooldown({
  duration: 5000,
  message: 'Please wait {time} before using this again!'
}));

// Command-specific middleware
bot.useCommand('ban', async (ctx, next) => {
  console.log(`${ctx.user.tag} used ban command`);
  await next();
});

Plugins

import { DatabasePlugin, LoggerPlugin } from '@axrxvm/bdjs/plugins';

// Database plugin
bot.plugin(new DatabasePlugin({
  adapter: 'sqlite',
  connection: './bot.db',
  models: {
    User: {
      id: 'string',
      balance: 'number',
      xp: 'number'
    }
  }
}));

// Logger plugin
bot.plugin(new LoggerPlugin({
  level: 'info',
  transports: ['console', 'file']
}));

// Use plugins
await bot.db.users.create({ id: '123', balance: 1000 });
bot.logger.info('Bot started');

File-Based Loading

const bot = new BetterBot({
  token: process.env.TOKEN,
  commands: './commands',
  events: './events',
  components: './components'
});

// Automatically loads all files from directories
// commands/
//   ├── general/
//   │   ├── ping.js
//   │   └── help.js
//   └── moderation/
//       ├── ban.js
//       └── kick.js

Context System

The Context object is the universal interface for all interactions:

bot.command('example', async (ctx) => {
  // Universal properties
  ctx.user              // User who triggered
  ctx.guild             // Guild (null in DMs)
  ctx.channel           // Channel
  ctx.bot               // BetterBot instance
  
  // Command-specific
  ctx.command           // Command name
  ctx.args              // Prefix command args
  ctx.options           // Slash command options
  
  // Component-specific
  ctx.customId          // Component custom ID
  ctx.params            // Parsed params from customId
  ctx.values            // Select menu values
  
  // Universal methods
  await ctx.reply('Hello!')
  await ctx.send('Message')
  await ctx.edit('Updated')
  await ctx.defer()
  await ctx.followUp('Follow-up')
  
  // Type-safe getters
  const user = ctx.get('user')
  const text = ctx.getString('text')
  const num = ctx.getInteger('number')
  
  // Permission checks
  ctx.userHasPermission('ADMINISTRATOR')
  ctx.botHasPermission('SEND_MESSAGES')
});

🔧 Configuration

const bot = new BetterBot({
  // Authentication
  token: process.env.DISCORD_TOKEN,
  
  // Client Options
  intents: ['GUILDS', 'GUILD_MESSAGES', 'MESSAGE_CONTENT'],
  partials: ['MESSAGE', 'CHANNEL', 'REACTION'],
  
  // Command System
  prefix: '!',
  mentionPrefix: true,
  caseSensitive: false,
  
  // Slash Commands
  slashCommands: {
    global: true,
    autoSync: true,
    autoDelete: false
  },
  
  // File Loading
  commands: './commands',
  events: './events',
  components: './components',
  
  // Behavior
  owners: ['YOUR_USER_ID'],
  dev: false,
  debug: false,
  
  // Error Handling
  handleErrors: true,
  errorChannel: 'CHANNEL_ID'
});

🌟 Advanced Features

Pagination

import { EmbedPaginator } from '@axrxvm/bdjs';

const pages = [
  new EmbedBuilder().setTitle('Page 1'),
  new EmbedBuilder().setTitle('Page 2'),
  new EmbedBuilder().setTitle('Page 3')
];

const paginator = new EmbedPaginator(ctx, pages, {
  time: 300000,
  filter: (i) => i.user.id === ctx.user.id
});

await paginator.start();

Menus

import { ButtonMenu } from '@axrxvm/bdjs';

const menu = new ButtonMenu(ctx, {
  title: 'Choose an action',
  buttons: [
    {
      label: 'Option 1',
      customId: 'opt1',
      style: 'PRIMARY',
      handler: async (i) => {
        await i.reply('You chose option 1!');
      }
    }
  ]
});

await menu.start();

Collectors

// Message collector
const collected = await ctx.awaitMessages({
  filter: m => m.author.id === ctx.user.id,
  max: 1,
  time: 60000
});

// Reaction collector
const reactions = await message.awaitReactions({
  filter: (r, u) => r.emoji.name === '👍',
  max: 10,
  time: 60000
});

// Component collector
const interaction = await message.awaitMessageComponent({
  filter: i => i.customId === 'my-button',
  time: 60000
});

Production Features

// Sharding
const bot = new BetterBot({
  shards: 'auto',
  shardCount: 'auto',
  token: process.env.TOKEN
});

// Clustering
import { ClusterManager } from '@axrxvm/bdjs';

const manager = new ClusterManager('./bot.js', {
  totalShards: 'auto',
  totalClusters: 4,
  token: process.env.TOKEN
});

await manager.spawn();

// Health Monitoring
import { HealthMonitor } from '@axrxvm/bdjs';

const monitor = new HealthMonitor(bot, {
  port: 3000,
  checks: {
    websocket: true,
    database: true,
    memory: true
  }
});

await monitor.start();

📖 Examples

Check out the examples directory for complete bot templates:

🟢 Minimal Examples

🔵 Basic Examples

  • JavaScript - Feature-rich bot with commands, buttons, embeds
  • TypeScript - Basic bot with full TypeScript support

🔴 Advanced Examples

  • JavaScript - Production-ready with database, logging, middleware
  • TypeScript - Advanced bot with type safety

📚 View all examples →

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide first.

📝 License

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

🔗 Links

❤️ Support

If you like this project, please give it a ⭐️ on GitHub!


Made with ❤️ by the BetterDiscordJS team