diff --git a/README.md b/README.md index e2c0b22..1220530 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,19 @@ volume. Agents send with `scripts/whatsapp-push.py` and resolve contacts with `scripts/whatsapp-contacts.py`. +Sends to a **first-contact recipient** (a number whose device list the bridge +has not cached) can stall in the usync device-list lookup while the link — and +`/health` — stay green. The gateway handles this itself: on a usync failure it +retries against the recipient's **LID** when the store knows one (any contact +who has messaged this account before — that path delivers where the raw-number +lookup stalls), then once more after a backoff (`WHATSAPP_SEND_USYNC_RETRIES`, +default 1; `WHATSAPP_SEND_USYNC_BACKOFF`, default 15 s), without ever re-sending +the parts of a multi-part send that already went out. If every attempt fails, +the pending send terminates with a clear error on `/sends`, and `/health` +reports `recipient_lookup_ok: false` (shown as a warning on `/gateways`; the +evidence decays after `WHATSAPP_RECIPIENT_LOOKUP_TTL`, default 30 min, since it +cannot be re-probed safely). + ### Telegram accounts Telegram is reached through the sibling `telegram-gateway` service, which logs in diff --git a/scripts/web-gateway.py b/scripts/web-gateway.py index 96eb9c1..5e5593f 100644 --- a/scripts/web-gateway.py +++ b/scripts/web-gateway.py @@ -1621,6 +1621,7 @@ def _start_conv_turn(cid: str) -> None: _SEND_SINGLE_RE = re.compile(r"^/sends/([^/]+)/([^/]+?)/?$") _SEND_ACTION_RE = re.compile(r"^/sends/([^/]+)/([^/]+)/(approve|reject)/?$") +_SEND_STATUS_RE = re.compile(r"^/sends/([^/]+)/([^/]+)/status/?$") _GATEWAY_HEALTH_RE = re.compile(r"^/gateways/([A-Za-z0-9._-]+)/health/?$") _GATEWAY_QR_RE = re.compile(r"^/gateways/([A-Za-z0-9._-]+)/qr/?$") @@ -1793,7 +1794,10 @@ def _render_channel_send_html(detail: dict, channel: str, request_id: str, next_ recipient = html.escape(detail.get("recipient") or detail.get("to") or "") cat = html.escape(detail.get("category") or "") msg = html.escape(detail.get("message") or "") - skip = html.escape(next_url) if next_url else "/sends" + # "Skip" jumps to the next pending request — rendered only when one exists + # (the nav already links back to /sends and the dashboard). + skip_btn = (f' Skip\n' + if next_url else "") meta_rows = [ f"
{msg}\n'
- + f'\n'
+ + f'