LNIMMessageData.swift 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //
  2. // LNIMMessageData.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/12/10.
  6. //
  7. import Foundation
  8. import AutoCodable
  9. enum LNIMMessageDataType {
  10. case none
  11. case official
  12. case system
  13. case text
  14. case image
  15. case voice
  16. case order
  17. case call(String)
  18. }
  19. enum LNIMMessageCustomType: String, Decodable {
  20. case none
  21. case official_image_text
  22. case playmate_order
  23. }
  24. @AutoCodable
  25. class LNIMCustomMessage: Decodable {
  26. var businessID: LNIMMessageCustomType = .none
  27. }
  28. @AutoCodable
  29. class LNIMVoiceCallMessage: Decodable {
  30. var actionType: Int = 0
  31. var businessID: Int = 0
  32. var inviteID: String = ""
  33. var data: String = ""
  34. private var decodedData: LNIMVoiceCallData?
  35. var callData: LNIMVoiceCallData? {
  36. if let decodedData {
  37. return decodedData
  38. }
  39. guard let data = data.data(using: .utf8) else { return nil }
  40. let decoder = JSONDecoder()
  41. decoder.keyDecodingStrategy = .useDefaultKeys
  42. decodedData = try? decoder.decode(LNIMVoiceCallData.self, from: data)
  43. return decodedData
  44. }
  45. }
  46. @AutoCodable
  47. class LNIMVoiceCallData: Decodable {
  48. var call_end: Int = 0
  49. var data: LNIMVoiceCallExtraData?
  50. init() { }
  51. }
  52. @AutoCodable
  53. class LNIMVoiceCallExtraData: Decodable {
  54. var excludeFromHistoryMessage: Bool = false
  55. var cmd: String = ""
  56. }
  57. @AutoCodable
  58. class LNIMOfficialMessage: Decodable {
  59. var title: String = ""
  60. var content: String = ""
  61. var image: String = ""
  62. var link: String = ""
  63. }
  64. @AutoCodable
  65. class LNIMOrderMessage: Decodable {
  66. var orderId: String = ""
  67. var bizCategoryName: String = ""
  68. var categoryIcon: String = ""
  69. var price: Double = 0.0
  70. var unit: String = ""
  71. var purchaseQty: Int = 0
  72. var status: LNOrderStatus = .created
  73. var createTime: Double = 0.0
  74. var star: Double = 0.0
  75. var refundApply: Bool = false
  76. var customerRemark: String = ""
  77. var buyerUserNo: String = ""
  78. }
  79. private extension V2TIMElemType {
  80. var toDataType: LNIMMessageDataType {
  81. switch self {
  82. case .ELEM_TYPE_TEXT: .text
  83. case .ELEM_TYPE_IMAGE: .image
  84. case .ELEM_TYPE_SOUND: .voice
  85. case .ELEM_TYPE_CUSTOM: .none
  86. default: .none
  87. }
  88. }
  89. }
  90. class LNIMMessageData: NSObject {
  91. let imMessage: V2TIMMessage
  92. private(set) var type: LNIMMessageDataType
  93. private(set) var content: String?
  94. private(set) var textContent: NSAttributedString?
  95. private var customMessage: (any Decodable)?
  96. var readReceipt: V2TIMMessageReceipt?
  97. init(imMessage: V2TIMMessage) {
  98. self.imMessage = imMessage
  99. self.type = imMessage.elemType.toDataType
  100. super.init()
  101. switch imMessage.elemType {
  102. case .ELEM_TYPE_TEXT: setupTextContent()
  103. case .ELEM_TYPE_IMAGE: setupImageContent()
  104. case .ELEM_TYPE_SOUND: setupVoiceContent()
  105. case .ELEM_TYPE_CUSTOM: setupCustomContent()
  106. default:
  107. break
  108. }
  109. }
  110. func decodeCustomMessage<T: Decodable>() -> T? {
  111. if let cached = customMessage as? T {
  112. return cached
  113. }
  114. guard let data = imMessage.customElem?.data else { return nil }
  115. let decoder = JSONDecoder()
  116. decoder.keyDecodingStrategy = .useDefaultKeys // 处理蛇形命名
  117. let result = try? decoder.decode(T.self, from: data)
  118. if let result {
  119. customMessage = result
  120. }
  121. return result
  122. }
  123. static func buildDateMessage(date: Date) -> LNIMMessageData {
  124. let data = LNIMMessageData(imMessage: V2TIMMessage())
  125. data.type = .system
  126. data.content = date.timeIntervalSince1970.tencentIMTimeDesc
  127. return data
  128. }
  129. }
  130. extension LNIMMessageData {
  131. private func setupTextContent() {
  132. content = imMessage.textElem?.text
  133. textContent = content?.getEmojiString(with: .body_l)
  134. }
  135. private func setupImageContent() {
  136. if imMessage.isSelf, let path = imMessage.imageElem?.path {
  137. if FileManager.default.fileExists(atPath: path) {
  138. content = path // 使用本地文件路径
  139. return
  140. } else if let fixedPath = path.fixedFilePath,
  141. FileManager.default.fileExists(atPath: fixedPath) {
  142. content = fixedPath // UUID 变化后的本地文件路径
  143. return
  144. }
  145. }
  146. content = imMessage.imageElem?.imageList.first(where: { $0.type == .IMAGE_TYPE_THUMB })?.url
  147. }
  148. private func setupVoiceContent() {
  149. content = imMessage.soundElem?.path
  150. }
  151. private func setupCustomContent() {
  152. guard let data = imMessage.customElem?.data else { return }
  153. let decoder = JSONDecoder()
  154. decoder.keyDecodingStrategy = .useDefaultKeys // 处理蛇形命名
  155. // 判断是否是通话消息
  156. if let voiceCall = try? decoder.decode(LNIMVoiceCallMessage.self, from: data),
  157. voiceCall.actionType != 0, voiceCall.businessID != 0 {
  158. type = .call(voiceCall.inviteID)
  159. return
  160. }
  161. // 判断是否是自定义消息
  162. if let result = try? decoder.decode(LNIMCustomMessage.self, from: data) {
  163. switch result.businessID {
  164. case .playmate_order:
  165. type = .order // 订单消息
  166. case .official_image_text:
  167. type = .official // 官方消息
  168. case .none: type = .none
  169. }
  170. return
  171. }
  172. type = .none
  173. }
  174. }