diff --git a/.gitignore b/.gitignore
index 336e0a7..4d82527 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
signal-cli-config
src/main
+src/signal-cli-rest-api
diff --git a/Dockerfile b/Dockerfile
index 8503cf1..df24c9e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
FROM golang:1.13-buster
ARG SIGNAL_CLI_VERSION=0.6.8
+ARG SWAG_VERSION=1.6.7
ENV GIN_MODE=release
@@ -12,34 +13,36 @@ RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
dpkg-reconfigure --frontend=noninteractive locales && \
update-locale LANG=en_US.UTF-8
-ENV LANG en_US.UTF-8
-
-#RUN cd /tmp/ \
-# && wget -P /tmp/ https://github.com/AsamK/signal-cli/archive/v${SIGNAL_CLI_VERSION}.tar.gz \
-# && tar -xvf /tmp/v${SIGNAL_CLI_VERSION}.tar.gz \
-# && cd signal-cli-${SIGNAL_CLI_VERSION} \
-# && ./gradlew build \
-# && ./gradlew installDist \
-# && ln -s /tmp/signal-cli-${SIGNAL_CLI_VERSION}/build/install/signal-cli/bin/signal-cli /usr/bin/signal-cli \
-# && rm -rf /tmp/v${SIGNAL_CLI_VERSION}.tar.gz
-
-# https://github.com/AsamK/signal-cli/issues/259 is not yet in a release, so we need to check out the repository
+ENV LANG en_US.UTF-8
+RUN cd /tmp/ \
+ && git clone https://github.com/swaggo/swag.git swag-${SWAG_VERSION} \
+ && cd swag-${SWAG_VERSION} \
+ && git checkout v${SWAG_VERSION} \
+ && make \
+ && cp /tmp/swag-${SWAG_VERSION}/swag /usr/bin/swag \
+ && rm -r /tmp/swag-${SWAG_VERSION}
RUN cd /tmp/ \
&& git clone https://github.com/AsamK/signal-cli.git signal-cli-${SIGNAL_CLI_VERSION} \
&& cd signal-cli-${SIGNAL_CLI_VERSION} \
+ && git checkout v${SIGNAL_CLI_VERSION} \
&& ./gradlew build \
&& ./gradlew installDist \
&& ln -s /tmp/signal-cli-${SIGNAL_CLI_VERSION}/build/install/signal-cli/bin/signal-cli /usr/bin/signal-cli
RUN mkdir -p /signal-cli-config/
RUN mkdir -p /home/.local/share/signal-cli
-COPY src/ /tmp/signal-cli-rest-api-src
-RUN cd /tmp/signal-cli-rest-api-src && go get -d ./... && go build main.go
+
+COPY src/api /tmp/signal-cli-rest-api-src/api
+COPY src/main.go /tmp/signal-cli-rest-api-src/
+COPY src/go.mod /tmp/signal-cli-rest-api-src/
+COPY src/go.sum /tmp/signal-cli-rest-api-src/
+
+RUN cd /tmp/signal-cli-rest-api-src && swag init && go build
ENV PATH /tmp/signal-cli-rest-api-src/:/usr/bin/signal-cli-${SIGNAL_CLI_VERSION}/bin/:$PATH
EXPOSE 8080
-ENTRYPOINT ["main"]
+ENTRYPOINT ["signal-cli-rest-api"]
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a36f060
--- /dev/null
+++ b/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+ My New API
+
+
+
+
+
+
diff --git a/src/api/api.go b/src/api/api.go
new file mode 100644
index 0000000..277dce9
--- /dev/null
+++ b/src/api/api.go
@@ -0,0 +1,616 @@
+package api
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "github.com/gin-gonic/gin"
+ uuid "github.com/gofrs/uuid"
+ "github.com/h2non/filetype"
+ log "github.com/sirupsen/logrus"
+ qrcode "github.com/skip2/go-qrcode"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+)
+
+const groupPrefix = "group."
+
+type GroupEntry struct {
+ Name string `json:"name"`
+ Id string `json:"id"`
+ InternalId string `json:"internal_id"`
+ Members []string `json:"members"`
+ Active bool `json:"active"`
+ Blocked bool `json:"blocked"`
+}
+
+type RegisterNumberRequest struct {
+ UseVoice bool `json:"use_voice"`
+}
+
+type SendMessageV1 struct {
+ Number string `json:"number"`
+ Recipients []string `json:"recipients"`
+ Message string `json:"message"`
+ Base64Attachment string `json:"base64_attachment"`
+ IsGroup bool `json:"is_group"`
+}
+
+type SendMessageV2 struct {
+ Number string `json:"number"`
+ Recipients []string `json:"recipients"`
+ Message string `json:"message"`
+ Base64Attachments []string `json:"base64_attachments"`
+}
+
+type Error struct {
+ Msg string `json:"error"`
+}
+
+type About struct {
+ SupportedApiVersions []string `json:"versions"`
+ BuildNr int `json:"build"`
+}
+
+type CreateGroup struct {
+ Id string `json:"id"`
+}
+
+func convertInternalGroupIdToGroupId(internalId string) string {
+ return groupPrefix + base64.StdEncoding.EncodeToString([]byte(internalId))
+}
+
+func getStringInBetween(str string, start string, end string) (result string) {
+ i := strings.Index(str, start)
+ if i == -1 {
+ return
+ }
+ i += len(start)
+ j := strings.Index(str[i:], end)
+ if j == -1 {
+ return
+ }
+ return str[i : i+j]
+}
+
+func cleanupTmpFiles(paths []string) {
+ for _, path := range paths {
+ os.Remove(path)
+ }
+}
+
+func send(c *gin.Context, attachmentTmpDir string, signalCliConfig string, number string, message string,
+ recipients []string, base64Attachments []string, isGroup bool) {
+ cmd := []string{"--config", signalCliConfig, "-u", number, "send", "-m", message}
+
+ if len(recipients) == 0 {
+ c.JSON(400, gin.H{"error": "Please specify at least one recipient"})
+ return
+ }
+
+ if !isGroup {
+ cmd = append(cmd, recipients...)
+ } else {
+ if len(recipients) > 1 {
+ c.JSON(400, gin.H{"error": "More than one recipient is currently not allowed"})
+ return
+ }
+
+ groupId, err := base64.StdEncoding.DecodeString(recipients[0])
+ if err != nil {
+ c.JSON(400, gin.H{"error": "Invalid group id"})
+ return
+ }
+
+ cmd = append(cmd, []string{"-g", string(groupId)}...)
+ }
+
+ attachmentTmpPaths := []string{}
+ for _, base64Attachment := range base64Attachments {
+ u, err := uuid.NewV4()
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ dec, err := base64.StdEncoding.DecodeString(base64Attachment)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ fType, err := filetype.Get(dec)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ attachmentTmpPath := attachmentTmpDir + u.String() + "." + fType.Extension
+ attachmentTmpPaths = append(attachmentTmpPaths, attachmentTmpPath)
+
+ f, err := os.Create(attachmentTmpPath)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+ defer f.Close()
+
+ if _, err := f.Write(dec); err != nil {
+ cleanupTmpFiles(attachmentTmpPaths)
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+ if err := f.Sync(); err != nil {
+ cleanupTmpFiles(attachmentTmpPaths)
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ f.Close()
+ }
+
+ if len(attachmentTmpPaths) > 0 {
+ cmd = append(cmd, "-a")
+ cmd = append(cmd, attachmentTmpPaths...)
+ }
+
+ _, err := runSignalCli(true, cmd)
+ if err != nil {
+ cleanupTmpFiles(attachmentTmpPaths)
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+ c.JSON(201, nil)
+}
+
+func getGroups(number string, signalCliConfig string) ([]GroupEntry, error) {
+ groupEntries := []GroupEntry{}
+
+ out, err := runSignalCli(true, []string{"--config", signalCliConfig, "-u", number, "listGroups", "-d"})
+ if err != nil {
+ return groupEntries, err
+ }
+
+ lines := strings.Split(out, "\n")
+ for _, line := range lines {
+ var groupEntry GroupEntry
+ if line == "" {
+ continue
+ }
+
+ idIdx := strings.Index(line, " Name: ")
+ idPair := line[:idIdx]
+ groupEntry.InternalId = strings.TrimPrefix(idPair, "Id: ")
+ groupEntry.Id = convertInternalGroupIdToGroupId(groupEntry.InternalId)
+ lineWithoutId := strings.TrimLeft(line[idIdx:], " ")
+
+ nameIdx := strings.Index(lineWithoutId, " Active: ")
+ namePair := lineWithoutId[:nameIdx]
+ groupEntry.Name = strings.TrimRight(strings.TrimPrefix(namePair, "Name: "), " ")
+ lineWithoutName := strings.TrimLeft(lineWithoutId[nameIdx:], " ")
+
+ activeIdx := strings.Index(lineWithoutName, " Blocked: ")
+ activePair := lineWithoutName[:activeIdx]
+ active := strings.TrimPrefix(activePair, "Active: ")
+ if active == "true" {
+ groupEntry.Active = true
+ } else {
+ groupEntry.Active = false
+ }
+ lineWithoutActive := strings.TrimLeft(lineWithoutName[activeIdx:], " ")
+
+ blockedIdx := strings.Index(lineWithoutActive, " Members: ")
+ blockedPair := lineWithoutActive[:blockedIdx]
+ blocked := strings.TrimPrefix(blockedPair, "Blocked: ")
+ if blocked == "true" {
+ groupEntry.Blocked = true
+ } else {
+ groupEntry.Blocked = false
+ }
+ lineWithoutBlocked := strings.TrimLeft(lineWithoutActive[blockedIdx:], " ")
+
+ membersPair := lineWithoutBlocked
+ members := strings.TrimPrefix(membersPair, "Members: ")
+ trimmedMembers := strings.TrimRight(strings.TrimLeft(members, "["), "]")
+ trimmedMembersList := strings.Split(trimmedMembers, ",")
+ for _, member := range trimmedMembersList {
+ groupEntry.Members = append(groupEntry.Members, strings.Trim(member, " "))
+ }
+
+ groupEntries = append(groupEntries, groupEntry)
+ }
+
+ return groupEntries, nil
+}
+
+func runSignalCli(wait bool, args []string) (string, error) {
+ cmd := exec.Command("signal-cli", args...)
+ if wait {
+ var errBuffer bytes.Buffer
+ var outBuffer bytes.Buffer
+ cmd.Stderr = &errBuffer
+ cmd.Stdout = &outBuffer
+
+ err := cmd.Start()
+ if err != nil {
+ return "", err
+ }
+
+ done := make(chan error, 1)
+ go func() {
+ done <- cmd.Wait()
+ }()
+ select {
+ case <-time.After(60 * time.Second):
+ err := cmd.Process.Kill()
+ if err != nil {
+ return "", err
+ }
+ return "", errors.New("process killed as timeout reached")
+ case err := <-done:
+ if err != nil {
+ return "", errors.New(errBuffer.String())
+ }
+ }
+ return outBuffer.String(), nil
+ } else {
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return "", err
+ }
+ cmd.Start()
+ buf := bufio.NewReader(stdout) // Notice that this is not in a loop
+ line, _, _ := buf.ReadLine()
+ return string(line), nil
+ }
+}
+
+type Api struct {
+ signalCliConfig string
+ attachmentTmpDir string
+}
+
+func NewApi(signalCliConfig string, attachmentTmpDir string) *Api {
+ return &Api{
+ signalCliConfig: signalCliConfig,
+ attachmentTmpDir: attachmentTmpDir,
+ }
+}
+
+// @Summary Lists general information about the API
+// @Tags General
+// @Description Returns the supported API versions and the internal build nr
+// @Produce json
+// @Success 200 {object} About
+// @Router /v1/about [get]
+func (a *Api) About(c *gin.Context) {
+
+ about := About{SupportedApiVersions: []string{"v1", "v2"}, BuildNr: 2}
+ c.JSON(200, about)
+}
+
+// @Summary Register a phone number.
+// @Tags Devices
+// @Description Register a phone number with the signal network.
+// @Accept json
+// @Produce json
+// @Success 201
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Router /v1/register/{number} [post]
+func (a *Api) RegisterNumber(c *gin.Context) {
+ number := c.Param("number")
+
+ var req RegisterNumberRequest
+
+ buf := new(bytes.Buffer)
+ buf.ReadFrom(c.Request.Body)
+ if buf.String() != "" {
+ err := json.Unmarshal(buf.Bytes(), &req)
+ if err != nil {
+ log.Error("Couldn't register number: ", err.Error())
+ c.JSON(400, Error{Msg: "Couldn't process request - invalid request."})
+ return
+ }
+ } else {
+ req.UseVoice = false
+ }
+
+ if number == "" {
+ c.JSON(400, gin.H{"error": "Please provide a number"})
+ return
+ }
+
+ command := []string{"--config", a.signalCliConfig, "-u", number, "register"}
+
+ if req.UseVoice == true {
+ command = append(command, "--voice")
+ }
+
+ _, err := runSignalCli(true, command)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+ c.JSON(201, nil)
+}
+
+// @Summary Verify a registered phone number.
+// @Tags Devices
+// @Description Verify a registered phone number with the signal network.
+// @Accept json
+// @Produce json
+// @Success 201 {string} string "OK"
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Param token path string true "Verification Code"
+// @Router /v1/register/{number}/verify/{token} [post]
+func (a *Api) VerifyRegisteredNumber(c *gin.Context) {
+ number := c.Param("number")
+ token := c.Param("token")
+
+ if number == "" {
+ c.JSON(400, gin.H{"error": "Please provide a number"})
+ return
+ }
+
+ if token == "" {
+ c.JSON(400, gin.H{"error": "Please provide a verification code"})
+ return
+ }
+
+ _, err := runSignalCli(true, []string{"--config", a.signalCliConfig, "-u", number, "verify", token})
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+ c.JSON(201, nil)
+}
+
+// @Summary Send a signal message.
+// @Tags Messages
+// @Description Send a signal message
+// @Accept json
+// @Produce json
+// @Success 201 {string} string "OK"
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Param data body SendMessageV1 true "Input Data"
+// @Router /v1/send/{number} [post]
+// @Deprecated
+func (a *Api) Send(c *gin.Context) {
+
+ var req SendMessageV1
+ err := c.BindJSON(&req)
+ if err != nil {
+ c.JSON(400, gin.H{"error": "Couldn't process request - invalid request"})
+ return
+ }
+
+ base64Attachments := []string{}
+ if req.Base64Attachment != "" {
+ base64Attachments = append(base64Attachments, req.Base64Attachment)
+ }
+
+ send(c, a.signalCliConfig, a.signalCliConfig, req.Number, req.Message, req.Recipients, base64Attachments, req.IsGroup)
+}
+
+// @Summary Send a signal message.
+// @Tags Messages
+// @Description Send a signal message
+// @Accept json
+// @Produce json
+// @Success 201 {string} string "OK"
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Param data body SendMessageV2 true "Input Data"
+// @Router /v2/send/{number} [post]
+func (a *Api) SendV2(c *gin.Context) {
+ var req SendMessageV2
+ err := c.BindJSON(&req)
+ if err != nil {
+ c.JSON(400, gin.H{"error": "Couldn't process request - invalid request"})
+ log.Error(err.Error())
+ return
+ }
+
+ if len(req.Recipients) == 0 {
+ c.JSON(400, gin.H{"error": "Couldn't process request - please provide at least one recipient"})
+ return
+ }
+
+ groups := []string{}
+ recipients := []string{}
+
+ for _, recipient := range req.Recipients {
+ if strings.HasPrefix(recipient, groupPrefix) {
+ groups = append(groups, strings.TrimPrefix(recipient, groupPrefix))
+ } else {
+ recipients = append(recipients, recipient)
+ }
+ }
+
+ if len(recipients) > 0 && len(groups) > 0 {
+ c.JSON(400, gin.H{"error": "Signal Messenger Groups and phone numbers cannot be specified together in one request! Please split them up into multiple REST API calls."})
+ return
+ }
+
+ if len(groups) > 1 {
+ c.JSON(400, gin.H{"error": "A signal message cannot be sent to more than one group at once! Please use multiple REST API calls for that."})
+ return
+ }
+
+ for _, group := range groups {
+ send(c, a.attachmentTmpDir, a.signalCliConfig, req.Number, req.Message, []string{group}, req.Base64Attachments, true)
+ }
+
+ if len(recipients) > 0 {
+ send(c, a.attachmentTmpDir, a.signalCliConfig, req.Number, req.Message, recipients, req.Base64Attachments, false)
+ }
+}
+
+// @Summary Receive Signal Messages.
+// @Tags Messages
+// @Description Receives Signal Messages from the Signal Network.
+// @Accept json
+// @Produce json
+// @Success 200 {object} []string
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Router /v1/receive/{number} [get]
+func (a *Api) Receive(c *gin.Context) {
+ number := c.Param("number")
+
+ command := []string{"--config", a.signalCliConfig, "-u", number, "receive", "-t", "1", "--json"}
+ out, err := runSignalCli(true, command)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ out = strings.Trim(out, "\n")
+ lines := strings.Split(out, "\n")
+
+ jsonStr := "["
+ for i, line := range lines {
+ jsonStr += line
+ if i != (len(lines) - 1) {
+ jsonStr += ","
+ }
+ }
+ jsonStr += "]"
+
+ c.String(200, jsonStr)
+}
+
+// @Summary Create a new Signal Group.
+// @Tags Groups
+// @Description Create a new Signal Group with the specified members.
+// @Accept json
+// @Produce json
+// @Success 201 {object} CreateGroup
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Router /v1/groups/{number} [post]
+func (a *Api) CreateGroup(c *gin.Context) {
+ number := c.Param("number")
+
+ type Request struct {
+ Name string `json:"name"`
+ Members []string `json:"members"`
+ }
+
+ var req Request
+ err := c.BindJSON(&req)
+ if err != nil {
+ c.JSON(400, gin.H{"error": "Couldn't process request - invalid request"})
+ log.Error(err.Error())
+ return
+ }
+
+ cmd := []string{"--config", a.signalCliConfig, "-u", number, "updateGroup", "-n", req.Name, "-m"}
+ cmd = append(cmd, req.Members...)
+
+ out, err := runSignalCli(true, cmd)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ internalGroupId := getStringInBetween(out, `"`, `"`)
+ c.JSON(201, CreateGroup{Id: convertInternalGroupIdToGroupId(internalGroupId)})
+}
+
+// @Summary List all Signal Groups.
+// @Tags Groups
+// @Description List all Signal Groups.
+// @Accept json
+// @Produce json
+// @Success 200 {object} []GroupEntry
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Router /v1/groups/{number} [get]
+func (a *Api) GetGroups(c *gin.Context) {
+ number := c.Param("number")
+
+ groups, err := getGroups(number, a.signalCliConfig)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ c.JSON(200, groups)
+}
+
+// @Summary Delete a Signal Group.
+// @Tags Groups
+// @Description Delete a Signal Group.
+// @Accept json
+// @Produce json
+// @Success 200 {string} string "OK"
+// @Failure 400 {object} Error
+// @Param number path string true "Registered Phone Number"
+// @Param groupid path string true "Group Id"
+// @Router /v1/groups/{number}/{groupid} [delete]
+func (a *Api) DeleteGroup(c *gin.Context) {
+ base64EncodedGroupId := c.Param("groupid")
+ number := c.Param("number")
+
+ if base64EncodedGroupId == "" {
+ c.JSON(400, gin.H{"error": "Please specify a group id"})
+ return
+ }
+
+ groupId, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(base64EncodedGroupId, groupPrefix))
+ if err != nil {
+ c.JSON(400, gin.H{"error": "Invalid group id"})
+ return
+ }
+
+ _, err = runSignalCli(true, []string{"--config", a.signalCliConfig, "-u", number, "quitGroup", "-g", string(groupId)})
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+}
+
+// @Summary Link device and generate QR code.
+// @Tags Devices
+// @Description test
+// @Produce json
+// @Success 200 {string} string "Image"
+// @Router /v1/qrcodelink [get]
+func (a *Api) GetQrCodeLink(c *gin.Context) {
+ deviceName := c.Query("device_name")
+
+ if deviceName == "" {
+ c.JSON(400, gin.H{"error": "Please provide a name for the device"})
+ return
+ }
+
+ command := []string{"--config", a.signalCliConfig, "link", "-n", deviceName}
+
+ tsdeviceLink, err := runSignalCli(false, command)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+ }
+
+ q, err := qrcode.New(string(tsdeviceLink), qrcode.Medium)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ }
+
+ q.DisableBorder = true
+ var png []byte
+ png, err = q.PNG(256)
+ if err != nil {
+ c.JSON(400, gin.H{"error": err.Error()})
+ }
+
+ c.Data(200, "image/png", png)
+}
diff --git a/src/docs/docs.go b/src/docs/docs.go
new file mode 100644
index 0000000..3e91921
--- /dev/null
+++ b/src/docs/docs.go
@@ -0,0 +1,574 @@
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// This file was generated by swaggo/swag
+
+package docs
+
+import (
+ "bytes"
+ "encoding/json"
+ "strings"
+
+ "github.com/alecthomas/template"
+ "github.com/swaggo/swag"
+)
+
+var doc = `{
+ "schemes": {{ marshal .Schemes }},
+ "swagger": "2.0",
+ "info": {
+ "description": "{{.Description}}",
+ "title": "{{.Title}}",
+ "contact": {},
+ "license": {},
+ "version": "{{.Version}}"
+ },
+ "host": "{{.Host}}",
+ "basePath": "{{.BasePath}}",
+ "paths": {
+ "/v1/about": {
+ "get": {
+ "description": "Returns the supported API versions and the internal build nr",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "General"
+ ],
+ "summary": "Lists general information about the API",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/api.About"
+ }
+ }
+ }
+ }
+ },
+ "/v1/groups/{number}": {
+ "get": {
+ "description": "List all Signal Groups.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Groups"
+ ],
+ "summary": "List all Signal Groups.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/api.GroupEntry"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "description": "Create a new Signal Group with the specified members.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Groups"
+ ],
+ "summary": "Create a new Signal Group.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Created",
+ "schema": {
+ "$ref": "#/definitions/api.CreateGroup"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/groups/{number}/{groupid}": {
+ "delete": {
+ "description": "Delete a Signal Group.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Groups"
+ ],
+ "summary": "Delete a Signal Group.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "Group Id",
+ "name": "groupid",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/qrcodelink": {
+ "get": {
+ "description": "test",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Devices"
+ ],
+ "summary": "Link device and generate QR code.",
+ "responses": {
+ "200": {
+ "description": "Image",
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "/v1/receive/{number}": {
+ "get": {
+ "description": "Receives Signal Messages from the Signal Network.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Messages"
+ ],
+ "summary": "Receive Signal Messages.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/register/{number}": {
+ "post": {
+ "description": "Register a phone number with the signal network.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Devices"
+ ],
+ "summary": "Register a phone number.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {},
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/register/{number}/verify/{token}": {
+ "post": {
+ "description": "Verify a registered phone number with the signal network.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Devices"
+ ],
+ "summary": "Verify a registered phone number.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "Verification Code",
+ "name": "token",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/send/{number}": {
+ "post": {
+ "description": "Send a signal message",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Messages"
+ ],
+ "summary": "Send a signal message.",
+ "deprecated": true,
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "Input Data",
+ "name": "data",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api.SendMessageV1"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v2/send/{number}": {
+ "post": {
+ "description": "Send a signal message",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Messages"
+ ],
+ "summary": "Send a signal message.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "Input Data",
+ "name": "data",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api.SendMessageV2"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "api.About": {
+ "type": "object",
+ "properties": {
+ "build": {
+ "type": "integer"
+ },
+ "versions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "api.CreateGroup": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ }
+ }
+ },
+ "api.Error": {
+ "type": "object",
+ "properties": {
+ "error": {
+ "type": "string"
+ }
+ }
+ },
+ "api.GroupEntry": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean"
+ },
+ "blocked": {
+ "type": "boolean"
+ },
+ "id": {
+ "type": "string"
+ },
+ "internal_id": {
+ "type": "string"
+ },
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "api.SendMessageV1": {
+ "type": "object",
+ "properties": {
+ "base64_attachment": {
+ "type": "string"
+ },
+ "is_group": {
+ "type": "boolean"
+ },
+ "message": {
+ "type": "string"
+ },
+ "number": {
+ "type": "string"
+ },
+ "recipients": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "api.SendMessageV2": {
+ "type": "object",
+ "properties": {
+ "base64_attachments": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "message": {
+ "type": "string"
+ },
+ "number": {
+ "type": "string"
+ },
+ "recipients": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "tags": [
+ {
+ "description": "List general information.",
+ "name": "General"
+ },
+ {
+ "description": "Register and link Devices.",
+ "name": "Devices"
+ },
+ {
+ "description": "Create, List and Delete Signal Groups.",
+ "name": "Groups"
+ },
+ {
+ "description": "Send and Receive Signal Messages.",
+ "name": "Messages"
+ }
+ ]
+}`
+
+type swaggerInfo struct {
+ Version string
+ Host string
+ BasePath string
+ Schemes []string
+ Title string
+ Description string
+}
+
+// SwaggerInfo holds exported Swagger Info so clients can modify it
+var SwaggerInfo = swaggerInfo{
+ Version: "1.0",
+ Host: "127.0.0.1:8080",
+ BasePath: "/",
+ Schemes: []string{},
+ Title: "Signal Cli REST API",
+ Description: "This is the Signal Cli REST API documentation.",
+}
+
+type s struct{}
+
+func (s *s) ReadDoc() string {
+ sInfo := SwaggerInfo
+ sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
+
+ t, err := template.New("swagger_info").Funcs(template.FuncMap{
+ "marshal": func(v interface{}) string {
+ a, _ := json.Marshal(v)
+ return string(a)
+ },
+ }).Parse(doc)
+ if err != nil {
+ return doc
+ }
+
+ var tpl bytes.Buffer
+ if err := t.Execute(&tpl, sInfo); err != nil {
+ return doc
+ }
+
+ return tpl.String()
+}
+
+func init() {
+ swag.Register(swag.Name, &s{})
+}
diff --git a/src/docs/swagger.json b/src/docs/swagger.json
new file mode 100644
index 0000000..05b270c
--- /dev/null
+++ b/src/docs/swagger.json
@@ -0,0 +1,512 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "description": "This is the Signal Cli REST API documentation.",
+ "title": "Signal Cli REST API",
+ "contact": {},
+ "license": {},
+ "version": "1.0"
+ },
+ "host": "127.0.0.1:8080",
+ "basePath": "/",
+ "paths": {
+ "/v1/about": {
+ "get": {
+ "description": "Returns the supported API versions and the internal build nr",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "General"
+ ],
+ "summary": "Lists general information about the API",
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/api.About"
+ }
+ }
+ }
+ }
+ },
+ "/v1/groups/{number}": {
+ "get": {
+ "description": "List all Signal Groups.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Groups"
+ ],
+ "summary": "List all Signal Groups.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/api.GroupEntry"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ },
+ "post": {
+ "description": "Create a new Signal Group with the specified members.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Groups"
+ ],
+ "summary": "Create a new Signal Group.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "Created",
+ "schema": {
+ "$ref": "#/definitions/api.CreateGroup"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/groups/{number}/{groupid}": {
+ "delete": {
+ "description": "Delete a Signal Group.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Groups"
+ ],
+ "summary": "Delete a Signal Group.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "Group Id",
+ "name": "groupid",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/qrcodelink": {
+ "get": {
+ "description": "test",
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Devices"
+ ],
+ "summary": "Link device and generate QR code.",
+ "responses": {
+ "200": {
+ "description": "Image",
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "/v1/receive/{number}": {
+ "get": {
+ "description": "Receives Signal Messages from the Signal Network.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Messages"
+ ],
+ "summary": "Receive Signal Messages.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/register/{number}": {
+ "post": {
+ "description": "Register a phone number with the signal network.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Devices"
+ ],
+ "summary": "Register a phone number.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {},
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/register/{number}/verify/{token}": {
+ "post": {
+ "description": "Verify a registered phone number with the signal network.",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Devices"
+ ],
+ "summary": "Verify a registered phone number.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "type": "string",
+ "description": "Verification Code",
+ "name": "token",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v1/send/{number}": {
+ "post": {
+ "description": "Send a signal message",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Messages"
+ ],
+ "summary": "Send a signal message.",
+ "deprecated": true,
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "Input Data",
+ "name": "data",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api.SendMessageV1"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ },
+ "/v2/send/{number}": {
+ "post": {
+ "description": "Send a signal message",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Messages"
+ ],
+ "summary": "Send a signal message.",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Registered Phone Number",
+ "name": "number",
+ "in": "path",
+ "required": true
+ },
+ {
+ "description": "Input Data",
+ "name": "data",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/api.SendMessageV2"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "OK",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "400": {
+ "description": "Bad Request",
+ "schema": {
+ "$ref": "#/definitions/api.Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "api.About": {
+ "type": "object",
+ "properties": {
+ "build": {
+ "type": "integer"
+ },
+ "versions": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "api.CreateGroup": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ }
+ }
+ },
+ "api.Error": {
+ "type": "object",
+ "properties": {
+ "error": {
+ "type": "string"
+ }
+ }
+ },
+ "api.GroupEntry": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "boolean"
+ },
+ "blocked": {
+ "type": "boolean"
+ },
+ "id": {
+ "type": "string"
+ },
+ "internal_id": {
+ "type": "string"
+ },
+ "members": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "api.SendMessageV1": {
+ "type": "object",
+ "properties": {
+ "base64_attachment": {
+ "type": "string"
+ },
+ "is_group": {
+ "type": "boolean"
+ },
+ "message": {
+ "type": "string"
+ },
+ "number": {
+ "type": "string"
+ },
+ "recipients": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "api.SendMessageV2": {
+ "type": "object",
+ "properties": {
+ "base64_attachments": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "message": {
+ "type": "string"
+ },
+ "number": {
+ "type": "string"
+ },
+ "recipients": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "tags": [
+ {
+ "description": "List general information.",
+ "name": "General"
+ },
+ {
+ "description": "Register and link Devices.",
+ "name": "Devices"
+ },
+ {
+ "description": "Create, List and Delete Signal Groups.",
+ "name": "Groups"
+ },
+ {
+ "description": "Send and Receive Signal Messages.",
+ "name": "Messages"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml
new file mode 100644
index 0000000..65e4f19
--- /dev/null
+++ b/src/docs/swagger.yaml
@@ -0,0 +1,335 @@
+basePath: /
+definitions:
+ api.About:
+ properties:
+ build:
+ type: integer
+ versions:
+ items:
+ type: string
+ type: array
+ type: object
+ api.CreateGroup:
+ properties:
+ id:
+ type: string
+ type: object
+ api.Error:
+ properties:
+ error:
+ type: string
+ type: object
+ api.GroupEntry:
+ properties:
+ active:
+ type: boolean
+ blocked:
+ type: boolean
+ id:
+ type: string
+ internal_id:
+ type: string
+ members:
+ items:
+ type: string
+ type: array
+ name:
+ type: string
+ type: object
+ api.SendMessageV1:
+ properties:
+ base64_attachment:
+ type: string
+ is_group:
+ type: boolean
+ message:
+ type: string
+ number:
+ type: string
+ recipients:
+ items:
+ type: string
+ type: array
+ type: object
+ api.SendMessageV2:
+ properties:
+ base64_attachments:
+ items:
+ type: string
+ type: array
+ message:
+ type: string
+ number:
+ type: string
+ recipients:
+ items:
+ type: string
+ type: array
+ type: object
+host: 127.0.0.1:8080
+info:
+ contact: {}
+ description: This is the Signal Cli REST API documentation.
+ license: {}
+ title: Signal Cli REST API
+ version: "1.0"
+paths:
+ /v1/about:
+ get:
+ description: Returns the supported API versions and the internal build nr
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/api.About'
+ summary: Lists general information about the API
+ tags:
+ - General
+ /v1/groups/{number}:
+ get:
+ consumes:
+ - application/json
+ description: List all Signal Groups.
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ items:
+ $ref: '#/definitions/api.GroupEntry'
+ type: array
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: List all Signal Groups.
+ tags:
+ - Groups
+ post:
+ consumes:
+ - application/json
+ description: Create a new Signal Group with the specified members.
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "201":
+ description: Created
+ schema:
+ $ref: '#/definitions/api.CreateGroup'
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Create a new Signal Group.
+ tags:
+ - Groups
+ /v1/groups/{number}/{groupid}:
+ delete:
+ consumes:
+ - application/json
+ description: Delete a Signal Group.
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ - description: Group Id
+ in: path
+ name: groupid
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ type: string
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Delete a Signal Group.
+ tags:
+ - Groups
+ /v1/qrcodelink:
+ get:
+ description: test
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: Image
+ schema:
+ type: string
+ summary: Link device and generate QR code.
+ tags:
+ - Devices
+ /v1/receive/{number}:
+ get:
+ consumes:
+ - application/json
+ description: Receives Signal Messages from the Signal Network.
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ items:
+ type: string
+ type: array
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Receive Signal Messages.
+ tags:
+ - Messages
+ /v1/register/{number}:
+ post:
+ consumes:
+ - application/json
+ description: Register a phone number with the signal network.
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "201": {}
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Register a phone number.
+ tags:
+ - Devices
+ /v1/register/{number}/verify/{token}:
+ post:
+ consumes:
+ - application/json
+ description: Verify a registered phone number with the signal network.
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ - description: Verification Code
+ in: path
+ name: token
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "201":
+ description: OK
+ schema:
+ type: string
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Verify a registered phone number.
+ tags:
+ - Devices
+ /v1/send/{number}:
+ post:
+ consumes:
+ - application/json
+ deprecated: true
+ description: Send a signal message
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ - description: Input Data
+ in: body
+ name: data
+ required: true
+ schema:
+ $ref: '#/definitions/api.SendMessageV1'
+ produces:
+ - application/json
+ responses:
+ "201":
+ description: OK
+ schema:
+ type: string
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Send a signal message.
+ tags:
+ - Messages
+ /v2/send/{number}:
+ post:
+ consumes:
+ - application/json
+ description: Send a signal message
+ parameters:
+ - description: Registered Phone Number
+ in: path
+ name: number
+ required: true
+ type: string
+ - description: Input Data
+ in: body
+ name: data
+ required: true
+ schema:
+ $ref: '#/definitions/api.SendMessageV2'
+ produces:
+ - application/json
+ responses:
+ "201":
+ description: OK
+ schema:
+ type: string
+ "400":
+ description: Bad Request
+ schema:
+ $ref: '#/definitions/api.Error'
+ summary: Send a signal message.
+ tags:
+ - Messages
+swagger: "2.0"
+tags:
+- description: List general information.
+ name: General
+- description: Register and link Devices.
+ name: Devices
+- description: Create, List and Delete Signal Groups.
+ name: Groups
+- description: Send and Receive Signal Messages.
+ name: Messages
diff --git a/src/go.mod b/src/go.mod
new file mode 100644
index 0000000..6337774
--- /dev/null
+++ b/src/go.mod
@@ -0,0 +1,24 @@
+module github.com/bbernhard/signal-cli-rest-api
+
+go 1.14
+
+require (
+ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
+ github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
+ github.com/gin-gonic/gin v1.6.3
+ github.com/go-openapi/spec v0.19.8 // indirect
+ github.com/go-openapi/swag v0.19.9 // indirect
+ github.com/gofrs/uuid v3.3.0+incompatible
+ github.com/h2non/filetype v1.1.0
+ github.com/mailru/easyjson v0.7.1 // indirect
+ github.com/sirupsen/logrus v1.6.0
+ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
+ github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14
+ github.com/swaggo/gin-swagger v1.2.0
+ github.com/swaggo/swag v1.6.7
+ github.com/urfave/cli/v2 v2.2.0 // indirect
+ golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
+ golang.org/x/text v0.3.3 // indirect
+ golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f // indirect
+ gopkg.in/yaml.v2 v2.3.0 // indirect
+)
diff --git a/src/go.sum b/src/go.sum
new file mode 100644
index 0000000..d900900
--- /dev/null
+++ b/src/go.sum
@@ -0,0 +1,186 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
+github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
+github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
+github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
+github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/spec v0.19.8 h1:qAdZLh1r6QF/hI/gTq+TJTvsQUodZsM7KLqkAJdiJNg=
+github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=
+github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
+github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/gofrs/uuid v1.2.0 h1:coDhrjgyJaglxSjxuJdqQSSdUpG3w6p1OwN2od6frBU=
+github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
+github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
+github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
+github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
+github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
+github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
+github.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=
+github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
+github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
+github.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s=
+github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
+github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
+github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f h1:JcoF/bowzCDI+MXu1yLqQGNO3ibqWsWq+Sk7pOT218w=
+golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/src/main.go b/src/main.go
index 9ddd4b9..6309f8f 100644
--- a/src/main.go
+++ b/src/main.go
@@ -1,244 +1,37 @@
package main
import (
- "bufio"
- "bytes"
- "encoding/base64"
- "encoding/json"
- "errors"
"flag"
- "os"
- "os/exec"
- "strings"
- "time"
"github.com/gin-gonic/gin"
- "github.com/h2non/filetype"
- uuid "github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"
- qrcode "github.com/skip2/go-qrcode"
+ swaggerFiles "github.com/swaggo/files"
+ ginSwagger "github.com/swaggo/gin-swagger"
+ "github.com/bbernhard/signal-cli-rest-api/api"
+ _ "github.com/bbernhard/signal-cli-rest-api/docs"
+
)
-const groupPrefix = "group."
-type GroupEntry struct {
- Name string `json:"name"`
- Id string `json:"id"`
- InternalId string `json:"internal_id"`
- Members []string `json:"members"`
- Active bool `json:"active"`
- Blocked bool `json:"blocked"`
-}
-func convertInternalGroupIdToGroupId(internalId string) string {
- return groupPrefix + base64.StdEncoding.EncodeToString([]byte(internalId))
-}
+// @title Signal Cli REST API
+// @version 1.0
+// @description This is the Signal Cli REST API documentation.
-func getStringInBetween(str string, start string, end string) (result string) {
- i := strings.Index(str, start)
- if i == -1 {
- return
- }
- i += len(start)
- j := strings.Index(str[i:], end)
- if j == -1 {
- return
- }
- return str[i : i+j]
-}
+// @tag.name General
+// @tag.description List general information.
-func cleanupTmpFiles(paths []string) {
- for _, path := range paths {
- os.Remove(path)
- }
-}
+// @tag.name Devices
+// @tag.description Register and link Devices.
-func send(c *gin.Context, attachmentTmpDir string, signalCliConfig string, number string, message string,
- recipients []string, base64Attachments []string, isGroup bool) {
- cmd := []string{"--config", signalCliConfig, "-u", number, "send", "-m", message}
+// @tag.name Groups
+// @tag.description Create, List and Delete Signal Groups.
- if len(recipients) == 0 {
- c.JSON(400, gin.H{"error": "Please specify at least one recipient"})
- return
- }
-
- if !isGroup {
- cmd = append(cmd, recipients...)
- } else {
- if len(recipients) > 1 {
- c.JSON(400, gin.H{"error": "More than one recipient is currently not allowed"})
- return
- }
-
- groupId, err := base64.StdEncoding.DecodeString(recipients[0])
- if err != nil {
- c.JSON(400, gin.H{"error": "Invalid group id"})
- return
- }
-
- cmd = append(cmd, []string{"-g", string(groupId)}...)
- }
-
- attachmentTmpPaths := []string{}
- for _, base64Attachment := range base64Attachments {
- u, err := uuid.NewV4()
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- dec, err := base64.StdEncoding.DecodeString(base64Attachment)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- fType, err := filetype.Get(dec)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- attachmentTmpPath := attachmentTmpDir + u.String() + "." + fType.Extension
- attachmentTmpPaths = append(attachmentTmpPaths, attachmentTmpPath)
-
- f, err := os.Create(attachmentTmpPath)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
- defer f.Close()
-
- if _, err := f.Write(dec); err != nil {
- cleanupTmpFiles(attachmentTmpPaths)
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
- if err := f.Sync(); err != nil {
- cleanupTmpFiles(attachmentTmpPaths)
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- f.Close()
- }
-
- if len(attachmentTmpPaths) > 0 {
- cmd = append(cmd, "-a")
- cmd = append(cmd, attachmentTmpPaths...)
- }
-
- _, err := runSignalCli(true, cmd)
- if err != nil {
- cleanupTmpFiles(attachmentTmpPaths)
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
- c.JSON(201, nil)
-}
-
-func getGroups(number string, signalCliConfig string) ([]GroupEntry, error) {
- groupEntries := []GroupEntry{}
-
- out, err := runSignalCli(true, []string{"--config", signalCliConfig, "-u", number, "listGroups", "-d"})
- if err != nil {
- return groupEntries, err
- }
-
- lines := strings.Split(out, "\n")
- for _, line := range lines {
- var groupEntry GroupEntry
- if line == "" {
- continue
- }
-
- idIdx := strings.Index(line, " Name: ")
- idPair := line[:idIdx]
- groupEntry.InternalId = strings.TrimPrefix(idPair, "Id: ")
- groupEntry.Id = convertInternalGroupIdToGroupId(groupEntry.InternalId)
- lineWithoutId := strings.TrimLeft(line[idIdx:], " ")
-
- nameIdx := strings.Index(lineWithoutId, " Active: ")
- namePair := lineWithoutId[:nameIdx]
- groupEntry.Name = strings.TrimRight(strings.TrimPrefix(namePair, "Name: "), " ")
- lineWithoutName := strings.TrimLeft(lineWithoutId[nameIdx:], " ")
-
- activeIdx := strings.Index(lineWithoutName, " Blocked: ")
- activePair := lineWithoutName[:activeIdx]
- active := strings.TrimPrefix(activePair, "Active: ")
- if active == "true" {
- groupEntry.Active = true
- } else {
- groupEntry.Active = false
- }
- lineWithoutActive := strings.TrimLeft(lineWithoutName[activeIdx:], " ")
-
- blockedIdx := strings.Index(lineWithoutActive, " Members: ")
- blockedPair := lineWithoutActive[:blockedIdx]
- blocked := strings.TrimPrefix(blockedPair, "Blocked: ")
- if blocked == "true" {
- groupEntry.Blocked = true
- } else {
- groupEntry.Blocked = false
- }
- lineWithoutBlocked := strings.TrimLeft(lineWithoutActive[blockedIdx:], " ")
-
- membersPair := lineWithoutBlocked
- members := strings.TrimPrefix(membersPair, "Members: ")
- trimmedMembers := strings.TrimRight(strings.TrimLeft(members, "["), "]")
- trimmedMembersList := strings.Split(trimmedMembers, ",")
- for _, member := range trimmedMembersList {
- groupEntry.Members = append(groupEntry.Members, strings.Trim(member, " "))
- }
-
- groupEntries = append(groupEntries, groupEntry)
- }
-
- return groupEntries, nil
-}
-
-func runSignalCli(wait bool, args []string) (string, error) {
- cmd := exec.Command("signal-cli", args...)
- if wait {
- var errBuffer bytes.Buffer
- var outBuffer bytes.Buffer
- cmd.Stderr = &errBuffer
- cmd.Stdout = &outBuffer
-
- err := cmd.Start()
- if err != nil {
- return "", err
- }
-
- done := make(chan error, 1)
- go func() {
- done <- cmd.Wait()
- }()
- select {
- case <-time.After(60 * time.Second):
- err := cmd.Process.Kill()
- if err != nil {
- return "", err
- }
- return "", errors.New("process killed as timeout reached")
- case err := <-done:
- if err != nil {
- return "", errors.New(errBuffer.String())
- }
- }
- return outBuffer.String(), nil
- } else {
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return "", err
- }
- cmd.Start()
- buf := bufio.NewReader(stdout) // Notice that this is not in a loop
- line, _, _ := buf.ReadLine()
- return string(line), nil
- }
-}
+// @tag.name Messages
+// @tag.description Send and Receive Signal Messages.
+// @host 127.0.0.1:8080
+// @BasePath /
func main() {
signalCliConfig := flag.String("signal-cli-config", "/home/.local/share/signal-cli/", "Config directory where signal-cli config is stored")
attachmentTmpDir := flag.String("attachment-tmp-dir", "/tmp/", "Attachment tmp directory")
@@ -247,270 +40,53 @@ func main() {
router := gin.Default()
log.Info("Started Signal Messenger REST API")
- router.GET("/v1/about", func(c *gin.Context) {
- type About struct {
- SupportedApiVersions []string `json:"versions"`
- BuildNr int `json:"build"`
+ api := api.NewApi(*signalCliConfig, *attachmentTmpDir)
+ v1 := router.Group("/v1")
+ {
+ about := v1.Group("/about")
+ {
+ about.GET("", api.About)
}
- about := About{SupportedApiVersions: []string{"v1", "v2"}, BuildNr: 2}
- c.JSON(200, about)
- })
-
- router.POST("/v1/register/:number", func(c *gin.Context) {
- number := c.Param("number")
-
- type Request struct {
- UseVoice bool `json:"use_voice"`
+ register := v1.Group("/register")
+ {
+ register.POST(":number", api.RegisterNumber)
+ register.POST(":number/verify/:token", api.VerifyRegisteredNumber)
}
- var req Request
-
- buf := new(bytes.Buffer)
- buf.ReadFrom(c.Request.Body)
- if buf.String() != "" {
- err := json.Unmarshal(buf.Bytes(), &req)
- if err != nil {
- log.Error("Couldn't register number: ", err.Error())
- c.JSON(400, gin.H{"error": "Couldn't process request - invalid request."})
- return
- }
- } else {
- req.UseVoice = false
+ sendV1 := v1.Group("/send")
+ {
+ sendV1.POST("", api.Send)
}
- if number == "" {
- c.JSON(400, gin.H{"error": "Please provide a number"})
- return
+ receive := v1.Group("/receive")
+ {
+ receive.GET(":number", api.Receive)
}
- command := []string{"--config", *signalCliConfig, "-u", number, "register"}
-
- if req.UseVoice == true {
- command = append(command, "--voice")
+ groups := v1.Group("/groups")
+ {
+ groups.POST(":number", api.CreateGroup)
+ groups.GET(":number", api.GetGroups)
+ groups.DELETE(":number/:groupid", api.DeleteGroup)
}
- _, err := runSignalCli(true, command)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
+ link := v1.Group("qrcodelink")
+ {
+ link.GET("", api.GetQrCodeLink)
}
- c.JSON(201, nil)
- })
+ }
- router.POST("/v1/register/:number/verify/:token", func(c *gin.Context) {
- number := c.Param("number")
- token := c.Param("token")
-
- if number == "" {
- c.JSON(400, gin.H{"error": "Please provide a number"})
- return
+ v2 := router.Group("/v2")
+ {
+ sendV2 := v2.Group("/send")
+ {
+ sendV2.POST("", api.SendV2)
}
+ }
- if token == "" {
- c.JSON(400, gin.H{"error": "Please provide a verification code"})
- return
- }
-
- _, err := runSignalCli(true, []string{"--config", *signalCliConfig, "-u", number, "verify", token})
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
- c.JSON(201, nil)
- })
-
- router.POST("/v1/send", func(c *gin.Context) {
- type Request struct {
- Number string `json:"number"`
- Recipients []string `json:"recipients"`
- Message string `json:"message"`
- Base64Attachment string `json:"base64_attachment"`
- IsGroup bool `json:"is_group"`
- }
- var req Request
- err := c.BindJSON(&req)
- if err != nil {
- c.JSON(400, gin.H{"error": "Couldn't process request - invalid request"})
- return
- }
-
- base64Attachments := []string{}
- if req.Base64Attachment != "" {
- base64Attachments = append(base64Attachments, req.Base64Attachment)
- }
-
- send(c, *signalCliConfig, *signalCliConfig, req.Number, req.Message, req.Recipients, base64Attachments, req.IsGroup)
- })
-
- router.POST("/v2/send", func(c *gin.Context) {
- type Request struct {
- Number string `json:"number"`
- Recipients []string `json:"recipients"`
- Message string `json:"message"`
- Base64Attachments []string `json:"base64_attachments"`
- }
- var req Request
- err := c.BindJSON(&req)
- if err != nil {
- c.JSON(400, gin.H{"error": "Couldn't process request - invalid request"})
- log.Error(err.Error())
- return
- }
-
- if len(req.Recipients) == 0 {
- c.JSON(400, gin.H{"error": "Couldn't process request - please provide at least one recipient"})
- return
- }
-
- groups := []string{}
- recipients := []string{}
-
- for _, recipient := range req.Recipients {
- if strings.HasPrefix(recipient, groupPrefix) {
- groups = append(groups, strings.TrimPrefix(recipient, groupPrefix))
- } else {
- recipients = append(recipients, recipient)
- }
- }
-
- if len(recipients) > 0 && len(groups) > 0 {
- c.JSON(400, gin.H{"error": "Signal Messenger Groups and phone numbers cannot be specified together in one request! Please split them up into multiple REST API calls."})
- return
- }
-
- if len(groups) > 1 {
- c.JSON(400, gin.H{"error": "A signal message cannot be sent to more than one group at once! Please use multiple REST API calls for that."})
- return
- }
-
- for _, group := range groups {
- send(c, *attachmentTmpDir, *signalCliConfig, req.Number, req.Message, []string{group}, req.Base64Attachments, true)
- }
-
- if len(recipients) > 0 {
- send(c, *attachmentTmpDir, *signalCliConfig, req.Number, req.Message, recipients, req.Base64Attachments, false)
- }
- })
-
- router.GET("/v1/receive/:number", func(c *gin.Context) {
- number := c.Param("number")
-
- command := []string{"--config", *signalCliConfig, "-u", number, "receive", "-t", "1", "--json"}
- out, err := runSignalCli(true, command)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- out = strings.Trim(out, "\n")
- lines := strings.Split(out, "\n")
-
- jsonStr := "["
- for i, line := range lines {
- jsonStr += line
- if i != (len(lines) - 1) {
- jsonStr += ","
- }
- }
- jsonStr += "]"
-
- c.String(200, jsonStr)
- })
-
- router.POST("/v1/groups/:number", func(c *gin.Context) {
- number := c.Param("number")
-
- type Request struct {
- Name string `json:"name"`
- Members []string `json:"members"`
- }
-
- var req Request
- err := c.BindJSON(&req)
- if err != nil {
- c.JSON(400, gin.H{"error": "Couldn't process request - invalid request"})
- log.Error(err.Error())
- return
- }
-
- cmd := []string{"--config", *signalCliConfig, "-u", number, "updateGroup", "-n", req.Name, "-m"}
- cmd = append(cmd, req.Members...)
-
- out, err := runSignalCli(true, cmd)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- internalGroupId := getStringInBetween(out, `"`, `"`)
- c.JSON(201, gin.H{"id": convertInternalGroupIdToGroupId(internalGroupId)})
- })
-
- router.GET("/v1/groups/:number", func(c *gin.Context) {
- number := c.Param("number")
-
- groups, err := getGroups(number, *signalCliConfig)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- c.JSON(200, groups)
- })
-
- router.DELETE("/v1/groups/:number/:groupid", func(c *gin.Context) {
- base64EncodedGroupId := c.Param("groupid")
- number := c.Param("number")
-
- if base64EncodedGroupId == "" {
- c.JSON(400, gin.H{"error": "Please specify a group id"})
- return
- }
-
- groupId, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(base64EncodedGroupId, groupPrefix))
- if err != nil {
- c.JSON(400, gin.H{"error": "Invalid group id"})
- return
- }
-
- _, err = runSignalCli(true, []string{"--config", *signalCliConfig, "-u", number, "quitGroup", "-g", string(groupId)})
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
- })
-
- router.GET("/v1/qrcodelink", func(c *gin.Context) {
- deviceName := c.Query("device_name")
-
- if deviceName == "" {
- c.JSON(400, gin.H{"error": "Please provide a name for the device"})
- return
- }
-
- command := []string{"--config", *signalCliConfig, "link", "-n", deviceName}
-
- tsdeviceLink, err := runSignalCli(false, command)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- return
- }
-
- q, err := qrcode.New(string(tsdeviceLink), qrcode.Medium)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- }
-
- q.DisableBorder = true
- var png []byte
- png, err = q.PNG(256)
- if err != nil {
- c.JSON(400, gin.H{"error": err.Error()})
- }
-
- c.Data(200, "image/png", png)
- })
+ swaggerUrl := ginSwagger.URL("http://127.0.0.1:8080/swagger/doc.json")
+ router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, swaggerUrl))
router.Run()
}