NestCord

website·Indexed 29 days ago

https://nestcord.js.org/

A module for creating Discord bots using NestJS, integrating Discord.js v14. It provides a structured framework for building bots with support for slash commands, message components, modals, and text commands, alongside specialized packages for localization, pagination, sharding, and Lavalink integration.

Tokens
18.4K
Snippets
83
Records
96
Agent score
50%

What's inside NestCord

  1. Overview of NestCord

    NestCord is a module for creating Discord bots using NestJS, built on top of the Discord.js library. It allows developers to create well-architectured bots with deep integration into the NestJS ecosystem, supporting guards, interceptors, filters, and pipes. Key capabilities include handling Slash Commands, Context Menus, Message Components, and event listeners.
  2. Overview of NestCord for Discord bot development

    NestCord is a module that integrates Discord.js into NestJS applications. It allows developers to use NestJS patterns—such as decorators, dependency injection, and modular architecture—to build Discord bots. It supports Discord.js v14 and provides full type-safety for interactions, commands, and events. Supported interaction types include slash commands, context menus, buttons, select menus, modals, and autocomplete.
  3. Set up a local development environment for nestcord

    To contribute to the nestcord codebase, ensure you have Node.js version 16+ and npm installed. The project uses TypeScript for development, ESLint for code-style enforcement, and Prettier for code formatting.
    # 1. Fork and clone the repository
    # 2. Install dependencies
    npm ci
    # 3. Run linting to ensure styleguide compliance
    npm lint
  4. Handle arguments in text commands

    To capture the words following the command name in a message, use the @Arguments() decorator in the command handler method. This injects an array of strings (string[]) containing all words provided after the trigger command.
    import { Injectable } from '@nestjs/common';
    import { Context, TextCommand, TextCommandContext, Arguments } from '@globalart/nestcord';
    
    @Injectable()
    export class AppService {
        @TextCommand({
            name: 'echo',
            description: 'Echo command!',
        })
        public onEcho(@Context() [message]: TextCommandContext, @Arguments() args: string[]) {
            return message.reply(args.join(' '));
        }
    }
  5. Implement User Context Menu Commands with NestCord

    User commands are application commands that appear when a user right-clicks or taps on another user's profile. In NestCord, these are implemented using the @UserCommand decorator. To access the targeted user, use the @TargetUser() decorator in the method parameters, and use @Context() to access the UserCommandContext for sending replies.
    import { Injectable } from '@nestjs/common';
    import { Context, UserCommand, UserCommandContext, TargetUser } from '@globalart/nestcord';
    import { User } from 'discord.js';
    
    @Injectable()
    export class AppCommands {
        @UserCommand({ name: 'Get avatar' })
        public async getUserAvatar(
            @Context() [interaction]: UserCommandContext,
            @TargetUser() user: User
        ) {
            return interaction.reply({
                embeds: [
                    new MessageEmbed()
                        .setTitle(`Avatar ${user.username}`)
                        .setImage(user.displayAvatarURL({ size: 4096, dynamic: true }))
                ]
            });
        }
    }
  6. Validate Slash Command options using ValidationPipe

    The ValidationPipe is a built-in pipe used to enforce validation rules on incoming payloads. Rules are defined using decorators from the class-validator package within a DTO (Data Transfer Object) class. In Nestcord, you apply the pipe to the @Options decorator in your command handler to validate the command's options.
    import { IsEmail } from 'class-validator';
    import { Injectable } from '@nestjs/common';
    import { Context, SlashCommand, SlashCommandContext, Options } from '@globalart/nestcord';
    import { ValidationPipe } from '@nestjs/common';
    
    // 1. Define the DTO with validation rules
    export class CreateUserDto {
        @IsEmail()
        email: string;
    }
    
    @Injectable()
    export class UsersCommands {
        @SlashCommand({
            name: 'create',
            description: 'Create a new user'
        })
        public async onCreateUser(
            @Context() [interaction]: SlashCommandContext,
            // 2. Apply ValidationPipe to the Options decorator
            @Options(new ValidationPipe({ validateCustomDecorators: true })) createUserDto: CreateUserDto
        ) {
            return interaction.reply({ content: `User created: ${createUserDto.email}` });
        }
    }
  7. Apply per-user cooldowns to command handlers

    The @Cooldown(ms) decorator enforces a per-user rate limit on command or component handlers. It automatically applies the CooldownGuard, so @UseGuards() is not required. The cooldown is tracked independently for each user per handler. It works with Slash commands, Text commands, and User commands.
    import { Cooldown, SlashCommand, TextCommand } from '@globalart/nestcord';
    
    @Injectable()
    export class AppCommands {
      // 5 second cooldown for slash command
      @Cooldown(5_000)
      @SlashCommand({ name: 'roll', description: 'Roll a random number' })
      async onRoll(...) { ... }
    
      // 24 hour cooldown
      @Cooldown(86_400_000)
      @SlashCommand({ name: 'daily', description: 'Claim daily reward' })
      async onDaily(...) { ... }
    
      // Cooldown for text commands (based on message.author.id)
      @Cooldown(5_000)
      @TextCommand({ name: 'roll', description: 'Roll a random number' })
      async onRollText(...) { ... }
    }
    
  8. Integrate NestCordStatReporterModule into AppModule

    The NestCordStatReporterModule is used to send bot data (such as server and shard counts) to external monitoring services. It is imported into the root AppModule alongside the NestCordModule. In the services configuration, placeholders {{serverCount}} and {{shardCount}} are automatically replaced with the bot's actual statistics before the request is sent.
    import { Module } from '@nestjs/common';
    import { NestCordModule, NestCordStatReporterModule, StatCronExpression } from '@globalart/nestcord';
    
    @Module({
        imports: [
          NestCordModule.forRoot({
            token: process.env.DISCORD_TOKEN,
            intents: [/* ... intents ... */],
            prefix: '!',
            development: [process.env.DISCORD_TEST_GUILD]
          }),
          NestCordStatReporterModule.forRoot({
            services: [
              {
                name: 'top.gg',
                actions: [
                  {
                    name: 'name of the action',
                    url: 'https://top.gg/bots/:bot_id/stats',
                    bodyData: { server_count: '{{serverCount}}', shard_count: '{{shardCount}}' },
                  }
                ],
                headerData: { Authorization: process.env.TOP_GG_TOKEN },
                schedule: StatCronExpression.EVERY_5_MINUTES // or a crontab expression like '*/1 * * * *'
              },
            ],
          }),
        ],
    })
    export class AppModule {}
  9. Implement Dynamic Select Menus with path-to-regexp

    All select menu types (String, User, Role, Mentionable, Channel) support dynamic IDs using path-to-regexp. Use @ComponentParam to extract the dynamic segment of the ID in the handler.
    // Creating the dynamic menu
    new StringSelectMenuBuilder()
      .setCustomId('preferences/color')
      .setPlaceholder('Select a color')
      .setMaxValues(1)
      .setMinValues(1)
      .setOptions([
        { label: 'Red', value: 'Red' },
        { label: 'Blue', value: 'Blue' },
        { label: 'Yellow', value: 'Yellow' }
      ]);
    
    // Handling the interaction
    import { Injectable } from '@nestjs/common';
    import { Context, StringSelect, StringSelectContext, SelectedStrings, ComponentParam } from '@globalart/nestcord';
    
    @Injectable()
    export class AppComponents {
        @StringSelect('preferences/:item')
    	public onPreferenceSelect(
    		@Context() [interaction]: StringSelectContext,
    		@SelectedStrings() values: string[],
    		@ComponentParam('item') item: string
    	) {
    		return interaction.reply({
    			content: `${item} = ${values.join(',')}\n`
    		});
    	}
    }
  10. Create a basic text command with @TextCommand

    Use the @TextCommand decorator within a NestJS @Injectable service to handle specific text-based messages. The command handler receives the TextCommandContext via the @Context() decorator, which provides access to the Discord message object. Note: Text commands require the bot to have 'Message Content' access; verified bots in 100+ servers may be restricted by Discord's message content deprecation policy.
    import { Injectable } from '@nestjs/common';
    import { Context, TextCommand, TextCommandContext } from '@globalart/nestcord';
    
    @Injectable()
    export class AppService {
        @TextCommand({
            name: 'ping',
            description: 'Ping command!',
        })
        public onPing(@Context() [message]: TextCommandContext) {
            return message.reply('pong!');
        }
    }
  11. Listen to Sharding Manager and Shard events

    Use the @OnShardingManager decorator for manager-level events and the @OnShard decorator for events that occur on every spawned shard. These must be implemented within a provider registered in the ManagerModule.
    import { Injectable, Logger } from '@nestjs/common';
    import { Shard } from 'discord.js';
    import { OnShardingManager, OnShard } from '@globalart/nestcord';
    
    @Injectable()
    export class ShardEventsService {
        private readonly logger = new Logger(ShardEventsService.name);
    
        @OnShardingManager('shardCreate')
        onShardCreate(shard: Shard) {
            this.logger.log(`Shard #${shard.id} created`);
        }
    
        @OnShard('ready')
        onShardReady(shard: Shard) {
            this.logger.log(`Shard #${shard.id} is ready`);
        }
    
        @OnShard('error')
        onShardError(shard: Shard, error: Error) {
            this.logger.error(`Shard #${shard.id} error: ${error.message}`);
        }
    
        @OnShard('death')
        onShardDeath(shard: Shard) {
            this.logger.warn(`Shard #${shard.id} died`);
        }
    }
  12. Define Slash Command Options using DTOs

    To add parameters to a slash command, create a DTO class using option decorators and inject it into the command method using the @Options() decorator.

    Available option decorators include:

    • StringOption: string
    • NumberOption: number
    • IntegerOption: number
    • BooleanOption: boolean
    • UserOption: User
    • MemberOption: GuildMember
    • ChannelOption: GuildChannel
    • RoleOption: Role
    • MentionableOption: GuildMember | Role
    • AttachmentOption: AttachmentOption
    // length.dto.ts
    import { StringOption } from '@globalart/nestcord';
    
    export class TextDto {
        @StringOption({
            name: 'text',
            description: 'Your text',
            required: true
        })
        text: string;
    }
    
    // app.commands.ts
    import { Injectable } from '@nestjs/common';
    import { Context, SlashCommand, Options, SlashCommandContext } from '@globalart/nestcord';
    import { TextDto } from './length.dto';
    
    @Injectable()
    export class AppCommands {
        @SlashCommand({
            name: 'length',
            description: 'Get length of text'
        })
        public async onLength(@Context() [interaction]: SlashCommandContext, @Options() { text }: TextDto) {
            return interaction.reply({content: `Length of your text ${text.length}`});
        }
    }