使用discord-buttons实现点击按钮编辑Discord bot消息更新烘焙小游戏进度
问题修复与代码优化方案
原代码存在的核心问题
- 条件判断误用赋值运算符
=:你所有if (eggs = 1)这类写法都是把eggs赋值为1,不是判断相等,会导致逻辑完全不符合预期,应该用全等运算符=== - 重复注册事件监听:每次用户触发纸杯蛋糕制作逻辑时,都会给client新增一个
clickButton事件监听,多次触发后会有多个监听同时执行,进度数值会出现混乱,还会造成内存泄漏 - 重复代码冗余:所有编辑消息的内容只有进度数字不同,完全不需要写四层嵌套if,硬编码维护成本极高
- 缺少边界控制:鸡蛋数量达标后没有禁用按钮,用户还能继续点击导致数值溢出;也没有校验点击者是否是发起制作的玩家,其他人可以恶意干扰进度
- 异步操作未加await:
button.reply.defer()是异步方法,不加await可能会出现未捕获的异步报错
优化后可直接使用的代码
// 统一转小写匹配 const itemNames = args.join(' ').toLowerCase(); console.log(itemNames); if (itemNames === "cupcakes" || itemNames === "cupcake") { // 配置常量,后续改需求直接改这里就行 const TOTAL_EGGS = 4; const BASE_PROMPT = "Cupcakes, ok! Don't mess this up, dummy... We need to start with the eggs, why don't you crack a few into that bowl?"; // 记录发起制作的用户ID,避免其他人捣乱 const initiatorId = message.author.id; // 构建按钮 const eggsButton = new MessageButton() .setLabel("打鸡蛋!") .setStyle("blurple") // 如果你用的是旧版带按钮插件的discord.js v12,这里改回.setID("cupcakesStepOne") .setCustomId("cupcakesStepOne"); // 发送初始消息 // 旧版如果支持直接传按钮当第二个参数,可以按你原来的写法改 const stepMessage = await message.channel.send({ content: `${BASE_PROMPT} **0 / ${TOTAL_EGGS}**`, components: [{ type: 1, components: [eggsButton] }] }); let currentEggs = 0; // 用当前消息的组件收集器代替全局监听,任务结束自动销毁,不会内存泄漏 const buttonCollector = stepMessage.createMessageComponentCollector({ filter: i => i.user.id === initiatorId && i.customId === "cupcakesStepOne", time: 300000 // 可选:加5分钟超时,超时自动结束任务避免一直占用资源 }); buttonCollector.on('collect', async button => { // 旧版这里改回 await button.reply.defer(); await button.deferUpdate(); currentEggs += 1; // 动态更新进度,不需要写多个if分支 await stepMessage.edit(`${BASE_PROMPT} **${currentEggs} / ${TOTAL_EGGS}**`); // 达到目标后结束收集,禁用按钮 if (currentEggs >= TOTAL_EGGS) { eggsButton.setDisabled(true); await stepMessage.edit({ components: [{ type: 1, components: [eggsButton] }] }); buttonCollector.stop('completed'); // 这里可以直接加下一步的逻辑,比如触发加面粉的步骤 } }); // 可选:超时提示 buttonCollector.on('end', (_, reason) => { if (reason !== 'completed') { eggsButton.setDisabled(true); stepMessage.edit({ content: `${BASE_PROMPT} 操作超时已取消,请重新发起制作`, components: [{ type: 1, components: [eggsButton] }] }); } }); }
优化点说明
- 用消息组件收集器
createMessageComponentCollector代替全局事件监听,只会监听当前这条消息的按钮点击,任务结束自动销毁监听,不会有内存泄漏问题 - 增加了点击者校验,只有发起制作的玩家才能点击按钮,不会被其他人干扰
- 所有可配置的常量都抽出来放在顶部,后续要改鸡蛋数量、提示语直接改常量就行,不用到处找代码
- 动态拼接进度内容,删掉了冗余的嵌套if,代码逻辑更清晰
- 进度达标后自动禁用按钮,避免无效点击,还加了可选的超时逻辑,避免任务一直挂着占用资源
- 所有异步操作都加了await,避免异步报错
内容的提问来源于stack exchange,提问作者Pepega Pingu
相关产品推荐
相关产品推荐

