TM-SGNL-iOS/SignalServiceKit/MessageBackup/Archivers/ChatItem/ChatUpdateMessages/MessageBackupChatUpdateMessageArchiver.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

283 lines
12 KiB
Swift

//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
final class MessageBackupChatUpdateMessageArchiver: MessageBackupProtoArchiver {
typealias Details = MessageBackup.InteractionArchiveDetails
typealias ArchiveChatUpdateMessageResult = MessageBackup.ArchiveInteractionResult<Details>
typealias RestoreChatUpdateMessageResult = MessageBackup.RestoreInteractionResult<Void>
private let expirationTimerChatUpdateArchiver: MessageBackupExpirationTimerChatUpdateArchiver
private let groupCallArchiver: MessageBackupGroupCallArchiver
private let groupUpdateMessageArchiver: MessageBackupGroupUpdateMessageArchiver
private let individualCallArchiver: MessageBackupIndividualCallArchiver
private let learnedProfileChatUpdateArchiver: MessageBackupLearnedProfileChatUpdateArchiver
private let profileChangeChatUpdateArchiver: MessageBackupProfileChangeChatUpdateArchiver
private let sessionSwitchoverChatUpdateArchiver: MessageBackupSessionSwitchoverChatUpdateArchiver
private let simpleChatUpdateArchiver: MessageBackupSimpleChatUpdateArchiver
private let threadMergeChatUpdateArchiver: MessageBackupThreadMergeChatUpdateArchiver
init(
callRecordStore: any CallRecordStore,
contactManager: any MessageBackup.Shims.ContactManager,
groupCallRecordManager: any GroupCallRecordManager,
groupUpdateHelper: any GroupUpdateInfoMessageInserterBackupHelper,
groupUpdateItemBuilder: any GroupUpdateItemBuilder,
individualCallRecordManager: any IndividualCallRecordManager,
interactionStore: MessageBackupInteractionStore
) {
groupUpdateMessageArchiver = MessageBackupGroupUpdateMessageArchiver(
groupUpdateBuilder: groupUpdateItemBuilder,
groupUpdateHelper: groupUpdateHelper,
interactionStore: interactionStore
)
expirationTimerChatUpdateArchiver = MessageBackupExpirationTimerChatUpdateArchiver(
contactManager: contactManager,
groupUpdateArchiver: groupUpdateMessageArchiver,
interactionStore: interactionStore
)
groupCallArchiver = MessageBackupGroupCallArchiver(
callRecordStore: callRecordStore,
groupCallRecordManager: groupCallRecordManager,
interactionStore: interactionStore
)
individualCallArchiver = MessageBackupIndividualCallArchiver(
callRecordStore: callRecordStore,
individualCallRecordManager: individualCallRecordManager,
interactionStore: interactionStore
)
learnedProfileChatUpdateArchiver = MessageBackupLearnedProfileChatUpdateArchiver(
interactionStore: interactionStore
)
profileChangeChatUpdateArchiver = MessageBackupProfileChangeChatUpdateArchiver(
interactionStore: interactionStore
)
sessionSwitchoverChatUpdateArchiver = MessageBackupSessionSwitchoverChatUpdateArchiver(
interactionStore: interactionStore
)
simpleChatUpdateArchiver = MessageBackupSimpleChatUpdateArchiver(
interactionStore: interactionStore
)
threadMergeChatUpdateArchiver = MessageBackupThreadMergeChatUpdateArchiver(
interactionStore: interactionStore
)
}
// MARK: -
func archiveIndividualCall(
_ individualCallInteraction: TSCall,
context: MessageBackup.ChatArchivingContext
) -> ArchiveChatUpdateMessageResult {
return individualCallArchiver.archiveIndividualCall(
individualCallInteraction,
context: context
)
}
func archiveGroupCall(
_ groupCallInteraction: OWSGroupCallMessage,
context: MessageBackup.ChatArchivingContext
) -> ArchiveChatUpdateMessageResult {
return groupCallArchiver.archiveGroupCall(
groupCallInteraction,
context: context
)
}
func archiveErrorMessage(
_ errorMessage: TSErrorMessage,
context: MessageBackup.ChatArchivingContext
) -> ArchiveChatUpdateMessageResult {
/// All `TSErrorMessage`s map to simple chat updates.
return simpleChatUpdateArchiver.archiveSimpleChatUpdate(
errorMessage: errorMessage,
context: context
)
}
func archiveInfoMessage(
_ infoMessage: TSInfoMessage,
threadInfo: MessageBackup.ChatArchivingContext.CachedThreadInfo,
context: MessageBackup.ChatArchivingContext
) -> ArchiveChatUpdateMessageResult {
switch infoMessage.groupUpdateMetadata(
localIdentifiers: context.recipientContext.localIdentifiers
) {
case .legacyRawString:
// These will be dropped by the group update message archiver.
fallthrough
case .precomputed, .modelDiff, .newGroup:
return groupUpdateMessageArchiver.archiveGroupUpdate(
infoMessage: infoMessage,
context: context
)
case .nonGroupUpdate:
break
}
switch infoMessage.messageType {
case .typeGroupUpdate:
return .skippableChatUpdate(.skippableGroupUpdate(.missingUpdateMetadata))
case .userNotRegistered:
return .skippableChatUpdate(.legacyInfoMessage(.userNotRegistered))
case .typeUnsupportedMessage:
return .skippableChatUpdate(.legacyInfoMessage(.typeUnsupportedMessage))
case .typeGroupQuit:
return .skippableChatUpdate(.legacyInfoMessage(.typeGroupQuit))
case .addToContactsOffer:
return .skippableChatUpdate(.legacyInfoMessage(.addToContactsOffer))
case .addUserToProfileWhitelistOffer:
return .skippableChatUpdate(.legacyInfoMessage(.addUserToProfileWhitelistOffer))
case .addGroupToProfileWhitelistOffer:
return .skippableChatUpdate(.legacyInfoMessage(.addGroupToProfileWhitelistOffer))
case .syncedThread:
return .skippableChatUpdate(.legacyInfoMessage(.syncedThread))
case .recipientHidden:
/// This info message type is handled specially.
return .skippableChatUpdate(.contactHiddenInfoMessage)
case
.verificationStateChange,
.typeLocalUserEndedSession,
.typeRemoteUserEndedSession,
.unknownProtocolVersion,
.userJoinedSignal,
.phoneNumberChange,
.paymentsActivationRequest,
.paymentsActivated,
.reportedSpam,
.blockedOtherUser,
.blockedGroup,
.unblockedOtherUser,
.unblockedGroup,
.acceptedMessageRequest:
/// These info message types map to simple chat updates.
return simpleChatUpdateArchiver.archiveSimpleChatUpdate(
infoMessage: infoMessage,
threadInfo: threadInfo,
context: context
)
case .profileUpdate:
return profileChangeChatUpdateArchiver.archive(
infoMessage: infoMessage,
context: context
)
case .typeDisappearingMessagesUpdate:
return expirationTimerChatUpdateArchiver.archiveExpirationTimerChatUpdate(
infoMessage: infoMessage,
threadInfo: threadInfo,
context: context
)
case .threadMerge:
return threadMergeChatUpdateArchiver.archiveThreadMergeChatUpdate(
infoMessage: infoMessage,
threadInfo: threadInfo,
context: context
)
case .sessionSwitchover:
return sessionSwitchoverChatUpdateArchiver.archiveSessionSwitchoverChatUpdate(
infoMessage: infoMessage,
threadInfo: threadInfo,
context: context
)
case .learnedProfileName:
return learnedProfileChatUpdateArchiver.archiveLearnedProfileChatUpdate(
infoMessage: infoMessage,
context: context
)
}
}
// MARK: -
func restoreChatItem(
_ chatItem: BackupProto_ChatItem,
chatThread: MessageBackup.ChatThread,
context: MessageBackup.ChatItemRestoringContext
) -> RestoreChatUpdateMessageResult {
let chatUpdateMessage: BackupProto_ChatUpdateMessage
do {
switch chatItem.item {
case .updateMessage(let updateMessage):
chatUpdateMessage = updateMessage
default:
return .messageFailure([.restoreFrameError(
.developerError(OWSAssertionError("Non-chat update!")),
chatItem.id
)])
}
}
switch chatUpdateMessage.update {
case nil:
return .messageFailure([.restoreFrameError(
.invalidProtoData(.emptyChatUpdateMessage),
chatItem.id
)])
case .groupChange(let groupChangeChatUpdateProto):
return groupUpdateMessageArchiver.restoreGroupUpdate(
groupChangeChatUpdateProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .individualCall(let individualCallProto):
return individualCallArchiver.restoreIndividualCall(
individualCallProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .groupCall(let groupCallProto):
return groupCallArchiver.restoreGroupCall(
groupCallProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .simpleUpdate(let simpleChatUpdateProto):
return simpleChatUpdateArchiver.restoreSimpleChatUpdate(
simpleChatUpdateProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .expirationTimerChange(let expirationTimerUpdateProto):
return expirationTimerChatUpdateArchiver.restoreExpirationTimerChatUpdate(
expirationTimerUpdateProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .profileChange(let profileChangeUpdateProto):
return profileChangeChatUpdateArchiver.restoreProfileChangeChatUpdate(
profileChangeUpdateProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .threadMerge(let threadMergeUpdateProto):
return threadMergeChatUpdateArchiver.restoreThreadMergeChatUpdate(
threadMergeUpdateProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .sessionSwitchover(let sessionSwitchoverUpdateProto):
return sessionSwitchoverChatUpdateArchiver.restoreSessionSwitchoverChatUpdate(
sessionSwitchoverUpdateProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
case .learnedProfileChange(let learnedProfileChangeProto):
return learnedProfileChatUpdateArchiver.restoreLearnedProfileChatUpdate(
learnedProfileChangeProto,
chatItem: chatItem,
chatThread: chatThread,
context: context
)
}
}
}