From e17c7d985963e023018d6923519a9c61475f660a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 5 Feb 2026 17:27:47 +0400 Subject: [PATCH] Update API --- submodules/TelegramApi/Sources/Api0.swift | 1 + submodules/TelegramApi/Sources/Api16.swift | 44 ++ .../ApiUtils/StoreMessage_Telegram.swift | 27 +- .../Sources/ApiUtils/TelegramMediaFile.swift | 11 +- .../TextEntitiesMessageAttribute.swift | 108 +++-- .../PendingMessageUploadedContent.swift | 6 +- .../ManagedSecretChatOutgoingOperations.swift | 6 + .../Sources/State/Serialization.swift | 2 +- .../SyncCore/SyncCore_TelegramMediaFile.swift | 10 + ...yncCore_TextEntitiesMessageAttribute.swift | 446 ++++++++++-------- .../TelegramEngine/Calls/GroupCalls.swift | 2 + 11 files changed, 426 insertions(+), 237 deletions(-) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index c9da0e78c9..901e832230 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -649,6 +649,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[681706865] = { return Api.MessageEntity.parse_messageEntityCode($0) } dict[-925956616] = { return Api.MessageEntity.parse_messageEntityCustomEmoji($0) } dict[1692693954] = { return Api.MessageEntity.parse_messageEntityEmail($0) } + dict[-1874147385] = { return Api.MessageEntity.parse_messageEntityFormattedDate($0) } dict[1868782349] = { return Api.MessageEntity.parse_messageEntityHashtag($0) } dict[-2106619040] = { return Api.MessageEntity.parse_messageEntityItalic($0) } dict[-100378723] = { return Api.MessageEntity.parse_messageEntityMention($0) } diff --git a/submodules/TelegramApi/Sources/Api16.swift b/submodules/TelegramApi/Sources/Api16.swift index c06f17560a..38c7dc7769 100644 --- a/submodules/TelegramApi/Sources/Api16.swift +++ b/submodules/TelegramApi/Sources/Api16.swift @@ -78,6 +78,18 @@ public extension Api { self.length = length } } + public class Cons_messageEntityFormattedDate { + public var flags: Int32 + public var offset: Int32 + public var length: Int32 + public var date: Int32 + public init(flags: Int32, offset: Int32, length: Int32, date: Int32) { + self.flags = flags + self.offset = offset + self.length = length + self.date = date + } + } public class Cons_messageEntityHashtag { public var offset: Int32 public var length: Int32 @@ -189,6 +201,7 @@ public extension Api { case messageEntityCode(Cons_messageEntityCode) case messageEntityCustomEmoji(Cons_messageEntityCustomEmoji) case messageEntityEmail(Cons_messageEntityEmail) + case messageEntityFormattedDate(Cons_messageEntityFormattedDate) case messageEntityHashtag(Cons_messageEntityHashtag) case messageEntityItalic(Cons_messageEntityItalic) case messageEntityMention(Cons_messageEntityMention) @@ -270,6 +283,15 @@ public extension Api { serializeInt32(_data.offset, buffer: buffer, boxed: false) serializeInt32(_data.length, buffer: buffer, boxed: false) break + case .messageEntityFormattedDate(let _data): + if boxed { + buffer.appendInt32(-1874147385) + } + serializeInt32(_data.flags, buffer: buffer, boxed: false) + serializeInt32(_data.offset, buffer: buffer, boxed: false) + serializeInt32(_data.length, buffer: buffer, boxed: false) + serializeInt32(_data.date, buffer: buffer, boxed: false) + break case .messageEntityHashtag(let _data): if boxed { buffer.appendInt32(1868782349) @@ -380,6 +402,8 @@ public extension Api { return ("messageEntityCustomEmoji", [("offset", _data.offset as Any), ("length", _data.length as Any), ("documentId", _data.documentId as Any)]) case .messageEntityEmail(let _data): return ("messageEntityEmail", [("offset", _data.offset as Any), ("length", _data.length as Any)]) + case .messageEntityFormattedDate(let _data): + return ("messageEntityFormattedDate", [("flags", _data.flags as Any), ("offset", _data.offset as Any), ("length", _data.length as Any), ("date", _data.date as Any)]) case .messageEntityHashtag(let _data): return ("messageEntityHashtag", [("offset", _data.offset as Any), ("length", _data.length as Any)]) case .messageEntityItalic(let _data): @@ -544,6 +568,26 @@ public extension Api { return nil } } + public static func parse_messageEntityFormattedDate(_ reader: BufferReader) -> MessageEntity? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.MessageEntity.messageEntityFormattedDate(Cons_messageEntityFormattedDate(flags: _1!, offset: _2!, length: _3!, date: _4!)) + } + else { + return nil + } + } public static func parse_messageEntityHashtag(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 2e558722ab..858f183c6a 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -398,7 +398,7 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI case let .messageMediaDocument(messageMediaDocumentData): let (flags, document, altDocuments, coverPhoto, videoTimestamp, ttlSeconds) = (messageMediaDocumentData.flags, messageMediaDocumentData.document, messageMediaDocumentData.altDocuments, messageMediaDocumentData.videoCover, messageMediaDocumentData.videoTimestamp, messageMediaDocumentData.ttlSeconds) if let document = document { - if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto) { + if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto, isLivePhoto: (flags & (1 << 11)) != 0) { return (mediaFile, ttlSeconds, (flags & (1 << 3)) != 0, (flags & (1 << 4)) != 0, nil, videoTimestamp) } } else { @@ -785,6 +785,31 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes case let .messageEntityCustomEmoji(messageEntityCustomEmojiData): let (offset, length, documentId) = (messageEntityCustomEmojiData.offset, messageEntityCustomEmojiData.length, messageEntityCustomEmojiData.documentId) result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .CustomEmoji(stickerPack: nil, fileId: documentId))) + case let .messageEntityFormattedDate(messageEntityFormattedDateData): + let (flags, offset, length, date) = (messageEntityFormattedDateData.flags, messageEntityFormattedDateData.offset, messageEntityFormattedDateData.length, messageEntityFormattedDateData.date) + let format: MessageTextEntityType.DateTimeFormat + if (flags & (1 << 0)) != 0 { + format = .relative + } else { + let timeFormat: MessageTextEntityType.DateTimeFormat.TimeFormat + if (flags & (1 << 1)) != 0 { + timeFormat = .short + } else if (flags & (1 << 2)) != 0 { + timeFormat = .long + } else { + timeFormat = .short + } + let dateFormat: MessageTextEntityType.DateTimeFormat.DateFormat + if (flags & (1 << 3)) != 0 { + dateFormat = .short + } else if (flags & (1 << 4)) != 0 { + dateFormat = .long + } else { + dateFormat = .short + } + format = .full(timeFormat: timeFormat, dateFormat: dateFormat) + } + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .FormattedDate(format: format, date: date))) } } return result diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaFile.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaFile.swift index 42954883c6..a788e49441 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaFile.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaFile.swift @@ -108,7 +108,7 @@ extension StickerMaskCoords { } } -func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAttribute]) -> [TelegramMediaFileAttribute] { +func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAttribute], isLivePhoto: Bool = false) -> [TelegramMediaFileAttribute] { var result: [TelegramMediaFileAttribute] = [] for attribute in attributes { switch attribute { @@ -137,6 +137,9 @@ func telegramMediaFileAttributesFromApiAttributes(_ attributes: [Api.DocumentAtt if (flags & (1 << 3)) != 0 { videoFlags.insert(.isSilent) } + if isLivePhoto { + videoFlags.insert(.isLivePhoto) + } result.append(.Video(duration: Double(duration), size: PixelDimensions(width: w, height: h), flags: videoFlags, preloadSize: preloadSize, coverTime: videoStart, videoCodec: videoCodec)) case let .documentAttributeAudio(documentAttributeAudioData): let (flags, duration, title, performer, waveform) = (documentAttributeAudioData.flags, documentAttributeAudioData.duration, documentAttributeAudioData.title, documentAttributeAudioData.performer, documentAttributeAudioData.waveform) @@ -192,11 +195,11 @@ func telegramMediaFileThumbnailRepresentationsFromApiSizes(datacenterId: Int32, return (immediateThumbnailData, representations) } -func telegramMediaFileFromApiDocument(_ document: Api.Document, altDocuments: [Api.Document]?, videoCover: Api.Photo? = nil) -> TelegramMediaFile? { +func telegramMediaFileFromApiDocument(_ document: Api.Document, altDocuments: [Api.Document]?, videoCover: Api.Photo? = nil, isLivePhoto: Bool = false) -> TelegramMediaFile? { switch document { case let .document(documentData): let (id, accessHash, fileReference, mimeType, size, thumbs, videoThumbs, dcId, attributes) = (documentData.id, documentData.accessHash, documentData.fileReference, documentData.mimeType, documentData.size, documentData.thumbs, documentData.videoThumbs, documentData.dcId, documentData.attributes) - var parsedAttributes = telegramMediaFileAttributesFromApiAttributes(attributes) + var parsedAttributes = telegramMediaFileAttributesFromApiAttributes(attributes, isLivePhoto: isLivePhoto) var isSticker = false var isAnimated = false for attribute in parsedAttributes { @@ -238,7 +241,7 @@ func telegramMediaFileFromApiDocument(_ document: Api.Document, altDocuments: [A alternativeRepresentations = altDocuments.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) } } - return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: size, fileReference: fileReference.makeData(), fileName: fileNameFromFileAttributes(parsedAttributes)), previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, videoCover: videoCover.flatMap(telegramMediaImageFromApiPhoto), immediateThumbnailData: immediateThumbnail, mimeType: mimeType, size: size, attributes: parsedAttributes, alternativeRepresentations: alternativeRepresentations) + return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: size, fileReference: fileReference.makeData(), fileName: fileNameFromFileAttributes(parsedAttributes)), previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, videoCover: videoCover.flatMap(telegramMediaImageFromApiPhoto), immediateThumbnailData: immediateThumbnail, mimeType: mimeType, size: size, attributes: parsedAttributes, alternativeRepresentations: alternativeRepresentations) case .documentEmpty: return nil } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift index 11ed9a64c2..f48fe3b8c5 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift @@ -10,52 +10,72 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa let offset: Int32 = Int32(entity.range.lowerBound) let length: Int32 = Int32(entity.range.upperBound - entity.range.lowerBound) switch entity.type { - case .Unknown: - break - case .Mention: - apiEntities.append(.messageEntityMention(.init(offset: offset, length: length))) - case .Hashtag: - apiEntities.append(.messageEntityHashtag(.init(offset: offset, length: length))) - case .BotCommand: - apiEntities.append(.messageEntityBotCommand(.init(offset: offset, length: length))) - case .Url: - apiEntities.append(.messageEntityUrl(.init(offset: offset, length: length))) - case .Email: - apiEntities.append(.messageEntityEmail(.init(offset: offset, length: length))) - case .Bold: - apiEntities.append(.messageEntityBold(.init(offset: offset, length: length))) - case .Italic: - apiEntities.append(.messageEntityItalic(.init(offset: offset, length: length))) - case .Code: - apiEntities.append(.messageEntityCode(.init(offset: offset, length: length))) - case let .Pre(language): - apiEntities.append(.messageEntityPre(.init(offset: offset, length: length, language: language ?? ""))) - case let .TextUrl(url): - apiEntities.append(.messageEntityTextUrl(.init(offset: offset, length: length, url: url))) - case let .TextMention(peerId): - if let peer = associatedPeers[peerId], let inputUser = apiInputUser(peer) { - apiEntities.append(.inputMessageEntityMentionName(.init(offset: offset, length: length, userId: inputUser))) + case .Unknown: + break + case .Mention: + apiEntities.append(.messageEntityMention(.init(offset: offset, length: length))) + case .Hashtag: + apiEntities.append(.messageEntityHashtag(.init(offset: offset, length: length))) + case .BotCommand: + apiEntities.append(.messageEntityBotCommand(.init(offset: offset, length: length))) + case .Url: + apiEntities.append(.messageEntityUrl(.init(offset: offset, length: length))) + case .Email: + apiEntities.append(.messageEntityEmail(.init(offset: offset, length: length))) + case .Bold: + apiEntities.append(.messageEntityBold(.init(offset: offset, length: length))) + case .Italic: + apiEntities.append(.messageEntityItalic(.init(offset: offset, length: length))) + case .Code: + apiEntities.append(.messageEntityCode(.init(offset: offset, length: length))) + case let .Pre(language): + apiEntities.append(.messageEntityPre(.init(offset: offset, length: length, language: language ?? ""))) + case let .TextUrl(url): + apiEntities.append(.messageEntityTextUrl(.init(offset: offset, length: length, url: url))) + case let .TextMention(peerId): + if let peer = associatedPeers[peerId], let inputUser = apiInputUser(peer) { + apiEntities.append(.inputMessageEntityMentionName(.init(offset: offset, length: length, userId: inputUser))) + } + case .PhoneNumber: + break + case .Strikethrough: + apiEntities.append(.messageEntityStrike(.init(offset: offset, length: length))) + case let .BlockQuote(isCollapsed): + var flags: Int32 = 0 + if isCollapsed { + flags |= 1 << 0 + } + apiEntities.append(.messageEntityBlockquote(.init(flags: flags, offset: offset, length: length))) + case .Underline: + apiEntities.append(.messageEntityUnderline(.init(offset: offset, length: length))) + case .BankCard: + apiEntities.append(.messageEntityBankCard(.init(offset: offset, length: length))) + case .Spoiler: + apiEntities.append(.messageEntitySpoiler(.init(offset: offset, length: length))) + case let .CustomEmoji(_, fileId): + apiEntities.append(.messageEntityCustomEmoji(.init(offset: offset, length: length, documentId: fileId))) + case let .FormattedDate(format, date): + var flags: Int32 = 0 + switch format { + case .relative: + flags |= 1 << 0 + case let .full(timeFormat, dateFormat): + switch timeFormat { + case .short: + flags |= 1 << 1 + case .long: + flags |= 1 << 2 } - case .PhoneNumber: - break - case .Strikethrough: - apiEntities.append(.messageEntityStrike(.init(offset: offset, length: length))) - case let .BlockQuote(isCollapsed): - var flags: Int32 = 0 - if isCollapsed { - flags |= 1 << 0 + switch dateFormat { + case .short: + flags |= 1 << 3 + case .long: + flags |= 1 << 4 } - apiEntities.append(.messageEntityBlockquote(.init(flags: flags, offset: offset, length: length))) - case .Underline: - apiEntities.append(.messageEntityUnderline(.init(offset: offset, length: length))) - case .BankCard: - apiEntities.append(.messageEntityBankCard(.init(offset: offset, length: length))) - case .Spoiler: - apiEntities.append(.messageEntitySpoiler(.init(offset: offset, length: length))) - case let .CustomEmoji(_, fileId): - apiEntities.append(.messageEntityCustomEmoji(.init(offset: offset, length: length, documentId: fileId))) - case .Custom: - break + } + apiEntities.append(.messageEntityFormattedDate(.init(flags: flags, offset: offset, length: length, date: date))) + case .Custom: + break } } diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 50e7eae734..c45587a774 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -1169,7 +1169,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili if let _ = videoCoverPhoto { flags |= 1 << 6 } - + var ttlSeconds: Int32? var videoTimestamp: Int32? for attribute in attributes { @@ -1213,6 +1213,10 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili flags |= 1 << 7 } + if file.isLivePhoto { + flags |= 1 << 8 + } + if ttlSeconds != nil { return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(.init(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, videoCover: videoCoverPhoto, videoTimestamp: videoTimestamp, ttlSeconds: ttlSeconds)), text), reuploadInfo: nil, cacheReferenceKey: referenceKey))) } diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index 51c9526e33..1ebe2f011e 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -822,6 +822,8 @@ private func decryptedEntities73(_ entities: [MessageTextEntity]?) -> [SecretApi break case .CustomEmoji: break + case .FormattedDate: + break case .Custom: break } @@ -875,6 +877,8 @@ private func decryptedEntities101(_ entities: [MessageTextEntity]?) -> [SecretAp break case .CustomEmoji: break + case .FormattedDate: + break case .Custom: break } @@ -928,6 +932,8 @@ private func decryptedEntities144(_ entities: [MessageTextEntity]?) -> [SecretAp result.append(.messageEntitySpoiler(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count))) case let .CustomEmoji(_, fileId): result.append(.messageEntityCustomEmoji(offset: Int32(entity.range.lowerBound), length: Int32(entity.range.count), documentId: fileId)) + case .FormattedDate: + break case .Custom: break } diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index e1003fb62f..901c273fb4 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 222 + return 225 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift index b80a115dd0..62805c4e29 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaFile.swift @@ -311,6 +311,7 @@ public struct TelegramMediaVideoFlags: OptionSet { public static let instantRoundVideo = TelegramMediaVideoFlags(rawValue: 1 << 0) public static let supportsStreaming = TelegramMediaVideoFlags(rawValue: 1 << 1) public static let isSilent = TelegramMediaVideoFlags(rawValue: 1 << 3) + public static let isLivePhoto = TelegramMediaVideoFlags(rawValue: 1 << 4) } public struct StickerMaskCoords: PostboxCoding, Equatable { @@ -1060,6 +1061,15 @@ public final class TelegramMediaFile: Media, Equatable, Codable { return false } + public var isLivePhoto: Bool { + for attribute in self.attributes { + if case .Video(_, _, let flags, _, _, _) = attribute { + return flags.contains(.isLivePhoto) + } + } + return false + } + public var preloadSize: Int32? { for attribute in self.attributes { if case .Video(_, _, _, let preloadSize, _, _) = attribute { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index 5e51191dc0..d5022500ac 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -4,6 +4,67 @@ import Postbox public enum MessageTextEntityType: Equatable { public typealias CustomEntityType = Int32 + public enum DateTimeFormat: Equatable { + public enum TimeFormat: Equatable { + case short + case long + } + + public enum DateFormat: Equatable { + case short + case long + } + + case relative + case full(timeFormat: TimeFormat, dateFormat: DateFormat) + + public init(rawValue: Int32) { + if (rawValue & (1 << 0)) != 0 { + self = .relative + } else { + let timeFormat: MessageTextEntityType.DateTimeFormat.TimeFormat + if (rawValue & (1 << 1)) != 0 { + timeFormat = .short + } else if (rawValue & (1 << 2)) != 0 { + timeFormat = .long + } else { + timeFormat = .short + } + let dateFormat: MessageTextEntityType.DateTimeFormat.DateFormat + if (rawValue & (1 << 3)) != 0 { + dateFormat = .short + } else if (rawValue & (1 << 4)) != 0 { + dateFormat = .long + } else { + dateFormat = .short + } + self = .full(timeFormat: timeFormat, dateFormat: dateFormat) + } + } + + public var rawValue: Int32 { + var rawValue: Int32 = 0 + switch self { + case .relative: + rawValue |= 1 << 0 + case let .full(timeFormat, dateFormat): + switch timeFormat { + case .short: + rawValue |= 1 << 1 + case .long: + rawValue |= 1 << 2 + } + switch dateFormat { + case .short: + rawValue |= 1 << 3 + case .long: + rawValue |= 1 << 4 + } + } + return rawValue + } + } + case Unknown case Mention case Hashtag @@ -23,6 +84,7 @@ public enum MessageTextEntityType: Equatable { case BankCard case Spoiler case CustomEmoji(stickerPack: StickerPackReference?, fileId: Int64) + case FormattedDate(format: DateTimeFormat, date: Int32) case Custom(type: CustomEntityType) } @@ -39,47 +101,49 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { self.range = Int(decoder.decodeInt32ForKey("start", orElse: 0)) ..< Int(decoder.decodeInt32ForKey("end", orElse: 0)) let type: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) switch type { - case 1: - self.type = .Mention - case 2: - self.type = .Hashtag - case 3: - self.type = .BotCommand - case 4: - self.type = .Url - case 5: - self.type = .Email - case 6: - self.type = .Bold - case 7: - self.type = .Italic - case 8: - self.type = .Code - case 9: - self.type = .Pre(language: decoder.decodeOptionalStringForKey("language")) - case 10: - self.type = .TextUrl(url: decoder.decodeStringForKey("url", orElse: "")) - case 11: - self.type = .TextMention(peerId: PeerId(decoder.decodeInt64ForKey("peerId", orElse: 0))) - case 12: - self.type = .PhoneNumber - case 13: - self.type = .Strikethrough - case 14: - self.type = .BlockQuote(isCollapsed: decoder.decodeBoolForKey("cl", orElse: false)) - case 15: - self.type = .Underline - case 16: - self.type = .BankCard - case 17: - self.type = .Spoiler - case 18: - let stickerPack = decoder.decodeObjectForKey("s", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference - self.type = .CustomEmoji(stickerPack: stickerPack, fileId: decoder.decodeInt64ForKey("f", orElse: 0)) - case Int32.max: - self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0)) - default: - self.type = .Unknown + case 1: + self.type = .Mention + case 2: + self.type = .Hashtag + case 3: + self.type = .BotCommand + case 4: + self.type = .Url + case 5: + self.type = .Email + case 6: + self.type = .Bold + case 7: + self.type = .Italic + case 8: + self.type = .Code + case 9: + self.type = .Pre(language: decoder.decodeOptionalStringForKey("language")) + case 10: + self.type = .TextUrl(url: decoder.decodeStringForKey("url", orElse: "")) + case 11: + self.type = .TextMention(peerId: PeerId(decoder.decodeInt64ForKey("peerId", orElse: 0))) + case 12: + self.type = .PhoneNumber + case 13: + self.type = .Strikethrough + case 14: + self.type = .BlockQuote(isCollapsed: decoder.decodeBoolForKey("cl", orElse: false)) + case 15: + self.type = .Underline + case 16: + self.type = .BankCard + case 17: + self.type = .Spoiler + case 18: + let stickerPack = decoder.decodeObjectForKey("s", decoder: { StickerPackReference(decoder: $0) }) as? StickerPackReference + self.type = .CustomEmoji(stickerPack: stickerPack, fileId: decoder.decodeInt64ForKey("f", orElse: 0)) + case 19: + self.type = .FormattedDate(format: MessageTextEntityType.DateTimeFormat(rawValue: decoder.decodeInt32ForKey("format", orElse: 0)), date: decoder.decodeInt32ForKey("date", orElse: 0)) + case Int32.max: + self.type = .Custom(type: decoder.decodeInt32ForKey("type", orElse: 0)) + default: + self.type = .Unknown } } @@ -95,49 +159,51 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { self.range = Int(rangeStart) ..< Int(rangeEnd) switch type { - case 1: - self.type = .Mention - case 2: - self.type = .Hashtag - case 3: - self.type = .BotCommand - case 4: - self.type = .Url - case 5: - self.type = .Email - case 6: - self.type = .Bold - case 7: - self.type = .Italic - case 8: - self.type = .Code - case 9: - self.type = .Pre(language: try? container.decodeIfPresent(String.self, forKey: "language")) - case 10: - let url = (try? container.decode(String.self, forKey: "url")) ?? "" - self.type = .TextUrl(url: url) - case 11: - let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0 - self.type = .TextMention(peerId: PeerId(peerId)) - case 12: - self.type = .PhoneNumber - case 13: - self.type = .Strikethrough - case 14: - self.type = .BlockQuote(isCollapsed: try container.decodeIfPresent(Bool.self, forKey: "cl") ?? false) - case 15: - self.type = .Underline - case 16: - self.type = .BankCard - case 17: - self.type = .Spoiler - case 18: - self.type = .CustomEmoji(stickerPack: try container.decodeIfPresent(StickerPackReference.self, forKey: "s"), fileId: try container.decode(Int64.self, forKey: "f")) - case Int32.max: - let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0 - self.type = .Custom(type: customType) - default: - self.type = .Unknown + case 1: + self.type = .Mention + case 2: + self.type = .Hashtag + case 3: + self.type = .BotCommand + case 4: + self.type = .Url + case 5: + self.type = .Email + case 6: + self.type = .Bold + case 7: + self.type = .Italic + case 8: + self.type = .Code + case 9: + self.type = .Pre(language: try? container.decodeIfPresent(String.self, forKey: "language")) + case 10: + let url = (try? container.decode(String.self, forKey: "url")) ?? "" + self.type = .TextUrl(url: url) + case 11: + let peerId = (try? container.decode(Int64.self, forKey: "peerId")) ?? 0 + self.type = .TextMention(peerId: PeerId(peerId)) + case 12: + self.type = .PhoneNumber + case 13: + self.type = .Strikethrough + case 14: + self.type = .BlockQuote(isCollapsed: try container.decodeIfPresent(Bool.self, forKey: "cl") ?? false) + case 15: + self.type = .Underline + case 16: + self.type = .BankCard + case 17: + self.type = .Spoiler + case 18: + self.type = .CustomEmoji(stickerPack: try container.decodeIfPresent(StickerPackReference.self, forKey: "s"), fileId: try container.decode(Int64.self, forKey: "f")) + case 19: + self.type = .FormattedDate(format: MessageTextEntityType.DateTimeFormat(rawValue: try container.decode(Int32.self, forKey: "format")), date: try container.decode(Int32.self, forKey: "date")) + case Int32.max: + let customType: Int32 = (try? container.decode(Int32.self, forKey: "type")) ?? 0 + self.type = .Custom(type: customType) + default: + self.type = .Unknown } } @@ -145,61 +211,65 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { encoder.encodeInt32(Int32(self.range.lowerBound), forKey: "start") encoder.encodeInt32(Int32(self.range.upperBound), forKey: "end") switch self.type { - case .Unknown: - encoder.encodeInt32(0, forKey: "_rawValue") - case .Mention: - encoder.encodeInt32(1, forKey: "_rawValue") - case .Hashtag: - encoder.encodeInt32(2, forKey: "_rawValue") - case .BotCommand: - encoder.encodeInt32(3, forKey: "_rawValue") - case .Url: - encoder.encodeInt32(4, forKey: "_rawValue") - case .Email: - encoder.encodeInt32(5, forKey: "_rawValue") - case .Bold: - encoder.encodeInt32(6, forKey: "_rawValue") - case .Italic: - encoder.encodeInt32(7, forKey: "_rawValue") - case .Code: - encoder.encodeInt32(8, forKey: "_rawValue") - case let .Pre(language): - encoder.encodeInt32(9, forKey: "_rawValue") - if let language = language { - encoder.encodeString(language, forKey: "language") - } else { - encoder.encodeNil(forKey: "language") - } - case let .TextUrl(url): - encoder.encodeInt32(10, forKey: "_rawValue") - encoder.encodeString(url, forKey: "url") - case let .TextMention(peerId): - encoder.encodeInt32(11, forKey: "_rawValue") - encoder.encodeInt64(peerId.toInt64(), forKey: "peerId") - case .PhoneNumber: - encoder.encodeInt32(12, forKey: "_rawValue") - case .Strikethrough: - encoder.encodeInt32(13, forKey: "_rawValue") - case let .BlockQuote(isCollapsed): - encoder.encodeInt32(14, forKey: "_rawValue") - encoder.encodeBool(isCollapsed, forKey: "cl") - case .Underline: - encoder.encodeInt32(15, forKey: "_rawValue") - case .BankCard: - encoder.encodeInt32(16, forKey: "_rawValue") - case .Spoiler: - encoder.encodeInt32(17, forKey: "_rawValue") - case let .CustomEmoji(stickerPack, fileId): - encoder.encodeInt32(18, forKey: "_rawValue") - if let stickerPack = stickerPack { - encoder.encodeObject(stickerPack, forKey: "s") - } else { - encoder.encodeNil(forKey: "s") - } - encoder.encodeInt64(fileId, forKey: "f") - case let .Custom(type): - encoder.encodeInt32(Int32.max, forKey: "_rawValue") - encoder.encodeInt32(type, forKey: "type") + case .Unknown: + encoder.encodeInt32(0, forKey: "_rawValue") + case .Mention: + encoder.encodeInt32(1, forKey: "_rawValue") + case .Hashtag: + encoder.encodeInt32(2, forKey: "_rawValue") + case .BotCommand: + encoder.encodeInt32(3, forKey: "_rawValue") + case .Url: + encoder.encodeInt32(4, forKey: "_rawValue") + case .Email: + encoder.encodeInt32(5, forKey: "_rawValue") + case .Bold: + encoder.encodeInt32(6, forKey: "_rawValue") + case .Italic: + encoder.encodeInt32(7, forKey: "_rawValue") + case .Code: + encoder.encodeInt32(8, forKey: "_rawValue") + case let .Pre(language): + encoder.encodeInt32(9, forKey: "_rawValue") + if let language = language { + encoder.encodeString(language, forKey: "language") + } else { + encoder.encodeNil(forKey: "language") + } + case let .TextUrl(url): + encoder.encodeInt32(10, forKey: "_rawValue") + encoder.encodeString(url, forKey: "url") + case let .TextMention(peerId): + encoder.encodeInt32(11, forKey: "_rawValue") + encoder.encodeInt64(peerId.toInt64(), forKey: "peerId") + case .PhoneNumber: + encoder.encodeInt32(12, forKey: "_rawValue") + case .Strikethrough: + encoder.encodeInt32(13, forKey: "_rawValue") + case let .BlockQuote(isCollapsed): + encoder.encodeInt32(14, forKey: "_rawValue") + encoder.encodeBool(isCollapsed, forKey: "cl") + case .Underline: + encoder.encodeInt32(15, forKey: "_rawValue") + case .BankCard: + encoder.encodeInt32(16, forKey: "_rawValue") + case .Spoiler: + encoder.encodeInt32(17, forKey: "_rawValue") + case let .CustomEmoji(stickerPack, fileId): + encoder.encodeInt32(18, forKey: "_rawValue") + if let stickerPack = stickerPack { + encoder.encodeObject(stickerPack, forKey: "s") + } else { + encoder.encodeNil(forKey: "s") + } + encoder.encodeInt64(fileId, forKey: "f") + case let .FormattedDate(format, date): + encoder.encodeInt32(19, forKey: "_rawValue") + encoder.encodeInt32(format.rawValue, forKey: "format") + encoder.encodeInt32(date, forKey: "date") + case let .Custom(type): + encoder.encodeInt32(Int32.max, forKey: "_rawValue") + encoder.encodeInt32(type, forKey: "type") } } @@ -209,53 +279,57 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { try container.encode(Int32(self.range.lowerBound), forKey: "start") try container.encode(Int32(self.range.upperBound), forKey: "end") switch self.type { - case .Unknown: - try container.encode(0 as Int32, forKey: "_rawValue") - case .Mention: - try container.encode(1 as Int32, forKey: "_rawValue") - case .Hashtag: - try container.encode(2 as Int32, forKey: "_rawValue") - case .BotCommand: - try container.encode(3 as Int32, forKey: "_rawValue") - case .Url: - try container.encode(4 as Int32, forKey: "_rawValue") - case .Email: - try container.encode(5 as Int32, forKey: "_rawValue") - case .Bold: - try container.encode(6 as Int32, forKey: "_rawValue") - case .Italic: - try container.encode(7 as Int32, forKey: "_rawValue") - case .Code: - try container.encode(8 as Int32, forKey: "_rawValue") - case let .Pre(language): - try container.encode(9 as Int32, forKey: "_rawValue") - try container.encodeIfPresent(language, forKey: "language") - case let .TextUrl(url): - try container.encode(10 as Int32, forKey: "_rawValue") - try container.encode(url, forKey: "url") - case let .TextMention(peerId): - try container.encode(11 as Int32, forKey: "_rawValue") - try container.encode(peerId.toInt64(), forKey: "peerId") - case .PhoneNumber: - try container.encode(12 as Int32, forKey: "_rawValue") - case .Strikethrough: - try container.encode(13 as Int32, forKey: "_rawValue") - case let .BlockQuote(isCollapsed): - try container.encode(14 as Int32, forKey: "_rawValue") - try container.encode(isCollapsed, forKey: "cl") - case .Underline: - try container.encode(15 as Int32, forKey: "_rawValue") - case .BankCard: - try container.encode(16 as Int32, forKey: "_rawValue") - case .Spoiler: - try container.encode(17 as Int32, forKey: "_rawValue") - case let .CustomEmoji(stickerPack, fileId): - try container.encode(18 as Int32, forKey: "_rawValue") - try container.encodeIfPresent(stickerPack, forKey: "s") - try container.encode(fileId, forKey: "f") - case let .Custom(type): - try container.encode(Int32.max as Int32, forKey: "_rawValue") - try container.encode(type as Int32, forKey: "type") + case .Unknown: + try container.encode(0 as Int32, forKey: "_rawValue") + case .Mention: + try container.encode(1 as Int32, forKey: "_rawValue") + case .Hashtag: + try container.encode(2 as Int32, forKey: "_rawValue") + case .BotCommand: + try container.encode(3 as Int32, forKey: "_rawValue") + case .Url: + try container.encode(4 as Int32, forKey: "_rawValue") + case .Email: + try container.encode(5 as Int32, forKey: "_rawValue") + case .Bold: + try container.encode(6 as Int32, forKey: "_rawValue") + case .Italic: + try container.encode(7 as Int32, forKey: "_rawValue") + case .Code: + try container.encode(8 as Int32, forKey: "_rawValue") + case let .Pre(language): + try container.encode(9 as Int32, forKey: "_rawValue") + try container.encodeIfPresent(language, forKey: "language") + case let .TextUrl(url): + try container.encode(10 as Int32, forKey: "_rawValue") + try container.encode(url, forKey: "url") + case let .TextMention(peerId): + try container.encode(11 as Int32, forKey: "_rawValue") + try container.encode(peerId.toInt64(), forKey: "peerId") + case .PhoneNumber: + try container.encode(12 as Int32, forKey: "_rawValue") + case .Strikethrough: + try container.encode(13 as Int32, forKey: "_rawValue") + case let .BlockQuote(isCollapsed): + try container.encode(14 as Int32, forKey: "_rawValue") + try container.encode(isCollapsed, forKey: "cl") + case .Underline: + try container.encode(15 as Int32, forKey: "_rawValue") + case .BankCard: + try container.encode(16 as Int32, forKey: "_rawValue") + case .Spoiler: + try container.encode(17 as Int32, forKey: "_rawValue") + case let .CustomEmoji(stickerPack, fileId): + try container.encode(18 as Int32, forKey: "_rawValue") + try container.encodeIfPresent(stickerPack, forKey: "s") + try container.encode(fileId, forKey: "f") + case let .FormattedDate(format, date): + try container.encode(19 as Int32, forKey: "_rawValue") + try container.encode(format.rawValue, forKey: "format") + try container.encode(date, forKey: "date") + case let .Custom(type): + try container.encode(Int32.max as Int32, forKey: "_rawValue") + try container.encode(type as Int32, forKey: "type") } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index 005f450a05..e829e578d6 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -3594,6 +3594,8 @@ private func serializeGroupCallMessage(randomId: Int64, text: String, entities: case let .CustomEmoji(_, fileId): entityDict["_"] = "messageEntityCustomEmoji" entityDict["document_id"] = "\(fileId)" + case .FormattedDate: + return nil case .Custom: return nil }