Necord
Necord 是一个强大的模块,它简化了 Discord 机器人开发,并且可以无缝集成进你的 NestJS 应用。
注意
Necord 是第三方包,并不是由 NestJS 核心团队官方维护的。如果你遇到问题,请在其官方仓库中反馈。
安装
首先,你需要安装 Necord 以及它的依赖 Discord.js。
$ npm install necord discord.js用法
要在项目中使用 Necord,请导入 NecordModule 并使用必要选项进行配置。
// app.module.ts
import { Module } from '@nestjs/common';
import { NecordModule } from 'necord';
import { IntentsBitField } from 'discord.js';
import { AppService } from './app.service';
@Module({
imports: [
NecordModule.forRoot({
token: process.env.DISCORD_TOKEN,
intents: [IntentsBitField.Flags.Guilds],
development: [process.env.DISCORD_DEVELOPMENT_GUILD_ID],
}),
],
providers: [AppService],
})
export class AppModule {}提示
可用 intents 的完整列表请见这里。
完成这一步后,你就可以在 provider 中注入 AppService,轻松注册命令、事件等功能。
// app.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Context, On, Once, ContextOf } from 'necord';
import { Client } from 'discord.js';
@Injectable()
export class AppService {
private readonly logger = new Logger(AppService.name);
@Once('ready')
public onReady(@Context() [client]: ContextOf<'ready'>) {
this.logger.log(`Bot logged in as ${client.user.username}`);
}
@On('warn')
public onWarn(@Context() [message]: ContextOf<'warn'>) {
this.logger.warn(message);
}
}理解上下文
你可能已经注意到上面示例中的 @Context 装饰器。这个装饰器会把事件上下文注入到方法中,从而让你访问各种与事件相关的数据。由于事件类型有很多种,上下文类型会通过 ContextOf<type: string> 自动推导。你可以通过 @Context() 装饰器轻松获取上下文变量,它会为该变量填入与当前事件相关的参数数组。
文本命令
注意
文本命令依赖 message content,而对于已验证机器人以及加入超过 100 个服务器的应用,这项能力正在被弃用。这意味着如果你的机器人无法访问 message content,文本命令将无法工作。关于这一变更的更多信息见这里。
下面展示了如何使用 @TextCommand 装饰器创建一个简单的消息命令处理器。
// app.commands.ts
import { Injectable } from '@nestjs/common';
import { Context, TextCommand, TextCommandContext, Arguments } from 'necord';
@Injectable()
export class AppCommands {
@TextCommand({
name: 'ping',
description: 'Responds with pong!',
})
public onPing(
@Context() [message]: TextCommandContext,
@Arguments() args: string[],
) {
return message.reply('pong!');
}
}应用命令
应用命令为用户在 Discord 客户端内与应用交互提供了原生方式。应用命令有三种类型,可通过不同界面访问:聊天输入命令、消息上下文菜单命令(右键消息)以及用户上下文菜单命令(右键用户)。

Slash 命令
Slash 命令是一种非常适合与用户进行结构化交互的方式。它允许你创建带有精确定义参数和选项的命令,从而显著改善用户体验。
要使用 Necord 定义 Slash 命令,可以使用 SlashCommand 装饰器。
// app.commands.ts
import { Injectable } from '@nestjs/common';
import { Context, SlashCommand, SlashCommandContext } from 'necord';
@Injectable()
export class AppCommands {
@SlashCommand({
name: 'ping',
description: 'Responds with pong!',
})
public async onPing(@Context() [interaction]: SlashCommandContext) {
return interaction.reply({ content: 'Pong!' });
}
}提示
当机器人客户端登录后,它会自动注册所有定义好的命令。请注意,全局命令最多会被缓存一小时。为了避免全局缓存带来的问题,可以利用 Necord 模块中的 development 参数,将命令可见性限制在单个 guild 中。
选项
你可以使用 option 装饰器为 Slash 命令定义参数。下面我们创建一个 TextDto 类来演示:
// text.dto.ts
import { StringOption } from 'necord';
export class TextDto {
@StringOption({
name: 'text',
description: 'Input your text here',
required: true,
})
text: string;
}随后就可以在 AppCommands 类中使用这个 DTO:
// app.commands.ts
import { Injectable } from '@nestjs/common';
import { Context, SlashCommand, Options, SlashCommandContext } from 'necord';
import { TextDto } from './length.dto';
@Injectable()
export class AppCommands {
@SlashCommand({
name: 'length',
description: 'Calculate the length of your text',
})
public async onLength(
@Context() [interaction]: SlashCommandContext,
@Options() { text }: TextDto,
) {
return interaction.reply({
content: `The length of your text is: ${text.length}`,
});
}
}完整的内置 option 装饰器列表请参见这份文档。
自动补全
要为 Slash 命令实现自动补全功能,你需要创建一个拦截器。这个拦截器会在用户输入自动补全字段时处理请求。
// cats-autocomplete.interceptor.ts
import { Injectable } from '@nestjs/common';
import { AutocompleteInteraction } from 'discord.js';
import { AutocompleteInterceptor } from 'necord';
@Injectable()
class CatsAutocompleteInterceptor extends AutocompleteInterceptor {
public transformOptions(interaction: AutocompleteInteraction) {
const focused = interaction.options.getFocused(true);
let choices: string[];
if (focused.name === 'cat') {
choices = ['Siamese', 'Persian', 'Maine Coon'];
}
return interaction.respond(
choices
.filter((choice) => choice.startsWith(focused.value.toString()))
.map((choice) => ({ name: choice, value: choice })),
);
}
}你还需要在 options 类上标记 autocomplete: true:
// cat.dto.ts
import { StringOption } from 'necord';
export class CatDto {
@StringOption({
name: 'cat',
description: 'Choose a cat breed',
autocomplete: true,
required: true,
})
cat: string;
}最后,把该拦截器应用到 Slash 命令上:
// cats.commands.ts
import { Injectable, UseInterceptors } from '@nestjs/common';
import { Context, SlashCommand, Options, SlashCommandContext } from 'necord';
import { CatDto } from '/cat.dto';
import { CatsAutocompleteInterceptor } from './cats-autocomplete.interceptor';
@Injectable()
export class CatsCommands {
@UseInterceptors(CatsAutocompleteInterceptor)
@SlashCommand({
name: 'cat',
description: 'Retrieve information about a specific cat breed',
})
public async onSearch(
@Context() [interaction]: SlashCommandContext,
@Options() { cat }: CatDto,
) {
return interaction.reply({
content: `I found information on the breed of ${cat} cat!`,
});
}
}用户上下文菜单
用户命令会出现在右键点击用户(或触摸长按用户)时弹出的上下文菜单中。这类命令提供了直接面向用户的快捷操作。
// app.commands.ts
import { Injectable } from '@nestjs/common';
import { Context, UserCommand, UserCommandContext, TargetUser } from 'necord';
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 of ${user.username}`)
.setImage(user.displayAvatarURL({ size: 4096, dynamic: true })),
],
});
}
}消息上下文菜单
消息命令会出现在右键点击消息时的上下文菜单中,可用于执行与消息相关的快捷操作。
// app.commands.ts
import { Injectable } from '@nestjs/common';
import { Context, MessageCommand, MessageCommandContext, TargetMessage } from 'necord';
import { Message } from 'discord.js';
@Injectable()
export class AppCommands {
@MessageCommand({ name: 'Copy Message' })
public async copyMessage(
@Context() [interaction]: MessageCommandContext,
@TargetMessage() message: Message,
) {
return interaction.reply({ content: message.content });
}
}按钮
Buttons 是可以放进消息中的交互元素。用户点击后,会向你的应用发送一个 interaction。
// app.components.ts
import { Injectable } from '@nestjs/common';
import { Context, Button, ButtonContext } from 'necord';
@Injectable()
export class AppComponents {
@Button('BUTTON')
public onButtonClick(@Context() [interaction]: ButtonContext) {
return interaction.reply({ content: 'Button clicked!' });
}
}选择菜单
Select menus 是另一种可出现在消息上的交互组件。它们提供了类似下拉框的 UI,供用户选择选项。
// app.components.ts
import { Injectable } from '@nestjs/common';
import { Context, StringSelect, StringSelectContext, SelectedStrings } from 'necord';
@Injectable()
export class AppComponents {
@StringSelect('SELECT_MENU')
public onSelectMenu(
@Context() [interaction]: StringSelectContext,
@SelectedStrings() values: string[],
) {
return interaction.reply({ content: `You selected: ${values.join(', ')}` });
}
}完整的内置选择菜单组件列表请访问这里。
模态框
模态框(Modals)是弹出式表单,允许用户提交结构化输入。下面展示如何使用 Necord 创建并处理模态框:
// app.modals.ts
import { Injectable } from '@nestjs/common';
import { Context, Modal, ModalContext } from 'necord';
@Injectable()
export class AppModals {
@Modal('pizza')
public onModal(@Context() [interaction]: ModalContext) {
return interaction.reply({
content: `Your fav pizza : ${interaction.fields.getTextInputValue('pizza')}`
});
}
}更多信息
更多信息请访问 Necord 官网。