This package has been deprecated

Author message:

The version is no longer supported. Use @discord-nestjs/core and @discord-nestjs/common instead.

discord-nestjs
TypeScript icon, indicating that this package has built-in type declarations

1.2.1 • Public • Published

Nest Logo

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

NPM Version Package License

🧾 Description

NestJS package for discord.js

👨🏻‍💻 Installation

$ npm install discord-nestjs discord.js

Or via yarn

$ yarn add discord-nestjs discord.js

📑 Overview

The module declaration proceeds in the same way as it is done in NestJS by means creating a dynamic module through the forRoot and forRootAsync functions.

  • token * - Your discord bot token. You can get here
  • commandPrefix * - Global prefix for command
  • allowGuilds - List of Guild IDs that the bot is allowed to work with
  • denyGuilds - List of Guild IDs that the bot is not allowed to work with
  • allowCommands - Binding channels and user to a command. Can be overridden in the handler decorator
    • name * - Command name
    • channels - List of channel IDs with which the command will work
    • users - List of user IDs with which the command will work
  • webhook - Connecting with webhook
    • webhookId * - Webhook id
    • webhookToken * - Webhook token
  • usePipes - List of pipes that will be applied to all handlers with the @Content decorator (with class type other than string type). Can be overridden via the @UsePipes decorator
  • useGuards - A list of guards that will apply to all handlers. Can be overridden via the @UseGuards decorator
  • You can also set all options as for the client from the "discord.js" library

⚠️Import TransformPipe and ValidationPipe from discord-nestjs package

Below is an example of creating a dynamic module using the forRoot function

💡 Example

/*bot.module.ts*/

import { Module } from '@nestjs/common';
import { DiscordModule, TransformPipe, ValidationPipe } from 'discord-nestjs';
import { BotGateway } from './bot-gateway';

@Module({
  imports: [
    DiscordModule.forRoot({
      token: 'Njg2MzI2OTMwNTg4NTY1NTQx.XmVlww.EF_bMXRvYgMUCQhg_jYnieoBW-k',
      commandPrefix: '!',
      allowGuilds: ['745366351929016363'],
      denyGuilds: ['520622812742811698'],
      allowCommands: [
        {
          name: 'some',
          channels: ['745366352386326572'],
          users: ['261863053329563648'],
          channelType: ['dm'],
        },
      ],
      webhook: {
        webhookId: 'your_webhook_id',
        webhookToken: 'your_webhook_token',
      },
      usePipes: [TransformPipe, ValidationPipe],
      // and other discord options
    }),
  ],
  providers: [BotGateway],
})
export class BotModule {}

Or via the forRootAsync function

/*bot.module.ts*/

import { Module } from '@nestjs/common';
import { DiscordModule, TransformPipe, ValidationPipe } from 'discord-nestjs';
import { BotGateway } from './bot-gateway';

@Module({
  imports: [
    DiscordModule.forRootAsync({
      useFactory: () => ({
        token: 'Njg2MzI2OTMwNTg4NTY1NTQx.XmVlww.EF_bMXRvYgMUCQhg_jYnieoBW-k',
        commandPrefix: '!',
        allowGuilds: ['745366351929016363'],
        denyGuilds: ['520622812742811698'],
        allowCommands: [
          {
            name: 'some',
            channels: ['745366352386326572'],
            users: ['261863053329563648'],
            channelType: ['dm'],
          },
        ],
        webhook: {
          webhookId: 'your_webhook_id',
          webhookToken: 'your_webhook_token',
        },
        usePipes: [TransformPipe, ValidationPipe],
        // and other discord options
      }),
    }),
  ],
  providers: [BotGateway],
})
export class BotModule {}

Alternatively, you can use the useClass syntax

/*bot.module.ts*/

import { Module } from '@nestjs/common';
import { DiscordConfigService } from './discord-config-service';
import { BotGateway } from './bot-gateway';
import { DiscordModule } from 'discord-nestjs';

@Module({
  imports: [
    DiscordModule.forRootAsync({
      useClass: DiscordConfigService,
    }),
  ],
  providers: [BotGateway],
})
export class BotModule {}

You need to implement the DiscordOptionsFactory interface

/*discord-config-service.ts*/

import { Injectable } from '@nestjs/common';
import {
  DiscordModuleOption,
  DiscordOptionsFactory,
  TransformPipe,
  ValidationPipe,
} from 'discord-nestjs';

@Injectable()
export class DiscordConfigService implements DiscordOptionsFactory {
  createDiscordOptions(): DiscordModuleOption {
    return {
      token: 'Njg2MzI2OTMwNTg4NTY1NTQx.XmVlww.EF_bMXRvYgMUCQhg_jYnieoBW-k',
      commandPrefix: '!',
      allowGuilds: ['745366351929016363'],
      denyGuilds: ['520622812742811698'],
      allowCommands: [
        {
          name: 'some',
          channels: ['745366352386326572'],
          users: ['261863053329563648'],
          channelType: ['dm'],
        },
      ],
      webhook: {
        webhookId: 'your_webhook_id',
        webhookToken: 'your_webhook_token',
      },
      usePipes: [TransformPipe, ValidationPipe],
      // and other discord options
    };
  }
}

▶️ Usage

Create a class (for example BotGateway) and mark it with the decorator @Injectable or @Controller. You can get the client provider by adding a variable of type DiscordClientProvider to the dependency. Below is an example of how you can notify yourself that a bot has successfully established a connection to the Discord API.

💡 Example

/*bot.gateway.ts*/

import { Injectable, Logger } from '@nestjs/common';
import { Once, DiscordClientProvider } from 'discord-nestjs';

@Injectable()
export class BotGateway {
  private readonly logger = new Logger(BotGateway.name);

  constructor(private readonly discordProvider: DiscordClientProvider) {}

  @Once({ event: 'ready' })
  onReady(): void {
    this.logger.log(
      `Logged in as ${this.discordProvider.getClient().user.tag}!`,
    );
  }
}

You can also get the client provider using the @Client decorator.

💡 Example

/*bot.gateway.ts*/

import { Injectable, Logger } from '@nestjs/common';
import { Once, ClientProvider } from 'discord-nestjs';

@Injectable()
export class BotGateway {
  private readonly logger = new Logger(BotGateway.name);

  @Client()
  discordProvider: ClientProvider;

  @Once({ event: 'ready' })
  onReady(): void {
    this.logger.log(
      `Logged in as ${this.discordProvider.getClient().user.tag}!`,
    );
  }
}

ℹ️ Creating a handler for receiving messages by a bot

Use the @OnCommand decorator to declare a command handler.

An example of creating a command is shown below. By default, the handler arguments will be the same as if you were signing up for an event in the "discord.js" library. (hint)

The OnCommand decorator always subscribes to the "message" event

💡 Example

/*bot.gateway.ts*/

import { Injectable } from '@nestjs/common';
import { OnCommand } from 'discord-nestjs';
import { Message } from 'discord.js';

@Injectable()
export class BotGateway {
  @OnCommand({ name: 'start' })
  async onCommand(message: Message): Promise<void> {
    await message.reply(`Execute command: ${message.content}`);
  }
}

ℹ️ Subscribe to event

Subscription to incoming events (hint)

Use the @On decorator to subscribe to an event

💡 Example

/*bot.gateway.ts*/

import { Injectable } from '@nestjs/common';
import { On } from 'discord-nestjs';
import { Message } from 'discord.js';

@Injectable()
export class BotGateway {
  @On({ event: 'message' })
  async onMessage(message: Message): Promise<void> {
    if (!message.author.bot) {
      await message.reply("I'm watching you");
    }
  }
}

You can also subscribe to an event once using the @Once decorator

💡 Example

/*bot.gateway.ts*/

import { Injectable } from '@nestjs/common';
import { Once } from 'discord-nestjs';
import { Message } from 'discord.js';

@Injectable()
export class BotGateway {
  @Once({ event: 'message' })
  async onceMessage(message: Message): Promise<void> {
    if (!message.author.bot) {
      await message.reply("I'm watching you");
    }
  }
}

ℹ️ Getting content and context through a decorator

By default, the library sets the handler arguments on its own, as it was said above, but you can manipulate the arguments yourself using the @Content and @Context decorators

  • Content - Message body (applicable only for "message" event)
  • Context - Default handler arguments (hint)

⚠️Using decorators overrides the setting of arguments in the handler

💡 Example

/*bot.gateway.ts*/

import { Content, Context, OnCommand } from 'discord-nestjs';
import { Injectable, Logger } from '@nestjs/common';
import { Message } from 'discord.js';

@Injectable()
export class BotGateway {
  private readonly logger = new Logger(BotGateway.name);

  @OnCommand({ name: 'start' })
  async onCommand(
    @Content() content: string,
    @Context() [context]: [Message],
  ): Promise<void> {
    await context.reply(`Execute command: ${content}`, `Args: ${context}`);
  }
}

ℹ️ Pipes. Transformation and validation

To intercept messages before invoking the handler, use the @UsePipes decorator. Works only with the "message" event. For convenience, the library already has an implementation of TransformPipe and ValidationPipe.

First thing you need to do is create a DTO class. First, each field in the class must be marked with the @Expose decorator from the "class-transform" library, secondly, it is necessary to mark how we will enter data into the variable. Use the @ArgNum and @ArgRange decorators for markup. @ArgNum takes the value at the index. Think of @ArgRange as a slice function for arrays. Then you can add validation as you like.

⚠️Import @UsePipes, TransformPipe, ValidationPipe from the discord-nestjs package

💡 Example

/*registration.dto.ts*/

import { ArgNum, ArgRange } from 'discord-nestjs';
import { Expose } from 'class-transformer';
import { IsNumber, Min, IsArray } from 'class-validator';

export class RegistrationDto {
  @ArgRange(() => ({ formPosition: 1, toPosition: 4 }))
  @Expose()
  @IsArray()
  name: string[];

  @ArgNum((last: number) => ({ position: last }))
  @Expose()
  @Type(() => Number)
  @IsNumber()
  @Min(18)
  age: number;
}

After that we attach the decorator @UsePipes to the handler and pass TransformPipe and ValidationPipe to the arguments in the same sequence. Pipes are executed sequentially from left to right. The @UsePipes declaration overrides the global usePipes declaration.

💡 Example

/*bot.gateway.ts*/

import {
  UsePipes,
  Content,
  Context,
  OnCommand,
  TransformPipe,
  ValidationPipe,
} from 'discord-nestjs';
import { Injectable } from '@nestjs/common';
import { RegistrationDto } from './registration.dto';
import { Message } from 'discord.js';

@Injectable()
export class BotGateway {
  @OnCommand({ name: 'reg' })
  @UsePipes(TransformPipe, ValidationPipe)
  async onSomeEvent(
    @Content() content: RegistrationDto,
    @Context() [context]: [Message],
  ): Promise<void> {
    await context.reply(
      `User was created! Id: ${context.author.id}, FIO: ${content.name.join(
        '-',
      )}, Age: ${content.age}`,
    );
  }
}
Input:
!reg Ivan Ivanovich Ivanov 22
Output:
User was created!: Id: 261863053329563648, Ivan-Ivanovich-Ivanov, Age: 22

You can also set @UsePipes decorator on class. In this case, the decorator is applied to all methods in the class.

Transform alias to user class

💡 Example

/*user.dto.ts*/

import { ArgNum, TransformToUser } from 'discord-nestjs';
import { Expose } from 'class-transformer';
import { User } from 'discord.js';

export class UserDto {
  @ArgNum((last: number) => ({ position: 1 }))
  @Expose()
  @TransformToUser()
  user: User;
}

Create command handler

TransformPipe required for transform input string to DTO. You can also use ValidationPipe for validate input

/*bot.gateway.ts*/

import { Message } from 'discord.js';
import { Content, Context, OnCommand, UsePipes } from 'discord-nestjs';
import { UserDto } from './user.dto';
import { TransformPipe } from 'discord-nestjs';

@Injectable()
export class BotGateway {
  @OnCommand({ name: 'avatar' })
  @UsePipes(TransformPipe)
  async onCommand(
    @Content() content: UserDto,
    @Context() [context]: [Message],
  ): Promise<void> {
    await context.reply(`User avatar: ${content.user.avatarURL()}`);
  }
}
Input:
!avatar @Федок
Output:
User avatar: https://cdn.discordapp.com/avatars/261863053329563648/d12c5a04be7bcabea7b9778b7e4fa6d5.webp

In order to override error handling in ValidationPipe you need to create an instance of the class manually.

💡 Example

/*bot.gateway.ts*/

import {
  UsePipes,
  Content,
  Context,
  OnCommand,
  TransformPipe,
  ValidationPipe,
} from 'discord-nestjs';
import { Injectable } from '@nestjs/common';
import { RegistrationDto } from './registration.dto';
import { Message, MessageEmbed } from 'discord.js';
import { ValidationError } from 'class-validator';

@Injectable()
export class BotGateway {
  @OnCommand({ name: 'reg' })
  @UsePipes(
    TransformPipe,
    new ValidationPipe({
      exceptionFactory: (errors: ValidationError[], message: Message) =>
        new MessageEmbed().setTitle('Upss!').setDescription(message.content),
    }),
  )
  async onSomeEvent(
    @Content() content: RegistrationDto,
    @Context() [context]: [Message],
  ): Promise<void> {
    await context.reply(
      `User was created! Id: ${context.author.id}, FIO: ${content.name.join(
        '-',
      )}, Age: ${content.age}`,
    );
  }
}

You can also create your own TransformPipe or ValidationPipe by implementing the DiscordPipeTransform interface.

/*transform.pipe.ts*/

import {
  TransformProvider,
  ConstructorType,
  ValidationPipe,
  DiscordPipeTransform,
} from 'discord-nestjs';
import { ClientEvents, Message } from 'discord.js';
import { Injectable } from '@nestjs/common';

@Injectable()
export class TransformPipe implements DiscordPipeTransform {
  constructor(private readonly transformProvider: TransformProvider) {}

  transform(
    event: keyof ClientEvents,
    [context]: [Message],
    content?: any,
    type?: ConstructorType<any>,
  ): any {
    return this.transformProvider.transformContent(type, content);
  }
}

ℹ️ Guards

To protect commands and events, use. The canActive function returns boolean. If one of the guards returns false, then the chain will stop there and the handler itself will not be called. The @UseGuards declaration overrides the global useGuards declaration.

⚠️Import @UseGuards from the discord-nestjs package

You need to implement the DiscordGuard interface

💡 Example

/*bot.guard.ts*/

import { DiscordGuard } from 'discord-nestjs';
import { ClientEvents, MessageEmbed } from 'discord.js';

export class BotGuard implements DiscordGuard {
  async canActive(
    event: keyof ClientEvents,
    [context]: [any],
  ): Promise<boolean> {
    if (context.author.id === '766863033789563648') {
      return true;
    } else {
      const embed = new MessageEmbed().setColor().setTitle('Ups! Not allowed!');
      await context.reply(embed);
      return false;
    }
  }
}
/*bot.gateway.ts*/

import { On, UseGuards, OnCommand } from 'discord-nestjs';
import { Message } from 'discord.js';
import { BotGuard } from './bot.guard';
import { Injectable } from '@nestjs/common';

@Injectable()
export class BotGateway {
  @UseGuards(BotGuard)
  @OnCommand({ name: 'hide' })
  async guardCommand(message: Message): Promise<void> {
    // to do something
  }
}

You can also set @UseGuards decorator on class. In this case, the decorator is applied to all methods in the class.

ℹ️ Middleware

You can use a middleware to process all incoming messages. To do this, you need to implement the DiscordMiddleware interface.

💡 Example

/*bot.middleware.ts*/

import { Middleware, DiscordMiddleware } from 'discord-nestjs';
import { Logger } from '@nestjs/common';
import { ClientEvents } from 'discord.js';

@Middleware()
export class BotMiddleware implements DiscordMiddleware {
  private readonly logger = new Logger(BotMiddleware.name);

  use(event: keyof ClientEvents, context: any[]): void {
    if (event === 'message') {
      this.logger.log('On message event triggered');
    }
  }
}

Also don't forget to add your middleware to the providers.

@Module({
  providers: [BotMiddleware],
})
export class BotModule {}

🗂 Decorators description

ℹ️ @Client

Inject client provider

ℹ️ @OnCommand

Mark as command handler

Params

  • name * - Command name
  • prefix - Command prefix (If set, it overrides the global)
  • isRemoveCommandName - Remove command name from input string (Default true)
  • isRemovePrefix - Remove prefix from input string (Default true)
  • isIgnoreBotMessage - Ignore messages from bots (Default true)
  • allowChannels - List of channel IDs with which the command will work (If set, it overrides the global)
  • isRemoveMessage - Remove message from channel after processing (Default false)
  • allowUsers - List of user IDs with which the command will work (If set, it overrides the global)
  • channelType - Filter by text channel type (If set, it overrides the global)

ℹ️ @On

Handle discord events hint

Params

  • event * - Name of the event to listen to

ℹ️ @Once

Handle discord events (only once) hint

Params

  • event * - Name of the event to listen to

ℹ️ @Content

Message content (applicable only for "message" event)

ℹ️ @Context

Default handler arguments (hint)

ℹ️ @UsePipes

To intercept incoming messages for some function

Params

  • List of classes or instances that implement the DiscordPipeTransform interface

ℹ️ @TransformToUser

Transform alias to user class

Works only in conjunction with @ArgNum and @ArgRange decorator

Params

  • throwError - If an error occurs from Discord APIm it will be thrown (Default false)

ℹ️ @ArgNum

Set value by argument number

Params

  • arguments
    • last - Last index position
  • return
    • position * - Position index form input

ℹ️ @ArgRange

Set value by argument number

Params

  • arguments
    • last - Last index position
  • return
    • formPosition * - Start index position form input
    • toPosition - Finish index position form input (default last index of input)

ℹ️ @UseGuards

To guard incoming messages

Params

  • List of classes or instances that implement the DiscordGuard interface

ℹ️ @Middleware

For handling intermediate requests

Params

  • allowEvents - Handled events
  • denyEvents - Skipped events

Any questions or suggestions? Discord Федок#3051

Package Sidebar

Install

npm i discord-nestjs

Weekly Downloads

7

Version

1.2.1

License

MIT

Unpacked Size

184 kB

Total Files

177

Last publish

Collaborators

  • fedok