Browse Source

Re-add call start notices

Tulir Asokan 3 years ago
parent
commit
e0d79f2de1
7 changed files with 78 additions and 16 deletions
  1. 1 5
      config/bridge.go
  2. 4 0
      database/message.go
  3. 2 5
      example-config.yaml
  4. 1 1
      go.mod
  5. 2 2
      go.sum
  6. 42 2
      portal.go
  7. 26 1
      user.go

+ 1 - 5
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"`

+ 4 - 0
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)

+ 2 - 5
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

+ 1 - 1
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

+ 2 - 2
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=

+ 42 - 2
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

+ 26 - 1
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}