fix(signal): read groupV2.id in envelope, fall back to legacy groupInfo (#27051)

Port from qwibitai/nanoclaw#1962: modern Signal V2-only groups surface on
dataMessage.groupV2.id, not groupInfo.groupId. signal-cli versions differ
in which field they expose for V2 groups — some forward the underlying
libsignal envelope verbatim (groupV2), others normalize everything into
groupInfo. Without a groupV2 read, V2-only groups appear as DMs because
groupInfo is undefined and the adapter misroutes them to the sender's
DM session.

Reads groupV2.id first, falls back to groupInfo.groupId. Also hardens
chat_name extraction against non-dict groupInfo payloads (crashed with
AttributeError under malformed envelopes).

6 new tests cover V2 routing, V1 legacy compatibility, V2-preferred
precedence, no-group DM path, allowlist enforcement, and malformed
payloads.
This commit is contained in:
Teknium 2026-05-16 11:53:57 -07:00 committed by GitHub
parent 35f25523c6
commit 6c2406c5e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 172 additions and 3 deletions

View file

@ -490,9 +490,19 @@ class SignalAdapter(BasePlatformAdapter):
if not data_message:
return
# Check for group message
# Check for group message.
# Modern Signal groups surface on dataMessage.groupV2.id; legacy V1
# groups still arrive under dataMessage.groupInfo.groupId. signal-cli
# versions differ in which field they expose for V2 groups — some
# forward the underlying libsignal envelope verbatim (groupV2), others
# normalize everything into groupInfo. Read groupV2 first and fall
# back to groupInfo so V2-only groups aren't misrouted as DMs.
group_info = data_message.get("groupInfo")
group_id = group_info.get("groupId") if group_info else None
group_v2 = data_message.get("groupV2")
group_id = (
(group_v2.get("id") if isinstance(group_v2, dict) else None)
or (group_info.get("groupId") if isinstance(group_info, dict) else None)
)
is_group = bool(group_id)
# Group message filtering — derived from SIGNAL_GROUP_ALLOWED_USERS:
@ -562,7 +572,7 @@ class SignalAdapter(BasePlatformAdapter):
# Build session source
source = self.build_source(
chat_id=chat_id,
chat_name=group_info.get("groupName") if group_info else sender_name,
chat_name=(group_info.get("groupName") if isinstance(group_info, dict) else None) or sender_name,
chat_type=chat_type,
user_id=sender,
user_name=sender_name or sender,