如何为我的 Discord.js 机器人编写事件/命令处理程序?

Posted

技术标签:

【中文标题】如何为我的 Discord.js 机器人编写事件/命令处理程序?【英文标题】:How do I code event/command handlers for my Discord.js bot? 【发布时间】:2019-10-26 03:53:33 【问题描述】:

我已经开始使用 Discord.js 库在 Node.js 中创建 Discord 机器人。但是,所有代码都包含在一个索引文件中。

如何将命令和事件分别组织到单独的文件中,并在需要时运行它们?

【问题讨论】:

【参考方案1】:

为您的机器人组织代码的一种出色、简洁的方法是使用事件和命令处理程序。


简单来说。

您从一个小的索引文件开始初始化客户端和其余代码。事件处理程序保存每个事件的文件,并在事件发出时调用它们。然后,在您客户的 message 事件中,您可以通过运行预期命令文件中的代码来完全避免长 if 链和 switch/case


什么是模块?

您需要了解的基本 Node.js 结构是 module

[A module is a] 您希望在应用程序中包含的一组函数。

引自w3schools.com。

因此,可以将模块视为一个包含代码片段的整齐粘贴的盒子。你可以把包裹带到某个地方,打开它,然后拆开包装。在 javascript 术语中,您可以在程序中的其他地方要求该模块,并利用其中包含的代码。模块可以包含您需要在代码的不同位置使用的变量、类、函数等。


使用模块和导出。

既然您知道什么是模块,那么您必须了解如何使用它们。

出于处理程序的目的,您将只使用module 对象的exports 属性。通过将require() 用于模块,将返回module.exports。请考虑以下设置。

单个导出。

Question.js

class Question 
  constructor(author, details) 
    this.author = author;
    this.details = details;
    this.answers = [];
  


module.exports = Question;

newQuestion.js

const Question = require('./Question.js');

const myQuestion = new Question('me', 'How to code event/command handlers?');

Question.js 中,创建了一个新类 Question,并将其分配给 module.exports。然后,当newQuestion.js 中需要Question.js 时,Question 被声明为导出类。它可以像往常一样使用。

多个导出。

现在,例如,如果您需要导出多个类...

Posts.js

class Question ...
class Answer ...

module.exports =  Question, Answer ;

// Alternatively...
// module.exports.Question = Question;
// module.exports.Answer = Answer;

newQuestion.js

const  Question  = require('./Posts.js');

const myQuestion = new Question(...);

这样,module.exports 被定义为一个对象,包含创建的类。这意味着require() 将改为返回一个对象,因此您可以从该对象中destructure 所需的类。


创建事件处理程序。

您应该首先为您的活动创建一个文件夹,然后为每个活动创建一个文件。根据事件的名称命名文件。例如,对于您客户的message event,文件应命名为message.js

设置事件文件。

实现您现在对模块的了解,您可以编写事件文件。比如……

message.js

module.exports = (client, message) => 
  // This code will be executed when
  // the 'message' event is emitted.
;

设置处理程序。

要制作实际的处理程序,您可以将以下代码放在函数中以加载事件...

const requireAll = require('require-all');   // Don't forget to install!

const files = requireAll(                   // Require all the files within your
  dirname: `$__dirname/events`,            // event directory which have a name
  filter: /^(?!-)(.+)\.js$/                  // ending in '.js' NOT starting
);                                          // with '-' (a way to disable files).

client.removeAllListeners();                 // Prevent duplicate listeners on reload.
                                             // CAUTION: THIS REMOVES LISTENERS
                                             // ATTACHED BY DISCORD.JS!

for (const name in files)                   // Iterate through the files object
  const event = files[name];                 // and attach listeners to each
                                             // event, passing 'client' as the
  client.on(name, event.bind(null, client)); // first parameter, and the rest
                                             // of the expected parameters
  console.log(`Event loaded: $name`);      // afterwards. Then, log the
                                            // successful load to the console.

现在,当您的客户端发出您有文件的事件之一时,其中的代码就会运行。


创建命令处理程序。

就像事件处理程序一样,您应该首先为您的命令创建一个单独的文件夹,然后为每个单独的命令创建文件。

设置命令文件。

您可以导出一个“运行”函数一个配置对象,而不是只导出一个函数。

help.js

module.exports.run = async (client, message, args) => 
  // This code will be executed to
  // run the 'help' command.
;

module.exports.config = 
  name: 'help',
  aliases: ['h'] // Even if you don't want an alias, leave this as an array.
;

设置处理程序。

就像事件处理程序一样,将此代码放在一个函数中以加载命令...

const requireAll = require('require-all');   // Using the same npm module...

const files = requireAll(                   // Require all the files within your
  dirname: `$__dirname/commands`,          // command directory which have a name
  filter: /^(?!-)(.+)\.js$/                  // ending in '.js' NOT starting
);                                          // with '-' (a way to disable files).

client.commands = new Map();                 // Create new Maps for the corresponding
client.aliases = new Map();                  // command names/commands, and aliases.

for (const name in files)                   // Iterate through the files object
  const cmd = files[name];                   // and set up the 'commands' and
                                             // 'aliases' Maps. Then, log the
  client.commands.set(cmd.config.name, cmd); // successful load to the console.
  for (const a of cmd.config.aliases) client.aliases.set(a, cmd.config.name);

  console.log(`Command loaded: $cmd.config.name`);

在您客户端的message事件中,您可以使用以下代码运行命令...

const prefix = '!'; // Example
const [cmd, ...args] = message.content.trim().slice(prefix.length).split(/\s+/g);

const command = client.commands.get(cmd) || client.commands.get(client.aliases.get(cmd));
if (command) 
  command.run(client, message, args);
  console.log(`Executing $command.config.name command for $message.author.tag.`);


常见问题。

如果我需要传递事件/命令的数据库相关变量或其他变量怎么办?

对于事件,您可以在event.on(...) 中传递您的变量,在client 之后。那么在你的实际事件中,你的函数必须在client之后包含那个参数。

对于命令,您可以在 message 事件中调用时将变量传递给 run 函数。同样,在您的函数中,您需要包含正确放置的参数。

如果我想在子文件夹中包含命令/事件怎么办?

查看this 递归搜索的答案。

如何将这些处理程序用于重新加载命令?

如果您将它们的代码放在函数内,您可以设置一个“重新加载”命令来调用这些函数,再次加载事件和命令。


相关资源。

Node.js Documentation MDN Documentation W3Schools Tutorial require-all Package Discord.js Documentation

编辑...

client.removeAllListeners() 将删除 所有 附加到客户端的侦听器,包括那些源自客户端实例化的侦听器。这可能会导致与语音连接相关的错误,特别是抛出Voice connection not established within 15 seconds。为防止出现此问题,请跟踪每个侦听器函数并使用 client.removeListener(listener) 单独删除每个函数。

【讨论】:

以上是关于如何为我的 Discord.js 机器人编写事件/命令处理程序?的主要内容,如果未能解决你的问题,请参考以下文章

应该如何为我的 serverinfo.js 为 discord.js 定义执行/运行

如果 Discord.JS 中缺少要查找的字段,我如何为我的 MongoDB 集合添加新字段?

找不到节点:discord.js 的事件

使用 2 个 Dynos 在 Heroku 上运行 Discord.js 机器人会触发 2 个事件。每个 websocket 连接 1 个

如何为使用未知变量调用的对象获取智能感知?

如何为不和谐服务器制作“!踢”命令消息?