mirror of
https://github.com/bbernhard/signal-cli-rest-api.git
synced 2026-05-20 13:44:17 +00:00
Compare commits
17 Commits
ae1b35320c
...
368bf31a64
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
368bf31a64 | ||
|
|
ddc5aa55df | ||
|
|
00bbfca878 | ||
|
|
884582a71e | ||
|
|
e3d503c746 | ||
|
|
af34a0881c | ||
|
|
c7cb9ab13e | ||
|
|
af18c7aea8 | ||
|
|
ed3626fa77 | ||
|
|
6257fa754d | ||
|
|
dd6d763618 | ||
|
|
1ea89705d5 | ||
|
|
9e5d73b5c0 | ||
|
|
d080e8d478 | ||
|
|
a9c367a5b1 | ||
|
|
8d13f5f383 | ||
|
|
3a36a04b09 |
14
Dockerfile
14
Dockerfile
@ -1,13 +1,13 @@
|
|||||||
ARG SIGNAL_CLI_VERSION=0.14.0
|
ARG SIGNAL_CLI_VERSION=0.14.0
|
||||||
ARG LIBSIGNAL_CLIENT_VERSION=0.87.4
|
ARG LIBSIGNAL_CLIENT_VERSION=0.87.4
|
||||||
ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION=0.14.0+morph027+1
|
ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION=0.14.0+morph027+5
|
||||||
|
|
||||||
ARG SWAG_VERSION=1.16.4
|
ARG SWAG_VERSION=1.16.4
|
||||||
ARG GRAALVM_VERSION=25.0.2
|
ARG GRAALVM_VERSION=25.0.2
|
||||||
|
|
||||||
ARG BUILD_VERSION_ARG=unset
|
ARG BUILD_VERSION_ARG=unset
|
||||||
|
|
||||||
FROM golang:1.24-bookworm AS buildcontainer
|
FROM golang:1.26-trixie AS buildcontainer
|
||||||
|
|
||||||
ARG SIGNAL_CLI_VERSION
|
ARG SIGNAL_CLI_VERSION
|
||||||
ARG LIBSIGNAL_CLIENT_VERSION
|
ARG LIBSIGNAL_CLIENT_VERSION
|
||||||
@ -31,8 +31,8 @@ RUN arch="$(uname -m)"; \
|
|||||||
RUN dpkg-reconfigure debconf --frontend=noninteractive \
|
RUN dpkg-reconfigure debconf --frontend=noninteractive \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get -y install --no-install-recommends \
|
&& apt-get -y install --no-install-recommends \
|
||||||
wget software-properties-common git locales zip unzip \
|
wget git locales zip unzip \
|
||||||
file build-essential libz-dev zlib1g-dev \
|
file build-essential libz-dev zlib1g-dev binutils \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
|
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
|
||||||
@ -90,8 +90,8 @@ RUN if [ "$(uname -m)" = "x86_64" ]; then \
|
|||||||
&& ./gradlew -q nativeCompile; \
|
&& ./gradlew -q nativeCompile; \
|
||||||
elif [ "$(uname -m)" = "aarch64" ] ; then \
|
elif [ "$(uname -m)" = "aarch64" ] ; then \
|
||||||
echo "Use native image from @morph027 (https://packaging.gitlab.io/signal-cli/) for arm64 - many thanks to @morph027" \
|
echo "Use native image from @morph027 (https://packaging.gitlab.io/signal-cli/) for arm64 - many thanks to @morph027" \
|
||||||
&& curl -fsSL https://packaging.gitlab.io/signal-cli/gpg.key | apt-key add - \
|
&& curl -fsSL https://packaging.gitlab.io/signal-cli/gpg.key | gpg -o /usr/share/keyrings/signal-cli-native.pgp --dearmor \
|
||||||
&& echo "deb https://packaging.gitlab.io/signal-cli focal main" > /etc/apt/sources.list.d/morph027-signal-cli.list \
|
&& echo "deb [signed-by=/usr/share/keyrings/signal-cli-native.pgp] https://packaging.gitlab.io/signal-cli signalcli main" > /etc/apt/sources.list.d/morph027-signal-cli.list \
|
||||||
&& mkdir -p /tmp/signal-cli-native \
|
&& mkdir -p /tmp/signal-cli-native \
|
||||||
&& cd /tmp/signal-cli-native \
|
&& cd /tmp/signal-cli-native \
|
||||||
#&& wget https://gitlab.com/packaging/signal-cli/-/jobs/3716873649/artifacts/download?file_type=archive -O /tmp/signal-cli-native/archive.zip \
|
#&& wget https://gitlab.com/packaging/signal-cli/-/jobs/3716873649/artifacts/download?file_type=archive -O /tmp/signal-cli-native/archive.zip \
|
||||||
@ -172,7 +172,7 @@ ENV SIGNAL_CLI_REST_API_PLUGIN_SHARED_OBJ_DIR=/usr/bin/
|
|||||||
|
|
||||||
RUN dpkg-reconfigure debconf --frontend=noninteractive \
|
RUN dpkg-reconfigure debconf --frontend=noninteractive \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends util-linux supervisor netcat-openbsd openjdk-25-jre curl locales \
|
&& apt-get install -y --no-install-recommends util-linux supervisor openjdk-25-jre curl locales \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=buildcontainer /tmp/signal-cli-rest-api-src/signal-cli-rest-api /usr/bin/signal-cli-rest-api
|
COPY --from=buildcontainer /tmp/signal-cli-rest-api-src/signal-cli-rest-api /usr/bin/signal-cli-rest-api
|
||||||
|
|||||||
@ -4,6 +4,8 @@ This can be done by putting the docker container into debug mode with the follow
|
|||||||
|
|
||||||
```curl -X POST -H "Content-Type: application/json" -d '{"logging": {"level": "debug"}}' 'http://127.0.0.1:8080/v1/configuration'```
|
```curl -X POST -H "Content-Type: application/json" -d '{"logging": {"level": "debug"}}' 'http://127.0.0.1:8080/v1/configuration'```
|
||||||
|
|
||||||
|
Alternatively, you can set the `LOG_LEVEL` environment variable.
|
||||||
|
|
||||||
Once the docker container is in debug mode, execute the REST API command you want to debug.
|
Once the docker container is in debug mode, execute the REST API command you want to debug.
|
||||||
|
|
||||||
e.g Let's assume we are experiencing some problems with sending messages. So, let's send a Signal message with
|
e.g Let's assume we are experiencing some problems with sending messages. So, let's send a Signal message with
|
||||||
|
|||||||
@ -1,11 +1,22 @@
|
|||||||
version: "3"
|
|
||||||
services:
|
services:
|
||||||
signal-cli-rest-api:
|
signal-cli-rest-api:
|
||||||
image: bbernhard/signal-cli-rest-api:latest
|
#image: bbernhard/signal-cli-rest-api:latest-dev
|
||||||
|
build: "."
|
||||||
environment:
|
environment:
|
||||||
- MODE=normal #supported modes: json-rpc, native, normal
|
- MODE=normal #supported modes: json-rpc, json-rpc-native, native, normal
|
||||||
|
- ENABLE_PLUGINS=true
|
||||||
|
- DEFAULT_SIGNAL_TEXT_MODE=styled
|
||||||
|
- SWAGGER_IP=127.0.0.1
|
||||||
|
- PODMAN_USERNS=keep-id
|
||||||
|
#- JSON_RPC_IGNORE_ATTACHMENTS=true
|
||||||
|
#- JSON_RPC_IGNORE_STORIES=true
|
||||||
|
#- RECEIVE_WEBHOOK_URL=http://127.0.0.1:8089/webhook
|
||||||
|
#- JSON_RPC_TRUST_NEW_IDENTITIES=always
|
||||||
|
#- RECEIVE_WEBHOOK_URL=http://127.0.0.1:8080/v1/plugins/abc
|
||||||
#- AUTO_RECEIVE_SCHEDULE=0 22 * * * #enable this parameter on demand (see description below)
|
#- AUTO_RECEIVE_SCHEDULE=0 22 * * * #enable this parameter on demand (see description below)
|
||||||
|
#network_mode: host
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080" #map docker port 8080 to host port 8080.
|
- "8080:8080" #map docker port 8080 to host port 8080.
|
||||||
volumes:
|
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
|
- "./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
|
||||||
|
- "./plugins:/plugins"
|
||||||
|
|||||||
@ -843,6 +843,7 @@ func (a *Api) AddMembersToGroup(c *gin.Context) {
|
|||||||
|
|
||||||
err = a.signalClient.AddMembersToGroup(number, groupId, req.Members)
|
err = a.signalClient.AddMembersToGroup(number, groupId, req.Members)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Info("ERR NOT NULL")
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case *client.NotFoundError:
|
case *client.NotFoundError:
|
||||||
c.JSON(404, Error{Msg: err.Error()})
|
c.JSON(404, Error{Msg: err.Error()})
|
||||||
|
|||||||
@ -410,7 +410,7 @@ func (s *SignalClient) GetSignalCliMode() SignalCliMode {
|
|||||||
return s.signalCliMode
|
return s.signalCliMode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SignalClient) Init() error {
|
func (s *SignalClient) Init(maxRetries int) error {
|
||||||
s.signalCliApiConfig = utils.NewSignalCliApiConfig()
|
s.signalCliApiConfig = utils.NewSignalCliApiConfig()
|
||||||
err := s.signalCliApiConfig.Load(s.signalCliApiConfigPath)
|
err := s.signalCliApiConfig.Load(s.signalCliApiConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -427,7 +427,7 @@ func (s *SignalClient) Init() error {
|
|||||||
tcpPortsNumberMapping := s.jsonRpc2ClientConfig.GetTcpPortsForNumbers()
|
tcpPortsNumberMapping := s.jsonRpc2ClientConfig.GetTcpPortsForNumbers()
|
||||||
for number, tcpPort := range tcpPortsNumberMapping {
|
for number, tcpPort := range tcpPortsNumberMapping {
|
||||||
s.jsonRpc2Clients[number] = NewJsonRpc2Client(s.signalCliApiConfig, number)
|
s.jsonRpc2Clients[number] = NewJsonRpc2Client(s.signalCliApiConfig, number)
|
||||||
err := s.jsonRpc2Clients[number].Dial("127.0.0.1:" + strconv.FormatInt(tcpPort, 10))
|
err := s.jsonRpc2Clients[number].Dial("127.0.0.1:"+strconv.FormatInt(tcpPort, 10), maxRetries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1349,7 +1349,7 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) {
|
|||||||
}
|
}
|
||||||
pendingMembers = append(pendingMembers, identifier)
|
pendingMembers = append(pendingMembers, identifier)
|
||||||
}
|
}
|
||||||
groupEntry.PendingRequests = pendingMembers
|
groupEntry.PendingInvites = pendingMembers
|
||||||
|
|
||||||
requestingMembers := []string{}
|
requestingMembers := []string{}
|
||||||
for _, val := range signalCliGroupEntry.RequestingMembers {
|
for _, val := range signalCliGroupEntry.RequestingMembers {
|
||||||
@ -1359,7 +1359,7 @@ func (s *SignalClient) GetGroups(number string) ([]GroupEntry, error) {
|
|||||||
}
|
}
|
||||||
requestingMembers = append(requestingMembers, identifier)
|
requestingMembers = append(requestingMembers, identifier)
|
||||||
}
|
}
|
||||||
groupEntry.PendingInvites = requestingMembers
|
groupEntry.PendingRequests = requestingMembers
|
||||||
|
|
||||||
admins := []string{}
|
admins := []string{}
|
||||||
for _, val := range signalCliGroupEntry.Admins {
|
for _, val := range signalCliGroupEntry.Admins {
|
||||||
@ -2705,7 +2705,6 @@ func (s *SignalClient) ListContacts(number string, allRecipients bool, recipient
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
|
func (s *SignalClient) SetPin(number string, registrationLockPin string) error {
|
||||||
if s.signalCliMode == JsonRpc {
|
if s.signalCliMode == JsonRpc {
|
||||||
type Request struct {
|
type Request struct {
|
||||||
|
|||||||
@ -2,14 +2,14 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"net/http"
|
|
||||||
"bytes"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/bbernhard/signal-cli-rest-api/utils"
|
"github.com/bbernhard/signal-cli-rest-api/utils"
|
||||||
uuid "github.com/gofrs/uuid"
|
uuid "github.com/gofrs/uuid"
|
||||||
@ -60,11 +60,11 @@ type JsonRpc2Client struct {
|
|||||||
conn net.Conn
|
conn net.Conn
|
||||||
receivedResponsesById map[string]chan JsonRpc2MessageResponse
|
receivedResponsesById map[string]chan JsonRpc2MessageResponse
|
||||||
receivedMessagesChannels map[string]chan JsonRpc2ReceivedMessage
|
receivedMessagesChannels map[string]chan JsonRpc2ReceivedMessage
|
||||||
lastTimeErrorMessageSent time.Time
|
|
||||||
signalCliApiConfig *utils.SignalCliApiConfig
|
signalCliApiConfig *utils.SignalCliApiConfig
|
||||||
number string
|
number string
|
||||||
receivedMessagesMutex sync.Mutex
|
receivedMessagesMutex sync.Mutex
|
||||||
receivedResponsesMutex sync.Mutex
|
receivedResponsesMutex sync.Mutex
|
||||||
|
address string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJsonRpc2Client(signalCliApiConfig *utils.SignalCliApiConfig, number string) *JsonRpc2Client {
|
func NewJsonRpc2Client(signalCliApiConfig *utils.SignalCliApiConfig, number string) *JsonRpc2Client {
|
||||||
@ -76,10 +76,24 @@ func NewJsonRpc2Client(signalCliApiConfig *utils.SignalCliApiConfig, number stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *JsonRpc2Client) Dial(address string) error {
|
func (r *JsonRpc2Client) Dial(address string, maxRetries int) error {
|
||||||
var err error
|
var err error
|
||||||
|
r.address = address
|
||||||
|
connected := false
|
||||||
|
for i := 0; i < maxRetries; i++ {
|
||||||
r.conn, err = net.Dial("tcp", address)
|
r.conn, err = net.Dial("tcp", address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Info("Waiting for signal-cli to start up in daemon mode...")
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
connected = true
|
||||||
|
log.Info("Successfully connected to signal-cli in daemon mode")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !connected {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,11 +221,14 @@ func (r *JsonRpc2Client) ReceiveData(number string, receiveWebhookUrl string) {
|
|||||||
for {
|
for {
|
||||||
str, err := connbuf.ReadString('\n')
|
str, err := connbuf.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elapsed := time.Since(r.lastTimeErrorMessageSent)
|
log.Error("Lost connection to signal-cli...attempting to reconnect (", err.Error(), ")")
|
||||||
if (elapsed) > time.Duration(5*time.Minute) { //avoid spamming the log file and only log the message at max every 5 minutes
|
r.conn.Close()
|
||||||
log.Error("Couldn't read data for number ", number, ": ", err.Error(), ". Is the number properly registered?")
|
err = r.Dial(r.address, 15)
|
||||||
r.lastTimeErrorMessageSent = time.Now()
|
if err != nil {
|
||||||
|
log.Fatal("Unable to reconnect to signal-cli: ", err.Error(), "...aborting")
|
||||||
}
|
}
|
||||||
|
connbuf = bufio.NewReader(r.conn)
|
||||||
|
log.Info("Successfully reconnected to signal-cli")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Debug("json-rpc received data: ", str)
|
log.Debug("json-rpc received data: ", str)
|
||||||
@ -248,7 +265,7 @@ func (r *JsonRpc2Client) ReceiveData(number string, receiveWebhookUrl string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("Received unparsable message: ", str)
|
log.Warn("Received unparsable message: ", str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,7 +163,7 @@ func main() {
|
|||||||
jsonRpc2ClientConfigPathPath := *signalCliConfig + "/jsonrpc2.yml"
|
jsonRpc2ClientConfigPathPath := *signalCliConfig + "/jsonrpc2.yml"
|
||||||
signalCliApiConfigPath := *signalCliConfig + "/api-config.yml"
|
signalCliApiConfigPath := *signalCliConfig + "/api-config.yml"
|
||||||
signalClient := client.NewSignalClient(*signalCliConfig, *attachmentTmpDir, *avatarTmpDir, signalCliMode, jsonRpc2ClientConfigPathPath, signalCliApiConfigPath, webhookUrl)
|
signalClient := client.NewSignalClient(*signalCliConfig, *attachmentTmpDir, *avatarTmpDir, signalCliMode, jsonRpc2ClientConfigPathPath, signalCliApiConfigPath, webhookUrl)
|
||||||
err = signalClient.Init()
|
err = signalClient.Init(15)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Couldn't init Signal Client: ", err.Error())
|
log.Fatal("Couldn't init Signal Client: ", err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import (
|
|||||||
const supervisorctlConfigTemplate = `
|
const supervisorctlConfigTemplate = `
|
||||||
[program:%s]
|
[program:%s]
|
||||||
process_name=%s
|
process_name=%s
|
||||||
command=bash -c "nc -l -p %d <%s | signal-cli --output=json --config %s%s jsonRpc%s%s >%s"
|
command=signal-cli --output=json --config %s%s daemon %s%s --tcp 127.0.0.1:%d
|
||||||
autostart=true
|
autostart=true
|
||||||
autorestart=true
|
autorestart=true
|
||||||
startretries=10
|
startretries=10
|
||||||
@ -96,7 +96,7 @@ func main() {
|
|||||||
supervisorctlConfigFilename := "/etc/supervisor/conf.d/" + "signal-cli-json-rpc-1.conf"
|
supervisorctlConfigFilename := "/etc/supervisor/conf.d/" + "signal-cli-json-rpc-1.conf"
|
||||||
|
|
||||||
supervisorctlConfig := fmt.Sprintf(supervisorctlConfigTemplate, supervisorctlProgramName, supervisorctlProgramName,
|
supervisorctlConfig := fmt.Sprintf(supervisorctlConfigTemplate, supervisorctlProgramName, supervisorctlProgramName,
|
||||||
tcpPort, fifoPathname, signalCliConfigDir, trustNewIdentities, signalCliIgnoreAttachments, signalCliIgnoreStories, fifoPathname,
|
signalCliConfigDir, trustNewIdentities, signalCliIgnoreAttachments, signalCliIgnoreStories, tcpPort,
|
||||||
supervisorctlProgramName, supervisorctlProgramName)
|
supervisorctlProgramName, supervisorctlProgramName)
|
||||||
|
|
||||||
err = ioutil.WriteFile(supervisorctlConfigFilename, []byte(supervisorctlConfig), 0644)
|
err = ioutil.WriteFile(supervisorctlConfigFilename, []byte(supervisorctlConfig), 0644)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user