使用discord.js v14.9.0开发战斗命令时遇MessageActionRow构造函数错误
问题:discord.js v14.9.0 中 MessageActionRow 不是构造函数的报错
我正在用discord.js v14.9.0开发Discord机器人的战斗命令,代码如下:
const { SlashCommandBuilder } = require('@discordjs/builders'); const { MessageActionRow, MessageButtonComponent, Discord } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('combate') .setDescription('Empieza un combate contra un slime.'), async execute(interaction) { // Variables del usuario let usuario_vida = 100; let usuario_ataque = 0; // Variables del slime let slime_vida = 50; let slime_ataque = 0; // Función para lanzar el dado function rollDice(dado) { return Math.floor(Math.random() * dado) + 1; } // Función para mostrar el resultado del combate function mostrarResultado(ataque, defensa, ataque_roll, defensa_roll) { let resultado = ""; resultado += `El ${ataque} ha atacado y ha hecho ${ataque_roll} puntos de daño.\n`; resultado += `La ${defensa} ha recibido ${ataque_roll} puntos de daño y ahora tiene ${defensa_roll} puntos de vida.`; return resultado; } function mostrarTablaVidas(vida_usuario, vida_slime) { let tabla = "```"; tabla += `Slime: ${vida_slime} puntos de vida\n`; tabla += "- - -\n- - -\n- - -\n- - -\n- - -\n"; tabla += `Usuario: ${vida_usuario} puntos de vida\n` + "```"; return tabla; } // Función para actualizar la tabla de vidas async function actualizarTablaVidas(interaction, vida_usuario, vida_slime) { const tabla = mostrarTablaVidas(vida_usuario, vida_slime); const row = new MessageActionRow() .addComponents( new MessageButtonComponent() .setCustomId('ataque') .setLabel('Atacar') .setColor('PRIMARY') ); await interaction.editReply({ content: tabla, components: [row] }); } // Mostrar la tabla de vidas y el botón de ataque await actualizarTablaVidas(interaction, usuario_vida, slime_vida); // Registrar la función que se llamará cuando se haga clic en el botón de ataque const filter = i => i.customId === 'ataque' && i.user.id === interaction.user.id; const collector = interaction.channel.createMessageComponentCollector({ filter, time: 15000 }); collector.on('interactionCreate', async (buttonInteraction) => { if (buttonInteraction.isButton() && buttonInteraction.customId === 'ataque') { // El usuario ataca al slime const ataque_roll = rollDice(6); slime_vida -= ataque_roll; const resultado_usuario = mostrarResultado("usuario", "slime", ataque_roll, slime_vida); // El slime ataca al usuario const defensa_roll = rollDice(4); usuario_vida -= defensa_roll; const resultado_slime = mostrarResultado("slime", "usuario", defensa_roll, usuario_vida); // Actualizar la tabla de vidas await actualizarTablaVidas(buttonInteraction, usuario_vida, vida_slime); // Mostrar el resultado del ataque await buttonInteraction.followUp({ content: resultado_usuario + "\n" + resultado_slime }); // Comprobar si el combate ha terminado if (usuario_vida <= 0) { await buttonInteraction.followUp({ content: "Has perdido el combate." }); collector.stop(); } else if (slime_vida <= 0) { await buttonInteraction.followUp({ content: "Has ganado el combate." }); collector.stop(); } } }); // Tiempo máximo de espera para el botón de ataque collector.on('end', async () => { await interaction.editReply({ content: "El combate ha terminado." }); }); }, };
运行命令时控制台报错:
TypeError: MessageActionRow is not a constructor at actualizarTablaVidas (C:\Users\usago\OneDrive\Escritorio\RpgBot\commands\combate.js:43:25) at Object.execute (C:\Users\usago\OneDrive\Escritorio\RpgBot\commands\combate.js:54:15) at Client. (C:\Users\usago\OneDrive\Escritorio\RpgBot\index.js:33:17) at Client.emit (node:events:513:28) at InteractionCreateAction.handle (C:\Users\usago\OneDrive\Escritorio\RpgBot\node_modules\discord.js\src\client\actions\InteractionCreate.js:97:12) at module.exports [as INTERACTION_CREATE] (C:\Users\usago\OneDrive\Escritorio\RpgBot\node_modules\discord.js\src\client\websocket\handlers\INTERACTION_CREATE.js:4:36) at WebSocketManager.handlePacket (C:\Users\usago\OneDrive\Escritorio\RpgBot\node_modules\discord.js\src\client\websocket\WebSocketManager.js:352:31) at WebSocketShard.onPacket (C:\Users\usago\OneDrive\Escritorio\RpgBot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:494:22) at WebSocketShard.onMessage (C:\Users\usago\OneDrive\Escritorio\RpgBot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:328:10) at callListener (C:\Users\usago\OneDrive\Escritorio\RpgBot\node_modules\ws\lib\event-target.js:290:14)
预期效果:显示生命值表格和可点击的攻击按钮。
解决方案
1. 修正组件导入语句
discord.js v14 中,组件的命名和导入逻辑有更新:
- 移除无用的
Discord全局导入 - 按钮组件的正确名称是
MessageButton,而非MessageButtonComponent - 修正后的导入代码:
const { SlashCommandBuilder } = require('@discordjs/builders'); const { MessageActionRow, MessageButton } = require('discord.js');
2. 修正组件实例化代码
将new MessageButtonComponent()替换为new MessageButton(),对应修改actualizarTablaVidas函数内的代码:
const row = new MessageActionRow() .addComponents( new MessageButton() .setCustomId('ataque') .setLabel('Atacar') .setColor('PRIMARY') );
3. 修复其他潜在bug
- 组件收集器的触发事件应为
collect,而非interactionCreate - 调用
actualizarTablaVidas时,参数需传入实际变量slime_vida,而非未定义的vida_slime - 初始需先发送延迟回复(
deferReply),否则直接调用editReply会报错 - 按钮交互后需调用
deferUpdate确认响应,避免Discord提示交互超时
修正后的完整代码
const { SlashCommandBuilder } = require('@discordjs/builders'); const { MessageActionRow, MessageButton } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('combate') .setDescription('Empieza un combate contra un slime.'), async execute(interaction) { // Variables del usuario let usuario_vida = 100; let usuario_ataque = 0; // Variables del slime let slime_vida = 50; let slime_ataque = 0; // Función para lanzar el dado function rollDice(dado) { return Math.floor(Math.random() * dado) + 1; } // Función para mostrar el resultado del combate function mostrarResultado(ataque, defensa, ataque_roll, defensa_roll) { let resultado = ""; resultado += `El ${ataque} ha atacado y ha hecho ${ataque_roll} puntos de daño.\n`; resultado += `La ${defensa} ha recibido ${ataque_roll} puntos de daño y ahora tiene ${defensa_roll} puntos de vida.`; return resultado; } function mostrarTablaVidas(vida_usuario, vida_slime) { let tabla = "```"; tabla += `Slime: ${vida_slime} puntos de vida\n`; tabla += "- - -\n- - -\n- - -\n- - -\n- - -\n"; tabla += `Usuario: ${vida_usuario} puntos de vida\n` + "```"; return tabla; } // Función para actualizar la tabla de vidas async function actualizarTablaVidas(interaction, vida_usuario, vida_slime) { const tabla = mostrarTablaVidas(vida_usuario, vida_slime); const row = new MessageActionRow() .addComponents( new MessageButton() .setCustomId('ataque') .setLabel('Atacar') .setColor('PRIMARY') ); await interaction.editReply({ content: tabla, components: [row] }); } // 先发送延迟回复,避免editReply报错 await interaction.deferReply(); // Mostrar la tabla de vidas y el botón de ataque await actualizarTablaVidas(interaction, usuario_vida, slime_vida); // Registrar la función que se llamará cuando se haga clic en el botón de ataque const filter = i => i.customId === 'ataque' && i.user.id === interaction.user.id; const collector = interaction.channel.createMessageComponentCollector({ filter, time: 15000 }); collector.on('collect', async (buttonInteraction) => { if (buttonInteraction.isButton() && buttonInteraction.customId === 'ataque') { // El usuario ataca al slime const ataque_roll = rollDice(6); slime_vida -= ataque_roll; const resultado_usuario = mostrarResultado("usuario", "slime", ataque_roll, slime_vida); // El slime ataca al usuario const defensa_roll = rollDice(4); usuario_vida -= defensa_roll; const resultado_slime = mostrarResultado("slime", "usuario", defensa_roll, usuario_vida); // Actualizar la tabla de vidas await actualizarTablaVidas(buttonInteraction, usuario_vida, slime_vida); // Mostrar el resultado del ataque await buttonInteraction.followUp({ content: resultado_usuario + "\n" + resultado_slime }); // Comprobar si el combate ha terminado if (usuario_vida <= 0) { await buttonInteraction.followUp({ content: "Has perdido el combate." }); collector.stop(); } else if (slime_vida <= 0) { await buttonInteraction.followUp({ content: "Has ganado el combate." }); collector.stop(); } // 确认按钮交互,避免超时提示 await buttonInteraction.deferUpdate(); } }); // Tiempo máximo de espera para el botón de ataque collector.on('end', async () => { await interaction.editReply({ content: "El combate ha terminado.", components: [] }); }); }, };
内容的提问来源于stack exchange,提问作者TheX3Nzy
相关产品推荐
相关产品推荐

