Node.js调用WhatsApp云API批量发消息陷入循环问题求助
解决WhatsApp云API Webhook批量发送消息的无限循环问题
核心问题拆解
SendMessageWhatsApp未做异步等待:当前这个函数是同步逻辑,用https.request发起请求但没返回Promise,导致await SendMessageWhatsApp(msg)根本不会等API请求完成,批量发送逻辑彻底混乱,加上Webhook响应过早返回,触发WhatsApp重复推送事件。- Webhook响应时机错误:处理文本消息时,调用
sendMessagesToWhatsApp后靠.then()才返回200,但如果请求已经提前结束,会导致响应未正确发送,WhatsApp会反复推送同一条事件。 - 去重逻辑无效:仅靠时间戳过滤不可靠——WhatsApp重复推送的事件timestamp不会变,且你设置的12分钟窗口太大;没用到消息唯一ID做幂等校验,这是WhatsApp去重的标准方案。
具体修复步骤
1. 把SendMessageWhatsApp改成异步函数
必须让它返回Promise,保证await能等待请求完成,避免批量发送时的并发混乱:
async function SendMessageWhatsApp(message: any): Promise<void> { return new Promise((resolve, reject) => { const data = JSON.stringify(message); const options = { host: "graph.facebook.com", path: `/v16.0/${ID_CELL}/messages`, method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${TOKEN_CLOUD_API_WSP}` } }; const req = https.request(options, res => { let responseData = ''; res.on("data", d => responseData += d); res.on("end", () => { // 等请求完成再记录日志 registerEvents(data, res.statusCode || 200, "SendMessageWhatsApp", Date.now()); res.statusCode >= 200 && res.statusCode < 300 ? resolve() : reject(new Error(`请求失败:${res.statusCode} - ${responseData}`)); }); }); req.on("error", error => { console.error(error); supabase.from('logs').insert([ { message: JSON.stringify(message), status: 500, task: "Internal server", error: String(error) } ]); reject(error); }); req.write(data); req.end(); }); }
2. 修正Webhook的响应时机
确保所有异步操作完成后再返回200,彻底杜绝WhatsApp重复推送:
if (typeMessage === "text") { sendStatusMessage("Typing...", from); const messagesToSend = await textProcess(messages, from); try { if (Array.isArray(messagesToSend)) { await sendMessagesToWhatsApp(messagesToSend); console.log("批量消息发送完成"); } else { await SendMessageWhatsApp(messagesToSend); } res.sendStatus(200); } catch (error) { console.error("发送消息出错:", error); res.sendStatus(200); } }
另外,audio/button/interactive类型的消息处理,也要改成await SendMessageWhatsApp(data);,保证请求完成后再响应。
3. 用消息ID做幂等去重
WhatsApp每条消息都有唯一id,在Supabase建表记录已处理的消息ID,从根源避免重复处理:
// 先获取消息唯一ID const messageId = messageObject[0]?.id; if (!messageId) { res.sendStatus(200); return; } // 查Supabase是否已处理过这条消息 const { data: existingLog } = await supabase .from('processed_messages') .select('id') .eq('message_id', messageId) .single(); if (existingLog) { console.log("这条消息已经处理过:", messageId); res.sendStatus(200); return; } // 先标记为已处理,再执行后续逻辑 await supabase.from('processed_messages').insert([ { message_id: messageId, processed_at: new Date().toISOString() } ]); // 继续后续的消息类型判断、处理逻辑...
需要提前在Supabase创建processed_messages表,给message_id加唯一索引,字段包含message_id(字符串)和processed_at(时间戳)。
4. 优化时间过滤逻辑
WhatsApp的timestamp是秒级,Date.now()是毫秒级,转换要准确,同时缩小时间窗口避免误判:
// 只处理10分钟内的消息 const tenMinutesAgo = Math.floor((Date.now() - 10 * 60 * 1000) / 1000); messageObject = messageObject.filter((message) => message.timestamp > tenMinutesAgo);
额外注意点
- 确认
registerEvents是异步函数,调用时要加await,避免日志丢失。 - 检查WhatsApp Webhook配置,只订阅
messages和statuses事件,减少无关触发。 - 批量发送的2秒延迟要确保在异步等待下生效,避免被WhatsApp限流。
内容的提问来源于stack exchange,提问作者Izlia
相关产品推荐
相关产品推荐

