You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Discord.js RPS斜杠命令调用edit方法报undefined属性读取错误

报错原因

你遇到的两类报错本质是拼写错误+API使用不规范导致的:

  1. 核心拼写错误:discord.js 中interaction.reply()默认返回undefined,只有传入配置项fetchReply: true时,才会返回已发送消息的Message实例。你全程把fetchReply错写成了fetchyReply(多打了一个字母y),参数完全不生效,rpsMsg实际值为undefined,调用.edit()自然会触发"Cannot read properties of undefined (reading 'edit')"报错。
  2. 只读属性修改错误:discord.js 返回的 Message 对象里,embeds、components属性是只读结构化对象,直接修改嵌套的color、description、disabled属性会触发属性读取错误,不能直接在原返回对象上改值。
  3. 额外逻辑隐患:你把按钮收集器绑定在整个频道上,同频道其他消息的按钮交互也会触发该收集器逻辑,容易出现串响应问题;原有过滤规则没有限制只能由命令发起者点击,其他用户点按钮也会触发游戏逻辑。
修复方案
  • 把所有代码里写错的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:45:31