From 6df2ff725999ff82d0f3b171b44d748533bf34ee Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 8 Mar 2023 17:14:31 +0200 Subject: [PATCH] Add config option to bridge cross-room replies --- config/bridge.go | 1 + config/upgrade.go | 1 + example-config.yaml | 2 ++ go.mod | 4 ++-- go.sum | 8 ++++---- portal.go | 40 +++++++++++++++++++++++++++++++++++++--- 6 files changed, 47 insertions(+), 9 deletions(-) diff --git a/config/bridge.go b/config/bridge.go index a91435d..589536a 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -118,6 +118,7 @@ type BridgeConfig struct { CaptionInMessage bool `yaml:"caption_in_message"` ExtEvPolls bool `yaml:"extev_polls"` SendWhatsAppEdits bool `yaml:"send_whatsapp_edits"` + CrossRoomReplies bool `yaml:"cross_room_replies"` MessageHandlingTimeout struct { ErrorAfterStr string `yaml:"error_after"` diff --git a/config/upgrade.go b/config/upgrade.go index 8b0fb95..a050af5 100644 --- a/config/upgrade.go +++ b/config/upgrade.go @@ -107,6 +107,7 @@ func DoUpgrade(helper *up.Helper) { helper.Copy(up.Bool, "bridge", "extev_polls") } helper.Copy(up.Bool, "bridge", "send_whatsapp_edits") + helper.Copy(up.Bool, "bridge", "cross_room_replies") helper.Copy(up.Str|up.Null, "bridge", "message_handling_timeout", "error_after") helper.Copy(up.Str|up.Null, "bridge", "message_handling_timeout", "deadline") diff --git a/example-config.yaml b/example-config.yaml index c38302b..bfb1c32 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -312,6 +312,8 @@ bridge: # Should Matrix edits be bridged to WhatsApp edits? # Official WhatsApp clients don't render edits yet, but once they do, the bridge should work with them right away. send_whatsapp_edits: false + # Should cross-chat replies from WhatsApp be bridged? Most servers and clients don't support this. + cross_room_replies: false # Maximum time for handling Matrix events. Duration strings formatted for https://pkg.go.dev/time#ParseDuration # Null means there's no enforced timeout. message_handling_timeout: diff --git a/go.mod b/go.mod index 9d8a6ba..f05737f 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( golang.org/x/net v0.6.0 google.golang.org/protobuf v1.28.1 maunium.net/go/maulogger/v2 v2.4.1 - maunium.net/go/mautrix v0.15.0-beta.2 + maunium.net/go/mautrix v0.15.0-beta.2.0.20230308151314-83fcdcf30f00 ) require ( @@ -37,7 +37,7 @@ require ( github.com/tidwall/sjson v1.2.5 // indirect github.com/yuin/goldmark v1.5.4 // indirect go.mau.fi/libsignal v0.1.0 // indirect - go.mau.fi/zeroconfig v0.1.0 // indirect + go.mau.fi/zeroconfig v0.1.1 // indirect golang.org/x/crypto v0.6.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect diff --git a/go.sum b/go.sum index 362b60b..8e58776 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c= go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I= go.mau.fi/whatsmeow v0.0.0-20230306190159-5caded34a872 h1:jrIWy0l9kTxl7bdp3muFofZcyLyI1xxE7BXWeldVKr0= go.mau.fi/whatsmeow v0.0.0-20230306190159-5caded34a872/go.mod h1:zoTtv1CupGEyTew7TOwnBmTbHB4pVad2OzjTf5CVwa0= -go.mau.fi/zeroconfig v0.1.0 h1:SDGgreQevNJHb+QqGAs2Ff+OPXcGdO8rZencqP4BJi4= -go.mau.fi/zeroconfig v0.1.0/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= +go.mau.fi/zeroconfig v0.1.1 h1:igLhBbCkaO8yxtxCfzy28k3yitzXU43LcOwpWmpBjz4= +go.mau.fi/zeroconfig v0.1.1/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= @@ -127,5 +127,5 @@ maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= -maunium.net/go/mautrix v0.15.0-beta.2 h1:J7sEehF7taahVtoCsM6E41VauaBNQMPCCcxYCe/0Zbk= -maunium.net/go/mautrix v0.15.0-beta.2/go.mod h1:AE3TCX9q4W7fYfrL/1RsuOell9rTUBO27XUULuwArH4= +maunium.net/go/mautrix v0.15.0-beta.2.0.20230308151314-83fcdcf30f00 h1:tlEAjIGSnxZd2MDvgAIw946KoxqWQq/SJDEchoCJM0M= +maunium.net/go/mautrix v0.15.0-beta.2.0.20230308151314-83fcdcf30f00/go.mod h1:Ay2u2JljEKX2rEfA2Ss8Wxkj1tkAhUClpA+exZc7imk= diff --git a/portal.go b/portal.go index 59613d4..f3852b2 100644 --- a/portal.go +++ b/portal.go @@ -120,6 +120,16 @@ func (br *WABridge) GetPortalByJID(key database.PortalKey) *Portal { return portal } +func (br *WABridge) GetExistingPortalByJID(key database.PortalKey) *Portal { + br.portalsLock.Lock() + defer br.portalsLock.Unlock() + portal, ok := br.portalsByJID[key] + if !ok { + return br.loadDBPortal(br.DB.Portal.GetByJID(key), nil) + } + return portal +} + func (br *WABridge) GetAllPortals() []*Portal { return br.dbPortalsToPortals(br.DB.Portal.GetAll()) } @@ -1866,15 +1876,35 @@ func (portal *Portal) SetReply(content *event.MessageEventContent, replyTo *Repl if replyTo == nil { return false } - message := portal.bridge.DB.Message.GetByJID(portal.Key, replyTo.MessageID) + key := portal.Key + targetPortal := portal + defer func() { + if content.RelatesTo != nil && content.RelatesTo.InReplyTo != nil && targetPortal != portal { + content.RelatesTo.InReplyTo.UnstableRoomID = targetPortal.MXID + } + }() + if portal.bridge.Config.Bridge.CrossRoomReplies && !replyTo.Chat.IsEmpty() && replyTo.Chat != key.JID { + if replyTo.Chat.Server == types.GroupServer { + key = database.NewPortalKey(replyTo.Chat, types.EmptyJID) + } else if replyTo.Chat == types.BroadcastServerJID { + key = database.NewPortalKey(replyTo.Chat, key.Receiver) + } + if key != portal.Key { + targetPortal = portal.bridge.GetExistingPortalByJID(key) + if targetPortal == nil { + return false + } + } + } + message := portal.bridge.DB.Message.GetByJID(key, replyTo.MessageID) if message == nil || message.IsFakeMXID() { if isBackfill && portal.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareHungry { - content.RelatesTo = (&event.RelatesTo{}).SetReplyTo(portal.deterministicEventID(replyTo.Sender, replyTo.MessageID, "")) + content.RelatesTo = (&event.RelatesTo{}).SetReplyTo(targetPortal.deterministicEventID(replyTo.Sender, replyTo.MessageID, "")) return true } return false } - evt, err := portal.MainIntent().GetEvent(portal.MXID, message.MXID) + evt, err := targetPortal.MainIntent().GetEvent(targetPortal.MXID, message.MXID) if err != nil { portal.log.Warnln("Failed to get reply target:", err) content.RelatesTo = (&event.RelatesTo{}).SetReplyTo(message.MXID) @@ -2017,12 +2047,14 @@ func (portal *Portal) sendMessage(intent *appservice.IntentAPI, eventType event. type ReplyInfo struct { MessageID types.MessageID + Chat types.JID Sender types.JID } type Replyable interface { GetStanzaId() string GetParticipant() string + GetRemoteJid() string } func GetReply(replyable Replyable) *ReplyInfo { @@ -2033,8 +2065,10 @@ func GetReply(replyable Replyable) *ReplyInfo { if err != nil { return nil } + chat, _ := types.ParseJID(replyable.GetRemoteJid()) return &ReplyInfo{ MessageID: types.MessageID(replyable.GetStanzaId()), + Chat: chat, Sender: sender, } }