我已经开始使用Discord.js库在Node.js中创建Discord机器人。但是,所有代码都包含在一个索引文件中。
如何将命令和事件分别组织到单独的文件中,并在需要时运行它们?
答案 0 :(得分:4)
组织机器人代码的一种极好,简洁的方法是采用事件和命令处理程序。
从一个小的索引文件开始,以初始化客户端和其余代码。事件处理程序保留每个事件的文件,并在发出事件时对其进行调用。然后,在客户端的message
事件中,可以通过运行预期命令文件中的代码来避免长的if
链和switch
/ case
。
您需要了解的基本Node.js结构是module
。
[一个模块是]一组要包含在应用程序中的功能。
因此,将模块想像成一个整齐的,包含代码段的盒子。您可以将包装袋放在某个地方,打开它,然后拆开包装。用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.
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答案以进行递归搜索。
如何将这些处理程序用于重新加载命令?
如果将它们的代码放在函数内部,则可以设置一个“重载”命令来调用这些函数,然后再次装入事件和命令。