如何为Discord.js机器人编写事件/命令处理程序? [英] How do I code event/command handlers for my Discord.js bot?

查看:91
本文介绍了如何为Discord.js机器人编写事件/命令处理程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

解决方案

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


简单来说.

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


什么是模块?

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

[一个模块是]一组要包含在应用程序中的功能.

引自 w3schools.com .

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


使用模块和导出.

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

出于处理程序的目的,您将只使用module对象的exports属性.通过对模块使用 require() 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()会返回一个对象,因此您可以 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.
 

现在,当您的客户端发出您要为其处理文件的事件之一时,该文件中的代码将运行.


创建命令处理程序.

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

设置命令文件.

您可以导出一个运行"函数一个配置对象,而不是仅导出一个函数.

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}.`);
}
 


常见问题解答.

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

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

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

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

查看答案以递归搜索.

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

如果将它们的代码放在函数内部,则可以设置一个重载"命令来调用这些函数,然后再次装入事件和命令.


相关资源.


编辑...

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

I've started creating a Discord bot in Node.js using the Discord.js library. However, all the code is contained within a single index file.

How do I organize the commands and events each into separate files, and run them when needed?

解决方案

An excellent, clean way to organize the code for your bot is to employ event and command handlers.


In simple terms.

You start out with a small index file to initialize the client and the rest of the code. An event handler keeps the files for each event, and calls on them when the event is emitted. Then, in your client's message event, you can avoid long if chains and switch/case altogether by running the code from the intended command's file.


What's a module?

The basic Node.js structure you'll need to understand is a module.

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

Quoted from w3schools.com.

So, think of a module as a neatly taped up box containing pieces of code. You can take the package somewhere, open it up, and unpack the pieces. In JavaScript terms, you can require the module somewhere else in your program, and utilize the code contained within it. Modules can contain variables, classes, functions, etc. that you need to use throughout different locations across your code.


Working with modules and exports.

Now that you know what a module is, you have to understand how to work with them.

For the purpose of the handlers, you're only going to be using the exports property of the module object. By using require() for a module, module.exports is returned. Consider the following setups.

A single export.

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?');

In Question.js, a new class, Question, is created and assigned to module.exports. Then, when Question.js is required in newQuestion.js, Question is declared as the exported class. It can be used just as usual.

Multiple exports.

Now, for example, if you needed to export multiple classes...

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(...);

In this way, module.exports is defined as an object, containing the created classes. This means that require() will return an object instead, so you can destructure the needed class from the object.


Creating the event handler.

You should start by creating a folder for your events, and create a file for each one. Name the files according to the name of the event. For example, for your client's message event, the file should be named message.js.

Setting up the event files.

Implementing what you now know about modules, you can code the event files. For example...

message.js

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

Setting up the handler.

To make the actual handler, you can place the following code in a function to load events...

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.

Now, when your client emits one of the events you have a file for, the code inside of it is run.


Creating the command handler.

Just like for the event handler, you should start by creating a separate folder for your commands, and create files for each individual command.

Setting up the command files.

Instead of exporting just one function, you can export a "run" function and a configuration object.

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.
};

Setting up the handler.

Just like the event handler, place this code in a function to load commands...

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}`);
}

In your client's message event, you can use the following code to run the commands...

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}.`);
}


FAQ.

What if I have a database related or other variable I need to pass through events/commands?

For events, you can pass your variable in event.on(...), following client. Then in your actual events, your function must include that parameter after client.

For commands, you can pass your variable into the run function when calling it in the message event. Again, in your function, you need to include the properly placed parameter.

What if I want to have commands/events within subfolders?

Check out this answer to search recursively.

How do I use these handlers for a reload command?

If you placed the code for them inside of functions, you can set up a "reload" command that calls those functions, loading the events and commands again.


Related resources.


Edits...

  • client.removeAllListeners() will remove all listeners attached to the client, including those originating from client instantiation. This can cause voice connection related errors, specifically Voice connection not established within 15 seconds being thrown. To prevent this issue, keep track of every listener function and remove each individually using client.removeListener(listener).

这篇关于如何为Discord.js机器人编写事件/命令处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆