Discord.js实现messageDelete事件Embed批量聚合发送方法
批量聚合发送删除日志实现方案
核心思路是用模块级缓存队列暂存窗口期内生成的日志Embed,通过单一定时器控制等待窗口,凑够阈值或者窗口超时就批量发送,避免每条消息单独开定时器、单独发消息。
实现逻辑拆解
- 在事件文件模块顶层定义3个缓存变量,模块加载时仅初始化一次,不会随事件触发重置:
logQueue:数组类型,暂存待发送的删除日志EmbedwaitTimer:存当前等待窗口的定时器ID,避免重复创建定时器cachedWebhook:缓存获取到的日志频道webhook,避免每次触发事件都重复请求接口拉取webhook列表
- 保留原有前置校验、单条Embed生成逻辑,生成完Embed后不直接创建延迟发送任务,而是将Embed推入
logQueue队列 - 每次入队后做判断:
- 如果队列长度累计达到5条:立刻清除当前等待定时器,取出队列内所有Embed批量发送,发送完成后清空队列、重置定时器和webhook缓存标记
- 如果当前没有运行中的等待定时器:启动5秒等待定时器,定时器到期后不管队列里剩几条日志,都一次性批量发送,发送完成后清空队列、重置相关标记
- 修正原有代码的权限判断bug:
permissions.has传多个权限时需要用数组包裹,否则只会校验第一个传入的权限 - Discord单条消息最多支持携带10个Embed,攒5条批量发送完全符合接口限制,不会触发接口报错
修改后完整代码
const Discord = require('discord.js') // 模块级缓存,仅在模块加载时初始化一次 const logQueue = []; let waitTimer = null; let cachedWebhook = null; module.exports = { name: 'messageDelete', async execute(client, message) { if (message.channel.type === 'DM') return; if (message.author == client) return; if (!message.author) return; if(message.embeds.length > 0) return; if(message.attachments.size) return; let Channel = client.channels.cache.get("CHANNEL-ID") if (!Channel){ return; } else if (Channel.type !== 'GUILD_TEXT') { return; } else if (!data.Mod.Logs.isEnabled){ return; } else if(!Channel.guild.me.permissions.has(["EMBED_LINKS", "VIEW_CHANNEL", "READ_MESSAGE_HISTORY", "VIEW_AUDIT_LOG", "SEND_MESSAGES"])) { return; } const timestamp = Math.floor(Date.now() / 1000) const Msg = message.toString().substr(0, 500); const DeletedLog = new Discord.MessageEmbed() .setAuthor({ name: message.author.username, iconURL: message.author.displayAvatarURL({dynamic: true, size: 2048}) }) .setTitle(`<a:Down:853495989796470815> Deleted Message`) .setDescription(`<a:iNFO:853495450111967253> **Member**: \`${message.author.tag}\` (${message.author.id})\n<:pp198:853494893439352842> **In**: ${message.channel}\n• **At**: <t:${timestamp}>\n\n<a:Right:877975111846731847> **Content**: \`\`\`\n${Msg || '❌ | Unkown message!'}\n\`\`\``) .setColor('RED') .setFooter({ text: message.guild.name, iconURL: message.guild.iconURL({dynamic: true}) }) .setTimestamp() .setThumbnail(message.author.displayAvatarURL({dynamic: true})) // 新日志入队 logQueue.push(DeletedLog); // 批量发送公共方法 const sendBatchLogs = async () => { try { if (waitTimer) { clearTimeout(waitTimer); waitTimer = null; } if (logQueue.length === 0) return; // 无缓存webhook时拉取并缓存 if (!cachedWebhook) { const webhooks = await Channel.fetchWebhooks(); cachedWebhook = webhooks.filter((w)=>w.type === "Incoming" && w.token).first(); const botname = client.user.username; if(!cachedWebhook){ cachedWebhook = await Channel.createWebhook(botname, {avatar: client.user.displayAvatarURL({ format: 'png', dynamic: true, size: 128 })}) } } // 批量发送队列内所有embed await cachedWebhook.send({embeds: [...logQueue]}).catch(() => {}); // 发送完成清空队列 logQueue.length = 0; } catch (err) { // 异常时重置缓存和队列,避免阻塞后续日志 logQueue.length = 0; cachedWebhook = null; } } // 凑够5条立刻触发批量发送 if (logQueue.length >= 5) { sendBatchLogs(); } // 无运行中定时器时启动5秒等待窗口 else if (!waitTimer) { waitTimer = setTimeout(() => { sendBatchLogs(); }, 5000); } return; } }
内容的提问来源于stack exchange,提问作者WOLF
相关产品推荐
相关产品推荐

