mirror of
https://github.com/AsamK/signal-cli
synced 2025-09-02 04:20:38 +00:00
An interface for `signal-cli daemon --socket ...` to separate command responses from incoming messages. This also solved a problem I had with using the `--tcp` option inside a LXC container, since signal-cli binds to 127.0.0.1 and I needed access from outside the container.
99 lines
2.1 KiB
Go
99 lines
2.1 KiB
Go
/**
|
|
Accept commands on TCP socket and send to signal-cli daemon on UNIX socket.
|
|
Immediate responses are returned via the TCP socket, and other incoming
|
|
messages are logged to stdout.
|
|
|
|
With this program running as a service, we can follow incoming messages with:
|
|
journalctl -fu signal-cli-api --no-tail
|
|
**/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
unixSocketPath := "/tmp/signal-cli/socket"
|
|
unixConn, err := net.Dial("unix", unixSocketPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer unixConn.Close()
|
|
|
|
tcpPort := "5780"
|
|
tcpListener, err := net.Listen("tcp", ":"+tcpPort)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer tcpListener.Close()
|
|
|
|
responses := make(chan string)
|
|
|
|
// Read messages from the UNIX socket
|
|
go func() {
|
|
scanner := bufio.NewScanner(unixConn)
|
|
for scanner.Scan() {
|
|
resp := scanner.Text()
|
|
if isCommandResponse(resp) {
|
|
responses <- resp
|
|
} else {
|
|
log.Println(resp)
|
|
}
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
log.Println(err)
|
|
}
|
|
}()
|
|
|
|
for {
|
|
// Wait for TCP connection
|
|
tcpConn, err := tcpListener.Accept()
|
|
if err != nil {
|
|
log.Println(err)
|
|
continue
|
|
}
|
|
|
|
// Read command
|
|
cmd, err := bufio.NewReader(tcpConn).ReadString('\n')
|
|
if err != nil {
|
|
log.Println(err)
|
|
tcpConn.Close()
|
|
continue
|
|
}
|
|
|
|
// Write command to UNIX socket
|
|
fmt.Fprint(unixConn, cmd)
|
|
|
|
// Read responses
|
|
select {
|
|
case msg := <-responses:
|
|
fmt.Fprintln(tcpConn, msg)
|
|
case <-time.After(2 * time.Second):
|
|
fmt.Fprint(tcpConn, "Timed out")
|
|
}
|
|
|
|
// Close TCP connection
|
|
tcpConn.Close()
|
|
}
|
|
}
|
|
|
|
/** Check if signal-cli json-rpc message is a command response.
|
|
Command response & error formats:
|
|
{"jsonrpc":"2.0","result":{...},"id":1678829060000}
|
|
{"jsonrpc":"2.0","result":[...],"id":1678829060000}
|
|
{"jsonrpc":"2.0","error":{...},"id":1678829060000}
|
|
|
|
Incoming message format:
|
|
{"jsonrpc":"2.0","method":"receive","params":{...}}
|
|
|
|
This assumes signal-cli doesn't change jsonrpc format or version.
|
|
It would be more robust to parse the JSON, but shouldn't be needed.
|
|
**/
|
|
func isCommandResponse(resp string) bool {
|
|
return len(resp) >= 26 && resp[:26] != "{\"jsonrpc\":\"2.0\",\"method\":"
|
|
}
|