Skip to content

Commit 08cd2bd

Browse files
committed
added two new endpoints
* added endpoint to list a contact * added endpoint to get a contact's avatar
1 parent 3a2b77b commit 08cd2bd

File tree

6 files changed

+294
-16
lines changed

6 files changed

+294
-16
lines changed

src/api/api.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ func (a *Api) GetGroupAvatar(c *gin.Context) {
10141014
}
10151015
groupId := c.Param("groupid")
10161016

1017-
groupAvatar, err := a.signalClient.GetGroupAvatar(number, groupId)
1017+
groupAvatar, err := a.signalClient.GetAvatar(number, groupId, client.GroupAvatar)
10181018
if err != nil {
10191019
switch err.(type) {
10201020
case *client.NotFoundError:
@@ -2311,6 +2311,81 @@ func (a *Api) ListContacts(c *gin.Context) {
23112311
c.JSON(200, contacts)
23122312
}
23132313

2314+
// @Summary List Contact
2315+
// @Tags Contacts
2316+
// @Description List a specific contact.
2317+
// @Produce json
2318+
// @Success 200 {object} client.ListContactsResponse
2319+
// @Param number path string true "Registered Phone Number"
2320+
// @Router /v1/contacts/{number}/{uuid} [get]
2321+
func (a *Api) ListContact(c *gin.Context) {
2322+
number, err := url.PathUnescape(c.Param("number"))
2323+
if err != nil {
2324+
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
2325+
return
2326+
}
2327+
2328+
if number == "" {
2329+
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
2330+
return
2331+
}
2332+
2333+
uuid := c.Param("uuid")
2334+
if uuid == "" {
2335+
c.JSON(400, Error{Msg: "Couldn't process request - uuid missing"})
2336+
return
2337+
}
2338+
2339+
contact, err := a.signalClient.ListContact(number, uuid)
2340+
if err != nil {
2341+
switch err.(type) {
2342+
case *client.NotFoundError:
2343+
c.JSON(404, Error{Msg: err.Error()})
2344+
return
2345+
default:
2346+
c.JSON(400, Error{Msg: err.Error()})
2347+
return
2348+
}
2349+
}
2350+
2351+
c.JSON(200, contact)
2352+
}
2353+
2354+
// @Summary Returns the avatar of a contact
2355+
// @Tags Contacts
2356+
// @Description Returns the avatar of a contact.
2357+
// @Produce json
2358+
// @Success 200 {string} string "Image"
2359+
// @Param number path string true "Registered Phone Number"
2360+
// @Router /v1/contacts/{number}/{uuid}/avatar [get]
2361+
func (a *Api) GetProfileAvatar(c *gin.Context) {
2362+
number, err := url.PathUnescape(c.Param("number"))
2363+
if err != nil {
2364+
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
2365+
return
2366+
}
2367+
2368+
if number == "" {
2369+
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
2370+
return
2371+
}
2372+
2373+
uuid := c.Param("uuid")
2374+
if uuid == "" {
2375+
c.JSON(400, Error{Msg: "Couldn't process request - uuid missing"})
2376+
return
2377+
}
2378+
2379+
avatar, err := a.signalClient.GetAvatar(number, uuid, client.ProfileAvatar)
2380+
if err != nil {
2381+
c.JSON(400, Error{Msg: err.Error()})
2382+
return
2383+
}
2384+
2385+
mimeType := mimetype.Detect(avatar)
2386+
c.Data(200, mimeType.String(), avatar)
2387+
}
2388+
23142389
// @Summary Set Pin
23152390
// @Tags Accounts
23162391
// @Description Sets a new Signal Pin

src/client/client.go

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ const signalCliV2GroupError = "Cannot create a V2 group as self does not have a
2929

3030
const endpointNotSupportedInJsonRpcMode = "This endpoint is not supported in JSON-RPC mode."
3131

32+
type AvatarType int
33+
34+
const (
35+
GroupAvatar AvatarType = iota + 1
36+
ContactAvatar
37+
ProfileAvatar
38+
)
39+
3240
type GroupPermission int
3341

3442
const (
@@ -1323,21 +1331,33 @@ func (s *SignalClient) GetGroup(number string, groupId string) (*GroupEntry, err
13231331
return nil, nil
13241332
}
13251333

1326-
func (s *SignalClient) GetGroupAvatar(number string, groupId string) ([]byte, error) {
1334+
func (s *SignalClient) GetAvatar(number string, id string, avatarType AvatarType) ([]byte, error) {
13271335
var err error
13281336
var rawData string
13291337

1330-
internalGroupId, err := ConvertGroupIdToInternalGroupId(groupId)
1331-
if err != nil {
1332-
return []byte{}, errors.New("Invalid group id")
1338+
if avatarType == GroupAvatar {
1339+
id, err = ConvertGroupIdToInternalGroupId(id)
1340+
if err != nil {
1341+
return []byte{}, errors.New("Invalid group id")
1342+
}
13331343
}
13341344

13351345
if s.signalCliMode == JsonRpc {
13361346
type Request struct {
1337-
GroupId string `json:"groupId"`
1347+
GroupId string `json:"groupId,omitempty"`
1348+
Contact string `json:"contact,omitempty"`
1349+
Profile string `json:"profile,omitempty"`
13381350
}
13391351

1340-
request := Request{GroupId: internalGroupId}
1352+
var request Request
1353+
1354+
if avatarType == GroupAvatar {
1355+
request.GroupId = id
1356+
} else if avatarType == ContactAvatar {
1357+
request.Contact = id
1358+
} else if avatarType == ProfileAvatar {
1359+
request.Profile = id
1360+
}
13411361

13421362
jsonRpc2Client, err := s.getJsonRpc2Client()
13431363
if err != nil {
@@ -1351,8 +1371,21 @@ func (s *SignalClient) GetGroupAvatar(number string, groupId string) ([]byte, er
13511371
return []byte{}, err
13521372
}
13531373
} else {
1354-
rawData, err = s.cliClient.Execute(true, []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "getAvatar", "-g", internalGroupId}, "")
1374+
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "getAvatar"}
1375+
1376+
if avatarType == GroupAvatar {
1377+
cmd = append(cmd, []string{"-g", id}...)
1378+
} else if avatarType == ContactAvatar {
1379+
cmd = append(cmd, []string{"--contact", id}...)
1380+
} else if avatarType == ProfileAvatar {
1381+
cmd = append(cmd, []string{"--profile", id}...)
1382+
}
1383+
1384+
rawData, err = s.cliClient.Execute(true, cmd, "")
13551385
if err != nil {
1386+
if strings.Contains(err.Error(), "Could not find avatar") {
1387+
return []byte{}, &NotFoundError{Description: "No avatar found."}
1388+
}
13561389
return []byte{}, err
13571390
}
13581391
}
@@ -2528,6 +2561,21 @@ func (s *SignalClient) ListContacts(number string) ([]ListContactsResponse, erro
25282561
return resp, nil
25292562
}
25302563

2564+
func (s *SignalClient) ListContact(number string, uuid string) (ListContactsResponse, error) {
2565+
contacts, err := s.ListContacts(number)
2566+
if err != nil {
2567+
return ListContactsResponse{}, err
2568+
}
2569+
2570+
for _, contact := range contacts {
2571+
if contact.Uuid == uuid {
2572+
return contact, nil
2573+
}
2574+
}
2575+
2576+
return ListContactsResponse{}, &NotFoundError{Description: "No contact with that id (" + uuid + ") found"}
2577+
}
2578+
25312579
func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
25322580
if s.signalCliMode == JsonRpc {
25332581
type Request struct {
@@ -2544,11 +2592,10 @@ func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
25442592
}
25452593
} else {
25462594
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "setPin", registrationLockPin}
2547-
rawData, err := s.cliClient.Execute(true, cmd, "")
2595+
_, err := s.cliClient.Execute(true, cmd, "")
25482596
if err != nil {
25492597
return err
25502598
}
2551-
log.Info(string(rawData))
25522599
}
25532600
return nil
25542601
}

src/docs/docs.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,64 @@ const docTemplate = `{
665665
}
666666
}
667667
},
668+
"/v1/contacts/{number}/{uuid}": {
669+
"get": {
670+
"description": "List a specific contact.",
671+
"produces": [
672+
"application/json"
673+
],
674+
"tags": [
675+
"Contacts"
676+
],
677+
"summary": "List Contact",
678+
"parameters": [
679+
{
680+
"type": "string",
681+
"description": "Registered Phone Number",
682+
"name": "number",
683+
"in": "path",
684+
"required": true
685+
}
686+
],
687+
"responses": {
688+
"200": {
689+
"description": "OK",
690+
"schema": {
691+
"$ref": "#/definitions/client.ListContactsResponse"
692+
}
693+
}
694+
}
695+
}
696+
},
697+
"/v1/contacts/{number}/{uuid}/avatar": {
698+
"get": {
699+
"description": "Returns the avatar of a contact.",
700+
"produces": [
701+
"application/json"
702+
],
703+
"tags": [
704+
"Contacts"
705+
],
706+
"summary": "Returns the avatar of a contact",
707+
"parameters": [
708+
{
709+
"type": "string",
710+
"description": "Registered Phone Number",
711+
"name": "number",
712+
"in": "path",
713+
"required": true
714+
}
715+
],
716+
"responses": {
717+
"200": {
718+
"description": "Image",
719+
"schema": {
720+
"type": "string"
721+
}
722+
}
723+
}
724+
}
725+
},
668726
"/v1/devices/{number}": {
669727
"get": {
670728
"description": "List linked devices associated to this device.",

src/docs/swagger.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,64 @@
662662
}
663663
}
664664
},
665+
"/v1/contacts/{number}/{uuid}": {
666+
"get": {
667+
"description": "List a specific contact.",
668+
"produces": [
669+
"application/json"
670+
],
671+
"tags": [
672+
"Contacts"
673+
],
674+
"summary": "List Contact",
675+
"parameters": [
676+
{
677+
"type": "string",
678+
"description": "Registered Phone Number",
679+
"name": "number",
680+
"in": "path",
681+
"required": true
682+
}
683+
],
684+
"responses": {
685+
"200": {
686+
"description": "OK",
687+
"schema": {
688+
"$ref": "#/definitions/client.ListContactsResponse"
689+
}
690+
}
691+
}
692+
}
693+
},
694+
"/v1/contacts/{number}/{uuid}/avatar": {
695+
"get": {
696+
"description": "Returns the avatar of a contact.",
697+
"produces": [
698+
"application/json"
699+
],
700+
"tags": [
701+
"Contacts"
702+
],
703+
"summary": "Returns the avatar of a contact",
704+
"parameters": [
705+
{
706+
"type": "string",
707+
"description": "Registered Phone Number",
708+
"name": "number",
709+
"in": "path",
710+
"required": true
711+
}
712+
],
713+
"responses": {
714+
"200": {
715+
"description": "Image",
716+
"schema": {
717+
"type": "string"
718+
}
719+
}
720+
}
721+
}
722+
},
665723
"/v1/devices/{number}": {
666724
"get": {
667725
"description": "List linked devices associated to this device.",

src/docs/swagger.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,44 @@ paths:
903903
contact doesn’t exist yet, it will be added.
904904
tags:
905905
- Contacts
906+
/v1/contacts/{number}/{uuid}:
907+
get:
908+
description: List a specific contact.
909+
parameters:
910+
- description: Registered Phone Number
911+
in: path
912+
name: number
913+
required: true
914+
type: string
915+
produces:
916+
- application/json
917+
responses:
918+
"200":
919+
description: OK
920+
schema:
921+
$ref: '#/definitions/client.ListContactsResponse'
922+
summary: List Contact
923+
tags:
924+
- Contacts
925+
/v1/contacts/{number}/{uuid}/avatar:
926+
get:
927+
description: Returns the avatar of a contact.
928+
parameters:
929+
- description: Registered Phone Number
930+
in: path
931+
name: number
932+
required: true
933+
type: string
934+
produces:
935+
- application/json
936+
responses:
937+
"200":
938+
description: Image
939+
schema:
940+
type: string
941+
summary: Returns the avatar of a contact
942+
tags:
943+
- Contacts
906944
/v1/contacts/{number}/sync:
907945
post:
908946
consumes:

0 commit comments

Comments
 (0)