Fix duplicate WhatsApp outbound delivery.

Claim pending rows atomically and serialize the sidecar poller so overlapping polls cannot send the same reply twice; also stop inbound queue delivery from falling through to a second enqueue.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-07-22 11:41:52 +08:00
parent 10ae4abc1d
commit b6e023cd91
6 changed files with 193 additions and 14 deletions

View file

@ -1527,14 +1527,45 @@ def process_inbound_payload(payload: dict[str, Any]) -> dict[str, Any]:
except Exception:
pass
if wa_queue_delivery and last_outbound_id and not serial_replies:
return {
"ok": True,
"replies": [],
"delivery": "queued",
"outbound_message_id": last_outbound_id,
}
if serial_replies and not wa_queue_delivery:
if wa_queue_delivery:
# Always leave this branch when queue delivery is on — otherwise
# ``reply`` falls through to the bottom enqueue and WhatsApp
# gets the same final answer twice.
if last_outbound_id and not serial_replies:
return {
"ok": True,
"replies": [],
"delivery": "queued",
"outbound_message_id": last_outbound_id,
}
if serial_replies:
# Enqueue failed for at least one turn; sync fallback.
replies = serial_replies
if preface:
first = replies[0] if replies else None
if isinstance(first, dict) and first.get("text"):
first["text"] = f"{preface}\n\n{first.get('text')}"
elif preface:
replies.insert(
0,
{
"channel": inbound.channel,
"chat_id": inbound.external_chat_id,
"text": preface,
"attachments": [],
"metadata": {},
},
)
for r in replies or []:
if not isinstance(r, dict):
continue
ch = str(r.get("channel") or inbound.channel or "").strip().lower()
if ch in {"wechat", "weixin", "whatsapp"}:
_maybe_expand_reply_attachments_for_channel(r)
_maybe_add_media_path_for_wechat_reply(r)
return {"ok": True, "replies": replies}
return {"ok": True, "replies": [], "delivery": "queued"}
if serial_replies:
# Multiple serial turns: return all sync replies in order.
replies = serial_replies
# Skip the default single-reply assembly below.