diff --git a/historysync.go b/historysync.go index e94afdc..b1ad056 100644 --- a/historysync.go +++ b/historysync.go @@ -145,27 +145,29 @@ type messageIndex struct { Index int } -func (portal *Portal) convertBackfill(messages []*imessage.Message) ([]*event.Event, []messageWithIndex, map[messageIndex]int, bool, *imessage.Message, error) { +func (portal *Portal) convertBackfill(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 - var lastMessage *imessage.Message for _, msg := range messages { - if msg.Tapback != nil { - portal.log.Debugln("Skipping tapback", msg.GUID, "in backfill, handling later") + 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 } - //Skip the last message in the array, we will add it later to correct inbox sorting - if msg == messages[len(messages)-1] && len(messages) > 1 /* we call this function again with one element in the array for the last message, so we'll want to process it */ { - portal.log.Errorln("Skipping message", msg.GUID, "in backfill, last one in the convo") - lastMessage = msg + 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{ @@ -180,7 +182,7 @@ func (portal *Portal) convertBackfill(messages []*imessage.Message) ([]*event.Ev var err error evt.Type, err = portal.encrypt(intent, &evt.Content, evt.Type) if err != nil { - return nil, nil, nil, false, nil, err + return nil, nil, nil, false, err } intent.AddDoublePuppetValue(&evt.Content) if portal.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareHungry { @@ -193,70 +195,7 @@ func (portal *Portal) convertBackfill(messages []*imessage.Message) ([]*event.Ev } isRead = msg.IsRead || msg.IsFromMe || (unreadThreshold >= 0 && time.Since(msg.Time) > unreadThreshold) } - return events, metas, metaIndexes, isRead, lastMessage, nil -} - -func (portal *Portal) convertTapbacks(messages []*imessage.Message) ([]*event.Event, []messageWithIndex, map[messageIndex]int, bool, *imessage.Message, 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 - var lastMessage *imessage.Message - for _, msg := range messages { - //Only want tapbacks - if msg.Tapback == nil { - portal.log.Debugln("Skipping message", msg.GUID, "in backfill, should've already handled") - continue - } - - //Skip the last message in the array, we will add it later to correct inbox sorting - if msg == messages[len(messages)-1] && len(messages) > 1 /* we call this function again with one element in the array for the last message, so we'll want to process it */ { - portal.log.Errorln("Skipping message", msg.GUID, "in backfill, last one in the convo") - lastMessage = msg - 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(), - }, - }, - }, - } - - var err error - evt.Type, err = portal.encrypt(intent, &evt.Content, evt.Type) - if err != nil { - return nil, nil, nil, false, nil, err - } - 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, lastMessage, nil + return events, metas, metaIndexes, isRead, nil } func (portal *Portal) sendBackfill(backfillID string, messages []*imessage.Message, forward, forwardIfNoMessages, markAsRead bool) (success bool) { @@ -273,106 +212,16 @@ 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) - - 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 - if msg.Tapback.Remove { - portal.log.Debugln("Skipping", msg.GUID, "in backfill (it was a remove tapback)") - continue - } - } - - validMessages = append(validMessages, msg) - } - - events, metas, metaIndexes, isRead, lastMessage, err := portal.convertBackfill(validMessages) + events, metas, metaIndexes, isRead, err := portal.convertBackfill(messages) if err != nil { portal.log.Errorfln("Failed to convert messages for backfill: %v", err) return false } - portal.log.Debugfln("Converted %d messages into %d message 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) - - //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, lastTapback, 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)) + portal.log.Debugfln("Converted %d messages into %d 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) - - //Process the last message in the conversation that we skipped before in converting, - // this is dumb but Beeper sorts its inbox on when the event was recieved and not the timestamp - var lastMessageArray []*imessage.Message - if lastMessage != nil { - lastMessageArray = append(lastMessageArray, lastMessage) - events, metas, metaIndexes, isRead, _, err = portal.convertBackfill(lastMessageArray) - } else if lastTapback != nil { - lastMessageArray = append(lastMessageArray, lastTapback) - events, metas, metaIndexes, isRead, _, err = portal.convertTapbacks(lastMessageArray) - } - if err != nil { - portal.log.Errorfln("Failed to convert last message for backfill: %v", err) - return false - } - - 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) { + var eventIDs []id.EventID if batchSending { req := &mautrix.ReqBeeperBatchSend{ Events: events, @@ -385,7 +234,7 @@ func (portal *Portal) sendBackfillToMatrixServer(batchSending, forward, forwardI resp, err := portal.MainIntent().BeeperBatchSend(portal.MXID, req) if err != nil { portal.log.Errorln("Failed to batch send history:", err) - return nil, err + return false } eventIDs = resp.EventIDs } else { @@ -402,7 +251,7 @@ func (portal *Portal) sendBackfillToMatrixServer(batchSending, forward, forwardI 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 nil, err + return false } eventIDs[i] = resp.EventID } @@ -414,24 +263,42 @@ func (portal *Portal) sendBackfillToMatrixServer(batchSending, forward, forwardI } } } - return eventIDs, nil + 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 } 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 { - continue + // 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) } - 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 diff --git a/imessage/bluebubbles/api.go b/imessage/bluebubbles/api.go index df67fa6..302ff97 100644 --- a/imessage/bluebubbles/api.go +++ b/imessage/bluebubbles/api.go @@ -1681,7 +1681,7 @@ func (bb *blueBubbles) convertBBMessageToiMessage(bbMessage Message) (*imessage. bbMessage.AssociatedMessageType != "" { message.Tapback = &imessage.Tapback{ TargetGUID: bbMessage.AssociatedMessageGUID, - Type: bb.convertBBTapbackToImessageTapback(bbMessage.AssociatedMessageType), + Type: imessage.TapbackFromName(bbMessage.AssociatedMessageType), } message.Tapback.Parse() } else { @@ -1819,27 +1819,6 @@ func IsUrl(str string) bool { return true } -func (bb *blueBubbles) convertBBTapbackToImessageTapback(associatedMessageType string) (tbType imessage.TapbackType) { - if strings.Contains(associatedMessageType, "love") { - tbType = imessage.TapbackLove - } else if strings.Contains(associatedMessageType, "like") { - tbType = imessage.TapbackLike - } else if strings.Contains(associatedMessageType, "dislike") { - tbType = imessage.TapbackDislike - } 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))