ソースを参照

Move most double puppet source key adding to mautrix-go

Tulir Asokan 3 年 前
コミット
bf4c01648f
6 ファイル変更51 行追加120 行削除
  1. 1 1
      go.mod
  2. 2 2
      go.sum
  3. 1 18
      historysync.go
  4. 27 86
      portal.go
  5. 10 0
      puppet.go
  6. 10 13
      user.go

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

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

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

+ 27 - 86
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)

+ 10 - 0
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()

+ 10 - 13
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 {