Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 33 additions & 17 deletions Sources/IMsgCore/MessageStore+Messages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ private struct MessageRowColumns {
let date: Int
let isFromMe: Int
let service: Int
let chatStyle: Int
let isAudioMessage: Int
let destinationCallerID: Int
let guid: Int
Expand All @@ -29,6 +30,7 @@ private struct DecodedMessageRow {
let date: Date
let isFromMe: Bool
let service: String
let isGroup: Bool
let destinationCallerID: String
let guid: String
let associatedGUID: String
Expand All @@ -49,6 +51,8 @@ extension MessageStore {
let associatedTypeColumn = hasReactionColumns ? "m.associated_message_type" : "NULL"
let destinationCallerColumn = hasDestinationCallerID ? "m.destination_caller_id" : "NULL"
let audioMessageColumn = hasAudioMessageColumn ? "m.is_audio_message" : "0"
let chatStyleColumn = hasChatStyleColumn ? "c.style" : "NULL"
let chatJoin = hasChatStyleColumn ? "JOIN chat c ON cmj.chat_id = c.ROWID" : ""
let threadOriginatorColumn =
hasThreadOriginatorGUIDColumn ? "m.thread_originator_guid" : "NULL"
let reactionFilter =
Expand All @@ -57,13 +61,15 @@ extension MessageStore {
: ""
var sql = """
SELECT m.ROWID, m.handle_id, h.id, IFNULL(m.text, '') AS text, m.date, m.is_from_me, m.service,
\(chatStyleColumn) AS chat_style,
\(audioMessageColumn) AS is_audio_message, \(destinationCallerColumn) AS destination_caller_id,
\(guidColumn) AS guid, \(associatedGuidColumn) AS associated_guid, \(associatedTypeColumn) AS associated_type,
(SELECT COUNT(*) FROM message_attachment_join maj WHERE maj.message_id = m.ROWID) AS attachments,
\(bodyColumn) AS body,
\(threadOriginatorColumn) AS thread_originator_guid
FROM message m
JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
\(chatJoin)
LEFT JOIN handle h ON m.handle_id = h.ROWID
WHERE cmj.chat_id = ?\(reactionFilter)
"""
Expand Down Expand Up @@ -101,14 +107,15 @@ extension MessageStore {
date: 4,
isFromMe: 5,
service: 6,
isAudioMessage: 7,
destinationCallerID: 8,
guid: 9,
associatedGUID: 10,
associatedType: 11,
attachments: 12,
body: 13,
threadOriginatorGUID: 14
chatStyle: 7,
isAudioMessage: 8,
destinationCallerID: 9,
guid: 10,
associatedGUID: 11,
associatedType: 12,
attachments: 13,
body: 14,
threadOriginatorGUID: 15
)

return try withConnection { db in
Expand All @@ -128,6 +135,7 @@ extension MessageStore {
date: decoded.date,
isFromMe: decoded.isFromMe,
service: decoded.service,
isGroup: decoded.isGroup,
handleID: decoded.handleID,
attachmentsCount: decoded.attachments,
guid: decoded.guid,
Expand Down Expand Up @@ -165,6 +173,8 @@ extension MessageStore {
let associatedTypeColumn = hasReactionColumns ? "m.associated_message_type" : "NULL"
let destinationCallerColumn = hasDestinationCallerID ? "m.destination_caller_id" : "NULL"
let audioMessageColumn = hasAudioMessageColumn ? "m.is_audio_message" : "0"
let chatStyleColumn = hasChatStyleColumn ? "c.style" : "NULL"
let chatJoin = hasChatStyleColumn ? "LEFT JOIN chat c ON cmj.chat_id = c.ROWID" : ""
let balloonBundleIDColumn = hasBalloonBundleIDColumn ? "m.balloon_bundle_id" : "NULL"
let threadOriginatorColumn =
hasThreadOriginatorGUIDColumn ? "m.thread_originator_guid" : "NULL"
Expand All @@ -182,6 +192,7 @@ extension MessageStore {
}
var sql = """
SELECT m.ROWID, cmj.chat_id, m.handle_id, h.id, IFNULL(m.text, '') AS text, m.date, m.is_from_me, m.service,
\(chatStyleColumn) AS chat_style,
\(audioMessageColumn) AS is_audio_message, \(destinationCallerColumn) AS destination_caller_id,
\(guidColumn) AS guid, \(associatedGuidColumn) AS associated_guid, \(associatedTypeColumn) AS associated_type,
(SELECT COUNT(*) FROM message_attachment_join maj WHERE maj.message_id = m.ROWID) AS attachments,
Expand All @@ -190,6 +201,7 @@ extension MessageStore {
\(balloonBundleIDColumn) AS balloon_bundle_id
FROM message m
LEFT JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
\(chatJoin)
LEFT JOIN handle h ON m.handle_id = h.ROWID
WHERE m.ROWID > ?\(reactionFilter)
"""
Expand All @@ -209,17 +221,18 @@ extension MessageStore {
date: 5,
isFromMe: 6,
service: 7,
isAudioMessage: 8,
destinationCallerID: 9,
guid: 10,
associatedGUID: 11,
associatedType: 12,
attachments: 13,
body: 14,
threadOriginatorGUID: 15
chatStyle: 8,
isAudioMessage: 9,
destinationCallerID: 10,
guid: 11,
associatedGUID: 12,
associatedType: 13,
attachments: 14,
body: 15,
threadOriginatorGUID: 16
)

let balloonBundleIDIndex = 16
let balloonBundleIDIndex = 17

return try withConnection { db in
var messages: [Message] = []
Expand Down Expand Up @@ -260,6 +273,7 @@ extension MessageStore {
date: decoded.date,
isFromMe: decoded.isFromMe,
service: decoded.service,
isGroup: decoded.isGroup,
handleID: decoded.handleID,
attachmentsCount: decoded.attachments,
guid: decoded.guid,
Expand Down Expand Up @@ -295,6 +309,7 @@ extension MessageStore {
let date = appleDate(from: int64Value(row[columns.date]))
let isFromMe = boolValue(row[columns.isFromMe])
let service = stringValue(row[columns.service])
let isGroup = intValue(row[columns.chatStyle]) == 43
let isAudioMessage = boolValue(row[columns.isAudioMessage])
let destinationCallerID = stringValue(row[columns.destinationCallerID])
let guid = stringValue(row[columns.guid])
Expand Down Expand Up @@ -323,6 +338,7 @@ extension MessageStore {
date: date,
isFromMe: isFromMe,
service: service,
isGroup: isGroup,
destinationCallerID: destinationCallerID,
guid: guid,
associatedGUID: associatedGUID,
Expand Down
12 changes: 11 additions & 1 deletion Sources/IMsgCore/MessageStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public final class MessageStore: @unchecked Sendable {
let hasAudioMessageColumn: Bool
let hasAttachmentUserInfo: Bool
let hasBalloonBundleIDColumn: Bool
let hasChatStyleColumn: Bool

private struct URLBalloonDedupeEntry: Sendable {
let rowID: Int64
Expand All @@ -47,13 +48,15 @@ public final class MessageStore: @unchecked Sendable {
connection: self.connection,
table: "attachment"
)
let chatColumns = MessageStore.tableColumns(connection: self.connection, table: "chat")
self.hasAttributedBody = messageColumns.contains("attributedbody")
self.hasReactionColumns = MessageStore.reactionColumnsPresent(in: messageColumns)
self.hasThreadOriginatorGUIDColumn = messageColumns.contains("thread_originator_guid")
self.hasDestinationCallerID = messageColumns.contains("destination_caller_id")
self.hasAudioMessageColumn = messageColumns.contains("is_audio_message")
self.hasAttachmentUserInfo = attachmentColumns.contains("user_info")
self.hasBalloonBundleIDColumn = messageColumns.contains("balloon_bundle_id")
self.hasChatStyleColumn = chatColumns.contains("style")
} catch {
throw MessageStore.enhance(error: error, path: normalized)
}
Expand All @@ -68,7 +71,8 @@ public final class MessageStore: @unchecked Sendable {
hasDestinationCallerID: Bool? = nil,
hasAudioMessageColumn: Bool? = nil,
hasAttachmentUserInfo: Bool? = nil,
hasBalloonBundleIDColumn: Bool? = nil
hasBalloonBundleIDColumn: Bool? = nil,
hasChatStyleColumn: Bool? = nil
) throws {
self.path = path
self.queue = DispatchQueue(label: "imsg.db.test", qos: .userInitiated)
Expand All @@ -77,6 +81,7 @@ public final class MessageStore: @unchecked Sendable {
self.connection.busyTimeout = 5
let messageColumns = MessageStore.tableColumns(connection: connection, table: "message")
let attachmentColumns = MessageStore.tableColumns(connection: connection, table: "attachment")
let chatColumns = MessageStore.tableColumns(connection: connection, table: "chat")
if let hasAttributedBody {
self.hasAttributedBody = hasAttributedBody
} else {
Expand Down Expand Up @@ -112,6 +117,11 @@ public final class MessageStore: @unchecked Sendable {
} else {
self.hasBalloonBundleIDColumn = messageColumns.contains("balloon_bundle_id")
}
if let hasChatStyleColumn {
self.hasChatStyleColumn = hasChatStyleColumn
} else {
self.hasChatStyleColumn = chatColumns.contains("style")
}
}

public func listChats(limit: Int) throws -> [Chat] {
Expand Down
5 changes: 5 additions & 0 deletions Sources/IMsgCore/Models.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ public struct Message: Sendable, Equatable {
public let date: Date
public let isFromMe: Bool
public let service: String
public let isGroup: Bool
public let handleID: Int64?
public let attachmentsCount: Int
/// The destination_caller_id from the database. For messages where is_from_me is true,
Expand All @@ -287,6 +288,7 @@ public struct Message: Sendable, Equatable {
date: Date,
isFromMe: Bool,
service: String,
isGroup: Bool = false,
handleID: Int64?,
attachmentsCount: Int,
guid: String = "",
Expand All @@ -303,6 +305,7 @@ public struct Message: Sendable, Equatable {
self.date = date
self.isFromMe = isFromMe
self.service = service
self.isGroup = isGroup
self.handleID = handleID
self.attachmentsCount = attachmentsCount
self.destinationCallerID = routing.destinationCallerID
Expand All @@ -320,6 +323,7 @@ public struct Message: Sendable, Equatable {
date: Date,
isFromMe: Bool,
service: String,
isGroup: Bool = false,
handleID: Int64?,
attachmentsCount: Int,
guid: String = "",
Expand All @@ -339,6 +343,7 @@ public struct Message: Sendable, Equatable {
date: date,
isFromMe: isFromMe,
service: service,
isGroup: isGroup,
handleID: handleID,
attachmentsCount: attachmentsCount,
guid: guid,
Expand Down
4 changes: 4 additions & 0 deletions Sources/imsg/OutputModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ struct MessagePayload: Codable {
let threadOriginatorGUID: String?
let sender: String
let isFromMe: Bool
/// Whether this message is from a group chat (chat.style == 43 in database)
let isGroup: Bool
let text: String
let createdAt: String
let attachments: [AttachmentPayload]
Expand All @@ -57,6 +59,7 @@ struct MessagePayload: Codable {
self.threadOriginatorGUID = message.threadOriginatorGUID
self.sender = message.sender
self.isFromMe = message.isFromMe
self.isGroup = message.isGroup
self.text = message.text
self.createdAt = CLIISO8601.format(message.date)
self.attachments = attachments.map { AttachmentPayload(meta: $0) }
Expand Down Expand Up @@ -87,6 +90,7 @@ struct MessagePayload: Codable {
case threadOriginatorGUID = "thread_originator_guid"
case sender
case isFromMe = "is_from_me"
case isGroup = "is_group"
case text
case createdAt = "created_at"
case attachments
Expand Down
1 change: 1 addition & 0 deletions Tests/IMsgCoreTests/MessageStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func messagesByChatReturnsMessages() throws {
#expect(messages.count == 3)
#expect(messages[1].isFromMe)
#expect(messages[0].attachmentsCount == 0)
#expect(messages.allSatisfy { !$0.isGroup })
}

@Test
Expand Down