From 6ec6e198d7aad4672ec9072de2fcc5decbe10efe Mon Sep 17 00:00:00 2001 From: Dan Gabel Date: Thu, 12 Feb 2026 15:36:42 -0600 Subject: [PATCH] Adds --all-recipient handling for ListContact endpoints --- src/api/api.go | 18 +++++++-- src/client/client.go | 87 +++++++++++++++++++++++-------------------- src/docs/docs.go | 71 +++++++++++++++++++++-------------- src/docs/swagger.json | 71 +++++++++++++++++++++-------------- src/docs/swagger.yaml | 50 +++++++++++++++---------- 5 files changed, 177 insertions(+), 120 deletions(-) diff --git a/src/api/api.go b/src/api/api.go index c4c0731..f286979 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -2408,6 +2408,7 @@ func (a *Api) AddStickerPack(c *gin.Context) { // @Produce json // @Success 200 {object} []client.ListContactsResponse // @Param number path string true "Registered Phone Number" +// @Param all_recipients query string false "Include all known recipients, not only contacts." (default: false)" // @Router /v1/contacts/{number} [get] func (a *Api) ListContacts(c *gin.Context) { number, err := url.PathUnescape(c.Param("number")) @@ -2421,8 +2422,12 @@ func (a *Api) ListContacts(c *gin.Context) { return } - contacts, err := a.signalClient.ListContacts(number) - + allRecipients := c.DefaultQuery("all_recipients", "false") + if allRecipients != "true" && allRecipients != "false" { + c.JSON(400, Error{Msg: "Couldn't process request - all_recipients parameter needs to be either 'true' or 'false'"}) + return + } + contacts, err := a.signalClient.ListContacts(number, StringToBool(allRecipients)) if err != nil { c.JSON(400, Error{Msg: err.Error()}) return @@ -2437,6 +2442,7 @@ func (a *Api) ListContacts(c *gin.Context) { // @Produce json // @Success 200 {object} client.ListContactsResponse // @Param number path string true "Registered Phone Number" +// @Param all_recipients query string false "Include all known recipients, not only contacts." (default: false)" // @Router /v1/contacts/{number}/{uuid} [get] func (a *Api) ListContact(c *gin.Context) { number, err := url.PathUnescape(c.Param("number")) @@ -2456,7 +2462,13 @@ func (a *Api) ListContact(c *gin.Context) { return } - contact, err := a.signalClient.ListContact(number, uuid) + allRecipients := c.DefaultQuery("all_recipients", "false") + if allRecipients != "true" && allRecipients != "false" { + c.JSON(400, Error{Msg: "Couldn't process request - all_recipients parameter needs to be either 'true' or 'false'"}) + return + } + + contact, err := a.signalClient.ListContact(number, uuid, StringToBool(allRecipients)) if err != nil { switch err.(type) { case *client.NotFoundError: diff --git a/src/client/client.go b/src/client/client.go index 63770e6..3c882b3 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -2625,19 +2625,17 @@ type ListContactsSignlCliResponse struct { NickFamilyName string `json:"nickFamilyName"` } -func (s *SignalClient) ListContacts(number string) ([]ListContactsResponse, error) { +func (s *SignalClient) ListContacts(number string, allRecipients bool) ([]ListContactsResponse, error) { resp := []ListContactsResponse{} - type Request struct { - - AllRecipients bool `json:"allRecipients"` - } - - req :=Request{ AllRecipients: true} - + var err error var rawData string if s.signalCliMode == JsonRpc { + type Request struct { + AllRecipients bool `json:"allRecipients"` + } + req :=Request{ AllRecipients: allRecipients } jsonRpc2Client, err := s.getJsonRpc2Client() if err != nil { return nil, err @@ -2648,6 +2646,9 @@ func (s *SignalClient) ListContacts(number string) ([]ListContactsResponse, erro } } else { cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "listContacts"} + if !allRecipients { + cmd = append(cmd, "--all-recipients") + } rawData, err = s.cliClient.Execute(true, cmd, "") if err != nil { return resp, err @@ -2688,23 +2689,21 @@ func (s *SignalClient) ListContacts(number string) ([]ListContactsResponse, erro return resp, nil } -func (s *SignalClient) ListContact(number string, uuid string) ([]ListContactsResponse, error) { +func (s *SignalClient) ListContact(number string, uuid string, allRecipients bool) (ListContactsResponse, error) { + var resp ListContactsResponse + + var err error + var rawData string - resp := []ListContactsResponse{} + if s.signalCliMode == JsonRpc { type Request struct { Recipient string `json:"recipient"` AllRecipients bool `json:"allRecipients"` } - - req :=Request{ Recipient: uuid, AllRecipients: true} - - var err error - var rawData string - - if s.signalCliMode == JsonRpc { + req :=Request{ Recipient: uuid, AllRecipients: allRecipients } jsonRpc2Client, err := s.getJsonRpc2Client() if err != nil { - return nil, err + return resp, err } rawData, err = jsonRpc2Client.getRaw("listContacts", &number, req) if err != nil { @@ -2712,6 +2711,10 @@ func (s *SignalClient) ListContact(number string, uuid string) ([]ListContactsRe } } else { cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "listContacts"} + if allRecipients { + cmd = append(cmd, "--all-recipients") + } + cmd = append(cmd, uuid) rawData, err = s.cliClient.Execute(true, cmd, "") if err != nil { return resp, err @@ -2725,31 +2728,33 @@ func (s *SignalClient) ListContact(number string, uuid string) ([]ListContactsRe return resp, errors.New("Couldn't process request - invalid signal-cli response") } - for _, value := range signalCliResp { - entry := ListContactsResponse{ - Number: value.Number, - Uuid: value.Uuid, - Name: value.Name, - ProfileName: value.ProfileName, - Username: value.Username, - Color: value.Color, - Blocked: value.Blocked, - MessageExpiration: value.MessageExpiration, - Note: value.Note, - GivenName: value.GivenName, - } - entry.Profile.About = value.Profile.About - entry.Profile.HasAvatar = value.Profile.HasAvatar - entry.Profile.LastUpdatedTimestamp = value.Profile.LastUpdateTimestamp - entry.Profile.GivenName = value.Profile.GivenName - entry.Profile.FamilyName = value.Profile.FamilyName - entry.Nickname.Name = value.Nickname - entry.Nickname.GivenName = value.NickGivenName - entry.Nickname.FamilyName = value.NickFamilyName - resp = append(resp, entry) + if len(signalCliResp) == 0 { + return resp, &NotFoundError{Description: "No user with that id (" + uuid + ") found"} } - return resp, err + var value = signalCliResp[0] + contact := ListContactsResponse{ + Number: value.Number, + Uuid: value.Uuid, + Name: value.Name, + ProfileName: value.ProfileName, + Username: value.Username, + Color: value.Color, + Blocked: value.Blocked, + MessageExpiration: value.MessageExpiration, + Note: value.Note, + GivenName: value.GivenName, + } + contact.Profile.About = value.Profile.About + contact.Profile.HasAvatar = value.Profile.HasAvatar + contact.Profile.LastUpdatedTimestamp = value.Profile.LastUpdateTimestamp + contact.Profile.GivenName = value.Profile.GivenName + contact.Profile.FamilyName = value.Profile.FamilyName + contact.Nickname.Name = value.Nickname + contact.Nickname.GivenName = value.NickGivenName + contact.Nickname.FamilyName = value.NickFamilyName + + return contact, nil } func (s *SignalClient) SetPin(number string, registrationLockPin string) error { diff --git a/src/docs/docs.go b/src/docs/docs.go index f4fdbf7..3bf19e9 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -573,6 +573,12 @@ const docTemplate = `{ "name": "number", "in": "path", "required": true + }, + { + "type": "string", + "description": "Include all known recipients, not only contacts.", + "name": "all_recipients", + "in": "query" } ], "responses": { @@ -682,6 +688,12 @@ const docTemplate = `{ "name": "number", "in": "path", "required": true + }, + { + "type": "string", + "description": "Include all known recipients, not only contacts.", + "name": "all_recipients", + "in": "query" } ], "responses": { @@ -2653,7 +2665,7 @@ const docTemplate = `{ "type": "string" }, "permissions": { - "$ref": "#/definitions/api.GroupPermissions" + "$ref": "#/definitions/data.GroupPermissions" } } }, @@ -2727,32 +2739,6 @@ const docTemplate = `{ } } }, - "api.GroupPermissions": { - "type": "object", - "properties": { - "add_members": { - "type": "string", - "enum": [ - "only-admins", - "every-member" - ] - }, - "edit_group": { - "type": "string", - "enum": [ - "only-admins", - "every-member" - ] - }, - "send_messages": { - "type": "string", - "enum": [ - "only-admins", - "every-member" - ] - } - } - }, "api.LoggingConfiguration": { "type": "object", "properties": { @@ -3085,7 +3071,7 @@ const docTemplate = `{ "type": "string" }, "permissions": { - "$ref": "#/definitions/api.GroupPermissions" + "$ref": "#/definitions/data.GroupPermissions" } } }, @@ -3230,6 +3216,9 @@ const docTemplate = `{ "items": { "type": "string" } + }, + "permissions": { + "$ref": "#/definitions/data.GroupPermissions" } } }, @@ -3359,6 +3348,32 @@ const docTemplate = `{ } } }, + "data.GroupPermissions": { + "type": "object", + "properties": { + "add_members": { + "type": "string", + "enum": [ + "only-admins", + "every-member" + ] + }, + "edit_group": { + "type": "string", + "enum": [ + "only-admins", + "every-member" + ] + }, + "send_messages": { + "type": "string", + "enum": [ + "only-admins", + "every-member" + ] + } + } + }, "data.LinkPreviewType": { "type": "object", "properties": { diff --git a/src/docs/swagger.json b/src/docs/swagger.json index e509ae1..8e10645 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -570,6 +570,12 @@ "name": "number", "in": "path", "required": true + }, + { + "type": "string", + "description": "Include all known recipients, not only contacts.", + "name": "all_recipients", + "in": "query" } ], "responses": { @@ -679,6 +685,12 @@ "name": "number", "in": "path", "required": true + }, + { + "type": "string", + "description": "Include all known recipients, not only contacts.", + "name": "all_recipients", + "in": "query" } ], "responses": { @@ -2650,7 +2662,7 @@ "type": "string" }, "permissions": { - "$ref": "#/definitions/api.GroupPermissions" + "$ref": "#/definitions/data.GroupPermissions" } } }, @@ -2724,32 +2736,6 @@ } } }, - "api.GroupPermissions": { - "type": "object", - "properties": { - "add_members": { - "type": "string", - "enum": [ - "only-admins", - "every-member" - ] - }, - "edit_group": { - "type": "string", - "enum": [ - "only-admins", - "every-member" - ] - }, - "send_messages": { - "type": "string", - "enum": [ - "only-admins", - "every-member" - ] - } - } - }, "api.LoggingConfiguration": { "type": "object", "properties": { @@ -3082,7 +3068,7 @@ "type": "string" }, "permissions": { - "$ref": "#/definitions/api.GroupPermissions" + "$ref": "#/definitions/data.GroupPermissions" } } }, @@ -3227,6 +3213,9 @@ "items": { "type": "string" } + }, + "permissions": { + "$ref": "#/definitions/data.GroupPermissions" } } }, @@ -3356,6 +3345,32 @@ } } }, + "data.GroupPermissions": { + "type": "object", + "properties": { + "add_members": { + "type": "string", + "enum": [ + "only-admins", + "every-member" + ] + }, + "edit_group": { + "type": "string", + "enum": [ + "only-admins", + "every-member" + ] + }, + "send_messages": { + "type": "string", + "enum": [ + "only-admins", + "every-member" + ] + } + } + }, "data.LinkPreviewType": { "type": "object", "properties": { diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 7e7ee54..48c00b5 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -61,7 +61,7 @@ definitions: name: type: string permissions: - $ref: '#/definitions/api.GroupPermissions' + $ref: '#/definitions/data.GroupPermissions' type: object api.CreateGroupResponse: properties: @@ -110,24 +110,6 @@ definitions: error: type: string type: object - api.GroupPermissions: - properties: - add_members: - enum: - - only-admins - - every-member - type: string - edit_group: - enum: - - only-admins - - every-member - type: string - send_messages: - enum: - - only-admins - - every-member - type: string - type: object api.LoggingConfiguration: properties: Level: @@ -349,7 +331,7 @@ definitions: name: type: string permissions: - $ref: '#/definitions/api.GroupPermissions' + $ref: '#/definitions/data.GroupPermissions' type: object api.UpdateProfileRequest: properties: @@ -445,6 +427,8 @@ definitions: items: type: string type: array + permissions: + $ref: '#/definitions/data.GroupPermissions' type: object client.IdentityEntry: properties: @@ -528,6 +512,24 @@ definitions: username_link: type: string type: object + data.GroupPermissions: + properties: + add_members: + enum: + - only-admins + - every-member + type: string + edit_group: + enum: + - only-admins + - every-member + type: string + send_messages: + enum: + - only-admins + - every-member + type: string + type: object data.LinkPreviewType: properties: base64_thumbnail: @@ -928,6 +930,10 @@ paths: name: number required: true type: string + - description: Include all known recipients, not only contacts. + in: query + name: all_recipients + type: string produces: - application/json responses: @@ -978,6 +984,10 @@ paths: name: number required: true type: string + - description: Include all known recipients, not only contacts. + in: query + name: all_recipients + type: string produces: - application/json responses: