Telegram JS机器人重复发送消息问题排查求助
问题分析与解决方案
核心问题
- setInterval 未考虑异步任务耗时:7秒触发一次
watchChanges,但如果watchChanges内的异步操作(数据库查询、消息发送)执行时间超过7秒,setInterval会无视前一次任务是否完成,直接触发下一次,导致多轮任务同时处理群组,重复发送消息。 - forEach 遍历异步函数的错误用法:
groups.forEach(async (group) => {})中,forEach不会等待异步函数执行完成,会一次性启动所有checkGroup任务,但如果前一轮setInterval的任务还没结束,新的一轮又开始,就会重复处理同一个群组。 - 无任务锁机制:没有标记正在处理的群组或全局任务状态,无法阻止重复触发的任务继续执行。
- 回调时机错误:
getTransaction中的callback('Looped Successfully')在forEach之后立即调用,没有等待所有checkGroup完成,导致watchChanges提前结束,进一步加剧setInterval的重复触发问题。
修复步骤
1. 替换setInterval为带防抖的定时任务
用setTimeout替代setInterval,确保前一次任务完全执行后再启动下一次:
// index.js const Robot = require("bot"); const bot = new Telegraf(TGAPIKey); const instance = new Robot(bot); async function runLoop() { try { await instance.watchChanges(); } catch (error) { console.log(error); } finally { // 前一次任务完成后,再等待7秒启动下一次 setTimeout(runLoop, 7000); } } // 启动循环 runLoop();
2. 正确处理异步遍历
将forEach改为for...of,确保每个checkGroup完成后再处理下一个群组:
// crypt.js class transaction{ async getTransaction(callback) { let groups = await Group.find(); // 用for...of替代forEach,保证异步顺序执行 for (const group of groups) { try { await checkGroup(group); console.log('Checked Group', group?.groupName); } catch (err) { console.log('Failed to check group', err); } } // 所有群组处理完成后再调用回调 callback('Looped Successfully'); } }
3. 添加全局任务锁
在Bot类中添加一个标记,防止任务重复执行:
// bot.js const { transaction } = require("./crypt"); class Bot { constructor(bot) { this.bot = bot; this.transaction = new transaction(); this.isProcessing = false; // 任务锁标记 } completeLoop(message) { console.log("successfull loop", message); } async watchChanges() { // 如果正在处理,直接返回 if (this.isProcessing) return; this.isProcessing = true; try{ await Promise.all([ this.transaction.getTransaction(this.completeLoop.bind(this)), ]); } catch (error) { console.log(error); } finally { // 任务完成后释放锁 this.isProcessing = false; } } } module.exports = Bot;
4. 修复sendMessage的上下文问题
确保sendMessage能正确获取bot实例:
// crypt.js // 修改checkGroup,传入bot实例 async function checkGroup(group, bot) { // <--- Check Stuff---> if(checkStuff) { await sendMessage(msg, bot); } else { return; } } async function sendMessage(msg, bot) { await bot.telegram.sendMessage(msg).catch((error) => { console.log('error snd msg', error); }); } // 在bot.js的watchChanges中调用时传入this.bot // this.transaction.getTransaction(this.completeLoop.bind(this), this.bot); // 然后crypt.js的getTransaction接收bot参数,传递给checkGroup
额外建议
- 给每个群组添加处理标记,比如在数据库中记录
lastProcessedTime,避免短时间内重复处理同一群组。 - 增加日志的详细程度,记录每次处理的群组ID、时间戳,方便排查重复触发的具体场景。
内容的提问来源于stack exchange,提问作者plat_winz
相关产品推荐
相关产品推荐

