From 839d1ecfb19bc5deec1603ea1fb6b34ad91d4b7a Mon Sep 17 00:00:00 2001 From: franciscomaestre Date: Mon, 29 Jun 2026 21:14:09 -0500 Subject: [PATCH] feat(whatsapp): send read receipts (blue double-check) for incoming messages Mark each processed incoming WhatsApp message as read via neonize's mark_read(receipt=ReceiptType.READ), so senders see the blue double-check. The receipt is sent right after the message passes dedup and is best-effort: any failure is logged at debug level and swallowed, so it never blocks or breaks message handling. --- nanobot/channels/whatsapp.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/nanobot/channels/whatsapp.py b/nanobot/channels/whatsapp.py index 9e7e3e6f..27ac93fd 100644 --- a/nanobot/channels/whatsapp.py +++ b/nanobot/channels/whatsapp.py @@ -499,6 +499,32 @@ class WhatsAppChannel(BaseChannel): self._self_jids.add(jid) self._self_jids.add(_bare_jid(jid)) + async def _send_read_receipt( + self, client: Any, info: Any, source: Any, message_id: str + ) -> None: + """Send a read receipt (blue double-check) for an incoming message. + + Best-effort: any failure is logged at debug level and swallowed so it + never blocks message processing. + """ + if not message_id: + return + try: + from neonize.utils.enum import ReceiptType + + chat = _safe_attr(source, "Chat") + sender = _safe_attr(source, "Sender") + if chat is None or sender is None: + return + await client.mark_read( + message_id, + chat=chat, + sender=sender, + receipt=ReceiptType.READ, + ) + except Exception as exc: # noqa: BLE001 - read receipt is best-effort + self.logger.debug("Failed to send WhatsApp read receipt: {}", exc) + async def _handle_neonize_message(self, client: Any, event: Any) -> None: info = _safe_attr(event, "Info") message = _safe_attr(event, "Message") @@ -532,6 +558,9 @@ class WhatsAppChannel(BaseChannel): while len(self._processed_message_ids) > 1000: self._processed_message_ids.popitem(last=False) + # Mark the incoming message as read (blue double-check). Best-effort. + await self._send_read_receipt(client, info, source, message_id) + participant_jid = _normalize_jid(_safe_attr(source, "Sender")) sender_alt_jid = _normalize_jid(_safe_attr(source, "SenderAlt")) sender_candidates = [sender_alt_jid, participant_jid]