diff --git a/src/client/client.go b/src/client/client.go index 5dc5985..92008db 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -119,6 +119,7 @@ type GroupEntry struct { InternalId string `json:"internal_id"` Members []string `json:"members"` Blocked bool `json:"blocked"` + Member bool `json:"member"` PendingInvites []string `json:"pending_invites"` PendingRequests []string `json:"pending_requests"` InviteLink string `json:"invite_link"` @@ -143,6 +144,7 @@ type ExpandedGroupEntry struct { InternalId string `json:"internal_id"` Members []GroupMember `json:"members"` Blocked bool `json:"blocked"` + Member bool `json:"member"` PendingInvites []GroupMember `json:"pending_invites"` PendingRequests []GroupMember `json:"pending_requests"` InviteLink string `json:"invite_link"` @@ -1330,6 +1332,25 @@ func (s *SignalClient) RemoveAdminsFromGroup(number string, groupId string, admi return s.updateGroupAdmins(number, groupId, admins, false) } +func signalCliGroupEntryToExpandedGroupEntry(signalCliGroupEntry SignalCliGroupEntry) ExpandedGroupEntry { + var groupEntry ExpandedGroupEntry + groupEntry.InternalId = signalCliGroupEntry.Id + groupEntry.Name = signalCliGroupEntry.Name + groupEntry.Id = convertInternalGroupIdToGroupId(signalCliGroupEntry.Id) + groupEntry.Blocked = signalCliGroupEntry.IsBlocked + groupEntry.Member = signalCliGroupEntry.IsMember + groupEntry.Description = signalCliGroupEntry.Description + groupEntry.Permissions.SendMessages = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage) + groupEntry.Permissions.EditGroup = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage) + groupEntry.Permissions.AddMembers = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionAddMember) + groupEntry.Members = signalCliGroupEntry.Members + groupEntry.PendingInvites = signalCliGroupEntry.PendingMembers + groupEntry.PendingRequests = signalCliGroupEntry.RequestingMembers + groupEntry.Admins = signalCliGroupEntry.Admins + groupEntry.InviteLink = signalCliGroupEntry.GroupInviteLink + return groupEntry +} + func (s *SignalClient) GetGroupsExpanded(number string) ([]ExpandedGroupEntry, error) { groupEntries := []ExpandedGroupEntry{} @@ -1360,22 +1381,7 @@ func (s *SignalClient) GetGroupsExpanded(number string) ([]ExpandedGroupEntry, e } for _, signalCliGroupEntry := range signalCliGroupEntries { - var groupEntry ExpandedGroupEntry - groupEntry.InternalId = signalCliGroupEntry.Id - groupEntry.Name = signalCliGroupEntry.Name - groupEntry.Id = convertInternalGroupIdToGroupId(signalCliGroupEntry.Id) - groupEntry.Blocked = signalCliGroupEntry.IsBlocked - groupEntry.Description = signalCliGroupEntry.Description - groupEntry.Permissions.SendMessages = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage) - groupEntry.Permissions.EditGroup = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionSendMessage) - groupEntry.Permissions.AddMembers = signalCliGroupPermissionToRestApiGroupPermission(signalCliGroupEntry.PermissionAddMember) - groupEntry.Members = signalCliGroupEntry.Members - groupEntry.PendingInvites = signalCliGroupEntry.PendingMembers - groupEntry.PendingRequests = signalCliGroupEntry.RequestingMembers - groupEntry.Admins = signalCliGroupEntry.Admins - groupEntry.InviteLink = signalCliGroupEntry.GroupInviteLink - - groupEntries = append(groupEntries, groupEntry) + groupEntries = append(groupEntries, signalCliGroupEntryToExpandedGroupEntry(signalCliGroupEntry)) } return groupEntries, nil @@ -1390,7 +1396,7 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) { groupEntries := []GroupEntry{} for _, expandedGroupEntry := range expandedGroupEntries { groupEntry := GroupEntry{InternalId: expandedGroupEntry.InternalId, Name: expandedGroupEntry.Name, - Id: expandedGroupEntry.Id, Blocked: expandedGroupEntry.Blocked, Description: expandedGroupEntry.Description, + Id: expandedGroupEntry.Id, Blocked: expandedGroupEntry.Blocked, Member: expandedGroupEntry.Member, Description: expandedGroupEntry.Description, Permissions: expandedGroupEntry.Permissions, InviteLink: expandedGroupEntry.InviteLink} members := []string{} diff --git a/src/client/groups_test.go b/src/client/groups_test.go new file mode 100644 index 0000000..af58e6d --- /dev/null +++ b/src/client/groups_test.go @@ -0,0 +1,93 @@ +package client + +import ( + "encoding/json" + "testing" +) + +// sampleListGroupsJSON mirrors the shape of signal-cli's `listGroups` JSON output +// (see signal-cli's ListGroupsCommand / the jsonrpc man page). It contains a group +// the account is still in (isMember=true), a group it has left or been removed from +// (isMember=false) — the "ghost" group that the REST API previously could not +// distinguish — and a blocked-but-still-member group to verify the two flags are +// mapped independently. +const sampleListGroupsJSON = `[ + { + "id": "Pmpi+EfPWmsxiomLe9Nx2XF9HOE483p6iKiFj65iMwI=", + "name": "Current Group", + "description": "still a member", + "isMember": true, + "isBlocked": false, + "members": [{"number": "+15551230001", "uuid": "11111111-1111-1111-1111-111111111111"}], + "pendingMembers": [], + "requestingMembers": [], + "admins": [{"number": "+15551230001", "uuid": "11111111-1111-1111-1111-111111111111"}], + "groupInviteLink": "", + "permissionAddMember": "EVERY_MEMBER", + "permissionSendMessage": "EVERY_MEMBER" + }, + { + "id": "Zm9vYmFyYmF6cXV4MTIzNDU2Nzg5MGFiY2RlZmdoaWo=", + "name": "Left Group", + "description": "removed or left", + "isMember": false, + "isBlocked": false, + "members": [], + "pendingMembers": [], + "requestingMembers": [], + "admins": [], + "groupInviteLink": "" + }, + { + "id": "YmxvY2tlZGdyb3VwaWQwMDAwMDAwMDAwMDAwMDAwMDA=", + "name": "Blocked But Member", + "description": "blocked yet still a member", + "isMember": true, + "isBlocked": true, + "members": [], + "pendingMembers": [], + "requestingMembers": [], + "admins": [], + "groupInviteLink": "" + } +]` + +// TestSignalCliGroupEntryToExpandedGroupEntry verifies that the signal-cli isMember +// flag is carried through to the REST ExpandedGroupEntry.Member field, independently +// of the isBlocked -> Blocked mapping. +func TestSignalCliGroupEntryToExpandedGroupEntry(t *testing.T) { + var entries []SignalCliGroupEntry + if err := json.Unmarshal([]byte(sampleListGroupsJSON), &entries); err != nil { + t.Fatalf("failed to unmarshal sample listGroups JSON: %v", err) + } + if len(entries) != 3 { + t.Fatalf("expected 3 group entries, got %d", len(entries)) + } + + cases := []struct { + name string + wantMember bool + wantBlocked bool + }{ + {"Current Group", true, false}, + {"Left Group", false, false}, + {"Blocked But Member", true, true}, + } + + for i, c := range cases { + got := signalCliGroupEntryToExpandedGroupEntry(entries[i]) + + if got.Name != c.name { + t.Errorf("entry %d: Name = %q, want %q", i, got.Name, c.name) + } + if got.Member != c.wantMember { + t.Errorf("%s: Member = %v, want %v (must reflect signal-cli isMember)", c.name, got.Member, c.wantMember) + } + if got.Blocked != c.wantBlocked { + t.Errorf("%s: Blocked = %v, want %v", c.name, got.Blocked, c.wantBlocked) + } + if got.InternalId != entries[i].Id { + t.Errorf("%s: InternalId = %q, want %q", c.name, got.InternalId, entries[i].Id) + } + } +} diff --git a/src/docs/docs.go b/src/docs/docs.go index 08b0e9e..7e3b62b 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -806,6 +806,9 @@ const docTemplate = `{ "invite_link": { "type": "string" }, + "member": { + "type": "boolean" + }, "members": { "items": { "type": "string" @@ -838,6 +841,7 @@ const docTemplate = `{ "id", "internal_id", "invite_link", + "member", "members", "name", "pending_invites", diff --git a/src/docs/swagger.json b/src/docs/swagger.json index fd913c7..21ffe3d 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -801,6 +801,9 @@ "invite_link": { "type": "string" }, + "member": { + "type": "boolean" + }, "members": { "items": { "type": "string" @@ -833,6 +836,7 @@ "id", "internal_id", "invite_link", + "member", "members", "name", "pending_invites",