From 952af5ceb388710e78e1d944e6fd78d80f954acd Mon Sep 17 00:00:00 2001 From: Peter Molnar Date: Sat, 27 Feb 2021 22:57:48 +0000 Subject: [PATCH 1/6] signald proof of concent can receive from signald groupv2, but nothing else --- .gitmodules | 3 + bridge/config/config.go | 1 + bridge/signald/signald.go | 195 +++++++++++++++++++++++++++ gateway/bridgemap/bsignald.go | 11 ++ vendor/gitlab.com/signald/signald-go | 1 + 5 files changed, 211 insertions(+) create mode 100644 .gitmodules create mode 100644 bridge/signald/signald.go create mode 100644 gateway/bridgemap/bsignald.go create mode 160000 vendor/gitlab.com/signald/signald-go diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..af95a57f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/gitlab.com/signald/signald-go"] + path = vendor/gitlab.com/signald/signald-go + url = https://gitlab.com/signald/signald-go diff --git a/bridge/config/config.go b/bridge/config/config.go index 98935208..a99c6094 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -225,6 +225,7 @@ type BridgeValues struct { Zulip map[string]Protocol Keybase map[string]Protocol Mumble map[string]Protocol + Signald map[string]Protocol General Protocol Tengo Tengo Gateway []Gateway diff --git a/bridge/signald/signald.go b/bridge/signald/signald.go new file mode 100644 index 00000000..94e909bb --- /dev/null +++ b/bridge/signald/signald.go @@ -0,0 +1,195 @@ +package bsignald + +import ( + "bufio" + "net" + "encoding/json" + "github.com/42wim/matterbridge/bridge" + "github.com/42wim/matterbridge/bridge/config" + "gitlab.com/signald/signald-go/signald" + //"gitlab.com/signald/signald-go/signald/client-protocol/v0" + "gitlab.com/signald/signald-go/signald/client-protocol/v1" +) + +type JSONCMD map[string]interface{} + +const ( + cfgNumber = "Number" + cfgSocket = "UnixSocket" + cfgGroupID = "GroupID" +) + +type envelopeResponse struct { + ID string `json:",omitempty"` + Data v1.JsonMessageEnvelope `json:",omitempty"` + Type string `json:",omitempty"` +} + +type Bsignald struct { + *bridge.Config + socketpath string + socket net.Conn + subscribed bool + reader *bufio.Scanner + //listeners map[string]chan signald.BasicResponse + groupid string +} + +func New(cfg *bridge.Config) bridge.Bridger { + number := cfg.GetString(cfgNumber) + if number == "" { + cfg.Log.Fatalf("Missing configuration for Signald bridge: Number") + } + + socketpath := cfg.GetString(cfgSocket) + if socketpath == "" { + socketpath = "/var/run/signald/signald.sock" + } + + return &Bsignald{ + Config: cfg, + socketpath: socketpath, + subscribed: false, + } +} + +func (b *Bsignald) Connect() error { + b.Log.Infof("Connecting %s", b.socketpath) + + s, err := net.Dial("unix", b.socketpath) + if err != nil { + b.Log.Fatalf(err.Error()) + } + //defer s.Close() + b.socket = s + r := bufio.NewScanner(s) + b.reader = r + go b.Listen() + go b.Login() + return nil +} + +func (b *Bsignald) JoinChannel(channel config.ChannelInfo) error { + b.groupid = channel.Name + return nil +} + +func (b *Bsignald) Listen() { + for { + for b.reader.Scan() { + if err := b.reader.Err(); err != nil { + b.Log.Errorf(err.Error()) + continue + } + + raw := b.reader.Text() + b.Log.Debugln(raw); + + var msg signald.BasicResponse + if err := json.Unmarshal([]byte(raw), &msg); err != nil { + b.Log.Errorln("Error unmarshaling raw response:", err.Error()) + continue + } + + if msg.Type == "unexpected_error" { + var errorResponse signald.UnexpectedError + if err := json.Unmarshal(msg.Data, &errorResponse); err != nil { + b.Log.Errorln("signald-go: Error unmarshaling error response:", err.Error()) + continue + } + b.Log.Errorln("signald-go: Unexpected error", errorResponse.Message) + continue + } + + if msg.Type != "message" { + b.Log.Debugln("not 'message' from signald: ", raw); + continue + } + + response := envelopeResponse{ID: msg.ID, Type: msg.Type} + if err := json.Unmarshal(msg.Data, &response.Data); err != nil { + b.Log.Errorln("signald-go receive error: ", err) + continue + } + + b.Log.Debugf("%#v", response); + + if response.Data.DataMessage != nil { + if response.Data.DataMessage.GroupV2 != nil { + if b.groupid == response.Data.DataMessage.GroupV2.ID { + rmsg := config.Message{ + UserID: response.Data.Username, + Username: response.Data.Username, + Text: response.Data.DataMessage.Body, + Channel: response.Data.DataMessage.GroupV2.ID, + Account: b.Account, + Protocol: b.Protocol, + } + b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) + b.Log.Debugf("<= Message is %#v", rmsg) + b.Remote <- rmsg + } + } + } + + //if response.Data.SyncMessage != nil { + //if response.Data.SyncMessage.Sent != nil { + //if response.Data.SyncMessage.Sent.Message != nil { + //if response.Data.SyncMessage.Sent.Message != nil { + //if response.Data.SyncMessage.Sent.Message.GroupV2 != nil { + //if b.groupid == response.Data.SyncMessage.Sent.Message.GroupV2.id { + + //} + //} + //} + //} + //} + //} + } + } +} + + +func (b *Bsignald) Login() error { + if ! b.subscribed { + subscribe := JSONCMD{ + "type": "subscribe", + "username": b.GetString(cfgNumber), + } + err := json.NewEncoder(b.socket).Encode(subscribe) + if err != nil { + b.Log.Fatalf(err.Error()) + } + // TODO: this should be done from the listener after the response + // was checked + b.subscribed = true + } + return nil +} + +func (b *Bsignald) Disconnect() error { + b.Log.Debugln("Disconnecting..") + b.socket.Close() + return nil +} + +func (b *Bsignald) Send(msg config.Message) (string, error) { + + //req := v1.SendRequest{ + //Username: account, + //MessageBody: strings.Join(args[1:], " "), + //} + + //if strings.HasPrefix(args[0], "+") { + //req.RecipientAddress = &v1.JsonAddress{Number: args[0]} + //} else { + //req.RecipientGroupID = args[0] + //} + + //resp, err := req.Submit(common.Signald) + //if err != nil { + //log.Fatal("error sending request to signald: ", err) + //} + + return "", nil +} diff --git a/gateway/bridgemap/bsignald.go b/gateway/bridgemap/bsignald.go new file mode 100644 index 00000000..ace31b6d --- /dev/null +++ b/gateway/bridgemap/bsignald.go @@ -0,0 +1,11 @@ +// +build !nosignald + +package bridgemap + +import ( + bsignald "github.com/42wim/matterbridge/bridge/signald" +) + +func init() { + FullMap["signald"] = bsignald.New +} diff --git a/vendor/gitlab.com/signald/signald-go b/vendor/gitlab.com/signald/signald-go new file mode 160000 index 00000000..60778761 --- /dev/null +++ b/vendor/gitlab.com/signald/signald-go @@ -0,0 +1 @@ +Subproject commit 60778761c21dd68e082db03f7668097d03f86acc From 271df11556df57c4c131e99208b8e4e77a3b5a9c Mon Sep 17 00:00:00 2001 From: Peter Molnar Date: Sun, 28 Feb 2021 18:18:38 +0000 Subject: [PATCH 2/6] an extremely barebone, but working two way signald bridge I relied on learning from https://gitlab.com/signald/signald-go but because that project is under GPL and there are no plans on re-licensing (see ) it can't be directly used in matterbridge, which is Apache 2.0 Anyway: as it currently is, this module is crude, probably extremely prone to errors, and nowhere remotely close to stable or production use. Regardless of that, any help with it would be welcome. --- .gitmodules | 3 - bridge/signald/signald.go | 154 +++++++++++++++++++++------ vendor/gitlab.com/signald/signald-go | 1 - 3 files changed, 120 insertions(+), 38 deletions(-) delete mode 100644 .gitmodules delete mode 160000 vendor/gitlab.com/signald/signald-go diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index af95a57f..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "vendor/gitlab.com/signald/signald-go"] - path = vendor/gitlab.com/signald/signald-go - url = https://gitlab.com/signald/signald-go diff --git a/bridge/signald/signald.go b/bridge/signald/signald.go index 94e909bb..e93c7776 100644 --- a/bridge/signald/signald.go +++ b/bridge/signald/signald.go @@ -6,9 +6,6 @@ import ( "encoding/json" "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" - "gitlab.com/signald/signald-go/signald" - //"gitlab.com/signald/signald-go/signald/client-protocol/v0" - "gitlab.com/signald/signald-go/signald/client-protocol/v1" ) type JSONCMD map[string]interface{} @@ -19,10 +16,101 @@ const ( cfgGroupID = "GroupID" ) -type envelopeResponse struct { - ID string `json:",omitempty"` - Data v1.JsonMessageEnvelope `json:",omitempty"` - Type string `json:",omitempty"` +type signaldMessage struct { + ID string + Type string + Error json.RawMessage + Data json.RawMessage +} + +type signaldUnexpectedError struct { + Message string +} + +type signaldMessageData struct { + ID string `json:",omitempty"` + Data signaldData `json:",omitempty"` + Type string `json:",omitempty"` +} + +type signaldData struct { + CallMessage json.RawMessage `json:"callMessage,omitempty"` + DataMessage *signaldDataMessage `json:"dataMessage,omitempty"` + HasContent bool `json:"hasContent,omitempty"` + HasLegacyMessage bool `json:"hasLegacyMessage,omitempty"` + IsUnidentifiedSender bool `json:"isUnidentifiedSender,omitempty"` + Receipt json.RawMessage `json:"receipt,omitempty"` + Relay string `json:"relay,omitempty"` + ServerDeliveredTimestamp int64 `json:"serverDeliveredTimestamp,omitempty"` + ServerTimestamp int64 `json:"serverTimestamp,omitempty"` + Source *signaldAccount `json:"source,omitempty"` + SourceDevice int32 `json:"sourceDevice,omitempty"` + SyncMessage json.RawMessage `json:"syncMessage,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + TimestampISO string `json:"timestampISO,omitempty"` + Type string `json:"type,omitempty"` + Typing json.RawMessage `json:"typing,omitempty"` + Username string `json:"username,omitempty"` + UUID string `json:"uuid,omitempty"` +} + +type signaldAccount struct { + Number string `json:"number,omitempty"` + Relay string `json:"relay,omitempty"` + UUID string `json:"uuid,omitempty"` +} + +type signaldDataMessage struct { + Attachments json.RawMessage `json:"attachments,omitempty"` + Body string `json:"body,omitempty"` + Contacts json.RawMessage `json:"contacts,omitempty"` + EndSession bool `json:"endSession,omitempty"` + ExpiresInSeconds int32 `json:"expiresInSeconds,omitempty"` + Group *signaldGroupInfo `json:"group,omitempty"` + GroupV2 *signaldGroupV2Info `json:"groupV2,omitempty"` + Mentions json.RawMessage `json:"mentions,omitempty"` + Previews json.RawMessage `json:"previews,omitempty"` + ProfileKeyUpdate bool `json:"profileKeyUpdate,omitempty"` + Quote json.RawMessage `json:"quote,omitempty"` + Reaction json.RawMessage `json:"reaction,omitempty"` + RemoteDelete json.RawMessage `json:"remoteDelete,omitempty"` + Sticker json.RawMessage `json:"sticker,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + ViewOnce bool `json:"viewOnce,omitempty"` +} + +type signaldGroupInfo struct { + AvatarId int64 `json:"avatarId,omitempty"` + GroupId string `json:"groupId,omitempty"` + Members json.RawMessage `json:"members,omitempty"` + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` +} + +type signaldGroupV2Info struct { + AccessControl json.RawMessage `json:"accessControl,omitempty"` + Avatar string `json:"avatar,omitempty"` + ID string `json:"id,omitempty"` + InviteLink string `json:"inviteLink,omitempty"` + MemberDetail json.RawMessage `json:"memberDetail,omitempty"` + Members json.RawMessage `json:"members,omitempty"` + PendingMemberDetail json.RawMessage `json:"pendingMemberDetail,omitempty"` + PendingMembers json.RawMessage `json:"pendingMembers,omitempty"` + RequestingMembers json.RawMessage `json:"requestingMembers,omitempty"` + Revision int32 `json:"revision,omitempty"` + Timer int32 `json:"timer,omitempty"` + Title string `son:"title,omitempty"` +} + +type signaldSendMessage struct { + Username string `json:"username,omitempty"` + //RecipientAddress signaldAccount `json:"recipientAddress,omitempty"` + RecipientGroupId string `json:"recipientGroupId,omitempty"` + MessageBody string `json:"messageBody,omitempty"` + //Attachments json.RawMessage `json:"attachments,omitempty"` + //Quote json.RawMessage `json:"quote,omitempty"` + //Timestamp int64 `json:"timestamp,omitempty"` + //Mentions json.RawMessage `json:"mentions,omitempty"` } type Bsignald struct { @@ -83,43 +171,45 @@ func (b *Bsignald) Listen() { } raw := b.reader.Text() - b.Log.Debugln(raw); - var msg signald.BasicResponse + var msg signaldMessage if err := json.Unmarshal([]byte(raw), &msg); err != nil { b.Log.Errorln("Error unmarshaling raw response:", err.Error()) continue } if msg.Type == "unexpected_error" { - var errorResponse signald.UnexpectedError + var errorResponse signaldUnexpectedError if err := json.Unmarshal(msg.Data, &errorResponse); err != nil { - b.Log.Errorln("signald-go: Error unmarshaling error response:", err.Error()) + b.Log.Errorln("Error unmarshaling error response:", err.Error()) continue } - b.Log.Errorln("signald-go: Unexpected error", errorResponse.Message) + b.Log.Errorln("Unexpected error", errorResponse.Message) continue } if msg.Type != "message" { - b.Log.Debugln("not 'message' from signald: ", raw); + b.Log.Debugln("skipping: not 'message'"); continue + } else { + b.Log.Debugln("FOUND A MESSAGE!", raw); + } - response := envelopeResponse{ID: msg.ID, Type: msg.Type} + response := signaldMessageData{ID: msg.ID, Type: msg.Type} if err := json.Unmarshal(msg.Data, &response.Data); err != nil { - b.Log.Errorln("signald-go receive error: ", err) + b.Log.Errorln("receive error: ", err) continue } - b.Log.Debugf("%#v", response); + //b.Log.Debugf("%#v", response); if response.Data.DataMessage != nil { if response.Data.DataMessage.GroupV2 != nil { if b.groupid == response.Data.DataMessage.GroupV2.ID { rmsg := config.Message{ - UserID: response.Data.Username, - Username: response.Data.Username, + UserID: response.Data.Source.UUID, + Username: response.Data.Source.Number, Text: response.Data.DataMessage.Body, Channel: response.Data.DataMessage.GroupV2.ID, Account: b.Account, @@ -174,22 +264,18 @@ func (b *Bsignald) Disconnect() error { } func (b *Bsignald) Send(msg config.Message) (string, error) { + b.Log.Debugf("message to forward into signal: %#v", msg) - //req := v1.SendRequest{ - //Username: account, - //MessageBody: strings.Join(args[1:], " "), - //} + msgJSON := JSONCMD{ + "type": "send", + "username": b.GetString(cfgNumber), + "recipientGroupId": b.groupid, + "messageBody": msg.Text, + } + err := json.NewEncoder(b.socket).Encode(msgJSON) + if err != nil { + b.Log.Errorln(err.Error()) + } - //if strings.HasPrefix(args[0], "+") { - //req.RecipientAddress = &v1.JsonAddress{Number: args[0]} - //} else { - //req.RecipientGroupID = args[0] - //} - - //resp, err := req.Submit(common.Signald) - //if err != nil { - //log.Fatal("error sending request to signald: ", err) - //} - - return "", nil + return "", err } diff --git a/vendor/gitlab.com/signald/signald-go b/vendor/gitlab.com/signald/signald-go deleted file mode 160000 index 60778761..00000000 --- a/vendor/gitlab.com/signald/signald-go +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 60778761c21dd68e082db03f7668097d03f86acc From 7dd6a62a6598d1b53c47b5e5665a7a25f77ee8b1 Mon Sep 17 00:00:00 2001 From: Peter Molnar Date: Mon, 1 Mar 2021 13:32:28 +0000 Subject: [PATCH 3/6] - check subscribe - try to re-subscribe on fail - list contacts - use nice contact names instead of phone numbers --- bridge/signald/signald.go | 172 ++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 61 deletions(-) diff --git a/bridge/signald/signald.go b/bridge/signald/signald.go index e93c7776..663cb4dc 100644 --- a/bridge/signald/signald.go +++ b/bridge/signald/signald.go @@ -81,7 +81,7 @@ type signaldDataMessage struct { type signaldGroupInfo struct { AvatarId int64 `json:"avatarId,omitempty"` - GroupId string `json:"groupId,omitempty"` + ID string `json:"groupId,omitempty"` Members json.RawMessage `json:"members,omitempty"` Name string `json:"name,omitempty"` Type string `json:"type,omitempty"` @@ -103,14 +103,21 @@ type signaldGroupV2Info struct { } type signaldSendMessage struct { - Username string `json:"username,omitempty"` - //RecipientAddress signaldAccount `json:"recipientAddress,omitempty"` - RecipientGroupId string `json:"recipientGroupId,omitempty"` - MessageBody string `json:"messageBody,omitempty"` - //Attachments json.RawMessage `json:"attachments,omitempty"` - //Quote json.RawMessage `json:"quote,omitempty"` - //Timestamp int64 `json:"timestamp,omitempty"` - //Mentions json.RawMessage `json:"mentions,omitempty"` + Type string `json:"type,omitempty"` + Username string `json:"username,omitempty"` + RecipientGroupId string `json:"recipientGroupId,omitempty"` + MessageBody string `json:"messageBody,omitempty"` +} + +type signaldContact struct { + Name string `json:"name,omitempty"` + ProfileName string `json:"profile_name,omitempty"` + Account *signaldAccount `json:"address,omitempty"` + Avatar string `json:"avatar,omitempty"` + Color string `json:"color,omitempty"` + ProfileKey string `json:"profileKey,omitempty"` + MessageExpirationTime int32 `json:"messageExpirationTime,omitempty"` + InboxPosition int32 `json:"inboxPosition,omitempty"` } type Bsignald struct { @@ -119,8 +126,8 @@ type Bsignald struct { socket net.Conn subscribed bool reader *bufio.Scanner - //listeners map[string]chan signald.BasicResponse groupid string + contacts map[string]signaldContact } func New(cfg *bridge.Config) bridge.Bridger { @@ -138,6 +145,7 @@ func New(cfg *bridge.Config) bridge.Bridger { Config: cfg, socketpath: socketpath, subscribed: false, + contacts: make(map[string]signaldContact), } } @@ -148,7 +156,6 @@ func (b *Bsignald) Connect() error { if err != nil { b.Log.Fatalf(err.Error()) } - //defer s.Close() b.socket = s r := bufio.NewScanner(s) b.reader = r @@ -165,7 +172,8 @@ func (b *Bsignald) JoinChannel(channel config.ChannelInfo) error { func (b *Bsignald) Listen() { for { for b.reader.Scan() { - if err := b.reader.Err(); err != nil { + var err error + if err = b.reader.Err(); err != nil { b.Log.Errorf(err.Error()) continue } @@ -173,14 +181,28 @@ func (b *Bsignald) Listen() { raw := b.reader.Text() var msg signaldMessage - if err := json.Unmarshal([]byte(raw), &msg); err != nil { + if err = json.Unmarshal([]byte(raw), &msg); err != nil { b.Log.Errorln("Error unmarshaling raw response:", err.Error()) continue } + if msg.Type == "subscribed" { + b.Log.Debugln("subscribe successful", b.GetString(cfgNumber)) + b.subscribed = true + go b.GetContacts() + continue + } + + if msg.Type == "listen_stopped" { + b.Log.Errorln("got listen stopped, trying to re-subscribe") + b.subscribed = false + go b.Login() + continue + } + if msg.Type == "unexpected_error" { var errorResponse signaldUnexpectedError - if err := json.Unmarshal(msg.Data, &errorResponse); err != nil { + if err = json.Unmarshal(msg.Data, &errorResponse); err != nil { b.Log.Errorln("Error unmarshaling error response:", err.Error()) continue } @@ -188,73 +210,101 @@ func (b *Bsignald) Listen() { continue } + if msg.Type == "contact_list" { + var contacts []signaldContact + if err = json.Unmarshal(msg.Data, &contacts); err != nil { + b.Log.Errorln("failed to parse contact_list: ", err) + } else { + for _, contact := range contacts { + b.contacts[contact.Account.UUID] = contact + } + b.Log.Debugf("%#v", b.contacts) + } + continue + } + if msg.Type != "message" { b.Log.Debugln("skipping: not 'message'"); continue - } else { - b.Log.Debugln("FOUND A MESSAGE!", raw); - } response := signaldMessageData{ID: msg.ID, Type: msg.Type} - if err := json.Unmarshal(msg.Data, &response.Data); err != nil { + if err = json.Unmarshal(msg.Data, &response.Data); err != nil { b.Log.Errorln("receive error: ", err) continue } - //b.Log.Debugf("%#v", response); - if response.Data.DataMessage != nil { + groupMatched := false if response.Data.DataMessage.GroupV2 != nil { if b.groupid == response.Data.DataMessage.GroupV2.ID { - rmsg := config.Message{ - UserID: response.Data.Source.UUID, - Username: response.Data.Source.Number, - Text: response.Data.DataMessage.Body, - Channel: response.Data.DataMessage.GroupV2.ID, - Account: b.Account, - Protocol: b.Protocol, - } - b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) - b.Log.Debugf("<= Message is %#v", rmsg) - b.Remote <- rmsg + groupMatched = true } } + if response.Data.DataMessage.Group != nil { + if b.groupid == response.Data.DataMessage.Group.ID { + groupMatched = true + } + } + + if false == groupMatched { + b.Log.Debugln("skipping non-group message") + continue + } + + username := response.Data.Source.Number + if v, found := b.contacts[response.Data.Source.UUID]; found { + if "" != v.ProfileName { + username = v.ProfileName + } else if "" != v.Name { + username = v.Name + } + } + rmsg := config.Message{ + UserID: response.Data.Source.UUID, + Username: username, + Text: response.Data.DataMessage.Body, + Channel: b.groupid, + Account: b.Account, + Protocol: b.Protocol, + } + + b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) + b.Log.Debugf("<= Message is %#v", rmsg) + b.Remote <- rmsg + + // TODO: send read receipt } - - //if response.Data.SyncMessage != nil { - //if response.Data.SyncMessage.Sent != nil { - //if response.Data.SyncMessage.Sent.Message != nil { - //if response.Data.SyncMessage.Sent.Message != nil { - //if response.Data.SyncMessage.Sent.Message.GroupV2 != nil { - //if b.groupid == response.Data.SyncMessage.Sent.Message.GroupV2.id { - - //} - //} - //} - //} - //} - //} } } } +func (b *Bsignald) GetContacts() error { + cmd := JSONCMD{ + "type": "list_contacts", + "username": b.GetString(cfgNumber), + } + return b.SendRawJSON(cmd) +} func (b *Bsignald) Login() error { + var err error if ! b.subscribed { - subscribe := JSONCMD{ + cmd := JSONCMD{ "type": "subscribe", "username": b.GetString(cfgNumber), } - err := json.NewEncoder(b.socket).Encode(subscribe) - if err != nil { - b.Log.Fatalf(err.Error()) - } - // TODO: this should be done from the listener after the response - // was checked - b.subscribed = true + err = b.SendRawJSON(cmd) } - return nil + return err +} + +func (b *Bsignald) SendRawJSON(cmd JSONCMD) (error) { + err := json.NewEncoder(b.socket).Encode(cmd) + if err != nil { + b.Log.Errorln(err.Error()) + } + return err } func (b *Bsignald) Disconnect() error { @@ -266,16 +316,16 @@ func (b *Bsignald) Disconnect() error { func (b *Bsignald) Send(msg config.Message) (string, error) { b.Log.Debugf("message to forward into signal: %#v", msg) - msgJSON := JSONCMD{ - "type": "send", - "username": b.GetString(cfgNumber), - "recipientGroupId": b.groupid, - "messageBody": msg.Text, + msgJSON := signaldSendMessage { + Type: "send", + Username: b.GetString(cfgNumber), + RecipientGroupId: b.groupid, + MessageBody: msg.Username + msg.Text, } + err := json.NewEncoder(b.socket).Encode(msgJSON) if err != nil { b.Log.Errorln(err.Error()) } - - return "", err + return "", err } From 96637216803737f7c979957ede86278a5772086a Mon Sep 17 00:00:00 2001 From: Peter Molnar Date: Mon, 1 Mar 2021 18:19:57 +0000 Subject: [PATCH 4/6] making the matterbridge go linter happier --- bridge/signald/signald.go | 191 ++++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 88 deletions(-) diff --git a/bridge/signald/signald.go b/bridge/signald/signald.go index 663cb4dc..7adc4516 100644 --- a/bridge/signald/signald.go +++ b/bridge/signald/signald.go @@ -169,6 +169,96 @@ func (b *Bsignald) JoinChannel(channel config.ChannelInfo) error { return nil } +func (b *Bsignald) HandleUnexpectedErrorMessage(msg signaldMessage) { + var errorResponse signaldUnexpectedError + if err := json.Unmarshal(msg.Data, &errorResponse); err != nil { + b.Log.Errorln("Error unmarshaling error response:", err.Error()) + } + b.Log.Errorln("Unexpected error from signald: ", errorResponse.Message) +} + +func (b *Bsignald) HandleSubscribeMessage() { + b.Log.Debugln("subscribe successful", b.GetString(cfgNumber)) + b.subscribed = true + go b.GetContacts() +} + +func (b *Bsignald) HandleListenStoppedMessage() { + b.Log.Errorln("got listen stopped, trying to re-subscribe") + b.subscribed = false + go b.Login() +} + +func (b *Bsignald) HandleContactList(msg signaldMessage) { + var contacts []signaldContact + if err := json.Unmarshal(msg.Data, &contacts); err != nil { + b.Log.Errorln("failed to parse contact_list: ", err) + } else { + for _, contact := range contacts { + b.contacts[contact.Account.UUID] = contact + } + } +} + +func (b *Bsignald) GetUsername(uuid string) string { + username := "" + if v, found := b.contacts[uuid]; found { + if "" != v.ProfileName { + username = v.ProfileName + } else if "" != v.Name { + username = v.Name + } + } + return username +} + +func (b *Bsignald) HandleMessage(msg signaldMessage) { + response := signaldMessageData{ID: msg.ID, Type: msg.Type} + if err := json.Unmarshal(msg.Data, &response.Data); err != nil { + b.Log.Errorln("receive error: ", err) + return + } + + if nil == response.Data.DataMessage { + return + } + + groupMatched := false + if response.Data.DataMessage.GroupV2 != nil { + if b.groupid == response.Data.DataMessage.GroupV2.ID { + groupMatched = true + } + } else if response.Data.DataMessage.Group != nil { + if b.groupid == response.Data.DataMessage.Group.ID { + groupMatched = true + } + } + + if false == groupMatched { + b.Log.Debugln("skipping non-group message") + return + } + + username := b.GetUsername(response.Data.Source.UUID) + if "" == username { + username = response.Data.Source.Number + } + + rmsg := config.Message{ + UserID: response.Data.Source.UUID, + Username: username, + Text: response.Data.DataMessage.Body, + Channel: b.groupid, + Account: b.Account, + Protocol: b.Protocol, + } + + b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) + b.Log.Debugf("<= Message is %#v", rmsg) + b.Remote <- rmsg + +} + func (b *Bsignald) Listen() { for { for b.reader.Scan() { @@ -186,94 +276,19 @@ func (b *Bsignald) Listen() { continue } - if msg.Type == "subscribed" { - b.Log.Debugln("subscribe successful", b.GetString(cfgNumber)) - b.subscribed = true - go b.GetContacts() - continue - } - - if msg.Type == "listen_stopped" { - b.Log.Errorln("got listen stopped, trying to re-subscribe") - b.subscribed = false - go b.Login() - continue - } - - if msg.Type == "unexpected_error" { - var errorResponse signaldUnexpectedError - if err = json.Unmarshal(msg.Data, &errorResponse); err != nil { - b.Log.Errorln("Error unmarshaling error response:", err.Error()) - continue - } - b.Log.Errorln("Unexpected error", errorResponse.Message) - continue - } - - if msg.Type == "contact_list" { - var contacts []signaldContact - if err = json.Unmarshal(msg.Data, &contacts); err != nil { - b.Log.Errorln("failed to parse contact_list: ", err) - } else { - for _, contact := range contacts { - b.contacts[contact.Account.UUID] = contact - } - b.Log.Debugf("%#v", b.contacts) - } - continue - } - - if msg.Type != "message" { - b.Log.Debugln("skipping: not 'message'"); - continue - } - - response := signaldMessageData{ID: msg.ID, Type: msg.Type} - if err = json.Unmarshal(msg.Data, &response.Data); err != nil { - b.Log.Errorln("receive error: ", err) - continue - } - - if response.Data.DataMessage != nil { - groupMatched := false - if response.Data.DataMessage.GroupV2 != nil { - if b.groupid == response.Data.DataMessage.GroupV2.ID { - groupMatched = true - } - } - if response.Data.DataMessage.Group != nil { - if b.groupid == response.Data.DataMessage.Group.ID { - groupMatched = true - } - } - - if false == groupMatched { - b.Log.Debugln("skipping non-group message") - continue - } - - username := response.Data.Source.Number - if v, found := b.contacts[response.Data.Source.UUID]; found { - if "" != v.ProfileName { - username = v.ProfileName - } else if "" != v.Name { - username = v.Name - } - } - rmsg := config.Message{ - UserID: response.Data.Source.UUID, - Username: username, - Text: response.Data.DataMessage.Body, - Channel: b.groupid, - Account: b.Account, - Protocol: b.Protocol, - } - - b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) - b.Log.Debugf("<= Message is %#v", rmsg) - b.Remote <- rmsg - - // TODO: send read receipt + switch msg.Type { + case "unexpected_error": + b.HandleUnexpectedErrorMessage(msg) + case "subscribed": + b.HandleSubscribeMessage() + case "listen_stopped": + b.HandleListenStoppedMessage() + case "contact_list": + b.HandleContactList(msg) + case "message": + b.HandleMessage(msg) + default: + b.Log.Debugln("unsupported signald data received, skipping it"); } } } From 14a83e08cbeadf82187139712ed86c711ed566af Mon Sep 17 00:00:00 2001 From: Peter Molnar Date: Mon, 1 Mar 2021 19:28:11 +0000 Subject: [PATCH 5/6] go lint is a bloody picky little bastard --- bridge/signald/signald.go | 137 +++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 67 deletions(-) diff --git a/bridge/signald/signald.go b/bridge/signald/signald.go index 7adc4516..2e327031 100644 --- a/bridge/signald/signald.go +++ b/bridge/signald/signald.go @@ -2,10 +2,10 @@ package bsignald import ( "bufio" - "net" "encoding/json" "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" + "net" ) type JSONCMD map[string]interface{} @@ -13,7 +13,6 @@ type JSONCMD map[string]interface{} const ( cfgNumber = "Number" cfgSocket = "UnixSocket" - cfgGroupID = "GroupID" ) type signaldMessage struct { @@ -28,30 +27,30 @@ type signaldUnexpectedError struct { } type signaldMessageData struct { - ID string `json:",omitempty"` - Data signaldData `json:",omitempty"` - Type string `json:",omitempty"` + ID string `json:",omitempty"` + Data signaldData `json:",omitempty"` + Type string `json:",omitempty"` } type signaldData struct { - CallMessage json.RawMessage `json:"callMessage,omitempty"` - DataMessage *signaldDataMessage `json:"dataMessage,omitempty"` - HasContent bool `json:"hasContent,omitempty"` - HasLegacyMessage bool `json:"hasLegacyMessage,omitempty"` - IsUnidentifiedSender bool `json:"isUnidentifiedSender,omitempty"` - Receipt json.RawMessage `json:"receipt,omitempty"` - Relay string `json:"relay,omitempty"` - ServerDeliveredTimestamp int64 `json:"serverDeliveredTimestamp,omitempty"` - ServerTimestamp int64 `json:"serverTimestamp,omitempty"` - Source *signaldAccount `json:"source,omitempty"` - SourceDevice int32 `json:"sourceDevice,omitempty"` - SyncMessage json.RawMessage `json:"syncMessage,omitempty"` - Timestamp int64 `json:"timestamp,omitempty"` - TimestampISO string `json:"timestampISO,omitempty"` - Type string `json:"type,omitempty"` - Typing json.RawMessage `json:"typing,omitempty"` - Username string `json:"username,omitempty"` - UUID string `json:"uuid,omitempty"` + CallMessage json.RawMessage `json:"callMessage,omitempty"` + DataMessage *signaldDataMessage `json:"dataMessage,omitempty"` + HasContent bool `json:"hasContent,omitempty"` + HasLegacyMessage bool `json:"hasLegacyMessage,omitempty"` + IsUnidentifiedSender bool `json:"isUnidentifiedSender,omitempty"` + Receipt json.RawMessage `json:"receipt,omitempty"` + Relay string `json:"relay,omitempty"` + ServerDeliveredTimestamp int64 `json:"serverDeliveredTimestamp,omitempty"` + ServerTimestamp int64 `json:"serverTimestamp,omitempty"` + Source *signaldAccount `json:"source,omitempty"` + SourceDevice int32 `json:"sourceDevice,omitempty"` + SyncMessage json.RawMessage `json:"syncMessage,omitempty"` + Timestamp int64 `json:"timestamp,omitempty"` + TimestampISO string `json:"timestampISO,omitempty"` + Type string `json:"type,omitempty"` + Typing json.RawMessage `json:"typing,omitempty"` + Username string `json:"username,omitempty"` + UUID string `json:"uuid,omitempty"` } type signaldAccount struct { @@ -88,18 +87,18 @@ type signaldGroupInfo struct { } type signaldGroupV2Info struct { - AccessControl json.RawMessage `json:"accessControl,omitempty"` - Avatar string `json:"avatar,omitempty"` - ID string `json:"id,omitempty"` - InviteLink string `json:"inviteLink,omitempty"` - MemberDetail json.RawMessage `json:"memberDetail,omitempty"` - Members json.RawMessage `json:"members,omitempty"` - PendingMemberDetail json.RawMessage `json:"pendingMemberDetail,omitempty"` - PendingMembers json.RawMessage `json:"pendingMembers,omitempty"` - RequestingMembers json.RawMessage `json:"requestingMembers,omitempty"` - Revision int32 `json:"revision,omitempty"` - Timer int32 `json:"timer,omitempty"` - Title string `son:"title,omitempty"` + AccessControl json.RawMessage `json:"accessControl,omitempty"` + Avatar string `json:"avatar,omitempty"` + ID string `json:"id,omitempty"` + InviteLink string `json:"inviteLink,omitempty"` + MemberDetail json.RawMessage `json:"memberDetail,omitempty"` + Members json.RawMessage `json:"members,omitempty"` + PendingMemberDetail json.RawMessage `json:"pendingMemberDetail,omitempty"` + PendingMembers json.RawMessage `json:"pendingMembers,omitempty"` + RequestingMembers json.RawMessage `json:"requestingMembers,omitempty"` + Revision int32 `json:"revision,omitempty"` + Timer int32 `json:"timer,omitempty"` + Title string `son:"title,omitempty"` } type signaldSendMessage struct { @@ -126,8 +125,8 @@ type Bsignald struct { socket net.Conn subscribed bool reader *bufio.Scanner - groupid string - contacts map[string]signaldContact + groupid string + contacts map[string]signaldContact } func New(cfg *bridge.Config) bridge.Bridger { @@ -142,10 +141,10 @@ func New(cfg *bridge.Config) bridge.Bridger { } return &Bsignald{ - Config: cfg, + Config: cfg, socketpath: socketpath, subscribed: false, - contacts: make(map[string]signaldContact), + contacts: make(map[string]signaldContact), } } @@ -180,13 +179,17 @@ func (b *Bsignald) HandleUnexpectedErrorMessage(msg signaldMessage) { func (b *Bsignald) HandleSubscribeMessage() { b.Log.Debugln("subscribe successful", b.GetString(cfgNumber)) b.subscribed = true - go b.GetContacts() + if err := b.GetContacts(); err != nil { + b.Log.Errorln("Error getting contacts: ", err.Error()) + } } func (b *Bsignald) HandleListenStoppedMessage() { b.Log.Errorln("got listen stopped, trying to re-subscribe") b.subscribed = false - go b.Login() + if err := b.Login(); err != nil { + b.Log.Errorln("Error logging in: ", err.Error()) + } } func (b *Bsignald) HandleContactList(msg signaldMessage) { @@ -203,12 +206,13 @@ func (b *Bsignald) HandleContactList(msg signaldMessage) { func (b *Bsignald) GetUsername(uuid string) string { username := "" if v, found := b.contacts[uuid]; found { - if "" != v.ProfileName { + if v.ProfileName != "" { username = v.ProfileName - } else if "" != v.Name { + } else if v.Name != "" { username = v.Name } } + return username } @@ -219,7 +223,7 @@ func (b *Bsignald) HandleMessage(msg signaldMessage) { return } - if nil == response.Data.DataMessage { + if response.Data.DataMessage == nil { return } @@ -234,13 +238,13 @@ func (b *Bsignald) HandleMessage(msg signaldMessage) { } } - if false == groupMatched { + if !groupMatched { b.Log.Debugln("skipping non-group message") return } username := b.GetUsername(response.Data.Source.UUID) - if "" == username { + if username == "" { username = response.Data.Source.Number } @@ -256,7 +260,6 @@ func (b *Bsignald) HandleMessage(msg signaldMessage) { b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) b.Log.Debugf("<= Message is %#v", rmsg) b.Remote <- rmsg - } func (b *Bsignald) Listen() { @@ -277,18 +280,18 @@ func (b *Bsignald) Listen() { } switch msg.Type { - case "unexpected_error": - b.HandleUnexpectedErrorMessage(msg) - case "subscribed": - b.HandleSubscribeMessage() - case "listen_stopped": - b.HandleListenStoppedMessage() - case "contact_list": - b.HandleContactList(msg) - case "message": - b.HandleMessage(msg) - default: - b.Log.Debugln("unsupported signald data received, skipping it"); + case "unexpected_error": + b.HandleUnexpectedErrorMessage(msg) + case "subscribed": + b.HandleSubscribeMessage() + case "listen_stopped": + b.HandleListenStoppedMessage() + case "contact_list": + b.HandleContactList(msg) + case "message": + b.HandleMessage(msg) + default: + b.Log.Debugln("unsupported signald data received, skipping it") } } } @@ -296,7 +299,7 @@ func (b *Bsignald) Listen() { func (b *Bsignald) GetContacts() error { cmd := JSONCMD{ - "type": "list_contacts", + "type": "list_contacts", "username": b.GetString(cfgNumber), } return b.SendRawJSON(cmd) @@ -304,9 +307,9 @@ func (b *Bsignald) GetContacts() error { func (b *Bsignald) Login() error { var err error - if ! b.subscribed { + if !b.subscribed { cmd := JSONCMD{ - "type": "subscribe", + "type": "subscribe", "username": b.GetString(cfgNumber), } err = b.SendRawJSON(cmd) @@ -314,7 +317,7 @@ func (b *Bsignald) Login() error { return err } -func (b *Bsignald) SendRawJSON(cmd JSONCMD) (error) { +func (b *Bsignald) SendRawJSON(cmd JSONCMD) error { err := json.NewEncoder(b.socket).Encode(cmd) if err != nil { b.Log.Errorln(err.Error()) @@ -331,11 +334,11 @@ func (b *Bsignald) Disconnect() error { func (b *Bsignald) Send(msg config.Message) (string, error) { b.Log.Debugf("message to forward into signal: %#v", msg) - msgJSON := signaldSendMessage { - Type: "send", - Username: b.GetString(cfgNumber), + msgJSON := signaldSendMessage{ + Type: "send", + Username: b.GetString(cfgNumber), RecipientGroupId: b.groupid, - MessageBody: msg.Username + msg.Text, + MessageBody: msg.Username + msg.Text, } err := json.NewEncoder(b.socket).Encode(msgJSON) From db453a186e878fb64a677eeff33a676a07cac4fc Mon Sep 17 00:00:00 2001 From: Peter Molnar Date: Sun, 4 Apr 2021 09:17:04 +0100 Subject: [PATCH 6/6] golint happifying, round 2 --- bridge/signald/signald.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bridge/signald/signald.go b/bridge/signald/signald.go index 2e327031..175b367a 100644 --- a/bridge/signald/signald.go +++ b/bridge/signald/signald.go @@ -3,9 +3,10 @@ package bsignald import ( "bufio" "encoding/json" + "net" + "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" - "net" ) type JSONCMD map[string]interface{} @@ -79,7 +80,7 @@ type signaldDataMessage struct { } type signaldGroupInfo struct { - AvatarId int64 `json:"avatarId,omitempty"` + AvatarID int64 `json:"avatarId,omitempty"` ID string `json:"groupId,omitempty"` Members json.RawMessage `json:"members,omitempty"` Name string `json:"name,omitempty"` @@ -104,7 +105,7 @@ type signaldGroupV2Info struct { type signaldSendMessage struct { Type string `json:"type,omitempty"` Username string `json:"username,omitempty"` - RecipientGroupId string `json:"recipientGroupId,omitempty"` + RecipientGroupID string `json:"recipientGroupId,omitempty"` MessageBody string `json:"messageBody,omitempty"` } @@ -159,7 +160,9 @@ func (b *Bsignald) Connect() error { r := bufio.NewScanner(s) b.reader = r go b.Listen() - go b.Login() + if err := b.Login(); err != nil { + b.Log.Errorln("Error logging in: ", err.Error()) + } return nil } @@ -337,7 +340,7 @@ func (b *Bsignald) Send(msg config.Message) (string, error) { msgJSON := signaldSendMessage{ Type: "send", Username: b.GetString(cfgNumber), - RecipientGroupId: b.groupid, + RecipientGroupID: b.groupid, MessageBody: msg.Username + msg.Text, }