discord.js Guide

website·Indexed 35 days ago

https://discordjs.guide/

Comprehensive documentation and guides for building Discord bots using the discord.js library. Covers core concepts including slash commands, event handling, interactive components like buttons and select menus, OAuth2, sharding, and bot deployment.

Tokens
49.3K
Snippets
267
Records
317
Agent score
23%

What's inside discord.js

  1. Overview of Discord Audit Logs in discord.js

    Audit logs allow developers to track server events and the users responsible for them. To interact with audit logs, the bot must have the ViewAuditLog permission. Audit logs can be retrieved in two ways: by manually fetching them from a guild or by listening to the guildAuditLogEntryCreate gateway event (which requires the GuildModeration intent).
  2. Overview of the discord.js Collection class

    The Collection class is a utility provided by discord.js that extends the native JavaScript Map. It allows for associations between unique keys and values, providing all standard Map features plus additional array-like iteration and utility methods. It is particularly useful for managing Discord objects that are identified by unique IDs.
  3. Create a user information slash command

    Example of a slash command that retrieves and displays information about the user who invoked the command, including their username and the date they joined the guild.
    const { SlashCommandBuilder } = require('discord.js');
    
    module.exports = {
    	data: new SlashCommandBuilder().setName('user').setDescription('Provides information about the user.'),
    	async execute(interaction) {
    		// interaction.user is the object representing the User who ran the command
    		// interaction.member is the GuildMember object, which represents the user in the specific guild
    		await interaction.reply(
    			`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`,
    		);
    	},
    };
  4. Prevent CSRF attacks using the OAuth2 state parameter

    The state parameter is used to prevent Cross-Site Request Forgery (CSRF) and clickjacking. Your application should generate a unique, random string per user, store it locally (e.g., in localStorage), and append it to the OAuth2 authorization URL. Upon redirection, the server or client must verify that the state returned by Discord matches the stored value.
    // Generating and setting state
    function generateRandomString() {
    	let randomString = '';
    	const randomNumber = Math.floor(Math.random() * 10);
    	for (let i = 0; i < 20 + randomNumber; i++) {
    		randomString += String.fromCharCode(33 + Math.floor(Math.random() * 94));
    	}
    	return randomString;
    }
    
    const randomString = generateRandomString();
    localStorage.setItem('oauth-state', randomString);
    const authUrl = `https://discord.com/api/oauth2/authorize?...&state=${btoa(randomString)}`;
    
    // Verifying state on redirect
    const fragment = new URLSearchParams(window.location.hash.slice(1));
    const state = fragment.get('state');
    
    if (localStorage.getItem('oauth-state') !== atob(decodeURIComponent(state))) {
    	console.log('You may have been clickjacked!');
    }
  5. Search for emojis across all shards

    Emojis are tied to guilds, and since guilds are partitioned across shards, client.emojis.cache only contains emojis for the current shard. To find an emoji by ID or name across the entire bot, use broadcastEval with a search function.

    Important: Data returned from broadcastEval is serialized (JSON stringified/parsed), meaning it becomes a raw object and loses its class methods (e.g., it is no longer a GuildEmoji instance). If you need to call methods on the found object (like .delete()), you must do so inside the function passed to broadcastEval before the object is returned.

    // 1. Define the search function
    function findEmoji(c, { nameOrId }) {
    	const emoji = c.emojis.cache.get(nameOrId) || 
    		c.emojis.cache.find((e) => e.name.toLowerCase() === nameOrId.toLowerCase());
    	
    	if (!emoji) return null;
    	
    	// Perform actions on the emoji object HERE while it is still a class instance
    	// emoji.delete(); 
    	
    	return emoji;
    }
    
    // 2. Execute across shards in the command handler
    if (commandName === 'emoji') {
    	const emojiNameOrId = interaction.options.getString('emoji');
    
    	return client.shard.broadcastEval(findEmoji, { context: { nameOrId: emojiNameOrId } })
    		.then((emojiArray) => {
    			// Find the first non-null result in the array returned from all shards
    			const foundEmoji = emojiArray.find((emoji) => emoji);
    			if (!foundEmoji) return interaction.reply('I could not find such an emoji.');
    			
    			return interaction.reply(
    				`I have found the ${foundEmoji.animated ? `<${foundEmoji.identifier}>` : `<:${foundEmoji.identifier}> emoji!`}!`
    			);
    		});
    }
  6. Handle preset choices in slash command options

    When an option is configured with preset choices using .addChoices(), you retrieve the selected value using the same getter method as a free-entry option (e.g., .getString()). The returned value will be the value property defined in the choice object, not the display name.
    // Command definition
    .addStringOption((option) =>
        option
            .setName('category')
            .setDescription('The gif category')
            .setRequired(true)
            .addChoices(
                { name: 'Funny', value: 'gif_funny' },
                { name: 'Meme', value: 'gif_meme' },
                { name: 'Movie', value: 'gif_movie' },
            ),
    )
    
    // Execution
    async execute(interaction) {
        const category = interaction.options.getString('category');
        // category will be 'gif_funny', 'gif_meme', or 'gif_movie'
    }
  7. Synchronize channel permissions with category

    To make a channel's permission overwrites match its parent category, use the .lockPermissions() method. This ensures that future changes to the category's overwrites are automatically applied to the synchronized child channel.
    if (!channel.parent) {
        return console.log('This channel is not listed under a category');
    }
    
    channel
        .lockPermissions()
        .then(() => console.log('Successfully synchronized permissions with parent channel'))
        .catch(console.error);
  8. Add labels and descriptions to Modal components

    The LabelBuilder is used to wrap interactive components (like text inputs or select menus) to provide a header label and an optional description.

    • label: Max 45 characters.
    • description: Max 100 characters.
    const { LabelBuilder, ModalBuilder } = require('discord.js');
    
    const modal = new ModalBuilder().setCustomId('myModal').setTitle('My Modal');
    
    const hobbiesLabel = new LabelBuilder()
    	.setLabel('What are some of your favorite hobbies?')
    	.setDescription('Activities you like to participate in');
    
    modal.addLabelComponents(hobbiesLabel);
  9. Detect and handle partial data in discord.js

    Structures that support partials have a .partial boolean property. If true, the structure is partial and is only guaranteed to contain its ID. Do not assume any other properties or methods will work on a partial structure.
    if (message.partial) {
    	console.log('The message is partial.');
    } else {
    	console.log('The message is not partial.');
    }
  10. Migrate MessageAttachments to AttachmentBuilder in v14

    In v14, MessageAttachment has been renamed to AttachmentBuilder. The constructor now takes an options object for the filename instead of a second positional argument.
    // Old
    new MessageAttachment(buffer, 'image.png'); 
    
    // New
    new AttachmentBuilder(buffer, { name: 'image.png' }); 
  11. Handle multiple autocomplete options in one command

    When a command has multiple autocomplete options, pass true to interaction.options.getFocused(true) to receive the full focused option object instead of just the value. This allows you to check focusedOption.name to determine which option the user is currently typing in and provide the corresponding set of suggestions.
    async autocomplete(interaction) {
    	const focusedOption = interaction.options.getFocused(true);
    	let choices;
    
    	if (focusedOption.name === 'query') {
    		choices = ['Topic A', 'Topic B'];
    	}
    
    	if (focusedOption.name === 'version') {
    		choices = ['v13', 'v14'];
    	}
    
    	const filtered = choices.filter((choice) => choice.startsWith(focusedOption.value));
    	await interaction.respond(filtered.map((choice) => ({ name: choice, value: choice })));
    }