mirror of
https://github.com/bbernhard/signal-cli-rest-api.git
synced 2026-05-27 14:54:14 +00:00
added two new endpoints
* added endpoint to list a contact * added endpoint to get a contact's avatar
This commit is contained in:
parent
3a2b77b31f
commit
08cd2bd12c
@ -1014,7 +1014,7 @@ func (a *Api) GetGroupAvatar(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
groupId := c.Param("groupid")
|
groupId := c.Param("groupid")
|
||||||
|
|
||||||
groupAvatar, err := a.signalClient.GetGroupAvatar(number, groupId)
|
groupAvatar, err := a.signalClient.GetAvatar(number, groupId, client.GroupAvatar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case *client.NotFoundError:
|
case *client.NotFoundError:
|
||||||
@ -2311,6 +2311,81 @@ func (a *Api) ListContacts(c *gin.Context) {
|
|||||||
c.JSON(200, contacts)
|
c.JSON(200, contacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary List Contact
|
||||||
|
// @Tags Contacts
|
||||||
|
// @Description List a specific contact.
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} client.ListContactsResponse
|
||||||
|
// @Param number path string true "Registered Phone Number"
|
||||||
|
// @Router /v1/contacts/{number}/{uuid} [get]
|
||||||
|
func (a *Api) ListContact(c *gin.Context) {
|
||||||
|
number, err := url.PathUnescape(c.Param("number"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if number == "" {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := c.Param("uuid")
|
||||||
|
if uuid == "" {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - uuid missing"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contact, err := a.signalClient.ListContact(number, uuid)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case *client.NotFoundError:
|
||||||
|
c.JSON(404, Error{Msg: err.Error()})
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
c.JSON(400, Error{Msg: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Returns the avatar of a contact
|
||||||
|
// @Tags Contacts
|
||||||
|
// @Description Returns the avatar of a contact.
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {string} string "Image"
|
||||||
|
// @Param number path string true "Registered Phone Number"
|
||||||
|
// @Router /v1/contacts/{number}/{uuid}/avatar [get]
|
||||||
|
func (a *Api) GetProfileAvatar(c *gin.Context) {
|
||||||
|
number, err := url.PathUnescape(c.Param("number"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if number == "" {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - number missing"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid := c.Param("uuid")
|
||||||
|
if uuid == "" {
|
||||||
|
c.JSON(400, Error{Msg: "Couldn't process request - uuid missing"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
avatar, err := a.signalClient.GetAvatar(number, uuid, client.ProfileAvatar)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(400, Error{Msg: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mimeType := mimetype.Detect(avatar)
|
||||||
|
c.Data(200, mimeType.String(), avatar)
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Set Pin
|
// @Summary Set Pin
|
||||||
// @Tags Accounts
|
// @Tags Accounts
|
||||||
// @Description Sets a new Signal Pin
|
// @Description Sets a new Signal Pin
|
||||||
|
|||||||
@ -29,6 +29,14 @@ const signalCliV2GroupError = "Cannot create a V2 group as self does not have a
|
|||||||
|
|
||||||
const endpointNotSupportedInJsonRpcMode = "This endpoint is not supported in JSON-RPC mode."
|
const endpointNotSupportedInJsonRpcMode = "This endpoint is not supported in JSON-RPC mode."
|
||||||
|
|
||||||
|
type AvatarType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
GroupAvatar AvatarType = iota + 1
|
||||||
|
ContactAvatar
|
||||||
|
ProfileAvatar
|
||||||
|
)
|
||||||
|
|
||||||
type GroupPermission int
|
type GroupPermission int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -1323,21 +1331,33 @@ func (s *SignalClient) GetGroup(number string, groupId string) (*GroupEntry, err
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SignalClient) GetGroupAvatar(number string, groupId string) ([]byte, error) {
|
func (s *SignalClient) GetAvatar(number string, id string, avatarType AvatarType) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
var rawData string
|
var rawData string
|
||||||
|
|
||||||
internalGroupId, err := ConvertGroupIdToInternalGroupId(groupId)
|
if avatarType == GroupAvatar {
|
||||||
if err != nil {
|
id, err = ConvertGroupIdToInternalGroupId(id)
|
||||||
return []byte{}, errors.New("Invalid group id")
|
if err != nil {
|
||||||
|
return []byte{}, errors.New("Invalid group id")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.signalCliMode == JsonRpc {
|
if s.signalCliMode == JsonRpc {
|
||||||
type Request struct {
|
type Request struct {
|
||||||
GroupId string `json:"groupId"`
|
GroupId string `json:"groupId,omitempty"`
|
||||||
|
Contact string `json:"contact,omitempty"`
|
||||||
|
Profile string `json:"profile,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
request := Request{GroupId: internalGroupId}
|
var request Request
|
||||||
|
|
||||||
|
if avatarType == GroupAvatar {
|
||||||
|
request.GroupId = id
|
||||||
|
} else if avatarType == ContactAvatar {
|
||||||
|
request.Contact = id
|
||||||
|
} else if avatarType == ProfileAvatar {
|
||||||
|
request.Profile = id
|
||||||
|
}
|
||||||
|
|
||||||
jsonRpc2Client, err := s.getJsonRpc2Client()
|
jsonRpc2Client, err := s.getJsonRpc2Client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1351,8 +1371,21 @@ func (s *SignalClient) GetGroupAvatar(number string, groupId string) ([]byte, er
|
|||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rawData, err = s.cliClient.Execute(true, []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "getAvatar", "-g", internalGroupId}, "")
|
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "getAvatar"}
|
||||||
|
|
||||||
|
if avatarType == GroupAvatar {
|
||||||
|
cmd = append(cmd, []string{"-g", id}...)
|
||||||
|
} else if avatarType == ContactAvatar {
|
||||||
|
cmd = append(cmd, []string{"--contact", id}...)
|
||||||
|
} else if avatarType == ProfileAvatar {
|
||||||
|
cmd = append(cmd, []string{"--profile", id}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawData, err = s.cliClient.Execute(true, cmd, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "Could not find avatar") {
|
||||||
|
return []byte{}, &NotFoundError{Description: "No avatar found."}
|
||||||
|
}
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2528,6 +2561,21 @@ func (s *SignalClient) ListContacts(number string) ([]ListContactsResponse, erro
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SignalClient) ListContact(number string, uuid string) (ListContactsResponse, error) {
|
||||||
|
contacts, err := s.ListContacts(number)
|
||||||
|
if err != nil {
|
||||||
|
return ListContactsResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, contact := range contacts {
|
||||||
|
if contact.Uuid == uuid {
|
||||||
|
return contact, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListContactsResponse{}, &NotFoundError{Description: "No contact with that id (" + uuid + ") found"}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
|
func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
|
||||||
if s.signalCliMode == JsonRpc {
|
if s.signalCliMode == JsonRpc {
|
||||||
type Request struct {
|
type Request struct {
|
||||||
@ -2544,11 +2592,10 @@ func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "setPin", registrationLockPin}
|
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "setPin", registrationLockPin}
|
||||||
rawData, err := s.cliClient.Execute(true, cmd, "")
|
_, err := s.cliClient.Execute(true, cmd, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info(string(rawData))
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -665,6 +665,64 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v1/contacts/{number}/{uuid}": {
|
||||||
|
"get": {
|
||||||
|
"description": "List a specific contact.",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Contacts"
|
||||||
|
],
|
||||||
|
"summary": "List Contact",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Registered Phone Number",
|
||||||
|
"name": "number",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/client.ListContactsResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/contacts/{number}/{uuid}/avatar": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns the avatar of a contact.",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Contacts"
|
||||||
|
],
|
||||||
|
"summary": "Returns the avatar of a contact",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Registered Phone Number",
|
||||||
|
"name": "number",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Image",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v1/devices/{number}": {
|
"/v1/devices/{number}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "List linked devices associated to this device.",
|
"description": "List linked devices associated to this device.",
|
||||||
|
|||||||
@ -662,6 +662,64 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v1/contacts/{number}/{uuid}": {
|
||||||
|
"get": {
|
||||||
|
"description": "List a specific contact.",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Contacts"
|
||||||
|
],
|
||||||
|
"summary": "List Contact",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Registered Phone Number",
|
||||||
|
"name": "number",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/client.ListContactsResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/contacts/{number}/{uuid}/avatar": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns the avatar of a contact.",
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Contacts"
|
||||||
|
],
|
||||||
|
"summary": "Returns the avatar of a contact",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Registered Phone Number",
|
||||||
|
"name": "number",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Image",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v1/devices/{number}": {
|
"/v1/devices/{number}": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "List linked devices associated to this device.",
|
"description": "List linked devices associated to this device.",
|
||||||
|
|||||||
@ -903,6 +903,44 @@ paths:
|
|||||||
contact doesn’t exist yet, it will be added.
|
contact doesn’t exist yet, it will be added.
|
||||||
tags:
|
tags:
|
||||||
- Contacts
|
- Contacts
|
||||||
|
/v1/contacts/{number}/{uuid}:
|
||||||
|
get:
|
||||||
|
description: List a specific contact.
|
||||||
|
parameters:
|
||||||
|
- description: Registered Phone Number
|
||||||
|
in: path
|
||||||
|
name: number
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/client.ListContactsResponse'
|
||||||
|
summary: List Contact
|
||||||
|
tags:
|
||||||
|
- Contacts
|
||||||
|
/v1/contacts/{number}/{uuid}/avatar:
|
||||||
|
get:
|
||||||
|
description: Returns the avatar of a contact.
|
||||||
|
parameters:
|
||||||
|
- description: Registered Phone Number
|
||||||
|
in: path
|
||||||
|
name: number
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Image
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: Returns the avatar of a contact
|
||||||
|
tags:
|
||||||
|
- Contacts
|
||||||
/v1/contacts/{number}/sync:
|
/v1/contacts/{number}/sync:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
14
src/main.go
14
src/main.go
@ -3,6 +3,12 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"plugin"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/bbernhard/signal-cli-rest-api/api"
|
"github.com/bbernhard/signal-cli-rest-api/api"
|
||||||
"github.com/bbernhard/signal-cli-rest-api/client"
|
"github.com/bbernhard/signal-cli-rest-api/client"
|
||||||
docs "github.com/bbernhard/signal-cli-rest-api/docs"
|
docs "github.com/bbernhard/signal-cli-rest-api/docs"
|
||||||
@ -12,11 +18,6 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
swaggerFiles "github.com/swaggo/files"
|
swaggerFiles "github.com/swaggo/files"
|
||||||
ginSwagger "github.com/swaggo/gin-swagger"
|
ginSwagger "github.com/swaggo/gin-swagger"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"plugin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title Signal Cli REST API
|
// @title Signal Cli REST API
|
||||||
@ -69,7 +70,6 @@ func main() {
|
|||||||
avatarTmpDir := flag.String("avatar-tmp-dir", "/tmp/", "Avatar tmp directory")
|
avatarTmpDir := flag.String("avatar-tmp-dir", "/tmp/", "Avatar tmp directory")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
|
||||||
logLevel := utils.GetEnv("LOG_LEVEL", "")
|
logLevel := utils.GetEnv("LOG_LEVEL", "")
|
||||||
if logLevel != "" {
|
if logLevel != "" {
|
||||||
err := utils.SetLogLevel(logLevel)
|
err := utils.SetLogLevel(logLevel)
|
||||||
@ -305,6 +305,8 @@ func main() {
|
|||||||
{
|
{
|
||||||
contacts.GET(":number", api.ListContacts)
|
contacts.GET(":number", api.ListContacts)
|
||||||
contacts.PUT(":number", api.UpdateContact)
|
contacts.PUT(":number", api.UpdateContact)
|
||||||
|
contacts.GET(":number/:uuid", api.ListContact)
|
||||||
|
contacts.GET(":number/:uuid/avatar", api.GetProfileAvatar)
|
||||||
contacts.POST(":number/sync", api.SendContacts)
|
contacts.POST(":number/sync", api.SendContacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user