mirror of
https://github.com/aljazceru/signal-cli-rest-api.git
synced 2025-12-19 07:34:23 +01:00
initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
signal-cli-config
|
||||
src/main
|
||||
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
RUN apt-get update && apt-get install -y wget default-jre software-properties-common git
|
||||
|
||||
RUN add-apt-repository ppa:gophers/archive
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y golang-1.11-go
|
||||
|
||||
|
||||
# ARM
|
||||
#RUN wget -P /tmp/ https://dl.google.com/go/go1.12.5.linux-armv6l.tar.gz \
|
||||
# && tar -C /usr/local -xzf /tmp/go1.12.5.linux-armv6l.tar.gz \
|
||||
# && rm -rf /tmp/go1.12.5.linux-armv6l.tar.gz
|
||||
|
||||
#RUN wget -P /tmp/ https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz \
|
||||
# && tar -C /user/local -xzf /tmp/go1.12.5.linux-amd64.tar.gz \
|
||||
# && rm -rf /tmp/go1.12.5.linux-amd64.tar.gz
|
||||
|
||||
ENV PATH /usr/lib/go-1.11/bin/:$PATH
|
||||
|
||||
|
||||
RUN wget -P /tmp/ https://github.com/AsamK/signal-cli/releases/download/v0.6.2/signal-cli-0.6.2.tar.gz \
|
||||
&& tar -C /usr/bin -xzf /tmp/signal-cli-0.6.2.tar.gz \
|
||||
&& rm -rf /tmp/signal-cli-0.6.2.tar.gz
|
||||
|
||||
|
||||
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
|
||||
|
||||
ENV PATH /tmp/signal-cli-rest-api-src/:/usr/bin/signal-cli-0.6.2/bin/:$PATH
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["main"]
|
||||
56
README.md
Normal file
56
README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Dockerized signal-cli REST API
|
||||
|
||||
This project creates a small dockerized REST API around signal-cli(https://github.com/AsamK/signal-cli).
|
||||
|
||||
|
||||
At the moment, the following functionality is exposed via REST:
|
||||
|
||||
* Register a number
|
||||
* Verify the number using the code received via SMS
|
||||
* Send message to multiple recipients
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
Sample `docker-compose.yml`file:
|
||||
|
||||
```
|
||||
version: "3"
|
||||
services:
|
||||
signal-cli-rest-api:
|
||||
build: "."
|
||||
ports:
|
||||
- "8080:8080" #map docker port 8080 to host port 8080.
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- "./signal-cli-config:/home/.local/share/signal-cli" #map "signal-cli-config" folder on host system into docker container. the folder contains the password and cryptographic keys when a new number is registered
|
||||
|
||||
```
|
||||
|
||||
Sample REST API calls:
|
||||
|
||||
* Register a number (with SMS verification)
|
||||
```curl -X POST -H "Content-Type: application/json" 'http://127.0.0.1:8080/register/<number>'```
|
||||
|
||||
e.g:
|
||||
```curl -X POST -H "Content-Type: application/json" 'http://127.0.0.1:8080/v1/register/+431212131491291'```
|
||||
|
||||
* Verify the number using the code received via SMS
|
||||
```curl -X POST -H "Content-Type: application/json" 'http://127.0.0.1:8080/v1/register/<number>/verify/<verification code>'```
|
||||
|
||||
e.g:
|
||||
```curl -X POST -H "Content-Type: application/json" 'http://127.0.0.1:8080/v1/register/+431212131491291/verify/123-456'```
|
||||
|
||||
* Send message to multiple recipients
|
||||
|
||||
```curl -X POST -H "Content-Type: application/json" -d '{"message": "<message>", "number": "<number>", "recipients": ["<recipient1>", "<recipient2>"]}' 'http://127.0.0.1:8080/v1/send'```
|
||||
|
||||
e.g:
|
||||
|
||||
```curl -X POST -H "Content-Type: application/json" -d '{"message": "Hello World!", "number": "+431212131491291", "recipients": ["+4354546464654", "+4912812812121"]}' 'http://127.0.0.1:8080/v1/send'```
|
||||
|
||||
* Send a message (+ base64 encoded attachment) to multiple recipients
|
||||
|
||||
```curl -X POST -H "Content-Type: application/json" -d '{"message": "<message>", "base64_attachment": "<base64 encoded attachment>", "number": "<number>", "recipients": ["<recipient1>", "<recipient2>"]}' 'http://127.0.0.1:8080/v1/send'```
|
||||
|
||||
In case you need more functionality, please create a pull request!
|
||||
9
docker-compose.yml
Normal file
9
docker-compose.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: "3"
|
||||
services:
|
||||
signal-cli-rest-api:
|
||||
build: "."
|
||||
ports:
|
||||
- "8080:8080" #map docker port 8080 to host port 8080.
|
||||
network_mode: "host"
|
||||
volumes:
|
||||
- "./signal-cli-config:/home/.local/share/signal-cli" #map "signal-cli-config" folder on host system into docker container. the folder contains the password and cryptographic keys when a new number is registered
|
||||
166
src/main.go
Normal file
166
src/main.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/satori/go.uuid"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/h2non/filetype"
|
||||
"os/exec"
|
||||
"time"
|
||||
"errors"
|
||||
"flag"
|
||||
"bytes"
|
||||
"os"
|
||||
"encoding/base64"
|
||||
//"strings"
|
||||
)
|
||||
|
||||
func runSignalCli(args []string) error {
|
||||
cmd := exec.Command("signal-cli", args...)
|
||||
var errBuffer bytes.Buffer
|
||||
cmd.Stderr = &errBuffer
|
||||
|
||||
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 nil
|
||||
}
|
||||
|
||||
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")
|
||||
flag.Parse()
|
||||
|
||||
router := gin.Default()
|
||||
log.Info("Starting...")
|
||||
|
||||
router.POST("/v1/register/:number", func(c *gin.Context) {
|
||||
number := c.Param("number")
|
||||
|
||||
if number == "" {
|
||||
c.JSON(400, "Please provide a number")
|
||||
return
|
||||
}
|
||||
|
||||
err := runSignalCli([]string{"--config", *signalCliConfig, "-u", number, "register"})
|
||||
if err != nil {
|
||||
c.JSON(400, err.Error())
|
||||
return
|
||||
}
|
||||
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, "Please provide a number")
|
||||
return
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
c.JSON(400, gin.H{"error": "Please provide a verification code"})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
err := runSignalCli([]string{"--config", *signalCliConfig, "-u", number, "token", "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"`
|
||||
}
|
||||
var req Request
|
||||
err := c.BindJSON(&req)
|
||||
if err != nil {
|
||||
c.JSON(400, "Couldn't process request - invalid request")
|
||||
return
|
||||
}
|
||||
|
||||
cmd := []string{"--config", *signalCliConfig, "-u", req.Number, "send", "-m", req.Message}
|
||||
cmd = append(cmd, req.Recipients...)
|
||||
|
||||
attachmentTmpPath := ""
|
||||
if(req.Base64Attachment != "") {
|
||||
u, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
dec, err := base64.StdEncoding.DecodeString(req.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
|
||||
log.Info("attchment tmp path = ", 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 {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
cmd = append(cmd, "-a")
|
||||
cmd = append(cmd , attachmentTmpPath)
|
||||
}
|
||||
|
||||
err = runSignalCli(cmd)
|
||||
if err != nil {
|
||||
if attachmentTmpPath != "" {
|
||||
os.Remove(attachmentTmpPath)
|
||||
}
|
||||
c.JSON(400, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(201, nil)
|
||||
})
|
||||
|
||||
router.Run()
|
||||
}
|
||||
Reference in New Issue
Block a user