Generate client for documented actions.

This commit is contained in:
Finn 2020-10-12 22:24:11 -07:00
parent 6fccb53b2a
commit ffaa25780a
24 changed files with 1965 additions and 137 deletions

View file

@ -2,25 +2,27 @@ stages:
- build - build
lint: lint:
image: nixery.dev/shell/go/golangci-lint image: nixery.dev/shell/diffutils/go/golangci-lint
stage: build stage: build
before_script: before_script:
- cp /share/go/bin/go /bin && mkdir /tmp # fix weirdness from nixery image - cp /share/go/bin/go /bin && mkdir /tmp # fix weirdness from nixery image
- mkdir -p /go/src/git.callpipe.com/finn/signald-go - mkdir -p /go/src/src/gitlab.com/signald/signald-go
- cp -r * /go/src/git.callpipe.com/finn/signald-go - cp -r * /go/src/src/gitlab.com/signald/signald-go
- cd /go/src/git.callpipe.com/finn/signald-go - cd /go/src/src/gitlab.com/signald/signald-go
script: script:
- golangci-lint run - golangci-lint run
- go mod tidy
- diff --color=always go.mod "${CI_PROJECT_DIR}/go.mod"
- diff --color=always go.sum "${CI_PROJECT_DIR}/go.sum"
build: build:
stage: build stage: build
image: golang:latest image: golang:latest
before_script: before_script:
- mkdir -p /go/src/git.callpipe.com/finn/signald-go - mkdir -p /go/src/gitlab.com/signald/signald-go
- cp -r * /go/src/git.callpipe.com/finn/signald-go - cp -r * /go/src/gitlab.com/signald/signald-go
- cd /go/src/git.callpipe.com/finn/signald-go - cd /go/src/gitlab.com/signald/signald-go
script: script:
- go get ./... # TODO: Improve how dependencies are handled
- go build -o "${CI_PROJECT_DIR}/signald-cli" ./cmd/signald-cli - go build -o "${CI_PROJECT_DIR}/signald-cli" ./cmd/signald-cli
artifacts: artifacts:
paths: paths:

12
Makefile Normal file
View file

@ -0,0 +1,12 @@
signald-cli: signald/client-protocol
go build -o signald-cli ./cmd/signald-cli
protocol.json:
echo '{"type": "protocol", "version": "v1alpha1"}' | nc -q0 -U /var/run/signald/signald.sock | jq 'select(.type == "protocol").data' > protocol.json
signald/client-protocol: protocol.json tools/generator/*
go run ./tools/generator < protocol.json
clean:
rm -rf protocol.json
rm -rf signald/client-protocol/*

View file

@ -24,7 +24,7 @@ import (
"github.com/mdp/qrterminal" "github.com/mdp/qrterminal"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"git.callpipe.com/finn/signald-go/signald" "gitlab.com/signald/signald-go/signald/client-protocol/v0"
) )
var uriOrQR bool var uriOrQR bool
@ -36,7 +36,7 @@ var linkCmd = &cobra.Command{
Long: `Get a URI or QR code to link to an existing Signal account`, Long: `Get a URI or QR code to link to an existing Signal account`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
requestID := fmt.Sprint("signald-cli-", rand.Intn(1000)) requestID := fmt.Sprint("signald-cli-", rand.Intn(1000))
err := s.SendRequest(signald.Request{ err := s.RawRequest(v0.LegacyRequest{
Type: "link", Type: "link",
ID: requestID, ID: requestID,
}) })
@ -44,7 +44,7 @@ var linkCmd = &cobra.Command{
log.Fatal("error sending request: ", err) log.Fatal("error sending request: ", err)
} }
c := make(chan signald.Response) c := make(chan v0.LegacyResponse)
go s.Listen(c) go s.Listen(c)
for { for {
message := <-c message := <-c

View file

@ -22,7 +22,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"git.callpipe.com/finn/signald-go/signald" "gitlab.com/signald/signald-go/signald/client-protocol/v0"
) )
// listAccountsCmd represents the listAccounts command // listAccountsCmd represents the listAccounts command
@ -32,7 +32,7 @@ var listAccountsCmd = &cobra.Command{
Long: `Prints a list of all users to stdout.`, Long: `Prints a list of all users to stdout.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
requestID := fmt.Sprint("signald-cli-", rand.Intn(1000)) requestID := fmt.Sprint("signald-cli-", rand.Intn(1000))
err := s.SendRequest(signald.Request{ err := s.RawRequest(v0.LegacyRequest{
Type: "list_accounts", Type: "list_accounts",
ID: requestID, ID: requestID,
}) })
@ -40,7 +40,7 @@ var listAccountsCmd = &cobra.Command{
log.Fatal("error sending request: ", err) log.Fatal("error sending request: ", err)
} }
c := make(chan signald.Response) c := make(chan v0.LegacyResponse)
go s.Listen(c) go s.Listen(c)
for { for {
message := <-c message := <-c

View file

@ -22,7 +22,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"git.callpipe.com/finn/signald-go/signald" "gitlab.com/signald/signald-go/signald/client-protocol/v0"
) )
// listGroupsCmd represents the listGroups command // listGroupsCmd represents the listGroups command
@ -32,7 +32,7 @@ var listGroupsCmd = &cobra.Command{
Long: `Prints a list of all groups the user is in to stdout.`, Long: `Prints a list of all groups the user is in to stdout.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
requestID := fmt.Sprint("signald-cli-", rand.Intn(1000)) requestID := fmt.Sprint("signald-cli-", rand.Intn(1000))
err := s.SendRequest(signald.Request{ err := s.RawRequest(v0.LegacyRequest{
Type: "list_groups", Type: "list_groups",
Username: username, Username: username,
ID: requestID, ID: requestID,
@ -41,7 +41,7 @@ var listGroupsCmd = &cobra.Command{
log.Fatal("error sending request: ", err) log.Fatal("error sending request: ", err)
} }
c := make(chan signald.Response) c := make(chan v0.LegacyResponse)
go s.Listen(c) go s.Listen(c)
for { for {
message := <-c message := <-c

View file

@ -23,7 +23,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"git.callpipe.com/finn/signald-go/signald" "gitlab.com/signald/signald-go/signald"
) )
var cfgFile string var cfgFile string

View file

@ -16,13 +16,14 @@
package cmd package cmd
import ( import (
"encoding/json"
"log" "log"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"time"
"git.callpipe.com/finn/signald-go/signald" "gitlab.com/signald/signald-go/signald/client-protocol/v0"
"git.callpipe.com/finn/signald-go/signald/client-protocol/v1" "gitlab.com/signald/signald-go/signald/client-protocol/v1"
) )
var ( var (
@ -39,13 +40,10 @@ var sendCmd = &cobra.Command{
Short: "send a message to another user or group", Short: "send a message to another user or group",
Long: `send a message to another user or group on Signal`, Long: `send a message to another user or group on Signal`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
request := signald.Request{ request := v1.SendRequest{Username: username}
Type: "send",
Username: username,
}
if toUser != "" { if toUser != "" {
request.RecipientAddress = v1.JsonAddress{Number: toUser} request.RecipientAddress = &v1.JsonAddress{Number: toUser}
} else if toGroup != "" { } else if toGroup != "" {
request.RecipientGroupID = toGroup request.RecipientGroupID = toGroup
} else { } else {
@ -57,25 +55,16 @@ var sendCmd = &cobra.Command{
} }
if attachment != "" { if attachment != "" {
request.AttachmentFilenames = []string{attachment} request.Attachments = []*v0.JsonAttachment{{Filename: attachment}}
} }
err := s.SendRequest(request) go s.Listen(nil)
response, err := request.Submit(s)
if err != nil { if err != nil {
log.Fatal("error sending request: ", err) log.Fatal("error submitting request to signald: ", err)
} }
err = json.NewEncoder(os.Stdout).Encode(response)
timeout := 10 if err != nil {
log.Fatal("error encoding output ", err)
// Wait for the response
c := make(chan signald.Response)
go s.Listen(c)
select {
case <-c:
log.Println("Ok.")
case <-time.After(1 * time.Second):
// But timeout after a while
log.Fatalf("Timeout after %d seconds\n", timeout)
} }
}, },
} }

View file

@ -0,0 +1,49 @@
// Copyright © 2018 Finn Herzfeld <finn@janky.solutions>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"encoding/json"
"log"
"os"
"github.com/spf13/cobra"
"gitlab.com/signald/signald-go/signald/client-protocol/v1"
)
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "print the signald version",
Long: `print the signald version`,
Run: func(cmd *cobra.Command, args []string) {
go s.Listen(nil)
r := v1.VersionRequest{}
response, err := r.Submit(s)
if err != nil {
log.Fatal(err)
}
err = json.NewEncoder(os.Stdout).Encode(response)
if err != nil {
log.Fatal("error encoding output ", err)
}
},
}
func init() {
RootCmd.AddCommand(versionCmd)
}

View file

@ -15,7 +15,7 @@
package main package main
import "git.callpipe.com/finn/signald-go/cmd/signald-cli/cmd" import "gitlab.com/signald/signald-go/cmd/signald-cli/cmd"
func main() { func main() {
cmd.Execute() cmd.Execute()

7
go.mod
View file

@ -1,9 +1,14 @@
module git.callpipe.com/finn/signald-go module gitlab.com/signald/signald-go
go 1.14 go 1.14
require ( require (
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/mdp/qrterminal v1.0.1 github.com/mdp/qrterminal v1.0.1
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.7.0 github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
) )

11
go.sum
View file

@ -43,6 +43,8 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -184,6 +186,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -256,6 +260,9 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@ -306,6 +313,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -315,6 +324,8 @@ 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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

945
protocol.json Normal file
View file

@ -0,0 +1,945 @@
{
"doc_version": "v1alpha1",
"version": {
"name": "signald",
"version": "0.10.0+git2020-12-06rcea5cb72.51",
"branch": "refactor-client-request-handling",
"commit": "cea5cb720355afdca460aa30257bdd1317494c7b"
},
"info": "This document describes objects that may be used when communicating with signald. If this document lacks something you need to generate a client, please open an issue (https://gitlab.com/thefinn93/signald/-/issues/new). This is an initial proposal for the format, and I expect to change it before finalizing it. If it workswell, I hope to slowly move things out of the legacy request types.",
"types": {
"v1": {
"JsonMessageEnvelope": {
"fields": {
"username": {
"type": "String"
},
"uuid": {
"type": "String"
},
"source": {
"type": "JsonAddress",
"version": "v1"
},
"sourceDevice": {
"type": "int"
},
"type": {
"type": "String"
},
"relay": {
"type": "String"
},
"timestamp": {
"type": "long"
},
"timestampISO": {
"type": "String"
},
"serverTimestamp": {
"type": "long"
},
"serverDeliveredTimestamp": {
"type": "long"
},
"hasLegacyMessage": {
"type": "boolean"
},
"hasContent": {
"type": "boolean"
},
"isUnidentifiedSender": {
"type": "boolean"
},
"dataMessage": {
"type": "JsonDataMessage",
"version": "v1"
},
"syncMessage": {
"type": "JsonSyncMessage",
"version": "v1"
},
"callMessage": {
"type": "JsonCallMessage",
"version": "v0"
},
"receipt": {
"type": "JsonReceiptMessage",
"version": "v0"
},
"typing": {
"type": "JsonTypingMessage",
"version": "v0"
}
}
},
"SendRequest": {
"fields": {
"username": {
"type": "String"
},
"recipientAddress": {
"type": "JsonAddress",
"version": "v1"
},
"recipientGroupId": {
"type": "String"
},
"messageBody": {
"type": "String"
},
"attachments": {
"list": true,
"type": "JsonAttachment",
"version": "v0"
},
"quote": {
"type": "JsonQuote",
"version": "v1"
},
"timestamp": {
"type": "Long"
},
"mentions": {
"list": true,
"type": "JsonMention",
"version": "v1"
}
}
},
"SendResponse": {
"fields": {
"results": {
"list": true,
"type": "JsonSendMessageResult",
"version": "v1"
},
"timestamp": {
"type": "long"
}
}
},
"ReactRequest": {
"fields": {
"username": {
"type": "String"
},
"recipientAddress": {
"type": "JsonAddress",
"version": "v1"
},
"recipientGroupId": {
"type": "String"
},
"reaction": {
"type": "JsonReaction",
"version": "v1"
},
"timestamp": {
"type": "long"
}
},
"doc": "react to a previous message"
},
"VersionRequest": {
"fields": {}
},
"JsonVersionMessage": {
"fields": {
"name": {
"type": "String"
},
"version": {
"type": "String"
},
"branch": {
"type": "String"
},
"commit": {
"type": "String"
}
}
},
"JsonAddress": {
"fields": {
"number": {
"type": "String",
"doc": "An e164 phone number, starting with +. Currently the only available user-facing Signal identifier."
},
"uuid": {
"type": "UUID",
"doc": "A UUID, the unique identifier for a particular Signal account."
},
"relay": {
"type": "String"
}
}
},
"JsonDataMessage": {
"fields": {
"timestamp": {
"type": "long",
"doc": "the (unix) timestamp that the message was sent at, according to the sender's device. This is used to uniquely identify this message for things like reactions and quotes."
},
"attachments": {
"list": true,
"type": "JsonAttachment",
"version": "v0",
"doc": "files attached to the incoming message"
},
"body": {
"type": "String",
"doc": "the text body of the incoming message."
},
"group": {
"type": "JsonGroupInfo",
"version": "v1",
"doc": "if the incoming message was sent to a v1 group, information about that group will be here"
},
"groupV2": {
"type": "JsonGroupV2Info",
"version": "v1",
"doc": "is the incoming message was sent to a v2 group, basic identifying information about that group will be here. For full information, use list_groups"
},
"endSession": {
"type": "boolean"
},
"expiresInSeconds": {
"type": "int",
"doc": "the expiry timer on the incoming message. Clients should delete records of the message within this number of seconds"
},
"profileKeyUpdate": {
"type": "boolean"
},
"quote": {
"type": "JsonQuote",
"version": "v1",
"doc": "if the incoming message is a quote or reply to another message, this will contain information about that message"
},
"contacts": {
"list": true,
"type": "SharedContact",
"version": "v0",
"doc": "if the incoming message has a shared contact, the contact's information will be here"
},
"previews": {
"list": true,
"type": "JsonPreview",
"version": "v0",
"doc": "if the incoming message has a link preview, information about that preview will be here"
},
"sticker": {
"type": "JsonSticker",
"version": "v0",
"doc": "if the incoming message is a sticker, information about the sicker will be here"
},
"viewOnce": {
"type": "boolean",
"doc": "indicates the message is a view once message. View once messages typically include no body and a single image attachment. Official Signal clients will prevent the user from saving the image, and once the user has viewed the image once they will destroy the image."
},
"reaction": {
"type": "JsonReaction",
"version": "v1",
"doc": "if the message adds or removes a reaction to another message, this will indicate what change is being made"
},
"remoteDelete": {
"type": "RemoteDelete",
"version": "v0",
"doc": "if the inbound message is deleting a previously sent message, indicates which message should be deleted"
}
}
},
"JsonSyncMessage": {
"fields": {
"sent": {
"type": "JsonSentTranscriptMessage",
"version": "v1"
},
"contacts": {
"type": "JsonAttachment",
"version": "v0"
},
"contactsComplete": {
"type": "boolean"
},
"groups": {
"type": "JsonAttachment",
"version": "v0"
},
"blockedList": {
"type": "JsonBlockedListMessage",
"version": "v1"
},
"request": {
"type": "String"
},
"readMessages": {
"list": true,
"type": "JsonReadMessage",
"version": "v1"
},
"viewOnceOpen": {
"type": "JsonViewOnceOpenMessage",
"version": "v1"
},
"verified": {
"type": "JsonVerifiedMessage",
"version": "v1"
},
"configuration": {
"type": "ConfigurationMessage",
"version": "v0"
},
"stickerPackOperations": {
"list": true,
"type": "JsonStickerPackOperationMessage",
"version": "v0"
},
"fetchType": {
"type": "String"
},
"messageRequestResponse": {
"type": "JsonMessageRequestResponseMessage",
"version": "v1"
}
}
},
"JsonQuote": {
"fields": {
"id": {
"type": "long"
},
"author": {
"type": "JsonAddress",
"version": "v1"
},
"text": {
"type": "String"
},
"attachments": {
"list": true,
"type": "JsonQuotedAttachment",
"version": "v0"
},
"mentions": {
"list": true,
"type": "Mention",
"version": "v0"
}
},
"doc": "A quote is a reply to a previous message. ID is the sent time of the message being replied to"
},
"JsonMention": {
"fields": {
"uuid": {
"type": "String"
},
"start": {
"type": "int"
},
"length": {
"type": "int"
}
}
},
"JsonSendMessageResult": {
"fields": {
"address": {
"type": "JsonAddress",
"version": "v1"
},
"success": {
"type": "Success",
"version": "v0"
},
"networkFailure": {
"type": "boolean"
},
"unregisteredFailure": {
"type": "boolean"
},
"identityFailure": {
"type": "String"
}
}
},
"JsonReaction": {
"fields": {
"emoji": {
"type": "String",
"doc": "the emoji to react with"
},
"remove": {
"type": "boolean",
"doc": "set to true to remove the reaction. requires emoji be set to previously reacted emoji"
},
"targetAuthor": {
"type": "JsonAddress",
"version": "v1",
"doc": "the author of the message being reacted to"
},
"targetSentTimestamp": {
"type": "long",
"doc": "the client timestamp of the message being reacted to"
}
}
},
"JsonGroupInfo": {
"fields": {
"groupId": {
"type": "String"
},
"members": {
"list": true,
"type": "JsonAddress",
"version": "v1"
},
"name": {
"type": "String"
},
"type": {
"type": "String"
},
"avatarId": {
"type": "long"
}
}
},
"JsonGroupV2Info": {
"fields": {
"id": {
"type": "String"
},
"masterKey": {
"type": "String"
},
"revision": {
"type": "int"
},
"title": {
"type": "String"
},
"timer": {
"type": "int"
},
"members": {
"list": true,
"type": "JsonAddress",
"version": "v1"
},
"pendingMembers": {
"list": true,
"type": "JsonAddress",
"version": "v1"
},
"requestingMembers": {
"list": true,
"type": "JsonAddress",
"version": "v1"
},
"inviteLinkPassword": {
"type": "String"
}
}
},
"JsonSentTranscriptMessage": {
"fields": {
"destination": {
"type": "JsonAddress",
"version": "v1"
},
"timestamp": {
"type": "long"
},
"expirationStartTimestamp": {
"type": "long"
},
"message": {
"type": "JsonDataMessage",
"version": "v1"
},
"unidentifiedStatus": {
"type": "Map"
},
"isRecipientUpdate": {
"type": "boolean"
}
}
},
"JsonBlockedListMessage": {
"fields": {
"addresses": {
"list": true,
"type": "JsonAddress",
"version": "v1"
},
"groupIds": {
"list": true,
"type": "String"
}
}
},
"JsonReadMessage": {
"fields": {
"sender": {
"type": "JsonAddress",
"version": "v1"
},
"timestamp": {
"type": "long"
}
}
},
"JsonViewOnceOpenMessage": {
"fields": {
"sender": {
"type": "JsonAddress",
"version": "v1"
},
"timestamp": {
"type": "long"
}
}
},
"JsonVerifiedMessage": {
"fields": {
"destination": {
"type": "JsonAddress",
"version": "v1"
},
"identityKey": {
"type": "String"
},
"verified": {
"type": "String"
},
"timestamp": {
"type": "long"
}
}
},
"JsonMessageRequestResponseMessage": {
"fields": {
"person": {
"type": "JsonAddress",
"version": "v1"
},
"groupId": {
"type": "String"
},
"type": {
"type": "String"
}
}
}
},
"v0": {
"JsonAccountList": {
"fields": {
"accounts": {
"list": true,
"type": "JsonAccount",
"version": "v0"
}
}
},
"JsonCallMessage": {
"fields": {
"offerMessage": {
"type": "OfferMessage",
"version": "v0"
},
"answerMessage": {
"type": "AnswerMessage",
"version": "v0"
},
"busyMessage": {
"type": "BusyMessage",
"version": "v0"
},
"hangupMessage": {
"type": "HangupMessage",
"version": "v0"
},
"iceUpdateMessages": {
"list": true,
"type": "IceUpdateMessage",
"version": "v0"
},
"destinationDeviceId": {
"type": "int"
},
"isMultiRing": {
"type": "boolean"
}
}
},
"JsonReceiptMessage": {
"fields": {
"type": {
"type": "String"
},
"timestamps": {
"list": true,
"type": "Long"
},
"when": {
"type": "long"
}
}
},
"JsonTypingMessage": {
"fields": {
"action": {
"type": "String"
},
"timestamp": {
"type": "long"
},
"groupId": {
"type": "String"
}
}
},
"JsonAccount": {
"fields": {
"deviceId": {
"type": "int"
},
"username": {
"type": "String"
},
"filename": {
"type": "String"
},
"uuid": {
"type": "String"
},
"registered": {
"type": "boolean"
},
"has_keys": {
"type": "boolean"
},
"subscribed": {
"type": "boolean"
}
}
},
"JsonAttachment": {
"fields": {
"contentType": {
"type": "String"
},
"id": {
"type": "String"
},
"size": {
"type": "int"
},
"storedFilename": {
"type": "String"
},
"filename": {
"type": "String"
},
"customFilename": {
"type": "String"
},
"caption": {
"type": "String"
},
"width": {
"type": "int"
},
"height": {
"type": "int"
},
"voiceNote": {
"type": "boolean"
},
"key": {
"type": "String"
},
"digest": {
"type": "String"
},
"blurhash": {
"type": "String"
}
}
},
"SharedContact": {
"fields": {
"name": {
"type": "Name",
"version": "v0"
},
"avatar": {
"type": "Optional",
"version": "v0"
},
"phone": {
"type": "Optional",
"version": "v0"
},
"email": {
"type": "Optional",
"version": "v0"
},
"address": {
"type": "Optional",
"version": "v0"
},
"organization": {
"type": "Optional",
"version": "v0"
}
}
},
"JsonPreview": {
"fields": {
"url": {
"type": "String"
},
"title": {
"type": "String"
},
"attachment": {
"type": "JsonAttachment",
"version": "v0"
}
}
},
"JsonSticker": {
"fields": {
"packID": {
"type": "String"
},
"packKey": {
"type": "String"
},
"stickerID": {
"type": "int"
},
"attachment": {
"type": "JsonAttachment",
"version": "v0"
}
}
},
"RemoteDelete": {
"fields": {
"targetSentTimestamp": {
"type": "long"
}
}
},
"ConfigurationMessage": {
"fields": {
"readReceipts": {
"type": "Optional",
"version": "v0"
},
"unidentifiedDeliveryIndicators": {
"type": "Optional",
"version": "v0"
},
"typingIndicators": {
"type": "Optional",
"version": "v0"
},
"linkPreviews": {
"type": "Optional",
"version": "v0"
}
}
},
"JsonStickerPackOperationMessage": {
"fields": {
"packID": {
"type": "String"
},
"packKey": {
"type": "String"
},
"type": {
"type": "String"
}
}
},
"OfferMessage": {
"fields": {
"id": {
"type": "long"
},
"sdp": {
"type": "String"
},
"type": {
"type": "Type",
"version": "v0"
},
"opaque": {
"type": "String"
}
}
},
"AnswerMessage": {
"fields": {
"id": {
"type": "long"
},
"sdp": {
"type": "String"
},
"opaque": {
"type": "String"
}
}
},
"BusyMessage": {
"fields": {
"id": {
"type": "long"
}
}
},
"HangupMessage": {
"fields": {
"id": {
"type": "long"
},
"type": {
"type": "Type",
"version": "v0"
},
"deviceId": {
"type": "int"
},
"legacy": {
"type": "boolean"
}
}
},
"IceUpdateMessage": {
"fields": {
"id": {
"type": "long"
},
"opaque": {
"type": "String"
},
"sdp": {
"type": "String"
}
}
},
"JsonQuotedAttachment": {
"fields": {
"contentType": {
"type": "String"
},
"fileName": {
"type": "String"
},
"thumbnail": {
"type": "JsonAttachment",
"version": "v0"
}
}
},
"Mention": {
"fields": {
"uuid": {
"type": "UUID"
},
"start": {
"type": "int"
},
"length": {
"type": "int"
}
}
},
"Success": {
"fields": {
"unidentified": {
"type": "boolean"
},
"needsSync": {
"type": "boolean"
},
"duration": {
"type": "long"
}
}
},
"Name": {
"fields": {
"display": {
"type": "Optional",
"version": "v0"
},
"given": {
"type": "Optional",
"version": "v0"
},
"family": {
"type": "Optional",
"version": "v0"
},
"prefix": {
"type": "Optional",
"version": "v0"
},
"suffix": {
"type": "Optional",
"version": "v0"
},
"middle": {
"type": "Optional",
"version": "v0"
}
}
},
"Optional": {
"fields": {
"present": {
"type": "boolean"
}
}
},
"Type": {
"fields": {}
}
},
"v1alpha1": {
"ProtocolRequest": {
"fields": {}
}
}
},
"actions": {
"v1": {
"send": {
"request": "SendRequest",
"response": "SendResponse"
},
"react": {
"request": "ReactRequest",
"response": "SendResponse",
"doc": "react to a previous message"
},
"version": {
"request": "VersionRequest",
"response": "JsonVersionMessage"
}
},
"v1alpha1": {
"protocol": {
"request": "ProtocolRequest"
}
}
}
}

View file

@ -13,19 +13,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
package signald package v0
import (
"git.callpipe.com/finn/signald-go/signald/client-protocol/v1"
)
// Request represents a message sent to signald // Request represents a message sent to signald
type Request struct { type LegacyRequest struct {
Type string `json:"type"` Type string `json:"type"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
MessageBody string `json:"messageBody,omitempty"` MessageBody string `json:"messageBody,omitempty"`
RecipientAddress v1.JsonAddress `json:"recipientAddress,omitempty"` RecipientAddress JsonAddress `json:"recipientAddress,omitempty"`
RecipientGroupID string `json:"recipientGroupId,omitempty"` RecipientGroupID string `json:"recipientGroupId,omitempty"`
Voice bool `json:"voice,omitempty"` Voice bool `json:"voice,omitempty"`
Code string `json:"code,omitempty"` Code string `json:"code,omitempty"`
@ -38,11 +34,7 @@ type Request struct {
Avatar string `json:"avatar,omitempty"` Avatar string `json:"avatar,omitempty"`
} }
type JsonAttachment struct { type JsonAddress struct {
Filename string `json:"filename"` Number string
Caption string `json:"caption"` UUID string
Width int `json:"width"`
Height int `json:"height"`
VoiceNote bool `json:"voiceNote"`
Preview bool `json:"preview"`
} }

View file

@ -1,30 +1,25 @@
package signald package v0
import ( type LegacyResponse struct {
"git.callpipe.com/finn/signald-go/signald/client-protocol/v1"
)
// Response is a response to a request to signald, or a new inbound message
type Response struct {
ID string ID string
Data ResponseData Data LegacyResponseData
Type string Type string
} }
// ResponseData is where most of the data in the response is stored. // ResponseData is where most of the data in the response is stored.
type ResponseData struct { type LegacyResponseData struct {
Groups []Group Groups []Group
Accounts []Account Accounts []Account
URI string URI string
DataMessage DataMessage DataMessage DataMessage
Message string Message string
Username string Username string
Source v1.JsonAddress Source JsonAddress
SourceDevice int SourceDevice int
Type string Type string
IsReceipt bool IsReceipt bool
Timestamp float64 Timestamp int64
ServerTimestamp float64 ServerTimestamp int64
} }
// Group represents a group in signal // Group represents a group in signal
@ -47,10 +42,10 @@ type Account struct {
// DataMessage is the main component of incoming text messages // DataMessage is the main component of incoming text messages
type DataMessage struct { type DataMessage struct {
Timestamp float64 Timestamp int64
Message string Body string
ExpiresInSeconds float64 ExpiresInSeconds int64
GroupInfo IncomingGroupInfo GroupInfo IncomingGroupInfo `json:"group"`
} }
// IncomingGroupInfo is information about a particular group // IncomingGroupInfo is information about a particular group

View file

@ -0,0 +1,164 @@
package v0
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
type Request struct {
ID string `json:"id"`
Version string `json:"version"`
Type string `json:"type"`
}
type AnswerMessage struct {
ID int64 `json:"id,omitempty"`
Opaque string `json:"opaque,omitempty"`
Sdp string `json:"sdp,omitempty"`
}
type BusyMessage struct {
ID int64 `json:"id,omitempty"`
}
type ConfigurationMessage struct {
LinkPreviews *Optional `json:"linkPreviews,omitempty"`
ReadReceipts *Optional `json:"readReceipts,omitempty"`
TypingIndicators *Optional `json:"typingIndicators,omitempty"`
UnidentifiedDeliveryIndicators *Optional `json:"unidentifiedDeliveryIndicators,omitempty"`
}
type HangupMessage struct {
DeviceId int32 `json:"deviceId,omitempty"`
ID int64 `json:"id,omitempty"`
Legacy bool `json:"legacy,omitempty"`
Type *Type `json:"type,omitempty"`
}
type IceUpdateMessage struct {
ID int64 `json:"id,omitempty"`
Opaque string `json:"opaque,omitempty"`
Sdp string `json:"sdp,omitempty"`
}
type JsonAccount struct {
DeviceId int32 `json:"deviceId,omitempty"`
Filename string `json:"filename,omitempty"`
Has_keys bool `json:"has_keys,omitempty"`
Registered bool `json:"registered,omitempty"`
Subscribed bool `json:"subscribed,omitempty"`
Username string `json:"username,omitempty"`
UUID string `json:"uuid,omitempty"`
}
type JsonAccountList struct {
Accounts []*JsonAccount `json:"accounts,omitempty"`
}
type JsonAttachment struct {
Blurhash string `json:"blurhash,omitempty"`
Caption string `json:"caption,omitempty"`
ContentType string `json:"contentType,omitempty"`
CustomFilename string `json:"customFilename,omitempty"`
Digest string `json:"digest,omitempty"`
Filename string `json:"filename,omitempty"`
Height int32 `json:"height,omitempty"`
ID string `json:"id,omitempty"`
Key string `json:"key,omitempty"`
Size int32 `json:"size,omitempty"`
StoredFilename string `json:"storedFilename,omitempty"`
VoiceNote bool `json:"voiceNote,omitempty"`
Width int32 `json:"width,omitempty"`
}
type JsonCallMessage struct {
AnswerMessage *AnswerMessage `json:"answerMessage,omitempty"`
BusyMessage *BusyMessage `json:"busyMessage,omitempty"`
DestinationDeviceId int32 `json:"destinationDeviceId,omitempty"`
HangupMessage *HangupMessage `json:"hangupMessage,omitempty"`
IceUpdateMessages []*IceUpdateMessage `json:"iceUpdateMessages,omitempty"`
IsMultiRing bool `json:"isMultiRing,omitempty"`
OfferMessage *OfferMessage `json:"offerMessage,omitempty"`
}
type JsonPreview struct {
Attachment *JsonAttachment `json:"attachment,omitempty"`
Title string `json:"title,omitempty"`
Url string `json:"url,omitempty"`
}
type JsonQuotedAttachment struct {
ContentType string `json:"contentType,omitempty"`
FileName string `json:"fileName,omitempty"`
Thumbnail *JsonAttachment `json:"thumbnail,omitempty"`
}
type JsonReceiptMessage struct {
Timestamps []int64 `json:"timestamps,omitempty"`
Type string `json:"type,omitempty"`
When int64 `json:"when,omitempty"`
}
type JsonSticker struct {
Attachment *JsonAttachment `json:"attachment,omitempty"`
PackID string `json:"packID,omitempty"`
PackKey string `json:"packKey,omitempty"`
StickerID int32 `json:"stickerID,omitempty"`
}
type JsonStickerPackOperationMessage struct {
PackID string `json:"packID,omitempty"`
PackKey string `json:"packKey,omitempty"`
Type string `json:"type,omitempty"`
}
type JsonTypingMessage struct {
Action string `json:"action,omitempty"`
GroupId string `json:"groupId,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
}
type Mention struct {
Length int32 `json:"length,omitempty"`
Start int32 `json:"start,omitempty"`
UUID string `json:"uuid,omitempty"`
}
type Name struct {
Display *Optional `json:"display,omitempty"`
Family *Optional `json:"family,omitempty"`
Given *Optional `json:"given,omitempty"`
Middle *Optional `json:"middle,omitempty"`
Prefix *Optional `json:"prefix,omitempty"`
Suffix *Optional `json:"suffix,omitempty"`
}
type OfferMessage struct {
ID int64 `json:"id,omitempty"`
Opaque string `json:"opaque,omitempty"`
Sdp string `json:"sdp,omitempty"`
Type *Type `json:"type,omitempty"`
}
type Optional struct {
Present bool `json:"present,omitempty"`
}
type RemoteDelete struct {
TargetSentTimestamp int64 `json:"targetSentTimestamp,omitempty"`
}
type SharedContact struct {
Address *Optional `json:"address,omitempty"`
Avatar *Optional `json:"avatar,omitempty"`
Email *Optional `json:"email,omitempty"`
Name *Name `json:"name,omitempty"`
Organization *Optional `json:"organization,omitempty"`
Phone *Optional `json:"phone,omitempty"`
}
type Success struct {
Duration int64 `json:"duration,omitempty"`
NeedsSync bool `json:"needsSync,omitempty"`
Unidentified bool `json:"unidentified,omitempty"`
}
type Type struct {
}

View file

@ -0,0 +1,121 @@
package v1
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"gitlab.com/signald/signald-go/signald"
)
// Submit: react to a previous message
func (r *ReactRequest) Submit(conn *signald.Signald) (response SendResponse, err error) {
r.Version = "v1"
r.Type = "react"
if r.ID == "" {
r.ID = generateID()
}
err = conn.RawRequest(r)
if err != nil {
log.Println("signald-go: error submitting request to signald")
return response, err
}
responseChannel := conn.GetResponseListener(r.ID)
defer conn.CloseResponseListener(r.ID)
rawResponse := <-responseChannel
if rawResponse.Error != nil {
err = fmt.Errorf("signald error: %s", string(rawResponse.Error))
return
}
err = json.Unmarshal(rawResponse.Data, &response)
if err != nil {
rawResponseJson, _ := rawResponse.Data.MarshalJSON()
log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson))
return response, err
}
return response, nil
}
func (r *SendRequest) Submit(conn *signald.Signald) (response SendResponse, err error) {
r.Version = "v1"
r.Type = "send"
if r.ID == "" {
r.ID = generateID()
}
err = conn.RawRequest(r)
if err != nil {
log.Println("signald-go: error submitting request to signald")
return response, err
}
responseChannel := conn.GetResponseListener(r.ID)
defer conn.CloseResponseListener(r.ID)
rawResponse := <-responseChannel
if rawResponse.Error != nil {
err = fmt.Errorf("signald error: %s", string(rawResponse.Error))
return
}
err = json.Unmarshal(rawResponse.Data, &response)
if err != nil {
rawResponseJson, _ := rawResponse.Data.MarshalJSON()
log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson))
return response, err
}
return response, nil
}
func (r *VersionRequest) Submit(conn *signald.Signald) (response JsonVersionMessage, err error) {
r.Version = "v1"
r.Type = "version"
if r.ID == "" {
r.ID = generateID()
}
err = conn.RawRequest(r)
if err != nil {
log.Println("signald-go: error submitting request to signald")
return response, err
}
responseChannel := conn.GetResponseListener(r.ID)
defer conn.CloseResponseListener(r.ID)
rawResponse := <-responseChannel
if rawResponse.Error != nil {
err = fmt.Errorf("signald error: %s", string(rawResponse.Error))
return
}
err = json.Unmarshal(rawResponse.Data, &response)
if err != nil {
rawResponseJson, _ := rawResponse.Data.MarshalJSON()
log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson))
return response, err
}
return response, nil
}
const idsize = 10
var charset = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
func generateID() string {
id := make([]rune, idsize)
for i := range id {
id[i] = charset[rand.Intn(len(charset))]
}
return string(id)
}

View file

@ -0,0 +1,199 @@
package v1
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
import (
"gitlab.com/signald/signald-go/signald/client-protocol/v0"
)
type Request struct {
ID string `json:"id"`
Version string `json:"version"`
Type string `json:"type"`
}
type JsonAddress struct {
Number string `json:"number,omitempty"` // An e164 phone number, starting with +. Currently the only available user-facing Signal identifier.
Relay string `json:"relay,omitempty"`
UUID string `json:"uuid,omitempty"` // A UUID, the unique identifier for a particular Signal account.
}
type JsonBlockedListMessage struct {
Addresses []*JsonAddress `json:"addresses,omitempty"`
GroupIds []string `json:"groupIds,omitempty"`
}
type JsonDataMessage struct {
Attachments []*v0.JsonAttachment `json:"attachments,omitempty"` // files attached to the incoming message
Body string `json:"body,omitempty"` // the text body of the incoming message.
Contacts []*v0.SharedContact `json:"contacts,omitempty"` // if the incoming message has a shared contact, the contact's information will be here
EndSession bool `json:"endSession,omitempty"`
ExpiresInSeconds int32 `json:"expiresInSeconds,omitempty"` // the expiry timer on the incoming message. Clients should delete records of the message within this number of seconds
Group *JsonGroupInfo `json:"group,omitempty"` // if the incoming message was sent to a v1 group, information about that group will be here
GroupV2 *JsonGroupV2Info `json:"groupV2,omitempty"` // is the incoming message was sent to a v2 group, basic identifying information about that group will be here. For full information, use list_groups
Previews []*v0.JsonPreview `json:"previews,omitempty"` // if the incoming message has a link preview, information about that preview will be here
ProfileKeyUpdate bool `json:"profileKeyUpdate,omitempty"`
Quote *JsonQuote `json:"quote,omitempty"` // if the incoming message is a quote or reply to another message, this will contain information about that message
Reaction *JsonReaction `json:"reaction,omitempty"` // if the message adds or removes a reaction to another message, this will indicate what change is being made
RemoteDelete *v0.RemoteDelete `json:"remoteDelete,omitempty"` // if the inbound message is deleting a previously sent message, indicates which message should be deleted
Sticker *v0.JsonSticker `json:"sticker,omitempty"` // if the incoming message is a sticker, information about the sicker will be here
Timestamp int64 `json:"timestamp,omitempty"` // the (unix) timestamp that the message was sent at, according to the sender's device. This is used to uniquely identify this message for things like reactions and quotes.
ViewOnce bool `json:"viewOnce,omitempty"` // indicates the message is a view once message. View once messages typically include no body and a single image attachment. Official Signal clients will prevent the user from saving the image, and once the user has viewed the image once they will destroy the image.
}
type JsonGroupInfo struct {
AvatarId int64 `json:"avatarId,omitempty"`
GroupId string `json:"groupId,omitempty"`
Members []*JsonAddress `json:"members,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
}
type JsonGroupV2Info struct {
ID string `json:"id,omitempty"`
InviteLinkPassword string `json:"inviteLinkPassword,omitempty"`
MasterKey string `json:"masterKey,omitempty"`
Members []*JsonAddress `json:"members,omitempty"`
PendingMembers []*JsonAddress `json:"pendingMembers,omitempty"`
RequestingMembers []*JsonAddress `json:"requestingMembers,omitempty"`
Revision int32 `json:"revision,omitempty"`
Timer int32 `json:"timer,omitempty"`
Title string `json:"title,omitempty"`
}
type JsonMention struct {
Length int32 `json:"length,omitempty"`
Start int32 `json:"start,omitempty"`
UUID string `json:"uuid,omitempty"`
}
type JsonMessageEnvelope struct {
CallMessage *v0.JsonCallMessage `json:"callMessage,omitempty"`
DataMessage *JsonDataMessage `json:"dataMessage,omitempty"`
HasContent bool `json:"hasContent,omitempty"`
HasLegacyMessage bool `json:"hasLegacyMessage,omitempty"`
IsUnidentifiedSender bool `json:"isUnidentifiedSender,omitempty"`
Receipt *v0.JsonReceiptMessage `json:"receipt,omitempty"`
Relay string `json:"relay,omitempty"`
ServerDeliveredTimestamp int64 `json:"serverDeliveredTimestamp,omitempty"`
ServerTimestamp int64 `json:"serverTimestamp,omitempty"`
Source *JsonAddress `json:"source,omitempty"`
SourceDevice int32 `json:"sourceDevice,omitempty"`
SyncMessage *JsonSyncMessage `json:"syncMessage,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
TimestampISO string `json:"timestampISO,omitempty"`
Type string `json:"type,omitempty"`
Typing *v0.JsonTypingMessage `json:"typing,omitempty"`
Username string `json:"username,omitempty"`
UUID string `json:"uuid,omitempty"`
}
type JsonMessageRequestResponseMessage struct {
GroupId string `json:"groupId,omitempty"`
Person *JsonAddress `json:"person,omitempty"`
Type string `json:"type,omitempty"`
}
// JsonQuote: A quote is a reply to a previous message. ID is the sent time of the message being replied to
type JsonQuote struct {
Attachments []*v0.JsonQuotedAttachment `json:"attachments,omitempty"`
Author *JsonAddress `json:"author,omitempty"`
ID int64 `json:"id,omitempty"`
Mentions []*v0.Mention `json:"mentions,omitempty"`
Text string `json:"text,omitempty"`
}
type JsonReaction struct {
Emoji string `json:"emoji,omitempty"` // the emoji to react with
Remove bool `json:"remove,omitempty"` // set to true to remove the reaction. requires emoji be set to previously reacted emoji
TargetAuthor *JsonAddress `json:"targetAuthor,omitempty"` // the author of the message being reacted to
TargetSentTimestamp int64 `json:"targetSentTimestamp,omitempty"` // the client timestamp of the message being reacted to
}
type JsonReadMessage struct {
Sender *JsonAddress `json:"sender,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
}
type JsonSendMessageResult struct {
Address *JsonAddress `json:"address,omitempty"`
IdentityFailure string `json:"identityFailure,omitempty"`
NetworkFailure bool `json:"networkFailure,omitempty"`
Success *v0.Success `json:"success,omitempty"`
UnregisteredFailure bool `json:"unregisteredFailure,omitempty"`
}
type JsonSentTranscriptMessage struct {
Destination *JsonAddress `json:"destination,omitempty"`
ExpirationStartTimestamp int64 `json:"expirationStartTimestamp,omitempty"`
IsRecipientUpdate bool `json:"isRecipientUpdate,omitempty"`
Message *JsonDataMessage `json:"message,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
UnidentifiedStatus map[string]string `json:"unidentifiedStatus,omitempty"`
}
type JsonSyncMessage struct {
BlockedList *JsonBlockedListMessage `json:"blockedList,omitempty"`
Configuration *v0.ConfigurationMessage `json:"configuration,omitempty"`
Contacts *v0.JsonAttachment `json:"contacts,omitempty"`
ContactsComplete bool `json:"contactsComplete,omitempty"`
FetchType string `json:"fetchType,omitempty"`
Groups *v0.JsonAttachment `json:"groups,omitempty"`
MessageRequestResponse *JsonMessageRequestResponseMessage `json:"messageRequestResponse,omitempty"`
ReadMessages []*JsonReadMessage `json:"readMessages,omitempty"`
Request string `json:"request,omitempty"`
Sent *JsonSentTranscriptMessage `json:"sent,omitempty"`
StickerPackOperations []*v0.JsonStickerPackOperationMessage `json:"stickerPackOperations,omitempty"`
Verified *JsonVerifiedMessage `json:"verified,omitempty"`
ViewOnceOpen *JsonViewOnceOpenMessage `json:"viewOnceOpen,omitempty"`
}
type JsonVerifiedMessage struct {
Destination *JsonAddress `json:"destination,omitempty"`
IdentityKey string `json:"identityKey,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Verified string `json:"verified,omitempty"`
}
type JsonVersionMessage struct {
Branch string `json:"branch,omitempty"`
Commit string `json:"commit,omitempty"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
}
type JsonViewOnceOpenMessage struct {
Sender *JsonAddress `json:"sender,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
}
// ReactRequest: react to a previous message
type ReactRequest struct {
Request
Reaction *JsonReaction `json:"reaction,omitempty"`
RecipientAddress *JsonAddress `json:"recipientAddress,omitempty"`
RecipientGroupID string `json:"recipientGroupId,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Username string `json:"username,omitempty"`
}
type SendRequest struct {
Request
Attachments []*v0.JsonAttachment `json:"attachments,omitempty"`
Mentions []*JsonMention `json:"mentions,omitempty"`
MessageBody string `json:"messageBody,omitempty"`
Quote *JsonQuote `json:"quote,omitempty"`
RecipientAddress *JsonAddress `json:"recipientAddress,omitempty"`
RecipientGroupID string `json:"recipientGroupId,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
Username string `json:"username,omitempty"`
}
type SendResponse struct {
Results []*JsonSendMessageResult `json:"results,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
}
type VersionRequest struct {
Request
}

View file

@ -1,44 +0,0 @@
package v1
// JsonAddress is a signal user's contact information. a phone number, UUID or both
type JsonAddress struct {
UUID string `json:"uuid,omitempty"`
Number string `json:"number"`
}
type JsonMessageRequestResponseMessage struct {
Person JsonAddress `json:"person"`
GroupID string `json:"groupId"`
Type string `json:"type"`
}
type JsonReaction struct {
Emoji string `json:"emoji"`
Remove bool `json:"remove"`
TargetAuthor JsonAddress `json:"targetAuthor"`
TargetSentTimestamp uint64 `json:"targetSentTimestamp"`
}
type JsonReadMessage struct {
Sender JsonAddress `json:"sender"`
Timestamp uint64 `json:"timestamp"`
}
type JsonSendMessageResult struct {
Address JsonAddress `json:"address"`
Success Success `json:"success"`
NetworkFailure bool `json:"networkFailure"`
UnregisteredFailure bool `json:"unregisteredFailure"`
IdentityFailure string `json:"identityFailure"`
}
type Success struct {
Unidentified bool `json:"unidentified"`
NeedsSync bool `json:"needsSync"`
}
type RequestValidationFailure struct {
ValidationResults []string `json:"validationResults"`
Type string `json:"type"`
Message string `json:"message"`
}

View file

@ -0,0 +1,32 @@
package v1alpha1
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
import (
"math/rand"
"gitlab.com/signald/signald-go/signald"
)
func (r *ProtocolRequest) Submit(conn *signald.Signald) error {
r.Version = "v1alpha1"
r.Type = "protocol"
if r.ID == "" {
r.ID = generateID()
}
return conn.RawRequest(r)
}
const idsize = 10
var charset = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
func generateID() string {
id := make([]rune, idsize)
for i := range id {
id[i] = charset[rand.Intn(len(charset))]
}
return string(id)
}

View file

@ -0,0 +1,13 @@
package v1alpha1
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
type Request struct {
ID string `json:"id"`
Version string `json:"version"`
Type string `json:"type"`
}
type ProtocolRequest struct {
Request
}

View file

@ -1,4 +1,4 @@
// Copyright © 2018 Finn Herzfeld <finn@janky.solutions> // Copyright © 2020 Finn Herzfeld <finn@janky.solutions>
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -16,17 +16,39 @@
package signald package signald
import ( import (
"bytes"
"encoding/json" "encoding/json"
"io"
"log" "log"
"net" "net"
"os"
"strconv"
"gitlab.com/signald/signald-go/signald/client-protocol/v0"
)
var (
debugSignaldIO, _ = strconv.ParseBool(os.Getenv("DEBUG_SIGNALD_IO"))
) )
// Signald is a connection to a signald instance. // Signald is a connection to a signald instance.
type Signald struct { type Signald struct {
socket net.Conn socket net.Conn
listeners map[string]chan BasicResponse
SocketPath string SocketPath string
} }
type BasicResponse struct {
ID string
Type string
Error json.RawMessage
Data json.RawMessage
}
type UnexpectedError struct {
Message string
}
// Connect connects to the signad socket // Connect connects to the signad socket
func (s *Signald) Connect() error { func (s *Signald) Connect() error {
if s.SocketPath == "" { if s.SocketPath == "" {
@ -37,32 +59,86 @@ func (s *Signald) Connect() error {
return err return err
} }
s.socket = socket s.socket = socket
log.Print("Connected to signald socket ", socket.RemoteAddr().String()) log.Println("signald-go: Connected to signald socket ", socket.RemoteAddr().String())
return nil return nil
} }
// Listen listens for events from signald // Listen listens for events from signald
func (s *Signald) Listen(c chan Response) { func (s *Signald) Listen(c chan v0.LegacyResponse) {
// we create a decoder that reads directly from the socket
d := json.NewDecoder(s.socket)
var msg Response
for { for {
if err := d.Decode(&msg); err != nil { msg, err := s.readNext()
log.Println("error decoding message from signald:", err) if err == io.EOF {
log.Println("signald-go: socket disconnected!")
return
}
if msg.Type == "unexpected_error" {
var errorResponse UnexpectedError
if err := json.Unmarshal(msg.Data, &errorResponse); err != nil {
log.Println("signald-go: Error unmarshaling error response:", err.Error())
continue
}
log.Println("signald-go: Unexpected error", errorResponse.Message)
continue
}
if subscribers, ok := s.listeners[msg.ID]; ok {
subscribers <- msg
}
if c != nil {
legacyResponse := v0.LegacyResponse{
ID: msg.ID,
Type: msg.Type,
}
_ = json.Unmarshal(msg.Data, &legacyResponse.Data)
c <- legacyResponse
} }
c <- msg
} }
} }
// SendRequest sends a request to signald. Mostly used interally. func (s *Signald) RawRequest(request interface{}) error {
func (s *Signald) SendRequest(request Request) error { if debugSignaldIO {
b, err := json.Marshal(request) buffer := bytes.Buffer{}
if err != nil { if err := json.NewEncoder(&buffer).Encode(request); err == nil {
return err log.Println("[to signald]", buffer.String())
}
} }
log.Print("Sending ", string(b)) return json.NewEncoder(s.socket).Encode(request)
e := json.NewEncoder(s.socket) }
return e.Encode(request)
func (s *Signald) GetResponseListener(requestid string) chan BasicResponse {
if s.listeners == nil {
s.listeners = map[string]chan BasicResponse{}
}
c, ok := s.listeners[requestid]
if !ok {
c = make(chan BasicResponse)
s.listeners[requestid] = c
}
return c
}
func (s *Signald) CloseResponseListener(requestid string) {
listener, ok := s.listeners[requestid]
if !ok {
return
}
close(listener)
delete(s.listeners, requestid)
}
func (s *Signald) readNext() (b BasicResponse, err error) {
if debugSignaldIO {
buffer := bytes.Buffer{}
err = json.NewDecoder(io.TeeReader(s.socket, &buffer)).Decode(&b)
log.Println("[from signald]", buffer.String())
} else {
err = json.NewDecoder(s.socket).Decode(&b)
}
if err != nil {
log.Println("signald-go: error decoding message from signald:", err)
return
}
return
} }

179
tools/generator/main.go Normal file
View file

@ -0,0 +1,179 @@
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"strings"
"text/template"
)
type Protocol struct {
Types map[string]map[string]*Type
Actions map[string]map[string]*Action
}
type Type struct {
Fields map[string]*DataType
Request bool `json:"-"`
Doc string
}
type DataType struct {
List bool
Type string
Version string
FieldName string
Doc string
}
type Action struct {
FnName string
Request string
Response string
Doc string
}
type StructsTemplateInput struct {
Types map[string]*Type
Version string
ImportVersions []string
}
type ActionsTemplateInput struct {
Actions map[string]*Action
Version string
Responses bool
}
var typeMap = map[string]string{
"int": "int32",
"Integer": "int32",
"Boolean": "bool",
"long": "int64",
"Long": "int64",
"UUID": "string",
"boolean": "bool",
"String": "string",
"Map": "map[string]string", // TODO: make signald print the actual key and value types
}
var fieldNameMap = map[string]string{
"id": "ID",
"recipientGroupId": "RecipientGroupID",
"uuid": "UUID",
}
func (d *DataType) fixForVersion(field, version string) {
response, ok := typeMap[d.Type]
if ok {
if d.Type == "byte" && d.List {
d.List = false
}
d.Type = response
} else {
if d.Version == version || d.Version == "" {
d.Type = fmt.Sprintf("*%s", d.Type)
} else {
d.Type = fmt.Sprintf("*%s.%s", d.Version, d.Type)
}
}
fieldName, ok := fieldNameMap[field]
if ok {
d.FieldName = fieldName
} else {
d.FieldName = strings.Title(field)
}
}
func main() {
var response Protocol
err := json.NewDecoder(os.Stdin).Decode(&response)
if err != nil {
log.Fatal(err, "\nError parsing stdin")
}
tmpl, err := template.ParseGlob("tools/generator/*.tmpl")
if err != nil {
log.Fatal(err, "\nError parsing templates from tools/generator/*.tmpl")
}
for version, actions := range response.Actions {
inputs := ActionsTemplateInput{Version: version, Responses: false}
for action, a := range actions {
actions[action].FnName = strings.Title(action)
if a.Request != "" {
response.Types[version][a.Request].Request = true
}
if a.Response != "" {
inputs.Responses = true
}
}
inputs.Actions = actions
outputDir := fmt.Sprintf("signald/client-protocol/%s", version)
err = os.MkdirAll(outputDir, os.ModePerm)
if err != nil {
log.Fatal("Error creating", outputDir, err)
}
outputFilename := fmt.Sprintf("%s/%s", outputDir, "requests.go")
log.Println("Opening", outputFilename)
f, err := os.Create(outputFilename)
if err != nil {
log.Fatal(err, "\nfailed to open output file ", outputFilename)
}
err = tmpl.ExecuteTemplate(f, "requests.go.tmpl", inputs)
if err != nil {
log.Fatal(err, "\nfailed to render template")
}
err = exec.Command("gofmt", "-w", outputFilename).Run()
if err != nil {
log.Fatal(err, " error running gofmt on ", outputFilename)
}
fmt.Println(outputFilename)
}
for version, types := range response.Types {
inputs := StructsTemplateInput{Version: version}
for typeName, t := range types {
for fieldName, field := range t.Fields {
types[typeName].Fields[fieldName].fixForVersion(fieldName, version)
if field.Version != "" && field.Version != version {
found := false
for _, v := range inputs.ImportVersions {
if v == field.Version {
found = true
break
}
}
if !found {
inputs.ImportVersions = append(inputs.ImportVersions, field.Version)
}
}
}
}
inputs.Types = types
outputDir := fmt.Sprintf("signald/client-protocol/%s", version)
err = os.MkdirAll(outputDir, os.ModePerm)
if err != nil {
log.Fatal("Error creating", outputDir, err)
}
outputFilename := fmt.Sprintf("%s/%s", outputDir, "structs.go")
log.Println("Opening", outputFilename)
f, err := os.Create(outputFilename)
if err != nil {
log.Fatal(err, "\nfailed to open output file ", outputFilename)
}
err = tmpl.ExecuteTemplate(f, "structs.go.tmpl", inputs)
if err != nil {
log.Fatal(err, "\nfailed to render template")
}
err = exec.Command("gofmt", "-w", outputFilename).Run()
if err != nil {
log.Fatal(err, " error running gofmt on ", outputFilename)
}
fmt.Println(outputFilename)
}
}

View file

@ -0,0 +1,67 @@
package {{.Version}}
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
import ({{if .Responses}}
"encoding/json"
"fmt"
"log"{{end}}
"math/rand"
"gitlab.com/signald/signald-go/signald"
)
{{range $type, $action := .Actions}}
{{if ne $action.Request ""}}
{{if ne $action.Doc ""}}// Submit: {{$action.Doc}}{{end}}
func (r *{{$action.Request}}) Submit(conn *signald.Signald) ({{if ne $action.Response ""}}response {{$action.Response}}, err {{end}}error) {
r.Version = "{{$.Version}}"
{{else}}
{{if ne $action.Doc ""}}// {{$action.FnName}}: {{$action.Doc}}{{end}}
func {{$action.FnName}}(conn *signald.Signald) ({{if ne $action.Response ""}}response {{$action.Response}}, {{end}}err error) {
r := Request{Version: "{{.Version}}"}
{{end}} r.Type = "{{$type}}"
if(r.ID == "") {
r.ID = generateID()
}
{{if ne $action.Response ""}}
err = conn.RawRequest(r)
if err != nil {
log.Println("signald-go: error submitting request to signald")
return response, err
}
responseChannel := conn.GetResponseListener(r.ID)
defer conn.CloseResponseListener(r.ID)
rawResponse := <- responseChannel
if rawResponse.Error != nil {
err = fmt.Errorf("signald error: %s", string(rawResponse.Error))
return
}
err = json.Unmarshal(rawResponse.Data, &response)
if err != nil {
rawResponseJson, _ := rawResponse.Data.MarshalJSON()
log.Println("signald-go: error unmarshalling response from signald of type", rawResponse.Type, string(rawResponseJson))
return response, err
}
return response, nil
{{else}}
return conn.RawRequest(r)
{{end}}
}
{{end}}
const idsize = 10
var charset = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
func generateID() string {
id := make([]rune, idsize)
for i := range id {
id[i]= charset[rand.Intn(len(charset))]
}
return string(id)
}

View file

@ -0,0 +1,21 @@
package {{.Version}}
// DO NOT EDIT: this file is automatically generated by ./tools/generator in this repo
{{if gt (.ImportVersions | len) 0}}
import (
{{range $version := .ImportVersions}}
"gitlab.com/signald/signald-go/signald/client-protocol/{{$version}}"{{end}}
)
{{end}}
type Request struct {
ID string `json:"id"`
Version string `json:"version"`
Type string `json:"type"`
}
{{ range $structName, $type := .Types }}{{if ne $type.Doc ""}}// {{$structName}}: {{$type.Doc}}{{end}}
type {{ $structName }} struct {
{{if $type.Request}} Request{{end}}
{{ range $fieldName, $field := $type.Fields }}{{ $field.FieldName }} {{if $field.List}}[]{{end}}{{ $field.Type }} `json:"{{$fieldName}},omitempty"`{{if ne $field.Doc ""}} // {{$field.Doc}}{{end}}
{{ end }}}
{{ end }}