Skip to content

Context (Ctx) Object

The Context object (Ctx) is the cornerstone of @axrxvm/betterdiscordjs. It provides a unified interface for interacting with Discord, whether you're handling messages, slash commands, or other interactions.

Overview

The Ctx object wraps Discord.js objects and provides additional functionality:

  • Unified API: Same methods work for messages and interactions
  • Enhanced Utilities: Built-in embed builder, component helpers, and more
  • Simplified Interactions: Easy-to-use methods for common Discord operations
  • Type Safety: Consistent behavior across different Discord event types

Constructor

const ctx = new Ctx(raw, bot, argsOverride);

Parameters: - raw: Discord.js Message or Interaction object - bot: @axrxvm/betterdiscordjs Bot instance - argsOverride: Optional array to override parsed arguments

Properties

Core Properties

ctx.raw             // Original Discord.js object (Message/Interaction)
ctx.bot             // @axrxvm/betterdiscordjs Bot instance
ctx.client          // Discord.js Client instance
ctx.user            // User who triggered the command
ctx.guild           // Guild where command was used (null for DMs)
ctx.channel         // Channel where command was used
ctx.member          // Guild member (null for DMs)

Context Information

ctx.isInteraction   // true if triggered by slash command
ctx.isDM            // true if used in direct message
ctx.isGuild         // true if used in a guild
ctx.args            // Array of command arguments
ctx.options         // Slash command options (interactions only)

Basic Methods

reply(content, options)

Send a reply to the command:

// Simple text reply
await ctx.reply('Hello, World!');

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

// Reply with components
await ctx.reply({
  content: 'Choose an option:',
  components: [buttonRow]
});

// Ephemeral reply (slash commands only)
await ctx.reply({
  content: 'Only you can see this!',
  ephemeral: true
});

defer()

Defer the reply (useful for long-running commands):

await ctx.defer(); // Shows "Bot is thinking..."

// Do some long operation
await someAsyncOperation();

// Send follow-up
await ctx.followUp('Operation completed!');

followUp(content)

Send a follow-up message (after defer or initial reply):

await ctx.reply('Initial response');
await ctx.followUp('Additional information');

Embed Builder

embed(content)

Create and send embeds easily:

// Quick embed
await ctx.embed('This is a simple embed');

// Advanced embed builder
const embed = ctx.embed()
  .title('🎉 Welcome!')
  .desc('Welcome to our Discord server!')
  .field('Members', guild.memberCount.toString(), true)
  .field('Channels', guild.channels.cache.size.toString(), true)
  .color('green')
  .thumbnail(guild.iconURL())
  .timestamp();

await embed.send();

Embed Builder Methods

const embed = ctx.embed()
  .title('Title')                    // Set title
  .desc('Description')               // Set description
  .field('Name', 'Value', true)      // Add field (inline optional)
  .author('Author', 'icon_url')      // Set author
  .footer('Footer', 'icon_url')      // Set footer
  .thumbnail('url')                  // Set thumbnail
  .image('url')                      // Set image
  .color('blue')                     // Set color (named or hex)
  .timestamp()                       // Add timestamp
  .send();                           // Send the embed

Color Options

// Named colors
.color('blue')      // Discord blue
.color('green')     // Success green
.color('red')       // Error red
.color('yellow')    // Warning yellow
.color('purple')    // Purple
.color('orange')    // Orange
.color('gold')      // Gold
.color('random')    // Random color

// Hex colors
.color('#FF5733')
.color(0xFF5733)

Quick Response Methods

success(message)

Send a success message:

await ctx.success('Operation completed successfully!');

error(message)

Send an error message:

await ctx.error('Something went wrong!');

info(message)

Send an info message:

await ctx.info('Here is some information.');

warn(message)

Send a warning message:

await ctx.warn('This action cannot be undone!');

Slash Command Helpers

getOption(name)

Get a slash command option value:

// For slash command: /ban user:@someone reason:"spam"
const user = ctx.getOption('user');
const reason = ctx.getOption('reason') || 'No reason provided';

getUser(name), getMember(name), getChannel(name), getRole(name)

Get specific option types:

const targetUser = ctx.getUser('user');
const targetMember = ctx.getMember('member');
const targetChannel = ctx.getChannel('channel');
const targetRole = ctx.getRole('role');

Interactive Components

button(label, options, handler)

Create a button:

const button = ctx.button('Click Me!', {
  style: 'primary',
  customId: 'my_button'
});

buttonRow(buttons)

Create a row of buttons:

const row = ctx.buttonRow([
  { customId: 'yes', label: '✅ Yes', style: 3 },
  { customId: 'no', label: '❌ No', style: 4 }
]);

const msg = await ctx.reply({
  content: 'Do you agree?',
  components: [row]
});

awaitButton(message, handlers, options)

Wait for button interactions:

const msg = await ctx.reply({
  content: 'Choose an option:',
  components: [buttonRow]
});

await ctx.awaitButton(msg, {
  yes: async (interaction) => {
    await interaction.reply('You chose yes!');
  },
  no: async (interaction) => {
    await interaction.reply('You chose no!');
  }
}, { time: 30000 });

Create a select menu:

const menu = ctx.menu(['Option 1', 'Option 2', 'Option 3']);

await ctx.reply({
  content: 'Choose an option:',
  components: [menu]
});

Modals and Forms

modal(fields, options)

Show a modal form:

const result = await ctx.modal([
  {
    customId: 'name',
    label: 'Your Name',
    style: 1, // Short text
    required: true
  },
  {
    customId: 'feedback',
    label: 'Feedback',
    style: 2, // Paragraph
    required: false
  }
], {
  title: 'Feedback Form',
  timeout: 60000
});

if (result) {
  await ctx.reply(`Thank you, ${result.name}! Your feedback: ${result.feedback}`);
}

Pagination

paginate(pages, options)

Create paginated embeds:

const pages = [
  ctx.embed().title('Page 1').desc('First page content'),
  ctx.embed().title('Page 2').desc('Second page content'),
  ctx.embed().title('Page 3').desc('Third page content')
];

await ctx.paginate(pages.map(p => p.embed));

paginator(pages, options)

Advanced pagination with custom controls:

const pages = [
  ctx.embed().title('Page 1').embed,
  ctx.embed().title('Page 2').embed,
  ctx.embed().title('Page 3').embed
];

await ctx.paginator(pages, {
  time: 60000 // 1 minute timeout
});

Utility Methods

react(emoji)

React to the message:

await ctx.react('👍');
await ctx.react('<:custom:123456789>');

file(filePath)

Send a file:

await ctx.file('./images/welcome.png');

delete()

Delete the command message or interaction:

await ctx.delete();

hasPerms(permissions)

Check user permissions:

if (ctx.hasPerms(['Administrator'])) {
  await ctx.reply('You have admin permissions!');
} else {
  await ctx.error('You need admin permissions!');
}

Text Formatting

bold(text), italic(text), code(text)

Format text:

const message = `
${ctx.bold('Bold text')}
${ctx.italic('Italic text')}
${ctx.code('Code text')}
`;

await ctx.reply(message);

Waiting for User Input

awaitMessage(filter, options)

Wait for a message from the user:

await ctx.reply('What is your favorite color?');

const response = await ctx.awaitMessage(
  m => m.author.id === ctx.user.id,
  { time: 30000 }
);

if (response) {
  await ctx.reply(`Your favorite color is ${response.content}!`);
} else {
  await ctx.reply('You took too long to respond.');
}

awaitReaction(emojis, options)

Wait for a reaction:

const msg = await ctx.reply('React with 👍 or 👎');
await msg.react('👍');
await msg.react('👎');

const reaction = await ctx.awaitReaction(['👍', '👎'], { time: 30000 });

if (reaction) {
  await ctx.reply(`You reacted with ${reaction.emoji.name}!`);
}

waitFor(type, filter, timeout)

Generic wait method:

// Wait for message
const message = await ctx.waitFor('message', 
  m => m.author.id === ctx.user.id, 
  15000
);

// Wait for reaction
const reaction = await ctx.waitFor('reaction',
  (r, u) => u.id === ctx.user.id,
  15000
);

Dialog System

dialog(steps, options)

Create multi-step dialogs:

const answers = await ctx.dialog([
  'What is your name?',
  'What is your age?',
  'What is your favorite hobby?'
], { timeout: 60000 });

if (answers.length === 3) {
  await ctx.reply(`Hello ${answers[0]}! You are ${answers[1]} years old and enjoy ${answers[2]}.`);
}

User and Guild Fetching

fetchUser(id), fetchMember(id)

Fetch users or members:

const user = await ctx.fetchUser('123456789');
const member = await ctx.fetchMember('123456789');

dm(user, content)

Send a direct message:

await ctx.dm(ctx.user, 'This is a private message!');

Utility Functions

randomChoice(array)

Pick a random element:

const colors = ['red', 'blue', 'green', 'yellow'];
const randomColor = ctx.randomChoice(colors);
await ctx.reply(`Random color: ${randomColor}`);

Examples

Basic Command Handler

// commands/general/userinfo.js
module.exports = {
  name: 'userinfo',
  description: 'Get information about a user',

  async run(ctx) {
    const target = ctx.args[0] ? await ctx.fetchUser(ctx.args[0]) : ctx.user;

    const embed = ctx.embed()
      .title(`👤 ${target.username}`)
      .thumbnail(target.displayAvatarURL())
      .field('ID', target.id, true)
      .field('Created', target.createdAt.toDateString(), true)
      .color('blue');

    await embed.send();
  }
};

Interactive Command

module.exports = {
  name: 'poll',
  description: 'Create a poll',

  async run(ctx) {
    if (!ctx.args.length) {
      return ctx.error('Please provide a question!');
    }

    const question = ctx.args.join(' ');

    const embed = ctx.embed()
      .title('📊 Poll')
      .desc(question)
      .color('blue');

    const row = ctx.buttonRow([
      { customId: 'yes', label: '✅ Yes', style: 3 },
      { customId: 'no', label: '❌ No', style: 4 }
    ]);

    const msg = await ctx.reply({
      embeds: [embed.embed],
      components: [row]
    });

    const votes = { yes: 0, no: 0 };

    await ctx.awaitButton(msg, {
      yes: async (interaction) => {
        votes.yes++;
        await interaction.reply({ 
          content: 'You voted Yes!', 
          ephemeral: true 
        });
      },
      no: async (interaction) => {
        votes.no++;
        await interaction.reply({ 
          content: 'You voted No!', 
          ephemeral: true 
        });
      }
    }, { time: 60000 });

    // Update with results
    const resultEmbed = ctx.embed()
      .title('📊 Poll Results')
      .desc(question)
      .field('✅ Yes', votes.yes.toString(), true)
      .field('❌ No', votes.no.toString(), true)
      .color('green');

    await msg.edit({
      embeds: [resultEmbed.embed],
      components: []
    });
  }
};
module.exports = {
  name: 'feedback',
  description: 'Submit feedback',
  slash: true,

  async run(ctx) {
    const result = await ctx.modal([
      {
        customId: 'subject',
        label: 'Subject',
        style: 1,
        required: true
      },
      {
        customId: 'message',
        label: 'Your Feedback',
        style: 2,
        required: true
      }
    ], {
      title: 'Feedback Form'
    });

    if (result) {
      // Send to feedback channel
      const feedbackChannel = ctx.client.channels.cache.get('FEEDBACK_CHANNEL_ID');
      if (feedbackChannel) {
        const embed = ctx.embed()
          .title(`📝 Feedback: ${result.subject}`)
          .desc(result.message)
          .author(ctx.user.tag, ctx.user.displayAvatarURL())
          .color('blue');

        await feedbackChannel.send({ embeds: [embed.embed] });
      }

      await ctx.success('Thank you for your feedback!');
    }
  }
};

Best Practices

1. Always Handle Errors

try {
  await ctx.reply('This might fail');
} catch (error) {
  console.error('Reply failed:', error);
  // Try alternative response method
}

2. Check Permissions

if (!ctx.hasPerms(['ManageMessages'])) {
  return ctx.error('You need Manage Messages permission!');
}

3. Validate Input

if (!ctx.args.length) {
  return ctx.error('Please provide some arguments!');
}

const userId = ctx.args[0];
if (!/^\d{17,19}$/.test(userId)) {
  return ctx.error('Invalid user ID format!');
}

4. Use Appropriate Response Types

// For success operations
await ctx.success('User banned successfully!');

// For errors
await ctx.error('User not found!');

// For information
await ctx.info('This command has a 5-second cooldown.');

// For warnings
await ctx.warn('This action cannot be undone!');

Next Steps


The Context object makes Discord bot development intuitive and powerful, providing everything you need in a single, consistent interface.