Discord.js v13 如何检测用户是否已点击特定消息的指定按钮
问题原因与解决方案
现有代码的核心错误点
Array.indexOf()用法错误:该方法第二个参数为搜索起始位置,并非你要匹配的消息ID,传参后逻辑完全不符合预期- 存储结构错误:将用户ID和消息ID作为独立元素存入数组,无法建立「某用户点击某消息按钮」的关联关系
- 判断逻辑错误:
indexOf未匹配到元素时返回-1,JS中-1属于真值,所以即使用户首次点击也会触发「已投票」的判断 - 清理逻辑错误:直接删除数组前两位的操作在多投票场景下会误删其他投票的记录
可行实现方案
方案1:修改现有Collector逻辑(推荐,匹配你原有实现思路)
改用Map结构存储点击记录,用消息ID:用户ID格式的字符串作为唯一键,关联用户和消息的点击关系,修改后代码如下:
// 用Map存储点击记录,键为`${messageId}:${userId}`格式的唯一标识 const alreadyPressed = new Map() // 如果你需要限制只有发起该交互的用户才能点击按钮,可以保留原filter中m.user.id === interaction.user.id的判断,多用户投票场景可删除该条件 const filter = m => m.customId === "No" && m.messageId === interaction.messageId; const collector = embedMsg.createMessageComponentCollector({filter: filter, time: 10000}); collector.on('collect', async i => { client.logger("collector1 collected") // 生成唯一标识 const voteKey = `${i.message.id}:${i.user.id}` // 判断是否已经点击过 if (alreadyPressed.has(voteKey)) { return i.reply({ content: `You've already voted ${i.user.username}!`, ephemeral: true }) } // 未点击过的逻辑 i.reply({ content: `Thank you for your vote ${i.user.username}!`, ephemeral: true }); alreadyPressed.set(voteKey, true) }); collector.on('end', collected => { client.logger(collected.size) // 直接清空当前collector对应的所有记录,不会影响其他投票 alreadyPressed.clear() });
方案2:通过interactionCreate全局监听实现
适合多投票同时存在的场景:
// 全局存储所有投票的点击记录 const globalVoteRecord = new Map() client.on('interactionCreate', async interaction => { // 只处理指定customId的按钮交互 if (!interaction.isButton() || interaction.customId !== 'No') return const voteKey = `${interaction.message.id}:${interaction.user.id}` if (globalVoteRecord.has(voteKey)) { return interaction.reply({ content: `你已经投过票了${interaction.user.username}!`, ephemeral: true }) } await interaction.reply({ content: `感谢你的投票${interaction.user.username}!`, ephemeral: true }); globalVoteRecord.set(voteKey, true) // 配合定时器在投票结束后删除对应记录,避免内存占用过高 setTimeout(() => { globalVoteRecord.delete(voteKey) }, 10000) })
内容的提问来源于stack exchange,提问作者TestUser404
相关产品推荐
相关产品推荐

