discord.js v13 中如何使用文件对命令进行分类
How to categorize commands with files in discord.js v13
问题:
大家好,我想知道如何将我的命令分类到文件中,而不是全部放在 commands
文件中。例如,文件布局如下所示:
discord-bot/
├── node_modules
├── src/
├── commands/
└── Moderation/
└── command.js
├── Data/
└── config.json
├── .env
└── index.js
├── package-lock.json
└── package.json
我的index.js代码:
client.once('ready', () => {
incrementVersionNumber(config.version, ".");
console.log(`Successfully logged in as ${client.user.tag}`);
// Where the main part of adding the command files begins
const commands = [];
const commands_information = new Collection();
const commandFiles = fs.readdirSync("./src/commands").filter(file => file.endsWith(".js"));
const clientId = process.env.CLIENTID;
for(const file of commandFiles){
const command = require(`./commands/${file}`);
console.log(`Command loaded: ${command.data.name}`);
commands.push(command.data.toJSON());
commands_information.set(command.data.name, command);
}
// Where getting the command files ends
const rest = new REST({ version: '9' }).setToken(token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(clientId, 'guildId'),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
console.log('-----------------------------------------------');
} catch (error) {
console.error(error);
}
})();
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const { commandName } = interaction;
if (!commands_information.has(commandName)) return;
try {
await commands_information.get(commandName).execute(client, interaction, config);
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
});
});
我想知道的:
我知道这个显示有点乱,但请注意 ├── ── commands/
,请注意我是如何添加另一个文件夹并将命令放入其中的,而不是将命令放入 ├── ── commands/
本身。如何自定义 index.js
文件以查看 commands
文件夹中的每个文件夹,然后获取 category
文件夹中的每个文件?我试图添加一个 *
以查看它是否会抓取每个文件夹,但它只是抛出了一个错误,说 commands/*
不是一个目录。那我该怎么做呢?我以前也看到过这样做,所以我知道这是可能的。
对于这种类型的文件匹配,您需要使用glob。它 returns 一组东西,你可以使用模式 commands/**/*
让它工作。
我想通了,并使用以下代码解决了这个问题:
client.once('ready', () => {
incrementVersionNumber(config.version, ".");
console.log(`Successfully logged in as ${client.user.tag}`);
client.user.setActivity(`${client.guilds.fetch.length} Servers`, {type: 'WATCHING'});
categories = [
"Config",
"Entertainment",
"Games",
"Information",
"Miscellaneous",
"Moderation",
"Music",
];
const commands = [];
for (var i = 0; i < fs.readdirSync('./src/commands').length - 1; i++) {
const commands_information = new Collection();
const commandFiles = fs.readdirSync(`./src/commands/${categories[i]}`).filter(file => file.endsWith(".js"));
for(const file of commandFiles){
const command = require(`./commands/${categories[i]}/${file}`);
console.log(`Command loaded: ${command.data.name}`);
commands.push(command.data.toJSON());
commands_information.set(command.data.name, command);
}
}
const rest = new REST({ version: '9' }).setToken(token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(clientId, '910339489770111036'),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
console.log('-----------------------------------------------');
} catch (error) {
console.error(error);
}
})();
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const { commandName } = interaction;
if (!commands_information.has(commandName)) return;
try {
await commands_information.get(commandName).execute(client, interaction, config);
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
});
});
如果我没理解错的话,你想做的事情可以使用 dynamic imports 来完成。我猜您没有使用工具链来构建它,而是直接从 CLI 执行脚本,所以我不打算进行转译。
尝试按照这些步骤操作,这些只是建议,因此请随意实施它,但最适合您的项目:
找一个可以帮助你检索文件列表的库,glob是一个很好的开始选择。
在您的脚本中,首先创建一个函数来注册命令,我们假设该函数的签名是 registerFileCommand(filename: string, handler: () => void)
检索并循环遍历您的模块,每次迭代导入并注册它们:
glob("./commands/*.js", {}, (err, files) => {
files.forEach(async file => {
const cmd = generateCommandName(file);
registerFileCommand(file, (await import(file)).default);
});
});
- 使用这种方法时需要注意以下几点:
registerFileCommand
应该处理命令注册逻辑,这意味着命令模块的默认导出应该是调用命令时由 DiscordJS 执行的函数。
立即导入所有模块可能会出现问题,因此您可能希望导入命令 on-demand 而不是在启动时。那看起来像这样:
registerFileCommand(file, params => {
import(file).then(({ default }) => default(...params));
});
您将需要实施额外的逻辑来将模块名称解析为命令名称。这将完全取决于您的命名方案。例如,我们选择以 kebab 大小写 (my-command).
命名我们的命令模块
如果您想提供提示或帮助对话框来记录命令的可能用法,您可能还需要在导出中实现额外的逻辑。
问题:
大家好,我想知道如何将我的命令分类到文件中,而不是全部放在 commands
文件中。例如,文件布局如下所示:
discord-bot/
├── node_modules
├── src/
├── commands/
└── Moderation/
└── command.js
├── Data/
└── config.json
├── .env
└── index.js
├── package-lock.json
└── package.json
我的index.js代码:
client.once('ready', () => {
incrementVersionNumber(config.version, ".");
console.log(`Successfully logged in as ${client.user.tag}`);
// Where the main part of adding the command files begins
const commands = [];
const commands_information = new Collection();
const commandFiles = fs.readdirSync("./src/commands").filter(file => file.endsWith(".js"));
const clientId = process.env.CLIENTID;
for(const file of commandFiles){
const command = require(`./commands/${file}`);
console.log(`Command loaded: ${command.data.name}`);
commands.push(command.data.toJSON());
commands_information.set(command.data.name, command);
}
// Where getting the command files ends
const rest = new REST({ version: '9' }).setToken(token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(clientId, 'guildId'),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
console.log('-----------------------------------------------');
} catch (error) {
console.error(error);
}
})();
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const { commandName } = interaction;
if (!commands_information.has(commandName)) return;
try {
await commands_information.get(commandName).execute(client, interaction, config);
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
});
});
我想知道的:
我知道这个显示有点乱,但请注意 ├── ── commands/
,请注意我是如何添加另一个文件夹并将命令放入其中的,而不是将命令放入 ├── ── commands/
本身。如何自定义 index.js
文件以查看 commands
文件夹中的每个文件夹,然后获取 category
文件夹中的每个文件?我试图添加一个 *
以查看它是否会抓取每个文件夹,但它只是抛出了一个错误,说 commands/*
不是一个目录。那我该怎么做呢?我以前也看到过这样做,所以我知道这是可能的。
对于这种类型的文件匹配,您需要使用glob。它 returns 一组东西,你可以使用模式 commands/**/*
让它工作。
我想通了,并使用以下代码解决了这个问题:
client.once('ready', () => {
incrementVersionNumber(config.version, ".");
console.log(`Successfully logged in as ${client.user.tag}`);
client.user.setActivity(`${client.guilds.fetch.length} Servers`, {type: 'WATCHING'});
categories = [
"Config",
"Entertainment",
"Games",
"Information",
"Miscellaneous",
"Moderation",
"Music",
];
const commands = [];
for (var i = 0; i < fs.readdirSync('./src/commands').length - 1; i++) {
const commands_information = new Collection();
const commandFiles = fs.readdirSync(`./src/commands/${categories[i]}`).filter(file => file.endsWith(".js"));
for(const file of commandFiles){
const command = require(`./commands/${categories[i]}/${file}`);
console.log(`Command loaded: ${command.data.name}`);
commands.push(command.data.toJSON());
commands_information.set(command.data.name, command);
}
}
const rest = new REST({ version: '9' }).setToken(token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(clientId, '910339489770111036'),
{ body: commands },
);
console.log('Successfully reloaded application (/) commands.');
console.log('-----------------------------------------------');
} catch (error) {
console.error(error);
}
})();
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const { commandName } = interaction;
if (!commands_information.has(commandName)) return;
try {
await commands_information.get(commandName).execute(client, interaction, config);
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
});
});
如果我没理解错的话,你想做的事情可以使用 dynamic imports 来完成。我猜您没有使用工具链来构建它,而是直接从 CLI 执行脚本,所以我不打算进行转译。
尝试按照这些步骤操作,这些只是建议,因此请随意实施它,但最适合您的项目:
找一个可以帮助你检索文件列表的库,glob是一个很好的开始选择。
在您的脚本中,首先创建一个函数来注册命令,我们假设该函数的签名是
registerFileCommand(filename: string, handler: () => void)
检索并循环遍历您的模块,每次迭代导入并注册它们:
glob("./commands/*.js", {}, (err, files) => { files.forEach(async file => { const cmd = generateCommandName(file); registerFileCommand(file, (await import(file)).default); }); });
- 使用这种方法时需要注意以下几点:
registerFileCommand
应该处理命令注册逻辑,这意味着命令模块的默认导出应该是调用命令时由 DiscordJS 执行的函数。立即导入所有模块可能会出现问题,因此您可能希望导入命令 on-demand 而不是在启动时。那看起来像这样:
registerFileCommand(file, params => { import(file).then(({ default }) => default(...params)); });
您将需要实施额外的逻辑来将模块名称解析为命令名称。这将完全取决于您的命名方案。例如,我们选择以 kebab 大小写 (my-command).
命名我们的命令模块如果您想提供提示或帮助对话框来记录命令的可能用法,您可能还需要在导出中实现额外的逻辑。