shepherdjerred/monorepo/discord-bot-helper

Claude Code and agent skill by shepherdjerred/monorepo

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Discord.js v14 for building Discord bots - slash commands, events, components, embeds, and permissions When user works with Discord bots, discord.js, slash commands, Discord API, or mentions discord.js patterns

Install

npx vskill@latest install shepherdjerred/monorepo/discord-bot-helper

SKILL.md

Discord Bot Helper Agent

What's New in Discord.js v14 (2024-2025)

  • Node.js 18.17+ required (v14.14+), 22+ recommended
  • PascalCase enums: ButtonStyle.Primary instead of 'PRIMARY'
  • Renamed builders: EmbedBuilder (was MessageEmbed), AttachmentBuilder (was MessageAttachment)
  • Display components: New layout and content elements beyond embeds
  • Gateway v10: Updated event handling and intents

Installation

bash
# Install discord.js
npm install discord.js
# or
bun add discord.js

Basic Bot Setup

Main File (index.js)
typescript
import { Client, Events, GatewayIntentBits } from "discord.js";
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent, // Privileged intent
],
});
client.once(Events.ClientReady, (readyClient) => {
console.log(`Logged in as ${readyClient.user.tag}!`);
});
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "ping") {
await interaction.reply("Pong!");
}
});
client.login(process.env.DISCORD_TOKEN);

Gateway Intents

Intents control which events your bot receives:

Common Intents
IntentEvents Received
GuildsGuild create/update/delete, channels, roles
GuildMembersMember join/leave/update (privileged)
GuildMessagesMessage events in guilds
MessageContentMessage content, attachments, embeds (privileged)
GuildVoiceStatesVoice channel activity
GuildPresencesMember presence updates (privileged)
DirectMessagesDM message events
Privileged Intents

Require manual enabling in Discord Developer Portal:

  • GuildMembers
  • GuildPresences
  • MessageContent
typescript
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers, // Privileged
GatewayIntentBits.GuildPresences, // Privileged
GatewayIntentBits.MessageContent, // Privileged
],
});

Slash Commands

Command Definition
typescript
// commands/ping.js
import { SlashCommandBuilder } from "discord.js";
export const data = new SlashCommandBuilder()
.setName("ping")
.setDescription("Replies with Pong!");
export async function execute(interaction) {
await interaction.reply("Pong!");
}
Command with Options
typescript
import { SlashCommandBuilder } from "discord.js";
export const data = new SlashCommandBuilder()
.setName("echo")
.setDescription("Replies with your input")
.addStringOption((option) =>
option
.setName("message")
.setDescription("The message to echo")
.setRequired(true),
)
.addUserOption((option) =>
option.setName("target").setDescription("User to mention"),
)
.addIntegerOption((option) =>
option
.setName("count")
.setDescription("Number of times")
.setMinValue(1)
.setMaxValue(10),
);
export async function execute(interaction) {
const message = interaction.options.getString("message");
const target = interaction.options.getUser("target");
const count = interaction.options.getInteger("count") ?? 1;
const reply = target
? `${target}, ${message.repeat(count)}`
: message.repeat(count);
await interaction.reply(reply);
}
Command with Choices
typescript
export const data = new SlashCommandBuilder()
.setName("gif")
.setDescription("Sends a gif")
.addStringOption((option) =>
option
.setName("category")
.setDescription("The gif category")
.setRequired(true)
.addChoices(
{ name: "Funny", value: "gif_funny" },
{ name: "Meme", value: "gif_meme" },
{ name: "Cute", value: "gif_cute" },
),
);
Subcommands
typescript
export const data = new SlashCommandBuilder()
.setName("user")
.setDescription("User commands")
.addSubcommand((subcommand) =>
subcommand
.setName("info")
.setDescription("Get user info")
.addUserOption((option) =>
option.setName("target").setDescription("The user"),
),
)
.addSubcommand((subcommand) =>
subcommand
.setName("avatar")
.setDescription("Get user avatar")
.addUserOption((option) =>
option.setName("target").setDescription("The user"),
),
);
export async function execute(interaction) {
const subcommand = interaction.options.getSubcommand();
const target = interaction.options.getUser("target") ?? interaction.user;
if (subcommand === "info") {
await interaction.reply(`User: ${target.tag}\nID: ${target.id}`);
} else if (subcommand === "avatar") {
await interaction.reply(target.displayAvatarURL({ size: 256 }));
}
}
Registering Commands
typescript
// deploy-commands.js
import { REST, Routes } from "discord.js";
import { commands } from "./commands/index.js";
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
// Guild commands (instant, for development)
await rest.put(Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), {
body: commands.map((c) => c.data.toJSON()),
});
// Global commands (takes up to 1 hour to propagate)
await rest.put(Routes.applicationCommands(CLIENT_ID), {
body: commands.map((c) => c.data.toJSON()),
});
Autocomplete
typescript
export const data = new SlashCommandBuilder()
.setName("search")
.setDescription("Search something")
.addStringOption((option) =>
option
.setName("query")
.setDescription("Search query")
.setAutocomplete(true),
);
export async function autocomplete(interaction) {
const focusedValue = interaction.options.getFocused();
const choices = ["apple", "banana", "cherry", "date", "elderberry"];
const filtered = choices.filter((c) => c.startsWith(focusedValue));
await interaction.respond(
filtered.slice(0, 25).map((choice) => ({ name: choice, value: choice })),
);
}
export async function execute(interaction) {
const query = interaction.options.getString("query");
await interaction.reply(`You searched for: ${query}`);
}

Response Methods

Basic Responses
typescript
// Simple reply
await interaction.reply("Hello!");
// Ephemeral reply (only visible to user)
await interaction.reply({ content: "Secret!", ephemeral: true });
// Deferred reply (for long operations)
await interaction.deferReply();
// ... do work ...
await interaction.editReply("Done!");
// Follow-up messages
await interaction.reply("First message");
await interaction.followUp("Second message");
await interaction.followUp({ content: "Ephemeral followup", ephemeral: true });
Fetching the Reply
typescript
const reply = await interaction.fetchReply();
console.log(reply.id);

Embeds

Creating Embeds
typescript
import { EmbedBuilder } from "discord.js";
const embed = new EmbedBuilder()
.setColor(0x0099ff)
.setTitle("Embed Title")
.setURL("https://discord.js.org/")
.setAuthor({
name: "Author Name",
iconURL: "https://example.com/icon.png",
url: "https://example.com",
})
.setDescription("This is the main description")
.setThumbnail("https://example.com/thumbnail.png")
.addFields(
{ name: "Field 1", value: "Value 1", inline: true },
{ name: "Field 2", value: "Value 2", inline: true },
{ name: "Field 3", value: "Value 3" },
)
.setImage("https://example.com/image.png")
.setTimestamp()
.setFooter({
text: "Footer text",
iconURL: "https://example.com/footer.png",
});
await interaction.reply({ embeds: [embed] });
Multiple Embeds
typescript
const embed1 = new EmbedBuilder().setTitle("Embed 1").setColor(0xff0000);
const embed2 = new EmbedBuilder().setTitle("Embed 2").setColor(0x00ff00);
await interaction.reply({ embeds: [embed1, embed2] });

Buttons

Creating Buttons
typescript
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId("primary")
.setLabel("Primary")
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId("secondary")
.setLabel("Secondary")
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId("success")
.setLabel("Success")
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId("danger")
.setLabel("Danger")
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setLabel("Link")
.setURL("https://discord.js.org")
.setStyle(ButtonStyle.Link),
);
await interaction.reply({ content: "Click a button!", components: [row] });
Handling Button Clicks
typescript
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isButton()) return;
if (interaction.customId === "primary") {
await interaction.reply("You clicked the primary button!");
}
});
Disabling Buttons After Click
typescript
const row = ActionRowBuilder.from(interaction.message.components[0]);
row.components.forEach((button) => button.setDisabled(true));
await interaction.update({ components: [row] });

Select Menus

String Select Menu
typescript
import { ActionRowBuilder, StringSelectMenuBuilder } from "discord.js";
const row = new ActionRowBuilder().addComponents(
new StringSelectMenuBuilder()
.setCustomId("select")
.setPlaceholder("Nothing selected")
.addOptions(
{ label: "Option 1", description: "Description 1", value: "first" },
{ label: "Option 2", description: "Description 2", value: "second" },
{ label: "Option 3", description: "Description 3", value: "third" },
),
);
await interaction.reply({ content: "Select an option!", components: [row] });
Handling Selection
typescript
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isStringSelectMenu()) return;
if (interaction.customId === "select") {
const selected = interaction.values[0];
await interaction.reply(`You selected: ${selected}`);
}
});
User/Role/Channel Select Menus
typescript
import {
UserSelectMenuBuilder,
RoleSelectMenuBuilder,
ChannelSelectMenuBuilder,
} from "discord.js";
const userSelect = new UserSelectMenuBuilder()
.setCustomId("user-select")
.setPlaceholder("Select a user")
.setMinValues(1)
.setMaxValues(3);
const roleSelect = new RoleSelectMenuBuilder()
.setCustomId("role-select")
.setPlaceholder("Select a role");
const channelSelect = new ChannelSelectMenuBuilder()
.setCustomId("channel-select")
.setPlaceholder("Select a channel");

Modals (Forms)

Creating a Modal
typescript
import {
ModalBuilder,
TextInputBuilder,
TextInputStyle,
ActionRowBuilder,
} from "discord.js";
const modal = new ModalBuilder()
.setCustomId("feedback-modal")
.setTitle("Feedback Form");
const titleInput = new TextInputBuilder()
.setCustomId("title")
.setLabel("Title")
.setStyle(TextInputStyle.Short)
.setPlaceholder("Enter a title")
.setRequired(true)
.setMaxLength(100);
const descriptionInput = new TextInputBuilder()
.setCustomId("description")
.setLabel("Description")
.setStyle(TextInputStyle.Paragraph)
.setPlaceholder("Enter your feedback")
.setRequired(true)
.setMinLength(10)
.setMaxLength(1000);
modal.addComponents(
new ActionRowBuilder().addComponents(titleInput),
new ActionRowBuilder().addComponents(descriptionInput),
);
// Show modal (must be first response)
await interaction.showModal(modal);
Handling Modal Submission
typescript
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isModalSubmit()) return;

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
5

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/shepherdjerred/monorepo/discord-bot-helper/badge)](https://verified-skill.com/skills/shepherdjerred/monorepo/discord-bot-helper)

Something wrong with this skill or its page? Tell us.