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

标签 javascript node.js discord.js

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

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

最佳答案

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

简单来说。

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

什么是模块?

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

[A module is a] set of functions you want to include in your application.



引用自w3schools.com

因此,可以将模块想像成一个整齐的,包含代码段的盒子。您可以将包装袋放在某个地方,打开它,然后拆开包装。用JavaScript术语,您可以在程序中的其他位置要求该模块,并利用其中包含的代码。模块可以包含变量,类,函数等,您需要在代码的不同位置使用它们。

处理模块和导出。

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

出于处理程序的目的,您将只使用exports对象的module属性。通过将 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。然后,当Question.js中需要newQuestion.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)单独删除每个监听器函数。
  • 关于javascript - 如何为Discord.js机器人编写事件/命令处理程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56548550/

    相关文章:

    mysql - 如何使用sequelize获取对象数据?

    linux - 如何匹配文本中的关键字/短语?

    javascript - 异步函数和 javascript 变量范围的问题

    javascript - 在 IPFS 中上传数组

    javascript - Node.js/Javascript 多重递归 promise 在 View 中返回

    javascript - Discord JS 类别和返回 ID

    node.js - 创建同步类别和 channel

    javascript - 如何判断视频是否受版权保护? YouTube API v3

    javascript - 在模型更改之前编译 Angular 不起作用

    javascript 对象嵌套回调