You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js调用WhatsApp云API批量发消息陷入循环问题求助

解决WhatsApp云API Webhook批量发送消息的无限循环问题

核心问题拆解

  1. SendMessageWhatsApp未做异步等待:当前这个函数是同步逻辑,用https.request发起请求但没返回Promise,导致await SendMessageWhatsApp(msg)根本不会等API请求完成,批量发送逻辑彻底混乱,加上Webhook响应过早返回,触发WhatsApp重复推送事件。
  2. Webhook响应时机错误:处理文本消息时,调用sendMessagesToWhatsApp后靠.then()才返回200,但如果请求已经提前结束,会导致响应未正确发送,WhatsApp会反复推送同一条事件。
  3. 去重逻辑无效:仅靠时间戳过滤不可靠——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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:08:09