diff --git a/plugins/platforms/simplex/adapter.py b/plugins/platforms/simplex/adapter.py index 9c3d22a429fa..9965a8d0adfb 100644 --- a/plugins/platforms/simplex/adapter.py +++ b/plugins/platforms/simplex/adapter.py @@ -354,13 +354,22 @@ async def _handle_new_chat_item(self, wrapper: dict) -> None: logger.debug("SimpleX: ignoring event with no chat_id") return - # Sender — for groups the message includes a chatItemMember sub-object - member = chat_item.get("chatItemMember") or {} + # Sender — current simplex-chat reports the group member under + # chatItem.chatDir.groupMember; older payloads used chatItemMember. + chat_dir = chat_item.get("chatDir") or {} + member = chat_dir.get("groupMember") or chat_item.get("chatItemMember") or {} if is_group and member: - sender_id = str(member.get("memberId") or member.get("id") or chat_id) + member_profile = member.get("memberProfile") or {} + sender_id = str( + member.get("memberId") + or member.get("groupMemberId") + or member.get("id") + or chat_id + ) sender_name = ( member.get("displayName") or member.get("localDisplayName") + or member_profile.get("displayName") or sender_id ) else: diff --git a/tests/gateway/test_simplex_plugin.py b/tests/gateway/test_simplex_plugin.py index 1048168aa6e6..61ed6a809981 100644 --- a/tests/gateway/test_simplex_plugin.py +++ b/tests/gateway/test_simplex_plugin.py @@ -263,6 +263,38 @@ async def test_handle_event_filters_own_corr_id(): assert own not in adapter._pending_corr_ids # discarded +@pytest.mark.asyncio +async def test_group_sender_from_chatdir_groupmember(): + """current simplex-chat reports the group sender under + chatItem.chatDir.groupMember, not the legacy chatItemMember key.""" + from gateway.config import PlatformConfig + cfg = PlatformConfig(enabled=True, extra={"ws_url": "ws://localhost:5225"}) + adapter = SimplexAdapter(cfg) + captured = AsyncMock() + adapter.handle_message = captured # type: ignore + + wrapper = { + "chatInfo": {"type": "group", "groupInfo": {"groupId": 7, "displayName": "hermes-agent"}}, + "chatItem": { + "content": {"msgContent": {"type": "text", "text": "hello"}}, + "meta": {"itemStatus": {"type": "rcvNew"}}, + "chatDir": { + "type": "groupRcv", + "groupMember": { + "memberId": "memb-abc", + "memberProfile": {"displayName": "Brandon"}, + }, + }, + }, + } + await adapter._handle_new_chat_item(wrapper) + + captured.assert_awaited_once() + event_obj = captured.await_args[0][0] + assert event_obj.source.user_id == "memb-abc" + assert event_obj.source.user_name == "Brandon" + + # --------------------------------------------------------------------------- # 9. Standalone (out-of-process) send for cron # ---------------------------------------------------------------------------