Skip to content

Commit

Permalink
Backfill Tapbacks/Reactions & BlueBubbles Fix for Removing a Reaction (
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuafhiggins authored Jun 1, 2024
1 parent 55d53d3 commit e37cea9
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 47 deletions.
179 changes: 133 additions & 46 deletions historysync.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,22 +152,11 @@ func (portal *Portal) convertBackfill(messages []*imessage.Message) ([]*event.Ev
unreadThreshold := time.Duration(portal.bridge.Config.Bridge.Backfill.UnreadHoursThreshold) * time.Hour
var isRead bool
for _, msg := range messages {
if msg.ItemType != imessage.ItemTypeMessage && msg.Tapback == nil {
portal.log.Debugln("Skipping", msg.GUID, "in backfill (not a message)")
continue
}
intent := portal.getIntentForMessage(msg, nil)
if intent == nil {
portal.log.Debugln("Skipping", msg.GUID, "in backfill (didn't get an intent)")
continue
}

if msg.Tapback != nil {
// TODO handle tapbacks
portal.log.Debugln("Skipping tapback", msg.GUID, "in backfill")
continue
}

intent := portal.getIntentForMessage(msg, nil)
converted := portal.convertiMessage(msg, intent)
for index, conv := range converted {
evt := &event.Event{
Expand Down Expand Up @@ -198,6 +187,55 @@ func (portal *Portal) convertBackfill(messages []*imessage.Message) ([]*event.Ev
return events, metas, metaIndexes, isRead, nil
}

func (portal *Portal) convertTapbacks(messages []*imessage.Message) ([]*event.Event, []messageWithIndex, map[messageIndex]int, bool, error) {
events := make([]*event.Event, 0, len(messages))
metas := make([]messageWithIndex, 0, len(messages))
metaIndexes := make(map[messageIndex]int, len(messages))
unreadThreshold := time.Duration(portal.bridge.Config.Bridge.Backfill.UnreadHoursThreshold) * time.Hour
var isRead bool
for _, msg := range messages {
//Only want tapbacks
if msg.Tapback == nil {
continue
}

intent := portal.getIntentForMessage(msg, nil)
dbMessage := portal.bridge.DB.Message.GetByGUID(portal.GUID, msg.Tapback.TargetGUID, msg.Tapback.TargetPart)
if dbMessage == nil {
//TODO BUG: This occurs when trying to find the target reaction for a rich link, related to #183
portal.log.Errorfln("Failed to get target message for tabpack, %+v", msg)
continue
}

evt := &event.Event{
Sender: intent.UserID,
Type: event.EventReaction,
Timestamp: msg.Time.UnixMilli(),
Content: event.Content{
Parsed: &event.ReactionEventContent{
RelatesTo: event.RelatesTo{
Type: event.RelAnnotation,
EventID: dbMessage.MXID,
Key: msg.Tapback.Type.Emoji(),
},
},
},
}

intent.AddDoublePuppetValue(&evt.Content)
if portal.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareHungry {
evt.ID = portal.deterministicEventID(msg.GUID, 0)
}

events = append(events, evt)
metas = append(metas, messageWithIndex{msg, intent, dbMessage, 0})
metaIndexes[messageIndex{msg.GUID, 0}] = len(metas)

isRead = msg.IsRead || msg.IsFromMe || (unreadThreshold >= 0 && time.Since(msg.Time) > unreadThreshold)
}
return events, metas, metaIndexes, isRead, nil
}

func (portal *Portal) sendBackfill(backfillID string, messages []*imessage.Message, forward, forwardIfNoMessages, markAsRead bool) (success bool) {
idMap := make(map[string][]id.EventID, len(messages))
for _, msg := range messages {
Expand All @@ -212,16 +250,83 @@ func (portal *Portal) sendBackfill(backfillID string, messages []*imessage.Messa
portal.bridge.IM.SendBackfillResult(portal.GUID, backfillID, success, idMap)
}()
batchSending := portal.bridge.SpecVersions.Supports(mautrix.BeeperFeatureBatchSending)
events, metas, metaIndexes, isRead, err := portal.convertBackfill(messages)

var validMessages []*imessage.Message
for _, msg := range messages {
if msg.ItemType != imessage.ItemTypeMessage && msg.Tapback == nil {
portal.log.Debugln("Skipping", msg.GUID, "in backfill (not a message)")
continue
}
intent := portal.getIntentForMessage(msg, nil)
if intent == nil {
portal.log.Debugln("Skipping", msg.GUID, "in backfill (didn't get an intent)")
continue
}
if msg.Tapback != nil && msg.Tapback.Remove {
//If we don't process it, there won't be a reaction; at least for BB, we never have to remove a reaction
portal.log.Debugln("Skipping", msg.GUID, "in backfill (it was a remove tapback)")
continue
}

validMessages = append(validMessages, msg)
}

events, metas, metaIndexes, isRead, err := portal.convertBackfill(validMessages)
if err != nil {
portal.log.Errorfln("Failed to convert messages for backfill: %v", err)
return false
}
portal.log.Debugfln("Converted %d messages into %d events to backfill", len(messages), len(events))
portal.log.Debugfln("Converted %d messages into %d message events to backfill", len(messages), len(events))
if len(events) == 0 {
return true
}
var eventIDs []id.EventID

eventIDs, sendErr := portal.sendBackfillToMatrixServer(batchSending, forward, forwardIfNoMessages, markAsRead, isRead, events, metas, metaIndexes)
if sendErr != nil {
return false
}
portal.addBackfillToDB(metas, eventIDs, idMap, backfillID)

//We have to process tapbacks after all other messages because we need texts in the DB in order to target them
events, metas, metaIndexes, isRead, err = portal.convertTapbacks(validMessages)
if err != nil {
portal.log.Errorfln("Failed to convert tapbacks for backfill: %v", err)
return false
}
portal.log.Debugfln("Converted %d messages into %d tapbacks events to backfill", len(messages), len(events))
if len(events) == 0 {
return true
}

eventIDs, sendErr = portal.sendBackfillToMatrixServer(batchSending, forward, forwardIfNoMessages, markAsRead, isRead, events, metas, metaIndexes)
if sendErr != nil {
return false
}
portal.addBackfillToDB(metas, eventIDs, idMap, backfillID)

portal.log.Infofln("Finished backfill %s", backfillID)
return true
}

func (portal *Portal) addBackfillToDB(metas []messageWithIndex, eventIDs []id.EventID, idMap map[string][]id.EventID, backfillID string) {
for i, meta := range metas {
idMap[meta.GUID] = append(idMap[meta.GUID], eventIDs[i])
}
txn, err := portal.bridge.DB.Begin()
if err != nil {
portal.log.Errorln("Failed to start transaction to save batch messages:", err)
}
portal.log.Debugfln("Inserting %d event IDs to database to finish backfill %s", len(eventIDs), backfillID)
portal.finishBackfill(txn, eventIDs, metas)
portal.Update(txn)
err = txn.Commit()
if err != nil {
portal.log.Errorln("Failed to commit transaction to save batch messages:", err)
}
}

func (portal *Portal) sendBackfillToMatrixServer(batchSending, forward, forwardIfNoMessages, markAsRead, isRead bool, events []*event.Event, metas []messageWithIndex,
metaIndexes map[messageIndex]int) (eventIDs []id.EventID, err error) {
if batchSending {
req := &mautrix.ReqBeeperBatchSend{
Events: events,
Expand All @@ -234,7 +339,7 @@ func (portal *Portal) sendBackfill(backfillID string, messages []*imessage.Messa
resp, err := portal.MainIntent().BeeperBatchSend(portal.MXID, req)
if err != nil {
portal.log.Errorln("Failed to batch send history:", err)
return false
return nil, err
}
eventIDs = resp.EventIDs
} else {
Expand All @@ -251,7 +356,7 @@ func (portal *Portal) sendBackfill(backfillID string, messages []*imessage.Messa
resp, err := meta.Intent.SendMassagedMessageEvent(portal.MXID, evt.Type, &evt.Content, evt.Timestamp)
if err != nil {
portal.log.Errorfln("Failed to send event #%d in history: %v", i, err)
return false
return nil, err
}
eventIDs[i] = resp.EventID
}
Expand All @@ -263,42 +368,24 @@ func (portal *Portal) sendBackfill(backfillID string, messages []*imessage.Messa
}
}
}
for i, meta := range metas {
idMap[meta.GUID] = append(idMap[meta.GUID], eventIDs[i])
}
txn, err := portal.bridge.DB.Begin()
if err != nil {
portal.log.Errorln("Failed to start transaction to save batch messages:", err)
return true
}
portal.log.Debugfln("Inserting %d event IDs to database to finish backfill %s", len(eventIDs), backfillID)
portal.finishBackfill(txn, eventIDs, metas)
portal.Update(txn)
err = txn.Commit()
if err != nil {
portal.log.Errorln("Failed to commit transaction to save batch messages:", err)
}
portal.log.Infofln("Finished backfill %s", backfillID)
return true
return eventIDs, nil
}

func (portal *Portal) finishBackfill(txn dbutil.Transaction, eventIDs []id.EventID, metas []messageWithIndex) {
for i, info := range metas {
if info.Tapback != nil {
if info.Tapback.Remove {
// TODO handle removing tapbacks?
} else {
// TODO can existing tapbacks be modified in backfill?
dbTapback := portal.bridge.DB.Tapback.New()
dbTapback.PortalGUID = portal.GUID
dbTapback.SenderGUID = info.Sender.String()
dbTapback.MessageGUID = info.TapbackTarget.GUID
dbTapback.MessagePart = info.TapbackTarget.Part
dbTapback.GUID = info.GUID
dbTapback.Type = info.Tapback.Type
dbTapback.MXID = eventIDs[i]
dbTapback.Insert(txn)
continue
}
dbTapback := portal.bridge.DB.Tapback.New()
dbTapback.PortalGUID = portal.GUID
dbTapback.SenderGUID = info.Sender.String()
dbTapback.MessageGUID = info.TapbackTarget.GUID
dbTapback.MessagePart = info.TapbackTarget.Part
dbTapback.GUID = info.GUID
dbTapback.Type = info.Tapback.Type
dbTapback.MXID = eventIDs[i]
dbTapback.Insert(txn)
} else {
dbMessage := portal.bridge.DB.Message.New()
dbMessage.PortalGUID = portal.GUID
Expand Down
23 changes: 22 additions & 1 deletion imessage/bluebubbles/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,7 @@ func (bb *blueBubbles) convertBBMessageToiMessage(bbMessage Message) (*imessage.
bbMessage.AssociatedMessageType != "" {
message.Tapback = &imessage.Tapback{
TargetGUID: bbMessage.AssociatedMessageGUID,
Type: imessage.TapbackFromName(bbMessage.AssociatedMessageType),
Type: bb.convertBBTapbackToImessageTapback(bbMessage.AssociatedMessageType),
}
message.Tapback.Parse()
} else {
Expand Down Expand Up @@ -1713,6 +1713,27 @@ func (bb *blueBubbles) convertBBMessageToiMessage(bbMessage Message) (*imessage.
return &message, nil
}

func (bb *blueBubbles) convertBBTapbackToImessageTapback(associatedMessageType string) (tbType imessage.TapbackType) {
if strings.Contains(associatedMessageType, "love") {
tbType = imessage.TapbackLove
} else if strings.Contains(associatedMessageType, "dislike") {
tbType = imessage.TapbackDislike
} else if strings.Contains(associatedMessageType, "like") {
tbType = imessage.TapbackLike
} else if strings.Contains(associatedMessageType, "laugh") {
tbType = imessage.TapbackLaugh
} else if strings.Contains(associatedMessageType, "emphasize") {
tbType = imessage.TapbackEmphasis
} else if strings.Contains(associatedMessageType, "question") {
tbType = imessage.TapbackQuestion
}

if strings.Contains(associatedMessageType, "-") {
tbType += imessage.TapbackRemoveOffset
}
return tbType
}

func (bb *blueBubbles) convertBBChatToiMessageChat(bbChat Chat) (*imessage.ChatInfo, error) {
members := make([]string, len(bbChat.Participants))

Expand Down

0 comments on commit e37cea9

Please sign in to comment.