From bf4c01648f0e1c92924ec9f3422c189461b51ae7 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 30 Jun 2022 20:56:25 +0300 Subject: [PATCH] Move most double puppet source key adding to mautrix-go --- go.mod | 2 +- go.sum | 4 +- historysync.go | 19 +-------- portal.go | 113 ++++++++++++------------------------------------- puppet.go | 10 +++++ user.go | 23 +++++----- 6 files changed, 51 insertions(+), 120 deletions(-) diff --git a/go.mod b/go.mod index 9966043..56e8414 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e google.golang.org/protobuf v1.28.0 maunium.net/go/maulogger/v2 v2.3.2 - maunium.net/go/mautrix v0.11.1-0.20220630105134-e8349f977b35 + maunium.net/go/mautrix v0.11.1-0.20220630174618-e98784f2fe26 ) require ( diff --git a/go.sum b/go.sum index 731c969..fc49cd9 100644 --- a/go.sum +++ b/go.sum @@ -108,5 +108,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.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0= maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= -maunium.net/go/mautrix v0.11.1-0.20220630105134-e8349f977b35 h1:aPiY7xtcRsP7o7u0TvkKY7JiyjVa0bUnsqCEMJQ3vU4= -maunium.net/go/mautrix v0.11.1-0.20220630105134-e8349f977b35/go.mod h1:Lj4pBam5P0zIvieIFHnGsuaj+xfFtI3y/sC8yGlyna8= +maunium.net/go/mautrix v0.11.1-0.20220630174618-e98784f2fe26 h1:wkfsp2ozyAQ9Vr9oAXbS9caWLhIffQ/Lxa04t7iUY54= +maunium.net/go/mautrix v0.11.1-0.20220630174618-e98784f2fe26/go.mod h1:Lj4pBam5P0zIvieIFHnGsuaj+xfFtI3y/sC8yGlyna8= diff --git a/historysync.go b/historysync.go index a4bd530..38920da 100644 --- a/historysync.go +++ b/historysync.go @@ -656,33 +656,16 @@ func (portal *Portal) appendBatchEvents(converted *ConvertedMessage, info *types return nil } -const backfillIDField = "fi.mau.whatsapp.backfill_msg_id" - func (portal *Portal) wrapBatchEvent(info *types.MessageInfo, intent *appservice.IntentAPI, eventType event.Type, content *event.MessageEventContent, extraContent map[string]interface{}) (*event.Event, error) { - if extraContent == nil { - extraContent = map[string]interface{}{} - } - extraContent[backfillIDField] = info.ID - if intent.IsCustomPuppet { - extraContent[doublePuppetKey] = doublePuppetValue - } wrappedContent := event.Content{ Parsed: content, Raw: extraContent, } - newEventType, err := portal.encrypt(&wrappedContent, eventType) + newEventType, err := portal.encrypt(intent, &wrappedContent, eventType) if err != nil { return nil, err } - if newEventType == event.EventEncrypted { - // Clear other custom keys if the event was encrypted, but keep the double puppet identifier - wrappedContent.Raw = map[string]interface{}{backfillIDField: info.ID} - if intent.IsCustomPuppet { - wrappedContent.Raw[doublePuppetKey] = doublePuppetValue - } - } - return &event.Event{ Sender: intent.UserID, Type: newEventType, diff --git a/portal.go b/portal.go index 0bae64e..f93c2ac 100644 --- a/portal.go +++ b/portal.go @@ -331,7 +331,7 @@ func (portal *Portal) handleReceipt(receipt *events.Receipt, source *User) { } intent := portal.bridge.GetPuppetByJID(receipt.Sender).IntentFor(portal) for _, msg := range markAsRead { - err := intent.SetReadMarkers(portal.MXID, makeReadMarkerContent(msg.MXID, intent.IsCustomPuppet)) + err := intent.SetReadMarkers(portal.MXID, source.makeReadMarkerContent(msg.MXID, intent.IsCustomPuppet)) if err != nil { portal.log.Warnfln("Failed to mark message %s as read by %s: %v", msg.MXID, intent.UserID, err) } else { @@ -740,7 +740,7 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message) { if source.MXID == intent.UserID { // There are some edge cases (like call notices) where previous messages aren't marked as read // when the user sends a message from another device, so just mark the new message as read to be safe. - err = intent.SetReadMarkers(portal.MXID, makeReadMarkerContent(lastEventID, true)) + err = intent.SetReadMarkers(portal.MXID, source.makeReadMarkerContent(lastEventID, true)) if err != nil { portal.log.Warnfln("Failed to mark own message %s as read by %s: %v", lastEventID, source.MXID, err) } @@ -1588,11 +1588,6 @@ func (portal *Portal) SetReply(content *event.MessageEventContent, replyToID typ return true } -type sendReactionContent struct { - event.ReactionEventContent - DoublePuppet string `json:"fi.mau.double_puppet_source,omitempty"` -} - func (portal *Portal) HandleMessageReaction(intent *appservice.IntentAPI, user *User, info *types.MessageInfo, reaction *waProto.ReactionMessage, existingMsg *database.Message) { if existingMsg != nil { _, _ = portal.MainIntent().RedactEvent(portal.MXID, existingMsg.MXID, mautrix.ReqRedact{ @@ -1608,11 +1603,7 @@ func (portal *Portal) HandleMessageReaction(intent *appservice.IntentAPI, user * return } - extra := make(map[string]interface{}) - if intent.IsCustomPuppet { - extra[doublePuppetKey] = doublePuppetValue - } - resp, err := intent.RedactEvent(portal.MXID, existing.MXID, mautrix.ReqRedact{Extra: extra}) + resp, err := intent.RedactEvent(portal.MXID, existing.MXID) if err != nil { portal.log.Errorfln("Failed to redact reaction %s/%s from %s to %s: %v", existing.MXID, existing.JID, info.Sender, targetJID, err) } @@ -1625,15 +1616,12 @@ func (portal *Portal) HandleMessageReaction(intent *appservice.IntentAPI, user * return } - var content sendReactionContent + var content event.ReactionEventContent content.RelatesTo = event.RelatesTo{ Type: event.RelAnnotation, EventID: target.MXID, Key: variationselector.Add(reaction.GetText()), } - if intent.IsCustomPuppet { - content.DoublePuppet = doublePuppetValue - } resp, err := intent.SendMassagedMessageEvent(portal.MXID, event.EventReaction, &content, info.Timestamp.UnixMilli()) if err != nil { portal.log.Errorfln("Failed to bridge reaction %s from %s to %s: %v", info.ID, info.Sender, target.JID, err) @@ -1651,14 +1639,10 @@ func (portal *Portal) HandleMessageRevoke(user *User, info *types.MessageInfo, k return false } intent := portal.bridge.GetPuppetByJID(info.Sender).IntentFor(portal) - redactionReq := mautrix.ReqRedact{Extra: map[string]interface{}{}} - if intent.IsCustomPuppet { - redactionReq.Extra[doublePuppetKey] = doublePuppetValue - } - _, err := intent.RedactEvent(portal.MXID, msg.MXID, redactionReq) + _, err := intent.RedactEvent(portal.MXID, msg.MXID) if err != nil { if errors.Is(err, mautrix.MForbidden) { - _, err = portal.MainIntent().RedactEvent(portal.MXID, msg.MXID, redactionReq) + _, err = portal.MainIntent().RedactEvent(portal.MXID, msg.MXID) if err != nil { portal.log.Errorln("Failed to redact %s: %v", msg.JID, err) } @@ -1673,49 +1657,29 @@ func (portal *Portal) sendMainIntentMessage(content *event.MessageEventContent) return portal.sendMessage(portal.MainIntent(), event.EventMessage, content, nil, 0) } -func (portal *Portal) encrypt(content *event.Content, eventType event.Type) (event.Type, error) { - if portal.Encrypted && portal.bridge.Crypto != nil { - // TODO maybe the locking should be inside mautrix-go? - portal.encryptLock.Lock() - encrypted, err := portal.bridge.Crypto.Encrypt(portal.MXID, eventType, *content) - portal.encryptLock.Unlock() - if err != nil { - return eventType, fmt.Errorf("failed to encrypt event: %w", err) - } - eventType = event.EventEncrypted - content.Parsed = encrypted +func (portal *Portal) encrypt(intent *appservice.IntentAPI, content *event.Content, eventType event.Type) (event.Type, error) { + if !portal.Encrypted || portal.bridge.Crypto == nil { + return eventType, nil } - return eventType, nil + intent.AddDoublePuppetValue(content) + // TODO maybe the locking should be inside mautrix-go? + portal.encryptLock.Lock() + defer portal.encryptLock.Unlock() + err := portal.bridge.Crypto.Encrypt(portal.MXID, eventType, content) + if err != nil { + return eventType, fmt.Errorf("failed to encrypt event: %w", err) + } + return event.EventEncrypted, nil } -const doublePuppetKey = "fi.mau.double_puppet_source" -const doublePuppetValue = "mautrix-whatsapp" - func (portal *Portal) sendMessage(intent *appservice.IntentAPI, eventType event.Type, content *event.MessageEventContent, extraContent map[string]interface{}, timestamp int64) (*mautrix.RespSendEvent, error) { wrappedContent := event.Content{Parsed: content, Raw: extraContent} - if timestamp != 0 && intent.IsCustomPuppet { - if wrappedContent.Raw == nil { - wrappedContent.Raw = map[string]interface{}{} - } - if intent.IsCustomPuppet { - wrappedContent.Raw[doublePuppetKey] = doublePuppetValue - } - } var err error - eventType, err = portal.encrypt(&wrappedContent, eventType) + eventType, err = portal.encrypt(intent, &wrappedContent, eventType) if err != nil { return nil, err } - if eventType == event.EventEncrypted { - // Clear other custom keys if the event was encrypted, but keep the double puppet identifier - if intent.IsCustomPuppet { - wrappedContent.Raw = map[string]interface{}{doublePuppetKey: doublePuppetValue} - } else { - wrappedContent.Raw = nil - } - } - _, _ = intent.UserTyping(portal.MXID, false, 0) if timestamp == 0 { return intent.SendMessageEvent(portal.MXID, eventType, &wrappedContent) @@ -2142,11 +2106,11 @@ func (portal *Portal) removeUser(isSameUser bool, kicker *appservice.IntentAPI, if err != nil { portal.log.Warnfln("Failed to kick %s from %s: %v", target, portal.MXID, err) if targetIntent != nil { - _, _ = portal.leaveWithPuppetMeta(targetIntent) + _, _ = targetIntent.LeaveRoom(portal.MXID) } } } else { - _, err := portal.leaveWithPuppetMeta(targetIntent) + _, err := targetIntent.LeaveRoom(portal.MXID) if err != nil { portal.log.Warnfln("Failed to leave portal as %s: %v", target, err) _, _ = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: target}) @@ -2178,19 +2142,6 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID types.JID, jids } } -func (portal *Portal) leaveWithPuppetMeta(intent *appservice.IntentAPI) (*mautrix.RespSendEvent, error) { - content := event.Content{ - Parsed: event.MemberEventContent{ - Membership: event.MembershipLeave, - }, - Raw: map[string]interface{}{ - doublePuppetKey: doublePuppetValue, - }, - } - // Bypass IntentAPI, we don't want to EnsureJoined here - return intent.Client.SendStateEvent(portal.MXID, event.StateMember, intent.UserID.String(), &content) -} - func (portal *Portal) HandleWhatsAppInvite(source *User, senderJID *types.JID, jids []types.JID) (evtID id.EventID) { intent := portal.MainIntent() if senderJID != nil && !senderJID.IsEmpty() { @@ -2200,17 +2151,11 @@ func (portal *Portal) HandleWhatsAppInvite(source *User, senderJID *types.JID, j for _, jid := range jids { puppet := portal.bridge.GetPuppetByJID(jid) puppet.SyncContact(source, true, false, "handling whatsapp invite") - content := event.Content{ - Parsed: event.MemberEventContent{ - Membership: "invite", - Displayname: puppet.Displayname, - AvatarURL: puppet.AvatarURL.CUString(), - }, - Raw: map[string]interface{}{ - doublePuppetKey: doublePuppetValue, - }, - } - resp, err := intent.SendStateEvent(portal.MXID, event.StateMember, puppet.MXID.String(), &content) + resp, err := intent.SendStateEvent(portal.MXID, event.StateMember, puppet.MXID.String(), &event.MemberEventContent{ + Membership: event.MembershipInvite, + Displayname: puppet.Displayname, + AvatarURL: puppet.AvatarURL.CUString(), + }) if err != nil { portal.log.Warnfln("Failed to invite %s as %s: %v", puppet.MXID, intent.UserID, err) _ = portal.MainIntent().EnsureInvited(portal.MXID, puppet.MXID) @@ -3270,11 +3215,7 @@ func (portal *Portal) upsertReaction(intent *appservice.IntentAPI, targetJID typ portal.log.Debugfln("Redacting old Matrix reaction %s after new one (%s) was sent", dbReaction.MXID, mxid) var err error if intent != nil { - extra := make(map[string]interface{}) - if intent.IsCustomPuppet { - extra[doublePuppetKey] = doublePuppetValue - } - _, err = intent.RedactEvent(portal.MXID, dbReaction.MXID, mautrix.ReqRedact{Extra: extra}) + _, err = intent.RedactEvent(portal.MXID, dbReaction.MXID) } if intent == nil || errors.Is(err, mautrix.MForbidden) { _, err = portal.MainIntent().RedactEvent(portal.MXID, dbReaction.MXID) diff --git a/puppet.go b/puppet.go index c84cb5f..8064095 100644 --- a/puppet.go +++ b/puppet.go @@ -201,6 +201,16 @@ type Puppet struct { syncLock sync.Mutex } +var _ bridge.GhostWithProfile = (*Puppet)(nil) + +func (puppet *Puppet) GetDisplayname() string { + return puppet.Displayname +} + +func (puppet *Puppet) GetAvatarURL() id.ContentURI { + return puppet.AvatarURL +} + func (puppet *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI { if puppet.customIntent == nil || portal.Key.JID == puppet.JID || (portal.Key.JID.Server == types.BroadcastServer && portal.Key.Receiver != puppet.JID) { return puppet.DefaultIntent() diff --git a/user.go b/user.go index 91ebccd..4c0b451 100644 --- a/user.go +++ b/user.go @@ -344,18 +344,15 @@ func (user *User) doPuppetResync() { } func (user *User) ensureInvited(intent *appservice.IntentAPI, roomID id.RoomID, isDirect bool) (ok bool) { - inviteContent := event.Content{ - Parsed: &event.MemberEventContent{ - Membership: event.MembershipInvite, - IsDirect: isDirect, - }, - Raw: map[string]interface{}{}, + extraContent := make(map[string]interface{}) + if isDirect { + extraContent["is_direct"] = true } customPuppet := user.bridge.GetPuppetByCustomMXID(user.MXID) if customPuppet != nil && customPuppet.CustomIntent() != nil { - inviteContent.Raw["fi.mau.will_auto_accept"] = true + extraContent["fi.mau.will_auto_accept"] = true } - _, err := intent.SendStateEvent(roomID, event.StateMember, user.MXID.String(), &inviteContent) + _, err := intent.InviteUser(roomID, &mautrix.ReqInviteUser{UserID: user.MXID}, extraContent) var httpErr mautrix.HTTPError if err != nil && errors.As(err, &httpErr) && httpErr.RespError != nil && strings.Contains(httpErr.RespError.Err, "is already in the room") { user.bridge.StateStore.SetMembership(roomID, user.MXID, event.MembershipJoin) @@ -981,9 +978,9 @@ func (user *User) updateChatTag(intent *appservice.IntentAPI, portal *Portal, ta currentTag, ok := existingTags.Tags[tag] if active && !ok { user.log.Debugln("Adding tag", tag, "to", portal.MXID) - data := CustomTagData{"0.5", doublePuppetValue} + data := CustomTagData{Order: "0.5", DoublePuppet: user.bridge.Name} err = intent.AddTagWithCustomData(portal.MXID, tag, &data) - } else if !active && ok && currentTag.DoublePuppet == doublePuppetValue { + } else if !active && ok && currentTag.DoublePuppet == user.bridge.Name { user.log.Debugln("Removing tag", tag, "from", portal.MXID) err = intent.RemoveTag(portal.MXID, tag) } else { @@ -1210,10 +1207,10 @@ func (user *User) handleReceipt(receipt *events.Receipt) { portal.messages <- PortalMessage{receipt: receipt, source: user} } -func makeReadMarkerContent(eventID id.EventID, doublePuppet bool) CustomReadMarkers { +func (user *User) makeReadMarkerContent(eventID id.EventID, doublePuppet bool) CustomReadMarkers { var extra CustomReadReceipt if doublePuppet { - extra.DoublePuppetSource = doublePuppetValue + extra.DoublePuppetSource = user.bridge.Name } return CustomReadMarkers{ ReqSetReadMarkers: mautrix.ReqSetReadMarkers{ @@ -1235,7 +1232,7 @@ func (user *User) markSelfReadFull(portal *Portal) { return } user.SetLastReadTS(portal.Key, lastMessage.Timestamp) - err := puppet.CustomIntent().SetReadMarkers(portal.MXID, makeReadMarkerContent(lastMessage.MXID, true)) + err := puppet.CustomIntent().SetReadMarkers(portal.MXID, user.makeReadMarkerContent(lastMessage.MXID, true)) if err != nil { user.log.Warnfln("Failed to mark %s (last message) in %s as read: %v", lastMessage.MXID, portal.MXID, err) } else {