From e0d79f2de1547c9501899770c4ce0297f52e1efe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 2 Nov 2021 15:46:31 +0200 Subject: [PATCH] Re-add call start notices --- config/bridge.go | 6 +----- database/message.go | 4 ++++ example-config.yaml | 7 ++----- go.mod | 2 +- go.sum | 4 ++-- portal.go | 44 ++++++++++++++++++++++++++++++++++++++++++-- user.go | 27 ++++++++++++++++++++++++++- 7 files changed, 78 insertions(+), 16 deletions(-) diff --git a/config/bridge.go b/config/bridge.go index a9d8a1f..9c7b026 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -34,11 +34,7 @@ type BridgeConfig struct { DeliveryReceipts bool `yaml:"delivery_receipts"` PortalMessageBuffer int `yaml:"portal_message_buffer"` - - CallNotices struct { - Start bool `yaml:"start"` - End bool `yaml:"end"` - } `yaml:"call_notices"` + CallStartNotices bool `yaml:"call_start_notices"` HistorySync struct { CreatePortals bool `yaml:"create_portals"` diff --git a/database/message.go b/database/message.go index b9adcbe..cecc191 100644 --- a/database/message.go +++ b/database/message.go @@ -124,6 +124,10 @@ func (msg *Message) IsFakeMXID() bool { return strings.HasPrefix(msg.MXID.String(), "net.maunium.whatsapp.fake::") } +func (msg *Message) IsFakeJID() bool { + return strings.HasPrefix(msg.JID, "FAKE::") +} + func (msg *Message) Scan(row Scannable) *Message { var ts int64 err := row.Scan(&msg.Chat.JID, &msg.Chat.Receiver, &msg.JID, &msg.MXID, &msg.Sender, &ts, &msg.Sent, &msg.DecryptionError) diff --git a/example-config.yaml b/example-config.yaml index 1edb8d1..3833d7a 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -85,11 +85,8 @@ bridge: # Should the bridge send a read receipt from the bridge bot when a message has been sent to WhatsApp? delivery_receipts: false - - # Should notices of incoming calls be sent to Matrix? - call_notices: - start: true - end: true + # Should incoming calls send a message to the Matrix room? + call_start_notices: true # Settings for handling history sync payloads. These settings only apply right after login, # because the phone only sends the history sync data once, and there's no way to re-request it diff --git a/go.mod b/go.mod index c55dfba..39f68a6 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.9 github.com/prometheus/client_golang v1.11.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - go.mau.fi/whatsmeow v0.0.0-20211101191317-2837c184bbfe + go.mau.fi/whatsmeow v0.0.0-20211102134251-7e0b153d9c9e golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 83c3073..3b68bca 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/tidwall/sjson v1.2.3 h1:5+deguEhHSEjmuICXZ21uSSsXotWMA0orU783+Z7Cp8= github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs= go.mau.fi/libsignal v0.0.0-20211024113310-f9fc6a1855f2 h1:xpQTMgJGGaF+c8jV/LA/FVXAPJxZbSAGeflOc+Ly6uQ= go.mau.fi/libsignal v0.0.0-20211024113310-f9fc6a1855f2/go.mod h1:3XlVlwOfp8f9Wri+C1D4ORqgUsN4ZvunJOoPjQMBhos= -go.mau.fi/whatsmeow v0.0.0-20211101191317-2837c184bbfe h1:ohSAIsW4Jg5GD2r0b+827vgQmheMKCMyhb7AGFppBDg= -go.mau.fi/whatsmeow v0.0.0-20211101191317-2837c184bbfe/go.mod h1:ODEmmqeUn9eBDQHFc1S902YA3YFLtmaBujYRRFl53jI= +go.mau.fi/whatsmeow v0.0.0-20211102134251-7e0b153d9c9e h1:HVlRWzEEmWkgInvj8VGxB3epRSd7ryRvLdAA2X0fbGk= +go.mau.fi/whatsmeow v0.0.0-20211102134251-7e0b153d9c9e/go.mod h1:ODEmmqeUn9eBDQHFc1S902YA3YFLtmaBujYRRFl53jI= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/portal.go b/portal.go index e6d381f..a3e5edd 100644 --- a/portal.go +++ b/portal.go @@ -161,9 +161,17 @@ func (bridge *Bridge) NewPortal(dbPortal *database.Portal) *Portal { const recentlyHandledLength = 100 +type fakeMessage struct { + Sender types.JID + Text string + ID string + Time time.Time +} + type PortalMessage struct { evt *events.Message undecryptable *events.UndecryptableMessage + fake *fakeMessage source *User } @@ -196,7 +204,7 @@ type Portal struct { func (portal *Portal) handleMessageLoop() { for msg := range portal.messages { if len(portal.MXID) == 0 { - if msg.evt == nil || !containsSupportedMessage(msg.evt.Message) { + if msg.fake == nil && (msg.evt == nil || !containsSupportedMessage(msg.evt.Message)) { portal.log.Debugln("Not creating portal room for incoming message: message is not a chat message") continue } @@ -211,6 +219,9 @@ func (portal *Portal) handleMessageLoop() { portal.handleMessage(msg.source, msg.evt) } else if msg.undecryptable != nil { portal.handleUndecryptableMessage(msg.source, msg.undecryptable) + } else if msg.fake != nil { + msg.fake.ID = "FAKE::" + msg.fake.ID + portal.handleFakeMessage(*msg.fake) } else { portal.log.Warnln("Unexpected PortalMessage with no message: %+v", msg) } @@ -336,6 +347,32 @@ func (portal *Portal) handleUndecryptableMessage(source *User, evt *events.Undec portal.finishHandling(nil, &evt.Info, resp.EventID, true) } +func (portal *Portal) handleFakeMessage(msg fakeMessage) { + if portal.isRecentlyHandled(msg.ID, false) { + portal.log.Debugfln("Not handling %s (fake): message was recently handled", msg.ID) + return + } else if existingMsg := portal.bridge.DB.Message.GetByJID(portal.Key, msg.ID); existingMsg != nil { + portal.log.Debugfln("Not handling %s (fake): message is duplicate", msg.ID) + return + } + intent := portal.bridge.GetPuppetByJID(msg.Sender).IntentFor(portal) + resp, err := portal.sendMessage(intent, event.EventMessage, &event.MessageEventContent{ + MsgType: event.MsgText, + Body: msg.Text, + }, nil, msg.Time.UnixMilli()) + if err != nil { + portal.log.Errorln("Failed to send %s to Matrix: %v", msg.ID, err) + } else { + portal.finishHandling(nil, &types.MessageInfo{ + ID: msg.ID, + Timestamp: msg.Time, + MessageSource: types.MessageSource{ + Sender: msg.Sender, + }, + }, resp.EventID, false) + } +} + func (portal *Portal) handleMessage(source *User, evt *events.Message) { if len(portal.MXID) == 0 { portal.log.Warnln("handleMessage called even though portal.MXID is empty") @@ -2140,7 +2177,7 @@ func (portal *Portal) convertMatrixMessage(sender *User, evt *event.Event) (*waP replyToID := content.GetReplyTo() if len(replyToID) > 0 { replyToMsg := portal.bridge.DB.Message.GetByMXID(replyToID) - if replyToMsg != nil { + if replyToMsg != nil && !replyToMsg.IsFakeJID() { ctxInfo.StanzaId = &replyToMsg.JID ctxInfo.Participant = proto.String(replyToMsg.Sender.ToNonAD().String()) // Using blank content here seems to work fine on all official WhatsApp apps. @@ -2360,6 +2397,9 @@ func (portal *Portal) HandleMatrixRedaction(sender *User, evt *event.Event) { if msg == nil { portal.log.Debugfln("Ignoring redaction %s of unknown event by %s", msg, senderLogIdentifier) return + } else if msg.IsFakeJID() { + portal.log.Debugfln("Ignoring redaction %s of fake event by %s", msg, senderLogIdentifier) + return } else if msg.Sender.User != sender.JID.User { portal.log.Debugfln("Ignoring redaction %s of %s/%s by %s: message was sent by someone else (%s, not %s)", evt.ID, msg.MXID, msg.JID, senderLogIdentifier, msg.Sender, sender.JID) return diff --git a/user.go b/user.go index 854dcb6..7e2d6e7 100644 --- a/user.go +++ b/user.go @@ -358,7 +358,7 @@ func containsSupportedMessages(conv *waProto.Conversation) bool { type portalToBackfill struct { portal *Portal - conv *waProto.Conversation + conv *waProto.Conversation } func (user *User) handleHistorySync(evt *waProto.HistorySync) { @@ -423,6 +423,25 @@ func (user *User) handleHistorySync(evt *waProto.HistorySync) { user.log.Infofln("Finished handling history sync with type %s, chunk order %d, progress %d%%", evt.GetSyncType(), evt.GetChunkOrder(), evt.GetProgress()) } +func (user *User) handleCallStart(sender types.JID, id, callType string) { + if !user.bridge.Config.Bridge.CallStartNotices { + return + } + portal := user.GetPortalByJID(sender) + text := "Incoming call" + if callType != "" { + text = fmt.Sprintf("Incoming %s call", text) + } + portal.messages <- PortalMessage{ + fake: &fakeMessage{ + Sender: sender, + Text: text, + ID: id, + }, + source: user, + } +} + func (user *User) HandleEvent(event interface{}) { switch v := event.(type) { case *events.LoggedOut: @@ -482,6 +501,12 @@ func (user *User) HandleEvent(event interface{}) { case *events.Message: portal := user.GetPortalByJID(v.Info.Chat) portal.messages <- PortalMessage{evt: v, source: user} + case *events.CallOffer: + user.handleCallStart(v.CallCreator, v.CallID, "") + case *events.CallOfferNotice: + user.handleCallStart(v.CallCreator, v.CallID, v.Type) + case *events.CallTerminate, *events.CallRelayLatency, *events.CallAccept, *events.UnknownCallEvent: + // ignore case *events.UndecryptableMessage: portal := user.GetPortalByJID(v.Info.Chat) portal.messages <- PortalMessage{undecryptable: v, source: user}