Fix duplicate messages & debug

This commit is contained in:
nielsandriesse 2020-11-27 15:13:42 +11:00
parent e3304a40f9
commit addc859c84
9 changed files with 56 additions and 15 deletions

View File

@ -377,7 +377,6 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel
}
guard let initialDetailItem = galleryItem else {
owsFailDebug("unexpectedly failed to build initialDetailItem.")
return
}

View File

@ -61,5 +61,26 @@ extension Storage {
guard let tsIncomingMessage = TSIncomingMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction) else { return }
tsIncomingMessage.touch(with: transaction)
}
private static let receivedMessageTimestampsCollection = "ReceivedMessageTimestampsCollection"
public func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] {
var result: [UInt64] = []
let transaction = transaction as! YapDatabaseReadWriteTransaction
transaction.enumerateRows(inCollection: Storage.receivedMessageTimestampsCollection) { _, object, _, _ in
guard let timestamp = object as? UInt64 else { return }
result.append(timestamp)
}
return result
}
public func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {
var receivedMessageTimestamps = getReceivedMessageTimestamps(using: transaction)
// TODO: Do we need to sort the timestamps here?
if receivedMessageTimestamps.count > 1000 { receivedMessageTimestamps.remove(at: 0) } // Limit the size of the collection to 1000
receivedMessageTimestamps.append(timestamp)
let transaction = transaction as! YapDatabaseReadWriteTransaction
transaction.setObject(receivedMessageTimestamps, forKey: "receivedMessageTimestamps", inCollection: Storage.receivedMessageTimestampsCollection)
}
}

View File

@ -78,7 +78,6 @@ public final class MessageSendJob : NSObject, Job, NSCoding { // NSObject/NSCodi
}
if !attachmentsToUpload.isEmpty { return } // Wait for all attachments to upload before continuing
}
// FIXME: This doesn't yet handle the attachment side of link previews, quotes, etc.
storage.withAsync({ transaction in // Intentionally capture self
MessageSender.send(self.message, to: self.destination, using: transaction).done(on: DispatchQueue.global(qos: .userInitiated)) {
self.handleSuccess()

View File

@ -46,12 +46,12 @@ public extension VisibleMessage {
}
public func toProto(using transaction: YapDatabaseReadWriteTransaction) -> SNProtoDataMessageQuote? {
guard let timestamp = timestamp, let publicKey = publicKey, let text = text else {
guard let timestamp = timestamp, let publicKey = publicKey else {
SNLog("Couldn't construct quote proto from: \(self).")
return nil
}
let quoteProto = SNProtoDataMessageQuote.builder(id: timestamp, author: publicKey)
quoteProto.setText(text)
if let text = text { quoteProto.setText(text) }
addAttachmentsIfNeeded(to: quoteProto, using: transaction)
do {
return try quoteProto.build()

View File

@ -25,12 +25,20 @@ public final class VisibleMessage : Message {
super.init(coder: coder)
if let text = coder.decodeObject(forKey: "body") as! String? { self.text = text }
if let attachmentIDs = coder.decodeObject(forKey: "attachments") as! [String]? { self.attachmentIDs = attachmentIDs }
if let quote = coder.decodeObject(forKey: "quote") as! Quote? { self.quote = quote }
if let linkPreview = coder.decodeObject(forKey: "linkPreview") as! LinkPreview? { self.linkPreview = linkPreview }
// TODO: Contact
if let profile = coder.decodeObject(forKey: "profile") as! Profile? { self.profile = profile }
}
public override func encode(with coder: NSCoder) {
super.encode(with: coder)
coder.encode(text, forKey: "body")
coder.encode(attachmentIDs, forKey: "attachments")
coder.encode(quote, forKey: "quote")
coder.encode(linkPreview, forKey: "linkPreview")
// TODO: Contact
coder.encode(profile, forKey: "profile")
}
// MARK: Proto Conversion

View File

@ -160,14 +160,23 @@ extension MessageReceiver {
// Persist the message
guard let (threadID, tsIncomingMessageID) = storage.persist(message, groupPublicKey: message.groupPublicKey, using: transaction) else { throw Error.noThread }
message.threadID = threadID
// Handle quoted attachment if needed
if message.quote != nil && proto.dataMessage?.quote != nil, let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) {
let tsQuote = TSQuotedMessage(for: proto.dataMessage!, thread: thread, transaction: transaction)
if let thumbnailID = tsQuote?.thumbnailAttachmentStreamId() ?? tsQuote?.thumbnailAttachmentPointerId() {
message.quote?.attachmentID = thumbnailID
}
}
// Start attachment downloads if needed
storage.withAsync({ transaction in
attachmentIDs.forEach { attachmentID in
let downloadJob = AttachmentDownloadJob(attachmentID: attachmentID, tsIncomingMessageID: tsIncomingMessageID)
if CurrentAppContext().isMainAppAndActive {
JobQueue.shared.add(downloadJob, using: transaction)
} else {
JobQueue.shared.addWithoutExecuting(downloadJob, using: transaction)
DispatchQueue.main.async {
attachmentIDs.forEach { attachmentID in
let downloadJob = AttachmentDownloadJob(attachmentID: attachmentID, tsIncomingMessageID: tsIncomingMessageID)
if CurrentAppContext().isMainAppAndActive { // This has to be called from the main thread
JobQueue.shared.add(downloadJob, using: transaction)
} else {
JobQueue.shared.addWithoutExecuting(downloadJob, using: transaction)
}
}
}
}, completion: { })

View File

@ -3,6 +3,7 @@ import SessionUtilitiesKit
internal enum MessageReceiver {
internal enum Error : LocalizedError {
case duplicateMessage
case invalidMessage
case unknownMessage
case unknownEnvelopeType
@ -18,13 +19,14 @@ internal enum MessageReceiver {
internal var isRetryable: Bool {
switch self {
case .invalidMessage, .unknownMessage, .unknownEnvelopeType, .noData, .senderBlocked, .selfSend: return false
case .duplicateMessage, .invalidMessage, .unknownMessage, .unknownEnvelopeType, .noData, .senderBlocked, .selfSend: return false
default: return true
}
}
internal var errorDescription: String? {
switch self {
case .duplicateMessage: return "Duplicate message."
case .invalidMessage: return "Invalid message."
case .unknownMessage: return "Unknown message type."
case .unknownEnvelopeType: return "Unknown envelope type."
@ -45,6 +47,9 @@ internal enum MessageReceiver {
let userPublicKey = Configuration.shared.storage.getUserPublicKey()
// Parse the envelope
let envelope = try SNProtoEnvelope.parseData(data)
let storage = Configuration.shared.storage
guard !Set(storage.getReceivedMessageTimestamps(using: transaction)).contains(envelope.timestamp) else { throw Error.duplicateMessage }
storage.addReceivedMessageTimestamp(envelope.timestamp, using: transaction)
// Decrypt the contents
let plaintext: Data
let sender: String

View File

@ -71,6 +71,8 @@ public protocol SessionMessagingKitStorageProtocol {
// MARK: - Message Handling
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64]
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any)
/// Returns the ID of the thread the message was stored under along with the ID of the `TSIncomingMessage` that was constructed.
func persist(_ message: VisibleMessage, groupPublicKey: String?, using transaction: Any) -> (String, String)?
/// Returns the IDs of the saved attachments.

View File

@ -20,16 +20,14 @@ extension MessageSender : SharedSenderKeysDelegate {
streams.append(stream)
stream.write($0.dataSource)
stream.save(with: transaction)
tsMessage.attachmentIds.add(stream.uniqueId!)
}
if let quotedMessageThumbnails = tsMessage.quotedMessage?.createThumbnailAttachmentsIfNecessary(with: transaction) {
streams += quotedMessageThumbnails
}
tsMessage.quotedMessage?.createThumbnailAttachmentsIfNecessary(with: transaction)
if let linkPreviewAttachmentID = tsMessage.linkPreview?.imageAttachmentId,
let stream = TSAttachment.fetch(uniqueId: linkPreviewAttachmentID, transaction: transaction) as? TSAttachmentStream {
streams.append(stream)
}
message.attachmentIDs = streams.map { $0.uniqueId! }
tsMessage.attachmentIds.addObjects(from: message.attachmentIDs)
tsMessage.save(with: transaction)
}