diff --git a/plugins/platforms/buzz/adapter.py b/plugins/platforms/buzz/adapter.py index 04013dd212..242e2c20a0 100644 --- a/plugins/platforms/buzz/adapter.py +++ b/plugins/platforms/buzz/adapter.py @@ -1897,7 +1897,7 @@ class BuzzAdapter(BasePlatformAdapter): message_type = MessageType.DOCUMENT source = self.build_source( chat_id=chat_id, chat_name=self._channel_names.get(chat_id, chat_id), chat_type=chat_type, - user_id=user_id, user_name=user_name, thread_id=thread_id, + user_id=user_id, user_name=user_name, thread_id=thread_id, message_id=message_id, ) event = MessageEvent( text=text, message_type=message_type, source=source, raw_message=raw_message, message_id=message_id, diff --git a/plugins/platforms/dingtalk/adapter.py b/plugins/platforms/dingtalk/adapter.py index 215296773e..77815819c6 100644 --- a/plugins/platforms/dingtalk/adapter.py +++ b/plugins/platforms/dingtalk/adapter.py @@ -365,7 +365,8 @@ class DingTalkAdapter(BasePlatformAdapter): if not text and not media_urls: return logger.debug("[%s] Empty message, skipping", self.name) source = self.build_source(chat_id=chat_id, chat_name=getattr(message, "conversation_title", None), chat_type="group" if is_group else "dm", - user_id=sender_id, user_name=sender_nick, user_id_alt=sender_staff_id if sender_staff_id else None) + user_id=sender_id, user_name=sender_nick, user_id_alt=sender_staff_id if sender_staff_id else None, + message_id=msg_id) create_at = getattr(message, "create_at", None) try: timestamp = datetime.fromtimestamp(int(create_at) / 1000, tz=timezone.utc) if create_at else datetime.now(tz=timezone.utc) diff --git a/plugins/platforms/email/adapter.py b/plugins/platforms/email/adapter.py index 47b57564ff..0839f19d5d 100644 --- a/plugins/platforms/email/adapter.py +++ b/plugins/platforms/email/adapter.py @@ -644,7 +644,8 @@ class EmailAdapter(BasePlatformAdapter): event = MessageEvent( text=text or "(empty email)", message_id=msg_data["message_id"], message_type=MessageType.DOCUMENT if "document" in kinds else MessageType.PHOTO if "image" in kinds else MessageType.TEXT, - source=self.build_source(chat_id=sender_addr, chat_name=name, chat_type="dm", user_id=sender_addr, user_name=name), + source=self.build_source(chat_id=sender_addr, chat_name=name, chat_type="dm", user_id=sender_addr, user_name=name, + message_id=msg_data["message_id"]), media_urls=[att["path"] for att in attachments], media_types=[att["media_type"] for att in attachments], reply_to_message_id=msg_data["in_reply_to"] or None) logger.info("[Email] New message from %s: %s", sender_addr, subject) diff --git a/plugins/platforms/google_chat/adapter.py b/plugins/platforms/google_chat/adapter.py index 6d664f4fb8..8b89b29c24 100644 --- a/plugins/platforms/google_chat/adapter.py +++ b/plugins/platforms/google_chat/adapter.py @@ -920,7 +920,7 @@ class GoogleChatAdapter(BasePlatformAdapter): # Email is the canonical id (allowlists use emails); the ``users/{id}`` # resource name moves to user_id_alt. user_id=(sender_email or sender_name), user_name=sender.get("displayName") or sender_email or sender_name, - thread_id=session_thread_id, user_id_alt=(sender_name or None), + thread_id=session_thread_id, user_id_alt=(sender_name or None), message_id=msg.get("name") or None, ) return MessageEvent( text=text, message_type=message_type, source=source, raw_message=msg, message_id=msg.get("name") or None, diff --git a/plugins/platforms/line/adapter.py b/plugins/platforms/line/adapter.py index dc3e7d760d..40abd2d5f4 100644 --- a/plugins/platforms/line/adapter.py +++ b/plugins/platforms/line/adapter.py @@ -582,7 +582,8 @@ class LineAdapter(BasePlatformAdapter): if chat_type == "dm" and self._client: # best-effort typing indicator (DM only) asyncio.create_task(self._client.loading(chat_id)) source_obj = self.build_source( - chat_id=chat_id, chat_type=chat_type, user_id=user_id, user_name=user_id, chat_name=chat_id) + chat_id=chat_id, chat_type=chat_type, user_id=user_id, user_name=user_id, chat_name=chat_id, + message_id=message_id) await self.handle_message(MessageEvent( text=text, message_type=_LINE_MESSAGE_TYPES.get(msg_type, MessageType.TEXT), source=source_obj, raw_message=event, message_id=message_id, media_urls=media_urls, media_types=media_types)) diff --git a/plugins/platforms/ntfy/adapter.py b/plugins/platforms/ntfy/adapter.py index b2a8614822..145c795c96 100644 --- a/plugins/platforms/ntfy/adapter.py +++ b/plugins/platforms/ntfy/adapter.py @@ -256,7 +256,7 @@ class NtfyAdapter(BasePlatformAdapter): # NOT drive authorization, so user_id is fixed to the topic name. topic = event.get("topic") or self._topic source = self.build_source( - chat_id=topic, chat_name=topic, chat_type="dm", user_id=topic, user_name=topic) + chat_id=topic, chat_name=topic, chat_type="dm", user_id=topic, user_name=topic, message_id=msg_id) unix_ts, timestamp = event.get("time"), datetime.now(tz=timezone.utc) try: timestamp = datetime.fromtimestamp(int(unix_ts), tz=timezone.utc) if unix_ts else timestamp diff --git a/plugins/platforms/photon/adapter.py b/plugins/platforms/photon/adapter.py index a9659dae2c..50d83a7fb6 100644 --- a/plugins/platforms/photon/adapter.py +++ b/plugins/platforms/photon/adapter.py @@ -759,7 +759,7 @@ class PhotonAdapter(BasePlatformAdapter): def _event(text: str, mtype: MessageType = MessageType.TEXT, **kwargs: Any) -> MessageEvent: source = self.build_source(chat_id=space_id, chat_name=space_id, chat_type=chat_type, - user_id=sender_id, user_name=sender_id or None) + user_id=sender_id, user_name=sender_id or None, message_id=message_id) return MessageEvent(text=text, message_type=mtype, source=source, message_id=message_id, raw_message=event, timestamp=timestamp, **kwargs) if ctype in {"read", "read_receipt"}: # presence signal, not a user turn (receipts for our sends) diff --git a/plugins/platforms/sms/adapter.py b/plugins/platforms/sms/adapter.py index 5ec644fb25..488a0d16a7 100644 --- a/plugins/platforms/sms/adapter.py +++ b/plugins/platforms/sms/adapter.py @@ -259,7 +259,8 @@ class SmsAdapter(BasePlatformAdapter): return _twiml_response() logger.info("[sms] inbound from %s -> %s: %s", redact_phone(from_number), redact_phone(to_number), text[:80]) source = self.build_source( - chat_id=from_number, chat_name=from_number, chat_type="dm", user_id=from_number, user_name=from_number) + chat_id=from_number, chat_name=from_number, chat_type="dm", user_id=from_number, user_name=from_number, + message_id=message_sid) event = MessageEvent( text=text, message_type=MessageType.TEXT, source=source, raw_message=form, message_id=message_sid) # Non-blocking: Twilio expects a fast response diff --git a/plugins/platforms/teams/adapter.py b/plugins/platforms/teams/adapter.py index f487883de1..649d843010 100644 --- a/plugins/platforms/teams/adapter.py +++ b/plugins/platforms/teams/adapter.py @@ -492,7 +492,8 @@ class TeamsAdapter(BasePlatformAdapter): chat_type=_CHAT_TYPES.get(getattr(conv, "conversation_type", None) or "", "dm"), user_id=str(user_id), user_name=getattr(from_account, "name", None) or "", - guild_id=getattr(conv, "tenant_id", None) or self._tenant_id) + guild_id=getattr(conv, "tenant_id", None) or self._tenant_id, + message_id=msg_id) media: list = [m for m in [await self._cache_attachment(a) for a in getattr(activity, "attachments", None) or []] if m] media_kinds = [kind for _, _, kind in media] # media items are (path, media_type, kind) msg_type = next((t for kind, t in _MEDIA_KIND_PRECEDENCE if kind in media_kinds), MessageType.TEXT) diff --git a/plugins/platforms/wecom/adapter.py b/plugins/platforms/wecom/adapter.py index 772d433e6b..66eaa6d2fe 100644 --- a/plugins/platforms/wecom/adapter.py +++ b/plugins/platforms/wecom/adapter.py @@ -462,7 +462,8 @@ class WeComAdapter(WeComStreamMixin, WeComMediaMixin, ChatSendQueueMixin, BasePl if not text and not media_urls: logger.info("[%s] Empty WeCom message skipped: is_group=%s chat=%s msgtype=%r", self.name, is_group, chat_id, body.get("msgtype")) return - source = self.build_source(chat_id=chat_id, chat_type="group" if is_group else "dm", user_id=sender_id or None, user_name=sender_id or None) + source = self.build_source(chat_id=chat_id, chat_type="group" if is_group else "dm", user_id=sender_id or None, user_name=sender_id or None, + message_id=msg_id) event = MessageEvent( text=text, message_type=message_type, source=source, raw_message=payload, message_id=msg_id, media_urls=media_urls, media_types=media_types, reply_to_message_id=f"quote:{msg_id}" if has_reply_context else None, reply_to_text=reply_text if has_reply_context else None, timestamp=datetime.now(tz=timezone.utc), diff --git a/plugins/platforms/whatsapp/adapter.py b/plugins/platforms/whatsapp/adapter.py index a08c48232d..09f407554d 100644 --- a/plugins/platforms/whatsapp/adapter.py +++ b/plugins/platforms/whatsapp/adapter.py @@ -827,7 +827,8 @@ class WhatsAppAdapter(WhatsAppBehaviorMixin, BasePlatformAdapter): return None msg_type = self._classify_bridge_message(data) source = self.build_source(chat_id=data.get("chatId", ""), chat_name=data.get("chatName"), chat_type="group" if data.get("isGroup", False) else "dm", - user_id=data.get("senderId"), user_name=data.get("senderName")) + user_id=data.get("senderId"), user_name=data.get("senderName"), + message_id=data.get("messageId")) cached_urls, media_types = await self._collect_bridge_media(data, msg_type) body = data.get("body", "") if data.get("isGroup"):