如何让Discord机器人检测单反应槽满8次用户反应后触发指定操作
Discord活动报名机器人功能实现方案
前置调整说明
- 必须补充
GUILD_MESSAGE_REACTIONS权限,否则机器人无法检测用户的表情反应 - 原cron表达式为每秒触发,已修改为每周五18点触发的配置,可按需调整时间
- 反应监听器配置了7天的有效期,完全满足数天的反应累计需求
- 请提前替换代码中的
NOTIFY_CHANNEL_ID为你的目标通知频道ID
完整修改后代码
const { Client, Intents } = require('discord.js'); const cron = require('cron'); // 配置项 const NOTIFY_CHANNEL_ID = '替换为你的通知频道ID'; const ACTIVE_DAYS = 7; // 报名有效时长,单位天 const MIN_PARTICIPANTS = 8; // 成团最低人数 const CRON_SCHEDULE = '0 0 18 * * 5'; // 每周五18点发送报名消息,从左到右参数:秒、分、时、日、月、周 const client = new Client({ intents: [ Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS // 新增反应监听权限 ] }); let currentMtgFormat = "VOW"; Date.prototype.addDays = function(days) { var date = new Date(this.valueOf()); date.setDate(date.getDate() + days); return date; } function getTimestampSeconds(daysFromToday) { let ms= new Date().addDays(daysFromToday); let sec= Math.floor((ms/1000)); return sec; }; // 生成报名消息 function generateSignupMsg() { let mondayDate = "<t:" + getTimestampSeconds(3) + ":D>"; let mondayDateShort = "<t:" + getTimestampSeconds(3) + ":d>"; let tuesdayDate = "<t:" + getTimestampSeconds(4) + ":D>"; let wednesdayDate = "<t:" + getTimestampSeconds(5) + ":D>"; let thursdayDate = "<t:" + getTimestampSeconds(6) + ":D>"; let fridayDate = "<t:" + getTimestampSeconds(7) + ":D>"; let saturdayDate = "<t:" + getTimestampSeconds(8) + ":D>"; let sundayDate = "<t:" + getTimestampSeconds(9) + ":D>"; let sundayDateShort = "<t:" + getTimestampSeconds(9) + ":d>"; return "**- Ouverture des inscriptions pour la semaine du " + mondayDateShort + " au " + sundayDateShort + " -**\n\nPour vous inscrire réagissez à ce poste avec vos jours de disponibilité : \n\n:regional_indicator_l: : Lundi " + mondayDate + " (20h) - Draft " + currentMtgFormat + "\n:regional_indicator_m: : Mardi " + tuesdayDate + " (20h) - Draft " + currentMtgFormat + "\n:regional_indicator_w: : Mercredi " + wednesdayDate + " (20h) - Draft " + currentMtgFormat + "\n:regional_indicator_j: : Jeudi " + thursdayDate + " (20h) - Draft " + currentMtgFormat + "\n:regional_indicator_v: : Vendredi " + fridayDate + " (20h30) - Draft " + currentMtgFormat + "\n:regional_indicator_s: : Samedi " + saturdayDate + " (20h30) - Draft " + currentMtgFormat + "\n:regional_indicator_d: : Dimanche " + sundayDate + " (20h) - Draft " + currentMtgFormat + "\n:alarm_clock: : Dimanche " + sundayDate + " : Draft Asynchrone (21h) - " + currentMtgFormat + "\n\nDès lors qu'une personne est le 8ème inscrit sur un créneau (ou 16ème pour un éventuel 2ème draft au même horaire), cette dernière DOIT lancer le message de Check-in dans le salon approprié selon le modèle indiqué dans <#911268701528002590> (ou prévenir un modérateur).\n\nLes joueurs inscrits supplémentaires (mais en nombre insuffisant pour constituer une POD) sont considérés comme prioritaires sur les remplacements éventuels (absence de check-in, désistement de dernière minute etc...)."; } client.once('ready', () => { console.log('IM SUPER READY YEA!') }) let scheduledPodsMessage = new cron.CronJob(CRON_SCHEDULE, async () => { const channel = client.channels.cache.get("910686979828633611"); const guild = client.guilds.cache.get("910603170336624640"); const notifyChannel = client.channels.cache.get(NOTIFY_CHANNEL_ID); if (!channel || !guild || !notifyChannel) return console.error('频道/服务器不存在'); // 发送报名消息 const sentMessage = await channel.send(generateSignupMsg()); // 预置表情 const emojis = [ guild.emojis.cache.get('911267403072167966'), guild.emojis.cache.get('911267403046998016'), guild.emojis.cache.get('911267403084730418'), guild.emojis.cache.get('911267403046985738'), guild.emojis.cache.get('911268283901177876'), guild.emojis.cache.get('911267403034415114'), guild.emojis.cache.get('911267403109912606'), '⏰' ]; for (const emoji of emojis) { await sentMessage.react(emoji); } // 反应过滤器:排除机器人,仅监听预置表情 const filter = (reaction, user) => { return !user.bot && emojis.some(e => e.id ? e.id === reaction.emoji.id : e === reaction.emoji.name); }; // 开启7天的反应监听 const reactionCollector = sentMessage.createReactionCollector({ filter, time: ACTIVE_DAYS * 24 * 60 * 60 * 1000, dispose: true // 支持用户取消反应的计数更新 }); // 监听新增反应 reactionCollector.on('collect', async (reaction, user) => { // 获取当前表情的所有非机器人用户 const users = await reaction.users.fetch(); const validUsers = users.filter(u => !u.bot); // 人数达到成团标准 if (validUsers.size >= MIN_PARTICIPANTS) { // 取前8个用户 const notifyUsers = validUsers.first(MIN_PARTICIPANTS); // 发送通知 await notifyChannel.send(`⏰ 活动场次成团!本次参与成员:${notifyUsers.map(u => `<@${u.id}>`).join(' ')}`); // 删除这8个用户的对应反应 for (const u of notifyUsers) { await reaction.users.remove(u.id); } } }); console.log('报名消息已发送,反应监听已开启') }, null, true); scheduledPodsMessage.start() client.login('你的机器人Token');
功能匹配说明
- 自动统计每个表情的非机器人反应数量,达到8人自动触发通知
- 通知自动@所有成团用户,发送到指定频道
- 通知发送后自动删除对应8人的反应,剩余用户继续累计可触发下一场成团通知
- 监听有效期默认7天,可通过
ACTIVE_DAYS参数灵活调整,满足长周期累计需求
内容的提问来源于stack exchange,提问作者Johann Kergal
相关产品推荐
相关产品推荐

