mirror of
https://github.com/aljazceru/signal-cli-rest-api.git
synced 2025-12-20 16:14:29 +01:00
Merge pull request #260 from zeetabit/master
Support attachment base64 and custom filename.
This commit is contained in:
@@ -118,7 +118,7 @@ COPY src/go.mod /tmp/signal-cli-rest-api-src/
|
|||||||
COPY src/go.sum /tmp/signal-cli-rest-api-src/
|
COPY src/go.sum /tmp/signal-cli-rest-api-src/
|
||||||
|
|
||||||
# build signal-cli-rest-api
|
# build signal-cli-rest-api
|
||||||
RUN cd /tmp/signal-cli-rest-api-src && swag init && go build
|
RUN cd /tmp/signal-cli-rest-api-src && swag init && go test ./client -v && go build
|
||||||
|
|
||||||
# build supervisorctl_config_creator
|
# build supervisorctl_config_creator
|
||||||
RUN cd /tmp/signal-cli-rest-api-src/scripts && go build -o jsonrpc2-helper
|
RUN cd /tmp/signal-cli-rest-api-src/scripts && go build -o jsonrpc2-helper
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ type SendMessageV1 struct {
|
|||||||
Number string `json:"number"`
|
Number string `json:"number"`
|
||||||
Recipients []string `json:"recipients"`
|
Recipients []string `json:"recipients"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Base64Attachment string `json:"base64_attachment"`
|
Base64Attachment string `json:"base64_attachment" example:"'<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'"`
|
||||||
IsGroup bool `json:"is_group"`
|
IsGroup bool `json:"is_group"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ type SendMessageV2 struct {
|
|||||||
Number string `json:"number"`
|
Number string `json:"number"`
|
||||||
Recipients []string `json:"recipients"`
|
Recipients []string `json:"recipients"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Base64Attachments []string `json:"base64_attachments"`
|
Base64Attachments []string `json:"base64_attachments" example:"<BASE64 ENCODED DATA>,data:<MIME-TYPE>;base64<comma><BASE64 ENCODED DATA>,data:<MIME-TYPE>;filename=<FILENAME>;base64<comma><BASE64 ENCODED DATA>"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TypingIndicatorRequest struct {
|
type TypingIndicatorRequest struct {
|
||||||
|
|||||||
169
src/client/attachment.go
Normal file
169
src/client/attachment.go
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gabriel-vasile/mimetype"
|
||||||
|
uuid "github.com/gofrs/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AttachmentEntry struct {
|
||||||
|
MimeInfo string
|
||||||
|
FileName string
|
||||||
|
DirName string
|
||||||
|
Base64 string
|
||||||
|
FilePath string
|
||||||
|
attachmentTmpDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAttachmentEntry(attachmentData string, attachmentTmpDir string) *AttachmentEntry {
|
||||||
|
attachmentEntry := AttachmentEntry{
|
||||||
|
MimeInfo: "",
|
||||||
|
FileName: "",
|
||||||
|
DirName: "",
|
||||||
|
Base64: "",
|
||||||
|
FilePath: "",
|
||||||
|
attachmentTmpDir: attachmentTmpDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentEntry.extractMetaData(attachmentData)
|
||||||
|
|
||||||
|
return &attachmentEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attachmentEntry *AttachmentEntry) extractMetaData(attachmentData string) {
|
||||||
|
base64FlagIndex := strings.LastIndex(attachmentData, "base64,")
|
||||||
|
|
||||||
|
if !strings.Contains(attachmentData, "data:") || base64FlagIndex == -1 {
|
||||||
|
attachmentEntry.Base64 = attachmentData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentEntry.Base64 = attachmentData[base64FlagIndex+len("base64,"):]
|
||||||
|
metaDataKeys := map[string]string{
|
||||||
|
"data:": "MimeInfo",
|
||||||
|
"filename=": "FileName",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, metaDataLineItem := range strings.Split(attachmentData[:base64FlagIndex-1], ";") {
|
||||||
|
for metaDataKey, metaDataFieldName := range metaDataKeys {
|
||||||
|
flagIndex := strings.Index(metaDataLineItem, metaDataKey)
|
||||||
|
|
||||||
|
if flagIndex != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentEntry.setFieldValueByName(metaDataFieldName, metaDataLineItem[len(metaDataKey):])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attachmentEntry *AttachmentEntry) storeBase64AsTemporaryFile() error {
|
||||||
|
if strings.Compare(attachmentEntry.Base64, "") == 0 {
|
||||||
|
return errors.New("The base64 data does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
dec, err := base64.StdEncoding.DecodeString(attachmentEntry.Base64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no custom filename
|
||||||
|
if strings.Compare(attachmentEntry.FileName, "") == 0 {
|
||||||
|
mimeType := mimetype.Detect(dec)
|
||||||
|
|
||||||
|
fileNameUuid, err := uuid.NewV4()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
attachmentEntry.FileName = fileNameUuid.String() + mimeType.Extension()
|
||||||
|
}
|
||||||
|
|
||||||
|
dirNameUuid, err := uuid.NewV4()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentEntry.DirName = dirNameUuid.String()
|
||||||
|
dirPath := attachmentEntry.attachmentTmpDir + attachmentEntry.DirName
|
||||||
|
if err := os.Mkdir(dirPath, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentEntry.FilePath = dirPath + string(os.PathSeparator) + attachmentEntry.FileName
|
||||||
|
|
||||||
|
f, err := os.Create(attachmentEntry.FilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if _, err := f.Write(dec); err != nil {
|
||||||
|
attachmentEntry.cleanUp()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.Sync(); err != nil {
|
||||||
|
attachmentEntry.cleanUp()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attachmentEntry *AttachmentEntry) cleanUp() {
|
||||||
|
if strings.Compare(attachmentEntry.FilePath, "") != 0 {
|
||||||
|
os.Remove(attachmentEntry.FilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Compare(attachmentEntry.DirName, "") != 0 {
|
||||||
|
dirPath := attachmentEntry.attachmentTmpDir + attachmentEntry.DirName
|
||||||
|
os.Remove(dirPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attachmentEntry *AttachmentEntry) setFieldValueByName(fieldName string, fieldValue string) {
|
||||||
|
reflectPointer := reflect.ValueOf(attachmentEntry)
|
||||||
|
reflectStructure := reflectPointer.Elem()
|
||||||
|
|
||||||
|
if reflectStructure.Kind() != reflect.Struct {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
field := reflectStructure.FieldByName(fieldName)
|
||||||
|
if !field.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !field.CanSet() || field.Kind() != reflect.String {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
field.SetString(fieldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attachmentEntry *AttachmentEntry) isWithMetaData() bool {
|
||||||
|
return len(attachmentEntry.MimeInfo) > 0 && len(attachmentEntry.Base64) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (attachmentEntry *AttachmentEntry) toDataForSignal() string {
|
||||||
|
if len(attachmentEntry.FilePath) > 0 {
|
||||||
|
return attachmentEntry.FilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
if !attachmentEntry.isWithMetaData() {
|
||||||
|
return attachmentEntry.Base64
|
||||||
|
}
|
||||||
|
|
||||||
|
result := "data:" + attachmentEntry.MimeInfo
|
||||||
|
|
||||||
|
if len(attachmentEntry.FileName) > 0 {
|
||||||
|
result = result + ";filename=" + attachmentEntry.FileName
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + ";base64," + attachmentEntry.Base64
|
||||||
|
}
|
||||||
107
src/client/attachment_test.go
Normal file
107
src/client/attachment_test.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Attachment_ExtractMetadata_ShouldPrepareDataFor_toDataForSignal(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
nameTest string
|
||||||
|
inputData string
|
||||||
|
resultIsWithMetaData bool
|
||||||
|
base64Expected string
|
||||||
|
base64Valid bool
|
||||||
|
fileNameExpected string
|
||||||
|
mimeInfoExpected string
|
||||||
|
toDataForSignal string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"BC base64 - compatibility", "MTIzNDU=", false, "MTIzNDU=", true, "", "", "MTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"+base64 -data -filename", "base64,MTIzNDU=", false, "base64,MTIzNDU=", false, "", "", "base64,MTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"+base64 +data -filename", "data:someData;base64,MTIzNDU=", true, "MTIzNDU=", true, "", "someData", "data:someData;base64,MTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"+base64 -data +filename", "filename=file.name;base64,MTIzNDU=", false, "filename=file.name;base64,MTIzNDU=", false, "", "", "filename=file.name;base64,MTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"+base64 +data +filename", "data:someData;filename=file.name;base64,MTIzNDU=", true, "MTIzNDU=", true, "file.name", "someData", "data:someData;filename=file.name;base64,MTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-base64 -data -filename", "INVALIDMTIzNDU=", false, "INVALIDMTIzNDU=", false, "", "", "INVALIDMTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-base64 +data -filename", "data:someData;INVALIDMTIzNDU=", false, "data:someData;INVALIDMTIzNDU=", false, "", "", "data:someData;INVALIDMTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-base64 -data +filename", "filename=file.name;INVALIDMTIzNDU=", false, "filename=file.name;INVALIDMTIzNDU=", false, "", "", "filename=file.name;INVALIDMTIzNDU=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-base64 +data +filename", "data:someData;filename=file.name;INVALIDMTIzNDU=", false, "data:someData;filename=file.name;INVALIDMTIzNDU=", false, "", "", "data:someData;filename=file.name;INVALIDMTIzNDU=",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentTmp := flag.String("attachment-tmp-dir", string(os.PathSeparator)+"tmp"+string(os.PathSeparator), "Attachment tmp directory")
|
||||||
|
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(tt.nameTest, func(t *testing.T) {
|
||||||
|
attachmentEntry := NewAttachmentEntry(tt.inputData, *attachmentTmp)
|
||||||
|
|
||||||
|
if attachmentEntry.isWithMetaData() != tt.resultIsWithMetaData {
|
||||||
|
t.Errorf("isWithMetaData() got \"%v\", want \"%v\"", attachmentEntry.isWithMetaData(), tt.resultIsWithMetaData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Compare(attachmentEntry.Base64, tt.base64Expected) != 0 {
|
||||||
|
t.Errorf("Base64 got \"%v\", want \"%v\"", attachmentEntry.Base64, tt.base64Expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Compare(attachmentEntry.FileName, tt.fileNameExpected) != 0 {
|
||||||
|
t.Errorf("FileName got \"%v\", want \"%v\"", attachmentEntry.FileName, tt.fileNameExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Compare(attachmentEntry.MimeInfo, tt.mimeInfoExpected) != 0 {
|
||||||
|
t.Errorf("MimeInfo got \"%v\", want \"%v\"", attachmentEntry.MimeInfo, tt.mimeInfoExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Compare(attachmentEntry.toDataForSignal(), tt.toDataForSignal) != 0 {
|
||||||
|
t.Errorf("toDataForSignal() got \"%v\", want \"%v\"", attachmentEntry.toDataForSignal(), tt.toDataForSignal)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := attachmentEntry.storeBase64AsTemporaryFile()
|
||||||
|
if err != nil && tt.base64Valid {
|
||||||
|
t.Error("storeBase64AsTemporaryFile error: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(attachmentEntry.FilePath)
|
||||||
|
if os.IsNotExist(err) && tt.base64Valid {
|
||||||
|
t.Error("file not exists after storeBase64AsTemporaryFile: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (info == nil || info.IsDir()) && tt.base64Valid {
|
||||||
|
t.Error("is not a file by path after storeBase64AsTemporaryFile")
|
||||||
|
t.Error(attachmentEntry)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentEntry.cleanUp()
|
||||||
|
|
||||||
|
info, err = os.Stat(attachmentEntry.FilePath)
|
||||||
|
if info != nil && !os.IsNotExist(err) && tt.base64Valid {
|
||||||
|
t.Error("no info or file exists after cleanUp")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info, err = os.Stat(*attachmentTmp + attachmentEntry.DirName)
|
||||||
|
if info != nil && !os.IsNotExist(err) && tt.base64Valid {
|
||||||
|
t.Error("dir exists after cleanUp")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,15 +4,14 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
securejoin "github.com/cyphar/filepath-securejoin"
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
"github.com/gabriel-vasile/mimetype"
|
|
||||||
"github.com/h2non/filetype"
|
"github.com/h2non/filetype"
|
||||||
|
|
||||||
uuid "github.com/gofrs/uuid"
|
uuid "github.com/gofrs/uuid"
|
||||||
@@ -134,6 +133,12 @@ func cleanupTmpFiles(paths []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanupAttachmentEntries(attachmentEntries []AttachmentEntry) {
|
||||||
|
for _, attachmentEntry := range attachmentEntries {
|
||||||
|
attachmentEntry.cleanUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func convertInternalGroupIdToGroupId(internalId string) string {
|
func convertInternalGroupIdToGroupId(internalId string) string {
|
||||||
return groupPrefix + base64.StdEncoding.EncodeToString([]byte(internalId))
|
return groupPrefix + base64.StdEncoding.EncodeToString([]byte(internalId))
|
||||||
}
|
}
|
||||||
@@ -194,7 +199,6 @@ func getContainerId() (string, error) {
|
|||||||
return containerId, nil
|
return containerId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func ConvertGroupIdToInternalGroupId(id string) (string, error) {
|
func ConvertGroupIdToInternalGroupId(id string) (string, error) {
|
||||||
|
|
||||||
groupIdWithoutPrefix := strings.TrimPrefix(id, groupPrefix)
|
groupIdWithoutPrefix := strings.TrimPrefix(id, groupPrefix)
|
||||||
@@ -300,39 +304,17 @@ func (s *SignalClient) send(number string, message string,
|
|||||||
groupId = string(grpId)
|
groupId = string(grpId)
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentTmpPaths := []string{}
|
attachmentEntries := []AttachmentEntry{}
|
||||||
for _, base64Attachment := range base64Attachments {
|
for _, base64Attachment := range base64Attachments {
|
||||||
u, err := uuid.NewV4()
|
attachmentEntry := NewAttachmentEntry(base64Attachment, s.attachmentTmpDir)
|
||||||
|
|
||||||
|
err := attachmentEntry.storeBase64AsTemporaryFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
cleanupAttachmentEntries(attachmentEntries)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dec, err := base64.StdEncoding.DecodeString(base64Attachment)
|
attachmentEntries = append(attachmentEntries, *attachmentEntry)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mimeType := mimetype.Detect(dec)
|
|
||||||
|
|
||||||
attachmentTmpPath := s.attachmentTmpDir + u.String() + mimeType.Extension()
|
|
||||||
attachmentTmpPaths = append(attachmentTmpPaths, attachmentTmpPath)
|
|
||||||
|
|
||||||
f, err := os.Create(attachmentTmpPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if _, err := f.Write(dec); err != nil {
|
|
||||||
cleanupTmpFiles(attachmentTmpPaths)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := f.Sync(); err != nil {
|
|
||||||
cleanupTmpFiles(attachmentTmpPaths)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.signalCliMode == JsonRpc {
|
if s.signalCliMode == JsonRpc {
|
||||||
@@ -354,13 +336,13 @@ func (s *SignalClient) send(number string, message string,
|
|||||||
} else {
|
} else {
|
||||||
request.Recipients = recipients
|
request.Recipients = recipients
|
||||||
}
|
}
|
||||||
if len(attachmentTmpPaths) > 0 {
|
for _, attachmentEntry := range attachmentEntries {
|
||||||
request.Attachments = append(request.Attachments, attachmentTmpPaths...)
|
request.Attachments = append(request.Attachments, attachmentEntry.toDataForSignal())
|
||||||
}
|
}
|
||||||
|
|
||||||
rawData, err := jsonRpc2Client.getRaw("send", request)
|
rawData, err := jsonRpc2Client.getRaw("send", request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanupTmpFiles(attachmentTmpPaths)
|
cleanupAttachmentEntries(attachmentEntries)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,14 +361,16 @@ func (s *SignalClient) send(number string, message string,
|
|||||||
cmd = append(cmd, []string{"-g", groupId}...)
|
cmd = append(cmd, []string{"-g", groupId}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(attachmentTmpPaths) > 0 {
|
if len(attachmentEntries) > 0 {
|
||||||
cmd = append(cmd, "-a")
|
cmd = append(cmd, "-a")
|
||||||
cmd = append(cmd, attachmentTmpPaths...)
|
for _, attachmentEntry := range attachmentEntries {
|
||||||
|
cmd = append(cmd, attachmentEntry.toDataForSignal())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rawData, err := s.cliClient.Execute(true, cmd, message)
|
rawData, err := s.cliClient.Execute(true, cmd, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanupTmpFiles(attachmentTmpPaths)
|
cleanupAttachmentEntries(attachmentEntries)
|
||||||
if strings.Contains(err.Error(), signalCliV2GroupError) {
|
if strings.Contains(err.Error(), signalCliV2GroupError) {
|
||||||
return nil, errors.New("Cannot send message to group - please first update your profile.")
|
return nil, errors.New("Cannot send message to group - please first update your profile.")
|
||||||
}
|
}
|
||||||
@@ -394,12 +378,12 @@ func (s *SignalClient) send(number string, message string,
|
|||||||
}
|
}
|
||||||
resp.Timestamp, err = strconv.ParseInt(strings.TrimSuffix(rawData, "\n"), 10, 64)
|
resp.Timestamp, err = strconv.ParseInt(strings.TrimSuffix(rawData, "\n"), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cleanupTmpFiles(attachmentTmpPaths)
|
cleanupAttachmentEntries(attachmentEntries)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupTmpFiles(attachmentTmpPaths)
|
cleanupAttachmentEntries(attachmentEntries)
|
||||||
|
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
@@ -444,7 +428,7 @@ func (s *SignalClient) UnregisterNumber(number string, deleteAccount bool, delet
|
|||||||
command := []string{"--config", s.signalCliConfig, "-a", number, "deleteLocalAccountData"}
|
command := []string{"--config", s.signalCliConfig, "-a", number, "deleteLocalAccountData"}
|
||||||
_, err2 := s.cliClient.Execute(true, command, "")
|
_, err2 := s.cliClient.Execute(true, command, "")
|
||||||
if (err2 != nil) && (err != nil) {
|
if (err2 != nil) && (err != nil) {
|
||||||
err = fmt.Errorf("%w (%w)", err, err2)
|
err = fmt.Errorf("%w (%s)", err, err2.Error())
|
||||||
} else if (err2 != nil) && (err == nil) {
|
} else if (err2 != nil) && (err == nil) {
|
||||||
err = err2
|
err = err2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,7 +241,9 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {},
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -281,7 +283,9 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -323,7 +327,9 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -365,7 +371,9 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -1200,7 +1208,9 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": {},
|
"201": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -1472,7 +1482,9 @@ var doc = `{
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -1686,7 +1698,8 @@ var doc = `{
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"base64_attachment": {
|
"base64_attachment": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"example": "'\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;base64,\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64,\u003cBASE64 ENCODED DATA\u003e'"
|
||||||
},
|
},
|
||||||
"is_group": {
|
"is_group": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@@ -1712,7 +1725,12 @@ var doc = `{
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
},
|
||||||
|
"example": [
|
||||||
|
"\u003cBASE64 ENCODED DATA\u003e",
|
||||||
|
"data:\u003cMIME-TYPE\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e",
|
||||||
|
"data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@@ -225,7 +225,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {},
|
"200": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -265,7 +267,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -307,7 +311,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -349,7 +355,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -1184,7 +1192,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": {},
|
"201": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -1456,7 +1466,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"204": {},
|
"204": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad Request",
|
"description": "Bad Request",
|
||||||
"schema": {
|
"schema": {
|
||||||
@@ -1670,7 +1682,8 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"base64_attachment": {
|
"base64_attachment": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"example": "'\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;base64,\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64,\u003cBASE64 ENCODED DATA\u003e'"
|
||||||
},
|
},
|
||||||
"is_group": {
|
"is_group": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@@ -1696,7 +1709,12 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
},
|
||||||
|
"example": [
|
||||||
|
"\u003cBASE64 ENCODED DATA\u003e",
|
||||||
|
"data:\u003cMIME-TYPE\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e",
|
||||||
|
"data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ definitions:
|
|||||||
api.SendMessageV1:
|
api.SendMessageV1:
|
||||||
properties:
|
properties:
|
||||||
base64_attachment:
|
base64_attachment:
|
||||||
|
example: '''<BASE64 ENCODED DATA>'' OR ''data:<MIME-TYPE>;base64,<BASE64 ENCODED
|
||||||
|
DATA>'' OR ''data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED
|
||||||
|
DATA>'''
|
||||||
type: string
|
type: string
|
||||||
is_group:
|
is_group:
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -121,6 +124,10 @@ definitions:
|
|||||||
api.SendMessageV2:
|
api.SendMessageV2:
|
||||||
properties:
|
properties:
|
||||||
base64_attachments:
|
base64_attachments:
|
||||||
|
example:
|
||||||
|
- <BASE64 ENCODED DATA>
|
||||||
|
- data:<MIME-TYPE>;base64<comma><BASE64 ENCODED DATA>
|
||||||
|
- data:<MIME-TYPE>;filename=<FILENAME>;base64<comma><BASE64 ENCODED DATA>
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
@@ -389,7 +396,8 @@ paths:
|
|||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"200": {}
|
"200":
|
||||||
|
description: ""
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -416,7 +424,8 @@ paths:
|
|||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"204": {}
|
"204":
|
||||||
|
description: ""
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -444,19 +453,22 @@ paths:
|
|||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"204": {}
|
"204":
|
||||||
|
description: ""
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/api.Error'
|
$ref: '#/definitions/api.Error'
|
||||||
summary: Updates the info associated to a number on the contact list. If the contact doesn’t exist yet, it will be added.
|
summary: Updates the info associated to a number on the contact list. If the
|
||||||
|
contact doesn’t exist yet, it will be added.
|
||||||
tags:
|
tags:
|
||||||
- Contacts
|
- Contacts
|
||||||
/v1/devices/{number}:
|
/v1/devices/{number}:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Links another device to this device. Only works, if this is the master device.
|
description: Links another device to this device. Only works, if this is the
|
||||||
|
master device.
|
||||||
parameters:
|
parameters:
|
||||||
- description: Registered Phone Number
|
- description: Registered Phone Number
|
||||||
in: path
|
in: path
|
||||||
@@ -472,7 +484,8 @@ paths:
|
|||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"204": {}
|
"204":
|
||||||
|
description: ""
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -844,7 +857,8 @@ paths:
|
|||||||
- Identities
|
- Identities
|
||||||
/v1/identities/{number}/trust/{numberToTrust}:
|
/v1/identities/{number}/trust/{numberToTrust}:
|
||||||
put:
|
put:
|
||||||
description: Trust an identity. When 'trust_all_known_keys' is set to' true', all known keys of this user are trusted. **This is only recommended for testing.**
|
description: Trust an identity. When 'trust_all_known_keys' is set to' true',
|
||||||
|
all known keys of this user are trusted. **This is only recommended for testing.**
|
||||||
parameters:
|
parameters:
|
||||||
- description: Input Data
|
- description: Input Data
|
||||||
in: body
|
in: body
|
||||||
@@ -979,7 +993,9 @@ paths:
|
|||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Receives Signal Messages from the Signal Network. If you are running the docker container in normal/native mode, this is a GET endpoint. In json-rpc mode this is a websocket endpoint.
|
description: Receives Signal Messages from the Signal Network. If you are running
|
||||||
|
the docker container in normal/native mode, this is a GET endpoint. In json-rpc
|
||||||
|
mode this is a websocket endpoint.
|
||||||
parameters:
|
parameters:
|
||||||
- description: Registered Phone Number
|
- description: Registered Phone Number
|
||||||
in: path
|
in: path
|
||||||
@@ -1025,7 +1041,8 @@ paths:
|
|||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"201": {}
|
"201":
|
||||||
|
description: ""
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@@ -1072,7 +1089,8 @@ paths:
|
|||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Check if one or more phone numbers are registered with the Signal Service.
|
description: Check if one or more phone numbers are registered with the Signal
|
||||||
|
Service.
|
||||||
parameters:
|
parameters:
|
||||||
- collectionFormat: multi
|
- collectionFormat: multi
|
||||||
description: Numbers to check
|
description: Numbers to check
|
||||||
@@ -1190,7 +1208,9 @@ paths:
|
|||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: Disables push support for this device. **WARNING:** If *delete_account* is set to *true*, the account will be deleted from the Signal Server. This cannot be undone without loss.
|
description: Disables push support for this device. **WARNING:** If *delete_account*
|
||||||
|
is set to *true*, the account will be deleted from the Signal Server. This
|
||||||
|
cannot be undone without loss.
|
||||||
parameters:
|
parameters:
|
||||||
- description: Registered Phone Number
|
- description: Registered Phone Number
|
||||||
in: path
|
in: path
|
||||||
@@ -1205,7 +1225,8 @@ paths:
|
|||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
"204": {}
|
"204":
|
||||||
|
description: ""
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|||||||
Reference in New Issue
Block a user