Discord.js RPS斜杠命令调用edit方法报undefined属性读取错误
报错原因
你遇到的两类报错本质是拼写错误+API使用不规范导致的:
- 核心拼写错误:discord.js 中
interaction.reply()默认返回undefined,只有传入配置项fetchReply: true时,才会返回已发送消息的Message实例。你全程把fetchReply错写成了fetchyReply(多打了一个字母y),参数完全不生效,rpsMsg实际值为undefined,调用.edit()自然会触发"Cannot read properties of undefined (reading 'edit')"报错。 - 只读属性修改错误:discord.js 返回的 Message 对象里,
embeds、components属性是只读结构化对象,直接修改嵌套的color、description、disabled属性会触发属性读取错误,不能直接在原返回对象上改值。 - 额外逻辑隐患:你把按钮收集器绑定在整个频道上,同频道其他消息的按钮交互也会触发该收集器逻辑,容易出现串响应问题;原有过滤规则没有限制只能由命令发起者点击,其他用户点按钮也会触发游戏逻辑。
修复方案
- 把所有代码里写错的
fetchyReply全部替换为正确的fetchReply。 - 不要直接修改
rpsMsg上的原有embed和组件,输出结果时重新构造Embed和按钮组件即可,避免只读属性报错。 - 把收集器绑定到你发送的RPS游戏消息上,不要绑定整个频道;修改过滤规则,仅允许发起命令的用户操作按钮。
- 补上代码中漏引入的
SlashCommandBuilder依赖。
修复后可运行代码
// 补全缺失的SlashCommandBuilder引入 const { SlashCommandBuilder } = require("@discordjs/builders"); const { MessageEmbed, MessageActionRow, MessageButton, } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() .setName("rps") .setDescription("Rock Paper Scissors"), async execute(interaction) { const hand = [ { txt: "Rock", emoji: "✊", index: 0 }, { txt: "Paper", emoji: "✋", index: 1 }, { txt: "Scissors", emoji: "✌", index: 2 }, ]; const botMove = hand[Math.floor(Math.random() * 3)]; // 修正fetchReply拼写 const rpsMsg = await interaction.reply({ embeds: [ new MessageEmbed() .setColor("GREEN") .setTitle("RockPaperScissors") .setDescription("Choose a handsign!"), ], components: [ new MessageActionRow().addComponents( new MessageButton() .setCustomId("rps_rock") .setLabel("✊ Rock") .setStyle("PRIMARY"), new MessageButton() .setCustomId("rps_paper") .setLabel("✋ Paper") .setStyle("PRIMARY"), new MessageButton() .setCustomId("rps_scissors") .setLabel("✌ Scissors") .setStyle("PRIMARY") ), ], fetchReply: true, }); let win = 0; let userMove; // 仅允许发起命令的用户点击按钮 const filter = (i) => i.user.id === interaction.user.id; // 收集器绑定到当前游戏消息,而非整个频道 const collector = rpsMsg.createMessageComponentCollector({ filter, componentType: "BUTTON", time: 10000, }); collector.on("collect", async (i) => { const move = i.customId.split("_")[1]; userMove = hand.find((v) => v.txt.toLowerCase() == move); switch (move) { case "rock": win = botMove.index == 0 ? 1 : botMove.index == 1 ? 0 : 2; break; case "paper": win = botMove.index == 0 ? 2 : botMove.index == 1 ? 1 : 0; break; case "scissors": win = botMove.index == 0 ? 0 : botMove.index == 1 ? 2 : 1; break; } // 重新构造结果embed,不直接修改原消息的只读属性 const resultEmbed = new MessageEmbed() .setColor("BLUE") .setTitle("RockPaperScissors") .setDescription(`I choose ${botMove.txt}! ${ win == 0 ? "You lost!" : win == 1 ? "We tied!" : "You win!" } (${userMove.emoji} ${win == 0 ? "<" : win == 1 ? "=" : ">"} ${botMove.emoji})`); // 重新构造全禁用状态的按钮行 const disabledRow = new MessageActionRow().addComponents( new MessageButton() .setCustomId("rps_rock") .setLabel("✊ Rock") .setStyle("SECONDARY") .setDisabled(true), new MessageButton() .setCustomId("rps_paper") .setLabel("✋ Paper") .setStyle("SECONDARY") .setDisabled(true), new MessageButton() .setCustomId("rps_scissors") .setLabel("✌ Scissors") .setStyle("SECONDARY") .setDisabled(true) ); // 直接用按钮交互的update方法响应,无需手动调用rpsMsg.edit await i.update({ embeds: [resultEmbed], components: [disabledRow] }); collector.stop(); }); // 增加超时处理逻辑 collector.on("end", async (collected, reason) => { if (reason === "time" && !collected.size) { const timeoutEmbed = new MessageEmbed() .setColor("RED") .setTitle("RockPaperScissors") .setDescription("No response received, game timed out!"); await rpsMsg.edit({ embeds: [timeoutEmbed], components: [] }); } }); }, };
内容的提问来源于stack exchange,提问作者Ivan Suzikov
相关产品推荐
相关产品推荐

