使用discord.js开发私有Bot时client.commands为空的问题求助
client.commands为空的排查与解决 问题描述
近期使用discord.js开发私有Discord机器人,初期运行状态正常:机器人登录无异常、Slash命令注册后输入“/”可正常显示、机器人出现在服务器成员列表中。但执行从其他项目复制的确认可用的命令时,触发如下报错:
TypeError: Cannot read properties of undefined (reading 'get') at Object.execute (..\moderationbot\src\events\general\interactionCreate.js:14:41) at Client.<anonymous> (..\moderationbot\src\deployEvents.js:22:58) at Client.emit (node:events:513:28) at InteractionCreateAction.handle (..\moderationbot\node_modules\discord.js\src\client\actions\InteractionCreate.js:97:12) at module.exports [as INTERACTION_CREATE] (..\moderationbot\node_modules\discord.js\src\client\websocket\handlers\INTERACTION_CREATE.js:4:36) at WebSocketManager.handlePacket (..\moderationbot\node_modules\discord.js\src\client\websocket\WebSocketManager.js:352:31) at WebSocketShard.onPacket (..\moderationbot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:494:22) at WebSocketShard.onMessage (..\moderationbot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:328:10) at callListener (..\moderationbot\node_modules\ws\lib\event-target.js:290:14) at WebSocket.onMessage (..\moderationbot\node_modules\ws\lib\event-target.js:209:9) Emitted 'error' event on Client instance at: at emitUnhandledRejectionOrErr (node:events:394:10) at process.processTicksAndRejections (node:internal/process/task_queues:84:21)
报错指向interactionCreate.js中尝试读取client.commands.get()时,client.commands未定义。已在index.js中定义client实例,在deployCommands.js中设置client.commands并导入到interactionCreate.js,但问题仍存在。
排查与解决步骤
1. 确认client.commands的初始化位置
如果deployCommands.js是单独运行的Slash命令注册脚本,该脚本内的client实例与机器人主进程的client实例完全独立,这里设置的client.commands不会同步到主进程。必须在机器人主进程(index.js)中初始化client.commands:
// index.js 主进程代码示例 const { Client, Collection, GatewayIntentBits } = require('discord.js'); const client = new Client({ intents: [GatewayIntentBits.Guilds] }); // 初始化commands集合 client.commands = new Collection(); // 加载所有命令文件 const fs = require('fs'); const commandFiles = fs.readdirSync('./src/commands').filter(file => file.endsWith('.js')); for (const file of commandFiles) { const command = require(`./src/commands/${file}`); // 将命令添加到集合中,key为命令名称 client.commands.set(command.data.name, command); } client.login('你的机器人Token');
2. 确保事件处理中使用的是同一个client实例
如果在interactionCreate.js中导入的是新创建的client实例,而非主进程中已初始化commands的实例,会导致client.commands为空。有两种解决方式:
- 方式一:直接导入主进程的client实例
// interactionCreate.js const client = require('../../index.js'); module.exports = { name: 'interactionCreate', execute(interaction) { if (!interaction.isChatInputCommand()) return; const command = client.commands.get(interaction.commandName); if (!command) return; // 执行命令逻辑 try { await command.execute(interaction); } catch (error) { console.error(error); await interaction.reply({ content: '执行命令时出错!', ephemeral: true }); } }, }; - 方式二:部署事件时传递client实例
对应的// deployEvents.js 加载事件时传递client const fs = require('fs'); const path = require('path'); module.exports = (client) => { const eventFolders = fs.readdirSync(path.join(__dirname, 'events')); for (const folder of eventFolders) { const eventFiles = fs.readdirSync(path.join(__dirname, 'events', folder)).filter(file => file.endsWith('.js')); for (const file of eventFiles) { const event = require(path.join(__dirname, 'events', folder, file)); if (event.once) { client.once(event.name, (...args) => event.execute(...args, client)); } else { client.on(event.name, (...args) => event.execute(...args, client)); } } } };interactionCreate.js接收client参数:module.exports = { name: 'interactionCreate', execute(interaction, client) { if (!interaction.isChatInputCommand()) return; const command = client.commands.get(interaction.commandName); // ...后续逻辑 }, };
3. 检查命令文件的加载逻辑
确认命令文件路径正确,且每个命令文件正确导出data和execute属性,data.name必须与注册到Discord的命令名称一致。例如一个标准命令文件:
// src/commands/ping.js const { SlashCommandBuilder } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('ping') .setDescription('回复Pong!'), async execute(interaction) { await interaction.reply('Pong!'); }, };
4. 分离命令部署与机器人运行逻辑
单独的deployCommands.js仅负责将命令注册到Discord API,不需要在其中处理client.commands。示例部署脚本:
// deployCommands.js const { REST, Routes } = require('discord.js'); const { clientId, guildId, token } = require('./config.json'); const fs = require('fs'); const commands = []; const commandFiles = fs.readdirSync('./src/commands').filter(file => file.endsWith('.js')); for (const file of commandFiles) { const command = require(`./src/commands/${file}`); commands.push(command.data.toJSON()); } const rest = new REST({ version: '10' }).setToken(token); (async () => { try { console.log(`正在刷新 ${commands.length} 条Slash命令...`); const data = await rest.put( Routes.applicationGuildCommands(clientId, guildId), { body: commands }, ); console.log(`成功重新加载 ${data.length} 条Slash命令`); } catch (error) { console.error(error); } })();
内容的提问来源于stack exchange,提问作者NamedDaniel

