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



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

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

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


简单来说。

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


什么是模块?

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

[模块是]要包含在应用程序中的一组函数。

引自 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中,一个新的类,问题,被创建并分配给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()将返回一个对象,因此您可以从对象中解构所需的类。


创建事件处理程序。

您应该首先为事件创建一个文件夹,然后为每个事件创建一个文件。根据事件的名称命名文件。例如,对于客户端的message事件,该文件应命名为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.

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


创建命令处理程序。

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

设置命令文件。

您可以导出"run"函数配置对象,而不是只导出一个函数。

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 函数中。同样,在函数中,您需要包含正确放置的参数。

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

查看此答案以递归搜索。

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

如果将它们的代码放在函数中,则可以设置调用这些函数的"reload"命令,再次加载事件和命令。


相关资源。

  • 节点.js文档
  • MDN 文档
  • W3学校教程
  • require-all套餐
  • 不和谐.js文档

编辑。。。

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

相关内容

  • 没有找到相关文章

最新更新