no message

This commit is contained in:
Peter 2016-11-22 21:33:06 +03:00
parent 3b147df197
commit e89f708e6f
24 changed files with 1150 additions and 151 deletions

View File

@ -210,10 +210,20 @@
D0B8444F1DAB9206005F29E1 /* ChangePeerNotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B843961DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift */; };
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */; };
D0CAF2EA1D75EC600011F558 /* MtProtoKitDynamic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */; };
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */; };
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354F1DE36900000195EB /* ChatContextResult.swift */; };
D0DC35511DE36908000195EB /* RequestChatContextResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */; };
D0DC35521DE36908000195EB /* ChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC354F1DE36900000195EB /* ChatContextResult.swift */; };
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C891D819C7E008AEB01 /* JoinChannel.swift */; };
D0DF0C911D81A857008AEB01 /* ImageRepresentationsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */; };
D0DF0C931D81AD09008AEB01 /* MessageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */; };
D0DF0CA81D82BF32008AEB01 /* PeerParticipants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0CA71D82BF32008AEB01 /* PeerParticipants.swift */; };
D0E35A0E1DE4953E00BC6096 /* FetchHttpResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0D1DE4953E00BC6096 /* FetchHttpResource.swift */; };
D0E35A101DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */; };
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */; };
D0E35A131DE4C69100BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */; };
D0E35A141DE4C69C00BC6096 /* FetchHttpResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0D1DE4953E00BC6096 /* FetchHttpResource.swift */; };
D0E35A151DE4C6A200BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */; };
D0F3CC791DDE2859008148FA /* SearchMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0D711D631ABA00955575 /* SearchMessages.swift */; };
D0F3CC7A1DDE2859008148FA /* RequestMessageActionCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC91C1DD5DA5E00E8160F /* RequestMessageActionCallback.swift */; };
D0F3CC7B1DDE2859008148FA /* RequestEditMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01AC9201DD5E7E500E8160F /* RequestEditMessage.swift */; };
@ -368,10 +378,15 @@
D0B843B11DA7FF30005F29E1 /* NBPhoneNumberUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBPhoneNumberUtil.m; sourceTree = "<group>"; };
D0B844521DAC0773005F29E1 /* TelegramUserPresence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramUserPresence.swift; sourceTree = "<group>"; };
D0CAF2E91D75EC600011F558 /* MtProtoKitDynamic.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MtProtoKitDynamic.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug-iphonesimulator/MtProtoKitDynamic.framework"; sourceTree = "<group>"; };
D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestChatContextResults.swift; sourceTree = "<group>"; };
D0DC354F1DE36900000195EB /* ChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatContextResult.swift; sourceTree = "<group>"; };
D0DF0C891D819C7E008AEB01 /* JoinChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinChannel.swift; sourceTree = "<group>"; };
D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRepresentationsUtils.swift; sourceTree = "<group>"; };
D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageUtils.swift; sourceTree = "<group>"; };
D0DF0CA71D82BF32008AEB01 /* PeerParticipants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerParticipants.swift; sourceTree = "<group>"; };
D0E35A0D1DE4953E00BC6096 /* FetchHttpResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchHttpResource.swift; sourceTree = "<group>"; };
D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageWithChatContextResult.swift; sourceTree = "<group>"; };
D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingChatContextResultMessageAttribute.swift; sourceTree = "<group>"; };
D0F3CC7C1DDE289E008148FA /* ResolvePeerByName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResolvePeerByName.swift; sourceTree = "<group>"; };
D0F7AB2B1DCE889D009AD9A1 /* EditedMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditedMessageAttribute.swift; sourceTree = "<group>"; };
D0F7AB2E1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplyMarkupMessageAttribute.swift; sourceTree = "<group>"; };
@ -536,6 +551,7 @@
D073CE5F1DCB9D14007511FD /* OutgoingMessageInfoAttribute.swift */,
D0F7AB2B1DCE889D009AD9A1 /* EditedMessageAttribute.swift */,
D0F7AB2E1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift */,
D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */,
);
name = Attributes;
sourceTree = "<group>";
@ -586,6 +602,7 @@
children = (
D03B0D431D6319F900955575 /* CloudFileMediaResource.swift */,
D03B0D391D6319E200955575 /* Fetch.swift */,
D0E35A0D1DE4953E00BC6096 /* FetchHttpResource.swift */,
);
name = Resources;
sourceTree = "<group>";
@ -635,6 +652,9 @@
D03B0D711D631ABA00955575 /* SearchMessages.swift */,
D01AC91C1DD5DA5E00E8160F /* RequestMessageActionCallback.swift */,
D01AC9201DD5E7E500E8160F /* RequestEditMessage.swift */,
D0DC354D1DE368F7000195EB /* RequestChatContextResults.swift */,
D0DC354F1DE36900000195EB /* ChatContextResult.swift */,
D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */,
);
name = Messages;
sourceTree = "<group>";
@ -936,6 +956,7 @@
D03B0D5F1D631A6900955575 /* Serialization.swift in Sources */,
D03B0D441D6319F900955575 /* CloudFileMediaResource.swift in Sources */,
D01AC9211DD5E7E500E8160F /* RequestEditMessage.swift in Sources */,
D0E35A0E1DE4953E00BC6096 /* FetchHttpResource.swift in Sources */,
D01AC91D1DD5DA5E00E8160F /* RequestMessageActionCallback.swift in Sources */,
D09BB6B61DB0428000A905C0 /* PendingMessageUploadedContent.swift in Sources */,
D0F3CC7D1DDE289E008148FA /* ResolvePeerByName.swift in Sources */,
@ -958,6 +979,7 @@
D03B0CF51D62250800955575 /* TelegramMediaContact.swift in Sources */,
D03B0CFB1D62250800955575 /* TelegramMediaWebpage.swift in Sources */,
D09A2FEB1D7CDC320018FB72 /* PeerAccessRestrictionInfo.swift in Sources */,
D0E35A101DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */,
D003702B1DA42586004308D3 /* PhoneNumber.swift in Sources */,
D03B0CF91D62250800955575 /* TelegramMediaMap.swift in Sources */,
D03B0D671D631A8B00955575 /* AccountViewTracker.swift in Sources */,
@ -974,8 +996,10 @@
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
D0B843971DA7FBBC005F29E1 /* ChangePeerNotificationSettings.swift in Sources */,
D09BB6B41DB02C2B00A905C0 /* PendingMessageManager.swift in Sources */,
D0DC354E1DE368F7000195EB /* RequestChatContextResults.swift in Sources */,
D0B843851DA6EDC4005F29E1 /* CachedChannelData.swift in Sources */,
D0B843831DA6EDB8005F29E1 /* CachedGroupData.swift in Sources */,
D0E35A121DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
D0B844531DAC0773005F29E1 /* TelegramUserPresence.swift in Sources */,
D0B843871DA6F705005F29E1 /* UpdateCachedPeerData.swift in Sources */,
D03B0D6D1D631AA300955575 /* ContactManagement.swift in Sources */,
@ -985,6 +1009,7 @@
D03B0D0C1D62255C00955575 /* StateManagement.swift in Sources */,
D073CE5D1DCB97F6007511FD /* ForwardSourceInfoAttribute.swift in Sources */,
D03B0D721D631ABA00955575 /* SearchMessages.swift in Sources */,
D0DC35501DE36900000195EB /* ChatContextResult.swift in Sources */,
D03B0D5C1D631A6900955575 /* Download.swift in Sources */,
D0B843C71DA7FF30005F29E1 /* NBPhoneNumberDefines.m in Sources */,
D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */,
@ -1046,6 +1071,7 @@
D0B8443B1DAB91EF005F29E1 /* Holes.swift in Sources */,
D0F3CC7A1DDE2859008148FA /* RequestMessageActionCallback.swift in Sources */,
D073CEA11DCBF3D3007511FD /* StickerPack.swift in Sources */,
D0E35A141DE4C69C00BC6096 /* FetchHttpResource.swift in Sources */,
D0B8443E1DAB91EF005F29E1 /* SynchronizePeerReadState.swift in Sources */,
D0B8440D1DAB91CD005F29E1 /* ImageRepresentationsUtils.swift in Sources */,
D03C536A1DAD5CA9004C17B3 /* TelegramUser.swift in Sources */,
@ -1057,6 +1083,7 @@
D0B8442D1DAB91E0005F29E1 /* NBMetadataCoreTest.m in Sources */,
D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */,
D0B844401DAB91EF005F29E1 /* UpdateMessageService.swift in Sources */,
D0E35A131DE4C69100BC6096 /* OutgoingChatContextResultMessageAttribute.swift in Sources */,
D0B418961D7E0580004562A4 /* TelegramMediaFile.swift in Sources */,
D0B4189B1D7E0580004562A4 /* TelegramMediaWebpage.swift in Sources */,
D0B844341DAB91E0005F29E1 /* NBPhoneNumberDefines.m in Sources */,
@ -1067,6 +1094,7 @@
D0B418A61D7E0592004562A4 /* CloudFileMediaResource.swift in Sources */,
D073CEA51DCBF3F5007511FD /* StickerManagement.swift in Sources */,
D03C536D1DAD5CA9004C17B3 /* ApiGroupOrChannel.swift in Sources */,
D0E35A151DE4C6A200BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */,
D0B418A91D7E0597004562A4 /* Buffer.swift in Sources */,
D0B8442E1DAB91E0005F29E1 /* NBMetadataCoreTestMapper.m in Sources */,
D0B8443F1DAB91EF005F29E1 /* UpdateGroup.swift in Sources */,
@ -1080,9 +1108,11 @@
D0B8444B1DAB91FD005F29E1 /* ManagedSynchronizePeerReadStates.swift in Sources */,
D073CE6C1DCBCF17007511FD /* TextEntitiesMessageAttribute.swift in Sources */,
D03C53751DAD5CA9004C17B3 /* TelegramUserPresence.swift in Sources */,
D0DC35521DE36908000195EB /* ChatContextResult.swift in Sources */,
D0F7AB301DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
D073CE6D1DCBCF17007511FD /* InlineBotMessageAttribute.swift in Sources */,
D0B8440F1DAB91CD005F29E1 /* Either.swift in Sources */,
D0DC35511DE36908000195EB /* RequestChatContextResults.swift in Sources */,
D03C536E1DAD5CA9004C17B3 /* PhoneNumber.swift in Sources */,
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */,
D0B8443C1DAB91EF005F29E1 /* SendUnsentMessage.swift in Sources */,

View File

@ -243,6 +243,7 @@ private var declaredEncodables: Void = {
declareEncodable(EditedMessageAttribute.self, f: { EditedMessageAttribute(decoder: $0) })
declareEncodable(ReplyMarkupMessageAttribute.self, f: { ReplyMarkupMessageAttribute(decoder: $0) })
declareEncodable(CachedResolvedByNamePeer.self, f: { CachedResolvedByNamePeer(decoder: $0) })
declareEncodable(OutgoingChatContextResultMessageAttribute.self, f: { OutgoingChatContextResultMessageAttribute(decoder: $0) })
return
}()

View File

@ -0,0 +1,411 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
import MtProtoKitMac
#else
import Postbox
import SwiftSignalKit
import MtProtoKitDynamic
#endif
public enum ChatContextResultMessage: Coding, Equatable {
case auto(caption: String, replyMarkup: ReplyMarkupMessageAttribute?)
case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?)
case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?)
case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?)
public init(decoder: Decoder) {
switch decoder.decodeInt32ForKey("_v") as Int32 {
case 0:
self = .auto(caption: decoder.decodeStringForKey("c"), replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 1:
self = .text(text: decoder.decodeStringForKey("t"), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, disableUrlPreview: decoder.decodeInt32ForKey("du") != 0, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 2:
self = .mapLocation(media: decoder.decodeObjectForKey("l") as! TelegramMediaMap, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 3:
self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
default:
self = .auto(caption: "", replyMarkup: nil)
}
}
public func encode(_ encoder: Encoder) {
switch self {
case let .auto(caption, replyMarkup):
encoder.encodeInt32(0, forKey: "_v")
encoder.encodeString(caption, forKey: "c")
if let replyMarkup = replyMarkup {
encoder.encodeObject(replyMarkup, forKey: "m")
} else {
encoder.encodeNil(forKey: "m")
}
case let .text(text, entities, disableUrlPreview, replyMarkup):
encoder.encodeInt32(1, forKey: "_v")
encoder.encodeString(text, forKey: "t")
if let entities = entities {
encoder.encodeObject(entities, forKey: "e")
} else {
encoder.encodeNil(forKey: "e")
}
encoder.encodeInt32(disableUrlPreview ? 1 : 0, forKey: "du")
if let replyMarkup = replyMarkup {
encoder.encodeObject(replyMarkup, forKey: "m")
} else {
encoder.encodeNil(forKey: "m")
}
case let .mapLocation(media, replyMarkup):
encoder.encodeInt32(2, forKey: "_v")
encoder.encodeObject(media, forKey: "l")
if let replyMarkup = replyMarkup {
encoder.encodeObject(replyMarkup, forKey: "m")
} else {
encoder.encodeNil(forKey: "m")
}
case let .contact(media, replyMarkup):
encoder.encodeInt32(3, forKey: "_v")
encoder.encodeObject(media, forKey: "c")
if let replyMarkup = replyMarkup {
encoder.encodeObject(replyMarkup, forKey: "m")
} else {
encoder.encodeNil(forKey: "m")
}
}
}
public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool {
switch lhs {
case let .auto(lhsCaption, lhsReplyMarkup):
if case let .auto(rhsCaption, rhsReplyMarkup) = rhs {
if lhsCaption != rhsCaption {
return false
}
if lhsReplyMarkup != rhsReplyMarkup {
return false
}
return true
} else {
return false
}
case let .text(lhsText, lhsEntities, lhsDisableUrlPreview, lhsReplyMarkup):
if case let .text(rhsText, rhsEntities, rhsDisableUrlPreview, rhsReplyMarkup) = rhs {
if lhsText != rhsText {
return false
}
if lhsEntities != rhsEntities {
return false
}
if lhsDisableUrlPreview != rhsDisableUrlPreview {
return false
}
if lhsReplyMarkup != rhsReplyMarkup {
return false
}
return true
} else {
return false
}
case let .mapLocation(lhsMedia, lhsReplyMarkup):
if case let .mapLocation(rhsMedia, rhsReplyMarkup) = rhs {
if !lhsMedia.isEqual(rhsMedia) {
return false
}
if lhsReplyMarkup != rhsReplyMarkup {
return false
}
return true
} else {
return false
}
case let .contact(lhsMedia, lhsReplyMarkup):
if case let .contact(rhsMedia, rhsReplyMarkup) = rhs {
if !lhsMedia.isEqual(rhsMedia) {
return false
}
if lhsReplyMarkup != rhsReplyMarkup {
return false
}
return true
} else {
return false
}
}
}
}
public enum ChatContextResult: Equatable {
case externalReference(id: String, type: String, title: String?, description: String?, url: String?, thumbnailUrl: String?, contentUrl: String?, contentType: String?, dimensions: CGSize?, duration: Int32?, message: ChatContextResultMessage)
case internalReference(id: String, type: String, title: String?, description: String?, image: TelegramMediaImage?, file: TelegramMediaFile?, message: ChatContextResultMessage)
public var id: String {
switch self {
case let .externalReference(id, _, _, _, _, _, _, _, _, _, _):
return id
case let .internalReference(id, _, _, _, _, _, _):
return id
}
}
public var type: String {
switch self {
case let .externalReference(_, type, _, _, _, _, _, _, _, _, _):
return type
case let .internalReference(_, type, _, _, _, _, _):
return type
}
}
public var title: String? {
switch self {
case let .externalReference(_, _, title, _, _, _, _, _, _, _, _):
return title
case let .internalReference(_, _, title, _, _, _, _):
return title
}
}
public var description: String? {
switch self {
case let .externalReference(_, _, _, description, _, _, _, _, _, _, _):
return description
case let .internalReference(_, _, _, description, _, _, _):
return description
}
}
public var message: ChatContextResultMessage {
switch self {
case let .externalReference(_, _, _, _, _, _, _, _, _, _, message):
return message
case let .internalReference(_, _, _, _, _, _, message):
return message
}
}
public static func ==(lhs: ChatContextResult, rhs: ChatContextResult) -> Bool {
switch lhs {
case let .externalReference(lhsId, lhsType, lhsTitle, lhsDescription, lhsUrl, lhsThumbnailUrl, lhsContentUrl, lhsContentType, lhsDimensions, lhsDuration, lhsMessage):
if case let .externalReference(rhsId, rhsType, rhsTitle, rhsDescription, rhsUrl, rhsThumbnailUrl, rhsContentUrl, rhsContentType, rhsDimensions, rhsDuration, rhsMessage) = rhs {
if lhsId != rhsId {
return false
}
if lhsType != rhsType {
return false
}
if lhsTitle != rhsTitle {
return false
}
if lhsDescription != rhsDescription {
return false
}
if lhsUrl != rhsUrl {
return false
}
if lhsThumbnailUrl != rhsThumbnailUrl {
return false
}
if lhsContentUrl != rhsContentUrl {
return false
}
if lhsContentType != rhsContentType {
return false
}
if lhsDimensions != rhsDimensions {
return false
}
if lhsDuration != rhsDuration {
return false
}
if lhsMessage != rhsMessage {
return false
}
return true
} else {
return false
}
case let .internalReference(lhsId, lhsType, lhsTitle, lhsDescription, lhsImage, lhsFile, lhsMessage):
if case let .internalReference(rhsId, rhsType, rhsTitle, rhsDescription, rhsImage, rhsFile, rhsMessage) = rhs {
if lhsId != rhsId {
return false
}
if lhsType != rhsType {
return false
}
if lhsTitle != rhsTitle {
return false
}
if lhsDescription != rhsDescription {
return false
}
if let lhsImage = lhsImage, let rhsImage = rhsImage {
if !lhsImage.isEqual(rhsImage) {
return false
}
} else if (lhsImage != nil) != (rhsImage != nil) {
return false
}
if let lhsFile = lhsFile, let rhsFile = rhsFile {
if !lhsFile.isEqual(rhsFile) {
return false
}
} else if (lhsFile != nil) != (rhsFile != nil) {
return false
}
if lhsMessage != rhsMessage {
return false
}
return true
} else {
return false
}
}
}
}
public enum ChatContextResultCollectionPresentation {
case media
case list
}
public struct ChatContextResultSwitchPeer: Equatable {
public let text: String
public let startParam: String
public static func ==(lhs: ChatContextResultSwitchPeer, rhs: ChatContextResultSwitchPeer) -> Bool {
return lhs.text == rhs.text && lhs.startParam == rhs.startParam
}
}
public final class ChatContextResultCollection: Equatable {
public let botId: PeerId
public let queryId: Int64
public let nextOffset: String?
public let presentation: ChatContextResultCollectionPresentation
public let switchPeer: ChatContextResultSwitchPeer?
public let results: [ChatContextResult]
public let cacheTimeout: Int32
init(botId: PeerId, queryId: Int64, nextOffset: String?, presentation: ChatContextResultCollectionPresentation, switchPeer: ChatContextResultSwitchPeer?, results: [ChatContextResult], cacheTimeout: Int32) {
self.botId = botId
self.queryId = queryId
self.nextOffset = nextOffset
self.presentation = presentation
self.switchPeer = switchPeer
self.results = results
self.cacheTimeout = cacheTimeout
}
public static func ==(lhs: ChatContextResultCollection, rhs: ChatContextResultCollection) -> Bool {
if lhs.botId != rhs.botId {
return false
}
if lhs.queryId != rhs.queryId {
return false
}
if lhs.nextOffset != rhs.nextOffset {
return false
}
if lhs.presentation != rhs.presentation {
return false
}
if lhs.switchPeer != rhs.switchPeer {
return false
}
if lhs.results != rhs.results {
return false
}
if lhs.cacheTimeout != rhs.cacheTimeout {
return false
}
return true
}
}
extension ChatContextResultMessage {
init(apiMessage: Api.BotInlineMessage) {
switch apiMessage {
case let .botInlineMessageMediaAuto(_, caption, replyMarkup):
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .auto(caption: caption, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageText(flags, message, entities, replyMarkup):
var parsedEntities: TextEntitiesMessageAttribute?
if let entities = entities, !entities.isEmpty {
parsedEntities = TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities))
}
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .text(text: message, entities: parsedEntities, disableUrlPreview: (flags & (1 << 0)) != 0, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageMediaGeo(_, geo, replyMarkup):
let media = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil)
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .mapLocation(media: media, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageMediaVenue(_, geo, title, address, provider, venueId, replyMarkup):
let media = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId)
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .mapLocation(media: media, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageMediaContact(_, phoneNumber, firstName, lastName, replyMarkup):
let media = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: nil)
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .contact(media: media, replyMarkup: parsedReplyMarkup)
}
}
}
extension ChatContextResult {
init(apiResult: Api.BotInlineResult) {
switch apiResult {
case let .botInlineResult(_, id, type, title, description, url, thumbUrl, contentUrl, contentType, w, h, duration, sendMessage):
var dimensions: CGSize?
if let w = w, let h = h {
dimensions = CGSize(width: CGFloat(w), height: CGFloat(h))
}
self = .externalReference(id: id, type: type, title: title, description: description, url: url, thumbnailUrl: thumbUrl, contentUrl: contentUrl, contentType: contentType, dimensions: dimensions, duration: duration, message: ChatContextResultMessage(apiMessage: sendMessage))
case let .botInlineMediaResult(flags, id, type, photo, document, title, description, sendMessage):
var image: TelegramMediaImage?
var file: TelegramMediaFile?
if let photo = photo, let parsedImage = telegramMediaImageFromApiPhoto(photo) {
image = parsedImage
}
if let document = document, let parsedFile = telegramMediaFileFromApiDocument(document) {
file = parsedFile
}
self = .internalReference(id: id, type: type, title: title, description: description, image: image, file: file, message: ChatContextResultMessage(apiMessage: sendMessage))
}
}
}
extension ChatContextResultSwitchPeer {
init(apiSwitchPeer: Api.InlineBotSwitchPM) {
switch apiSwitchPeer {
case let .inlineBotSwitchPM(text, startParam):
self.init(text: text, startParam: startParam)
}
}
}
extension ChatContextResultCollection {
convenience init(apiResults: Api.messages.BotResults, botId: PeerId) {
switch apiResults {
case let .botResults(flags, queryId, nextOffset, switchPm, results, cacheTime):
var switchPeer: ChatContextResultSwitchPeer?
if let switchPm = switchPm {
switchPeer = ChatContextResultSwitchPeer(apiSwitchPeer: switchPm)
}
self.init(botId: botId, queryId: queryId, nextOffset: nextOffset, presentation: (flags & (1 << 0) != 0) ? .media : .list, switchPeer: switchPeer, results: results.map { ChatContextResult(apiResult: $0) }, cacheTimeout: cacheTime)
}
}
}

View File

@ -132,7 +132,7 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource {
public let datacenterId: Int
let fileId: Int64
let accessHash: Int64
public let size: Int
public let size: Int?
public var id: MediaResourceId {
return CloudDocumentMediaResourceId(datacenterId: self.datacenterId, fileId: self.fileId, accessHash: self.accessHash)
@ -142,7 +142,7 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource {
return Api.InputFileLocation.inputDocumentFileLocation(id: self.fileId, accessHash: self.accessHash, version: 0)
}
public init(datacenterId: Int, fileId: Int64, accessHash: Int64, size: Int) {
public init(datacenterId: Int, fileId: Int64, accessHash: Int64, size: Int?) {
self.datacenterId = datacenterId
self.fileId = fileId
self.accessHash = accessHash
@ -153,14 +153,22 @@ public class CloudDocumentMediaResource: TelegramCloudMediaResource {
self.datacenterId = Int(decoder.decodeInt32ForKey("d") as Int32)
self.fileId = decoder.decodeInt64ForKey("f")
self.accessHash = decoder.decodeInt64ForKey("a")
self.size = Int(decoder.decodeInt32ForKey("n") as Int32)
if let size = (decoder.decodeInt32ForKey("n") as Int32?) {
self.size = Int(size)
} else {
self.size = nil
}
}
public func encode(_ encoder: Encoder) {
encoder.encodeInt32(Int32(self.datacenterId), forKey: "d")
encoder.encodeInt64(self.fileId, forKey: "f")
encoder.encodeInt64(self.accessHash, forKey: "a")
encoder.encodeInt32(Int32(size), forKey: "n")
if let size = self.size {
encoder.encodeInt32(Int32(size), forKey: "n")
} else {
encoder.encodeNil(forKey: "n")
}
}
public func isEqual(to: TelegramMediaResource) -> Bool {
@ -320,6 +328,66 @@ public class LocalFileReferenceMediaResource: TelegramMediaResource {
}
}
public struct HttpReferenceMediaResourceId: MediaResourceId {
public let url: String
public func isEqual(to: MediaResourceId) -> Bool {
if let to = to as? HttpReferenceMediaResourceId {
return self.url == to.url
} else {
return false
}
}
public var hashValue: Int {
return self.url.hashValue
}
public var uniqueId: String {
return "http-\(persistentHash32(self.url))"
}
}
public final class HttpReferenceMediaResource: TelegramMediaResource {
public let url: String
public let size: Int?
public init(url: String, size: Int?) {
self.url = url
self.size = size
}
public required init(decoder: Decoder) {
self.url = decoder.decodeStringForKey("u")
if let size = (decoder.decodeInt32ForKey("s") as Int32?) {
self.size = Int(size)
} else {
self.size = nil
}
}
public func encode(_ encoder: Encoder) {
encoder.encodeString(self.url, forKey: "u")
if let size = self.size {
encoder.encodeInt32(Int32(size), forKey: "s")
} else {
encoder.encodeNil(forKey: "s")
}
}
public var id: MediaResourceId {
return HttpReferenceMediaResourceId(url: self.url)
}
public func isEqual(to: TelegramMediaResource) -> Bool {
if let to = to as? HttpReferenceMediaResource {
return to.url == self.url
} else {
return false
}
}
}
func mediaResourceFromApiFileLocation(_ fileLocation: Api.FileLocation, size: Int?) -> TelegramMediaResource? {
switch fileLocation {
case let .fileLocation(dcId, volumeId, localId, secret):

View File

@ -8,19 +8,38 @@ import Foundation
#endif
public enum EnqueueMessage {
case message(text: String, media: Media?, replyToMessageId: MessageId?)
case message(text: String, attributes: [MessageAttribute], media: Media?, replyToMessageId: MessageId?)
case forward(source: MessageId)
public func withUpdatedReplyToMessageId(_ replyToMessageId: MessageId?) -> EnqueueMessage {
switch self {
case let .message(text, media, _):
return .message(text: text, media: media, replyToMessageId: replyToMessageId)
case let .message(text, attributes, media, _):
return .message(text: text, attributes: attributes, media: media, replyToMessageId: replyToMessageId)
case .forward:
return self
}
}
}
private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAttribute]) -> [MessageAttribute] {
return attributes.filter { attribute in
switch attribute {
case let _ as TextEntitiesMessageAttribute:
return true
case let _ as InlineBotMessageAttribute:
return true
case let _ as OutgoingMessageInfoAttribute:
return true
case let _ as ReplyMarkupMessageAttribute:
return true
case let _ as OutgoingChatContextResultMessageAttribute:
return true
default:
return false
}
}
}
private func filterMessageAttributesForForwardedMessage(_ attributes: [MessageAttribute]) -> [MessageAttribute] {
return attributes.filter { attribute in
switch attribute {
@ -49,7 +68,9 @@ public func enqueueMessages(account: Account, peerId: PeerId, messages: [Enqueue
attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId))
switch message {
case let .message(text, media, replyToMessageId):
case let .message(text, requestedAttributes, media, replyToMessageId):
attributes.append(contentsOf: filterMessageAttributesForOutgoingMessage(requestedAttributes))
if let replyToMessageId = replyToMessageId {
attributes.append(ReplyMessageAttribute(messageId: replyToMessageId))
}

View File

@ -79,6 +79,8 @@ func fetchResource(account: Account, resource: MediaResource, range: Range<Int>)
#endif
} else if let localFileResource = resource as? LocalFileReferenceMediaResource {
return fetchLocalFileResource(path: localFileResource.localFilePath)
} else if let httpReference = resource as? HttpReferenceMediaResource {
return fetchHttpResource(url: httpReference.url)
}
return .never()
}

View File

@ -0,0 +1,29 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
import MtProtoKitMac
#else
import Postbox
import SwiftSignalKit
import MtProtoKitDynamic
#endif
func fetchHttpResource(url: String) -> Signal<MediaResourceDataFetchResult, NoError> {
if let url = URL(string: url) {
let signal = MTHttpRequestOperation.data(forHttpUrl: url)!
return Signal { subscriber in
let disposable = signal.start(next: { next in
let fetchResult = MediaResourceDataFetchResult(data: next as! Data, complete: true)
subscriber.putNext(fetchResult)
subscriber.putCompletion()
})
return ActionDisposable {
disposable?.dispose()
}
}
} else {
return .never()
}
}

View File

@ -0,0 +1,26 @@
import Foundation
#if os(macOS)
import PostboxMac
#else
import Postbox
#endif
public class OutgoingChatContextResultMessageAttribute: MessageAttribute {
public let queryId: Int64
public let id: String
init(queryId: Int64, id: String) {
self.queryId = queryId
self.id = id
}
required public init(decoder: Decoder) {
self.queryId = decoder.decodeInt64ForKey("q")
self.id = decoder.decodeStringForKey("i")
}
public func encode(_ encoder: Encoder) {
encoder.encodeInt64(self.queryId, forKey: "q")
encoder.encodeString(self.id, forKey: "i")
}
}

View File

@ -0,0 +1,152 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
#else
import Postbox
import SwiftSignalKit
#endif
/*
if ([result isKindOfClass:[TGBotContextMediaResult class]]) {
TGBotContextMediaResult *concreteResult = (TGBotContextMediaResult *)result;
if ([concreteResult.type isEqualToString:@"game"]) {
TGGameMediaAttachment *gameMedia = [[TGGameMediaAttachment alloc] initWithGameId:0 accessHash:0 shortName:nil title:concreteResult.title gameDescription:concreteResult.resultDescription photo:concreteResult.photo document:concreteResult.document];
[strongSelf->_companion controllerWantsToSendGame:gameMedia asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
} else if (concreteResult.document != nil) {
TGDocumentAttributeVideo *video = nil;
bool isAnimated = false;
for (id attribute in concreteResult.document.attributes) {
if ([attribute isKindOfClass:[TGDocumentAttributeVideo class]]) {
video = attribute;
} else if ([attribute isKindOfClass:[TGDocumentAttributeAnimated class]]) {
isAnimated = true;
}
}
if (video != nil && !isAnimated) {
TGVideoMediaAttachment *videoMedia = [[TGVideoMediaAttachment alloc] init];
videoMedia = [[TGVideoMediaAttachment alloc] init];
videoMedia.videoId = concreteResult.document.documentId;
videoMedia.accessHash = concreteResult.document.accessHash;
videoMedia.duration = video.duration;
videoMedia.dimensions = video.size;
videoMedia.thumbnailInfo = concreteResult.document.thumbnailInfo;
TGVideoInfo *videoInfo = [[TGVideoInfo alloc] init];
[videoInfo addVideoWithQuality:1 url:[[NSString alloc] initWithFormat:@"video:%lld:%lld:%d:%d", videoMedia.videoId, videoMedia.accessHash, concreteResult.document.datacenterId, concreteResult.document.size] size:concreteResult.document.size];
videoMedia.videoInfo = videoInfo;
[strongSelf->_companion controllerWantsToSendRemoteVideoWithMedia:videoMedia asReplyToMessageId:[strongSelf currentReplyMessageId] text:concreteMessage.caption botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
} else {
[strongSelf->_companion controllerWantsToSendRemoteDocument:concreteResult.document asReplyToMessageId:[strongSelf currentReplyMessageId] text:concreteMessage.caption botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
}
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
} else if (concreteResult.photo != nil) {
[strongSelf->_companion controllerWantsToSendRemoteImage:concreteResult.photo text:concreteMessage.caption asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
}
} else if ([result isKindOfClass:[TGBotContextExternalResult class]]) {
TGBotContextExternalResult *concreteResult = (TGBotContextExternalResult *)result;
if ([concreteResult.type isEqualToString:@"gif"]) {
TGExternalGifSearchResult *externalGifSearchResult = [[TGExternalGifSearchResult alloc] initWithUrl:concreteResult.url originalUrl:concreteResult.originalUrl thumbnailUrl:concreteResult.thumbUrl size:concreteResult.size];
id description = [strongSelf->_companion documentDescriptionFromExternalGifSearchResult:externalGifSearchResult text:concreteMessage.caption botContextResult:botContextResult];
if (description != nil) {
[strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId] botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
[TGRecentContextBotsSignal addRecentBot:results.userId];
}
} else if ([concreteResult.type isEqualToString:@"photo"]) {
TGExternalImageSearchResult *externalImageSearchResult = [[TGExternalImageSearchResult alloc] initWithUrl:concreteResult.url originalUrl:concreteResult.originalUrl thumbnailUrl:concreteResult.thumbUrl title:concreteResult.title size:concreteResult.size];
id description = [strongSelf->_companion imageDescriptionFromExternalImageSearchResult:externalImageSearchResult text:concreteMessage.caption botContextResult:botContextResult];
if (description != nil) {
[strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId] botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
[TGRecentContextBotsSignal addRecentBot:results.userId];
}
} else if ([concreteResult.type isEqualToString:@"audio"] || [concreteResult.type isEqualToString:@"voice"] || [concreteResult.type isEqualToString:@"file"]) {
id description = [strongSelf->_companion documentDescriptionFromBotContextResult:concreteResult text:concreteMessage.caption botContextResult:botContextResult];
if (description != nil) {
[strongSelf->_companion controllerWantsToSendImagesWithDescriptions:@[description] asReplyToMessageId:[strongSelf currentReplyMessageId] botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
[TGRecentContextBotsSignal addRecentBot:results.userId];
}
} else {
if (![_companion allowMessageForwarding] && !TGAppDelegateInstance.allowSecretWebpages) {
for (id result in [TGMessage textCheckingResultsForText:concreteMessage.caption highlightMentionsAndTags:false highlightCommands:false entities:nil]) {
if ([result isKindOfClass:[NSTextCheckingResult class]] && ((NSTextCheckingResult *)result).resultType == NSTextCheckingTypeLink) {
[_companion maybeAskForSecretWebpages];
return;
}
}
}
[strongSelf->_companion controllerWantsToSendTextMessage:concreteMessage.caption entities:@[] asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:[strongSelf currentForwardMessages] disableLinkPreviews:false botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
}
}
} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageText class]]) {
TGBotContextResultSendMessageText *concreteMessage = (TGBotContextResultSendMessageText *)result.sendMessage;
if (![_companion allowMessageForwarding] && !TGAppDelegateInstance.allowSecretWebpages) {
for (id result in [TGMessage textCheckingResultsForText:concreteMessage.message highlightMentionsAndTags:false highlightCommands:false entities:nil]) {
if ([result isKindOfClass:[NSTextCheckingResult class]] && ((NSTextCheckingResult *)result).resultType == NSTextCheckingTypeLink) {
[_companion maybeAskForSecretWebpages];
return;
}
}
}
[strongSelf->_companion controllerWantsToSendTextMessage:concreteMessage.message entities:concreteMessage.entities asReplyToMessageId:[strongSelf currentReplyMessageId] withAttachedMessages:[strongSelf currentForwardMessages] disableLinkPreviews:false botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageGeo class]]) {
TGBotContextResultSendMessageGeo *concreteMessage = (TGBotContextResultSendMessageGeo *)result.sendMessage;
[strongSelf->_companion controllerWantsToSendMapWithLatitude:concreteMessage.location.latitude longitude:concreteMessage.location.longitude venue:concreteMessage.location.venue asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
} else if ([result.sendMessage isKindOfClass:[TGBotContextResultSendMessageContact class]]) {
TGBotContextResultSendMessageContact *concreteMessage = (TGBotContextResultSendMessageContact *)result.sendMessage;
TGUser *contactUser = [[TGUser alloc] init];
contactUser.firstName = concreteMessage.contact.firstName;
contactUser.lastName = concreteMessage.contact.lastName;
contactUser.phoneNumber = concreteMessage.contact.phoneNumber;
[strongSelf->_companion controllerWantsToSendContact:contactUser asReplyToMessageId:[strongSelf currentReplyMessageId] botContextResult:botContextResult botReplyMarkup:concreteMessage.replyMarkup];
[strongSelf->_inputTextPanel.inputField setText:@"" animated:true];
}
}*/
public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCollection, _ result: ChatContextResult) -> EnqueueMessage? {
var attributes: [MessageAttribute] = []
attributes.append(OutgoingChatContextResultMessageAttribute(queryId: results.queryId, id: result.id))
attributes.append(InlineBotMessageAttribute(peerId: results.botId))
switch result.message {
case let .auto(caption, replyMarkup):
switch result {
case let .internalReference(id, type, title, description, image, file, message):
if let image = image {
return .message(text: "", attributes: attributes, media: image, replyToMessageId: nil)
} else if let file = file {
return .message(text: "", attributes: attributes, media: file, replyToMessageId: nil)
} else {
return nil
}
case let .externalReference(id, type, title, description, url, thumbnailUrl, contentUrl, contentType, dimensions, duration, message):
return nil
}
case let .text(text, entities, disableUrlPreview, replyMarkup):
if let entities = entities {
attributes.append(entities)
}
if let replyMarkup = replyMarkup {
attributes.append(replyMarkup)
}
return .message(text: text, attributes: attributes, media: nil, replyToMessageId: nil)
case let .mapLocation(media, replyMarkup):
if let replyMarkup = replyMarkup {
attributes.append(replyMarkup)
}
return .message(text: "", attributes: attributes, media: media, replyToMessageId: nil)
case let .contact(media, replyMarkup):
if let replyMarkup = replyMarkup {
attributes.append(replyMarkup)
}
return .message(text: "", attributes: attributes, media: media, replyToMessageId: nil)
}
}

View File

@ -7,90 +7,37 @@ import Foundation
import SwiftSignalKit
#endif
public func peerParticipants(account: Account, id: PeerId) -> Signal<[Peer], NoError> {
if id.namespace == Namespaces.Peer.CloudGroup || id.namespace == Namespaces.Peer.CloudChannel {
return account.postbox.loadedPeerWithId(id)
|> take(1)
|> mapToSignal { peer -> Signal<[Peer], NoError> in
if let group = peer as? TelegramGroup {
return account.network.request(Api.functions.messages.getFullChat(chatId: group.id.id))
|> retryRequest
|> map { result -> [Peer] in
switch result {
case let .chatFull(fullChat, chats, users):
var peerIds = Set<PeerId>()
switch fullChat {
case let .chatFull(_, participants, _, _, _, _):
switch participants {
case let .chatParticipants(_, participants, _):
for participant in participants {
let peerId: PeerId
switch participant {
case let .chatParticipant(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .chatParticipantAdmin(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .chatParticipantCreator(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
}
peerIds.insert(peerId)
}
case .chatParticipantsForbidden:
break
}
case .channelFull:
break
}
let peers: [Peer] = users.filter({ user in
return peerIds.contains(user.peerId)
}).map({ user in
return TelegramUser(user: user)
})
return peers
}
return []
}
} else if let channel = peer as? TelegramChannel {
if let inputChannel = apiInputChannel(channel) {
return account.network.request(Api.functions.channels.getParticipants(channel: inputChannel, filter: .channelParticipantsRecent, offset: 0, limit: 32))
|> retryRequest
|> map { result -> [Peer] in
switch result {
case let .channelParticipants(_, participants, users):
var peerIds = Set<PeerId>()
for participant in participants {
let peerId: PeerId
switch participant {
case let .channelParticipant(userId, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .channelParticipantCreator(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .channelParticipantEditor(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .channelParticipantKicked(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .channelParticipantModerator(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .channelParticipantSelf(userId, _, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
}
peerIds.insert(peerId)
}
let peers: [Peer] = users.filter({ user in
return peerIds.contains(user.peerId)
}).map({ user in
return TelegramUser(user: user)
})
return peers
}
}
} else {
return .single([])
}
}
return .single([])
private struct PeerParticipants: Equatable {
let peers: [Peer]
static func ==(lhs: PeerParticipants, rhs: PeerParticipants) -> Bool {
if lhs.peers.count != rhs.peers.count {
return false
}
} else {
return .single([])
for i in 0 ..< lhs.peers.count {
if !lhs.peers[i].isEqual(rhs.peers[i]) {
return false
}
}
return true
}
}
public func peerParticipants(account: Account, id: PeerId) -> Signal<[Peer], NoError> {
return account.viewTracker.peerView(id) |> map { view -> PeerParticipants in
if let cachedGroupData = view.cachedData as? CachedGroupData, let participants = cachedGroupData.participants {
var peers: [Peer] = []
for participant in participants.participants {
if let peer = view.peers[participant.peerId] {
peers.append(peer)
}
}
return PeerParticipants(peers: peers)
} else {
return PeerParticipants(peers: [])
}
}
|> distinctUntilChanged |> map { participants in
return participants.peers
}
}

View File

@ -224,6 +224,7 @@ public final class PendingMessageManager {
if let peer = modifier.getPeer(message.id.peerId), let inputPeer = apiInputPeer(peer) {
var uniqueId: Int64 = 0
var forwardSourceInfoAttribute: ForwardSourceInfoAttribute?
var outgoingChatContextResultAttribute: OutgoingChatContextResultMessageAttribute?
var replyMessageId: Int32?
for attribute in message.attributes {
@ -231,8 +232,10 @@ public final class PendingMessageManager {
replyMessageId = replyAttribute.messageId.id
} else if let outgoingInfo = attribute as? OutgoingMessageInfoAttribute {
uniqueId = outgoingInfo.uniqueId
} else if let forwardSourceInfo = attribute as? ForwardSourceInfoAttribute {
forwardSourceInfoAttribute = forwardSourceInfo
} else if let attribute = attribute as? ForwardSourceInfoAttribute {
forwardSourceInfoAttribute = attribute
} else if let attribute = attribute as? OutgoingChatContextResultMessageAttribute {
outgoingChatContextResultAttribute = attribute
}
}
@ -264,6 +267,11 @@ public final class PendingMessageManager {
} else {
sendMessageRequest = .fail(NoError())
}
case let .chatContextResult(chatContextResult):
sendMessageRequest = network.request(Api.functions.messages.sendInlineBotResult(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, randomId: uniqueId, queryId: chatContextResult.queryId, id: chatContextResult.id))
|> mapError { _ -> NoError in
return NoError()
}
}
return sendMessageRequest

View File

@ -11,6 +11,7 @@ enum PendingMessageUploadedContent {
case text(String)
case media(Api.InputMedia)
case forward(ForwardSourceInfoAttribute)
case chatContextResult(OutgoingChatContextResultMessageAttribute)
}
enum PendingMessageUploadedContentResult {
@ -19,6 +20,13 @@ enum PendingMessageUploadedContentResult {
}
func uploadedMessageContent(network: Network, postbox: Postbox, message: Message) -> Signal<PendingMessageUploadedContentResult, NoError> {
var outgoingChatContextResultAttribute: OutgoingChatContextResultMessageAttribute?
for attribute in message.attributes {
if let attribute = attribute as? OutgoingChatContextResultMessageAttribute {
outgoingChatContextResultAttribute = attribute
}
}
if let forwardInfo = message.forwardInfo {
var forwardSourceInfo: ForwardSourceInfoAttribute?
for attribute in message.attributes {
@ -31,6 +39,8 @@ func uploadedMessageContent(network: Network, postbox: Postbox, message: Message
} else {
return .never()
}
} else if let outgoingChatContextResultAttribute = outgoingChatContextResultAttribute {
return .single(.content(message, .chatContextResult(outgoingChatContextResultAttribute)))
} else if let media = message.media.first {
if let image = media as? TelegramMediaImage, let largestRepresentation = largestImageRepresentation(image.representations) {
return uploadedMediaImageContent(network: network, postbox: postbox, image: image, message: message)

View File

@ -5,30 +5,7 @@ import Foundation
import Postbox
#endif
/*
keyboardButton#a2fa4880 text:string = KeyboardButton;
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton;
keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
keyboardButtonGame#50f41ccf text:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
*/
/*
replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup;
replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> = ReplyMarkup;
replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
*/
public enum ReplyMarkupButtonAction: Coding {
public enum ReplyMarkupButtonAction: Coding, Equatable {
case text
case url(String)
case callback(MemoryBuffer)
@ -75,14 +52,61 @@ public enum ReplyMarkupButtonAction: Coding {
case let .switchInline(samePeer, query):
encoder.encodeInt32(5, forKey: "v")
encoder.encodeInt32(samePeer ? 1 : 0, forKey: "s")
encoder.encodeString(query, forKey: "1")
encoder.encodeString(query, forKey: "q")
case .openWebApp:
encoder.encodeInt32(6, forKey: "v")
}
}
public static func ==(lhs: ReplyMarkupButtonAction, rhs: ReplyMarkupButtonAction) -> Bool {
switch lhs {
case .text:
if case .text = rhs {
return true
} else {
return false
}
case let .url(url):
if case .url(url) = rhs {
return true
} else {
return false
}
case let .callback(data):
if case .callback(data) = rhs {
return true
} else {
return false
}
case .requestPhone:
if case .requestPhone = rhs {
return true
} else {
return false
}
case .requestMap:
if case .requestMap = rhs {
return true
} else {
return false
}
case let .switchInline(samePeer, query):
if case .switchInline(samePeer, query) = rhs {
return true
} else {
return false
}
case .openWebApp:
if case .openWebApp = rhs {
return true
} else {
return false
}
}
}
}
public struct ReplyMarkupButton: Coding {
public struct ReplyMarkupButton: Coding, Equatable {
public let title: String
public let action: ReplyMarkupButtonAction
@ -100,9 +124,13 @@ public struct ReplyMarkupButton: Coding {
encoder.encodeString(self.title, forKey: ".t")
self.action.encode(encoder)
}
public static func ==(lhs: ReplyMarkupButton, rhs: ReplyMarkupButton) -> Bool {
return lhs.title == rhs.title && lhs.action == rhs.action
}
}
public struct ReplyMarkupRow: Coding {
public struct ReplyMarkupRow: Coding, Equatable {
public let buttons: [ReplyMarkupButton]
init(buttons: [ReplyMarkupButton]) {
@ -116,6 +144,10 @@ public struct ReplyMarkupRow: Coding {
public func encode(_ encoder: Encoder) {
encoder.encodeObjectArray(self.buttons, forKey: "b")
}
public static func ==(lhs: ReplyMarkupRow, rhs: ReplyMarkupRow) -> Bool {
return lhs.buttons == rhs.buttons
}
}
public struct ReplyMarkupMessageFlags: OptionSet {
@ -135,7 +167,7 @@ public struct ReplyMarkupMessageFlags: OptionSet {
public static let inline = ReplyMarkupMessageFlags(rawValue: 1 << 3)
}
public class ReplyMarkupMessageAttribute: MessageAttribute {
public class ReplyMarkupMessageAttribute: MessageAttribute, Equatable {
public let rows: [ReplyMarkupRow]
public let flags: ReplyMarkupMessageFlags
@ -153,6 +185,10 @@ public class ReplyMarkupMessageAttribute: MessageAttribute {
encoder.encodeObjectArray(self.rows, forKey: "r")
encoder.encodeInt32(self.flags.rawValue, forKey: "f")
}
public static func ==(lhs: ReplyMarkupMessageAttribute, rhs: ReplyMarkupMessageAttribute) -> Bool {
return lhs.flags == rhs.flags && lhs.rows == rhs.rows
}
}
extension ReplyMarkupButton {

View File

@ -0,0 +1,34 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
import MtProtoKitMac
#else
import Postbox
import SwiftSignalKit
import MtProtoKitDynamic
#endif
public func requestChatContextResults(account: Account, botId: PeerId, peerId: PeerId, query: String, offset: String) -> Signal<ChatContextResultCollection?, NoError> {
return account.postbox.modify { modifier -> (bot: Peer, peer: Peer)? in
if let bot = modifier.getPeer(botId), let peer = modifier.getPeer(peerId) {
return (bot, peer)
} else {
return nil
}
}
|> mapToSignal { botAndPeer -> Signal<ChatContextResultCollection?, NoError> in
if let (bot, peer) = botAndPeer, let inputBot = apiInputUser(bot), let inputPeer = apiInputPeer(peer) {
var flags: Int32 = 0
return account.network.request(Api.functions.messages.getInlineBotResults(flags: flags, bot: inputBot, peer: inputPeer, geoPoint: nil, query: query, offset: offset))
|> map { result -> ChatContextResultCollection? in
return ChatContextResultCollection(apiResults: result, botId: bot.id)
}
|> `catch` { _ -> Signal<ChatContextResultCollection?, NoError> in
return .single(nil)
}
} else {
return .single(nil)
}
}
}

View File

@ -9,7 +9,13 @@ import Foundation
import MtProtoKitDynamic
#endif
public func requestMessageActionCallback(account: Account, messageId: MessageId, data: MemoryBuffer?) -> Signal<Void, NoError> {
public enum MessageActionCallbackResult {
case none
case alert(String)
case url(String)
}
public func requestMessageActionCallback(account: Account, messageId: MessageId, data: MemoryBuffer?) -> Signal<MessageActionCallbackResult, NoError> {
return account.postbox.loadedPeerWithId(messageId.peerId)
|> take(1)
|> mapToSignal { peer in
@ -22,11 +28,22 @@ public func requestMessageActionCallback(account: Account, messageId: MessageId,
}
return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer))
|> retryRequest
|> map { result in
return Void()
|> map { result -> MessageActionCallbackResult in
//messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
switch result {
case let .botCallbackAnswer(_, message, url, cacheTime):
if let message = message {
return .alert(message)
} else if let url = url {
return .url(url)
} else {
return .none
}
}
}
} else {
return .complete()
return .single(.none)
}
}
}

View File

@ -46,7 +46,12 @@ final class CachedResolvedByNamePeer: Coding {
private let resolvedByNamePeersCollectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 150, highWaterItemCount: 200)
public func resolvePeerByName(account: Account, name: String, ageLimit: Int32 = 60 * 5) -> Signal<PeerId?, NoError> {
public enum ResolvePeerByNameCachedPolicy {
case remoteIfEarlierThan(timestamp: Int32)
case remote
}
public func resolvePeerByName(account: Account, name: String, ageLimit: Int32 = 60 * 60) -> Signal<PeerId?, NoError> {
var normalizedName = name
if normalizedName.hasPrefix("@") {
normalizedName = normalizedName.substring(from: name.index(after: name.startIndex))

View File

@ -880,6 +880,16 @@ private func finalStateWithUpdates(account: Account, state: MutableState, update
if let date = date {
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000)
if updatedState.peers[peerId] == nil {
updatedState.updatePeer(peerId, { peer in
if peer == nil {
return TelegramUser(id: peerId, accessHash: nil, firstName: "Telegram Notifications", lastName: nil, username: nil, phone: nil, photo: [], botInfo: BotUserInfo(flags: [], inlinePlaceholder: nil))
} else {
return peer
}
})
}
var alreadyStored = false
if let storedMessages = updatedState.storedMessagesByPeerIdAndTimestamp[peerId] {
for index in storedMessages {

View File

@ -203,13 +203,11 @@ func textAndMediaFromApiMedia(_ media: Api.MessageMedia?) -> (String?, Media?) {
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId)
return (nil, mediaContact)
case let .messageMediaGeo(geo):
if let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil) {
return (nil, mediaMap)
}
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil)
return (nil, mediaMap)
case let .messageMediaVenue(geo, title, address, provider, venueId):
if let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId) {
return (nil, mediaMap)
}
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId)
return (nil, mediaMap)
case let .messageMediaDocument(document, caption):
if let mediaFile = telegramMediaFileFromApiDocument(document) {
return (caption, mediaFile)
@ -263,8 +261,6 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
return result
}
//message#c09be45f flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int = Message;
extension StoreMessage {
convenience init?(apiMessage: Api.Message) {
switch apiMessage {

View File

@ -92,7 +92,7 @@ public final class TelegramMediaFile: Media, Equatable {
public let resource: TelegramMediaResource
public let previewRepresentations: [TelegramMediaImageRepresentation]
public let mimeType: String
public let size: Int
public let size: Int?
public let attributes: [TelegramMediaFileAttribute]
public let peerIds: [PeerId] = []
@ -100,7 +100,7 @@ public final class TelegramMediaFile: Media, Equatable {
return self.fileId
}
public init(fileId: MediaId, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], mimeType: String, size: Int, attributes: [TelegramMediaFileAttribute]) {
public init(fileId: MediaId, resource: TelegramMediaResource, previewRepresentations: [TelegramMediaImageRepresentation], mimeType: String, size: Int?, attributes: [TelegramMediaFileAttribute]) {
self.fileId = fileId
self.resource = resource
self.previewRepresentations = previewRepresentations
@ -114,7 +114,11 @@ public final class TelegramMediaFile: Media, Equatable {
self.resource = decoder.decodeObjectForKey("r") as! TelegramMediaResource
self.previewRepresentations = decoder.decodeObjectArrayForKey("pr")
self.mimeType = decoder.decodeStringForKey("mt")
self.size = Int(decoder.decodeInt32ForKey("s"))
if let size = (decoder.decodeInt32ForKey("s") as Int32?) {
self.size = Int(size)
} else {
self.size = nil
}
self.attributes = decoder.decodeObjectArrayForKey("at")
}
@ -125,7 +129,11 @@ public final class TelegramMediaFile: Media, Equatable {
encoder.encodeObject(self.resource, forKey: "r")
encoder.encodeObjectArray(self.previewRepresentations, forKey: "pr")
encoder.encodeString(self.mimeType, forKey: "mt")
encoder.encodeInt32(Int32(self.size), forKey: "s")
if let size = self.size {
encoder.encodeInt32(Int32(size), forKey: "s")
} else {
encoder.encodeNil(forKey: "s")
}
encoder.encodeObjectArray(self.attributes, forKey: "at")
}

View File

@ -130,7 +130,7 @@ public final class TelegramMediaMap: Media {
}
}
public func telegramMediaMapFromApiGeoPoint(_ geo: Api.GeoPoint, title: String?, address: String?, provider: String?, venueId: String?) -> TelegramMediaMap? {
public func telegramMediaMapFromApiGeoPoint(_ geo: Api.GeoPoint, title: String?, address: String?, provider: String?, venueId: String?) -> TelegramMediaMap {
var venue: MapVenue?
if let title = title {
venue = MapVenue(title: title, address: address, provider: provider, id: venueId)
@ -146,6 +146,6 @@ public func telegramMediaMapFromApiGeoPoint(_ geo: Api.GeoPoint, title: String?,
}
return TelegramMediaMap(latitude: lat, longitude: long, geoPlace: geoPlace, venue: venue)
case .geoPointEmpty:
return nil
return TelegramMediaMap(latitude: 0.0, longitude: 0.0, geoPlace: nil, venue: venue)
}
}

View File

@ -5,6 +5,50 @@ import Foundation
import Postbox
#endif
public struct BotUserInfoFlags: OptionSet {
public var rawValue: Int32
public init() {
self.rawValue = 0
}
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let hasAccessToChatHistory = BotUserInfoFlags(rawValue: (1 << 0))
public static let worksWithGroups = BotUserInfoFlags(rawValue: (1 << 1))
public static let requiresGeolocationForInlineRequests = BotUserInfoFlags(rawValue: (1 << 2))
}
public struct BotUserInfo: Coding, Equatable {
public let flags: BotUserInfoFlags
public let inlinePlaceholder: String?
init(flags: BotUserInfoFlags, inlinePlaceholder: String?) {
self.flags = flags
self.inlinePlaceholder = inlinePlaceholder
}
public init(decoder: Decoder) {
self.flags = BotUserInfoFlags(rawValue: decoder.decodeInt32ForKey("f"))
self.inlinePlaceholder = decoder.decodeStringForKey("ip")
}
public func encode(_ encoder: Encoder) {
encoder.encodeInt32(self.flags.rawValue, forKey: "f")
if let inlinePlaceholder = self.inlinePlaceholder {
encoder.encodeString(inlinePlaceholder, forKey: "ip")
} else {
encoder.encodeNil(forKey: "ip")
}
}
public static func ==(lhs: BotUserInfo, rhs: BotUserInfo) -> Bool {
return lhs.flags == rhs.flags && lhs.inlinePlaceholder == rhs.inlinePlaceholder
}
}
public final class TelegramUser: Peer {
public let id: PeerId
public let accessHash: Int64?
@ -13,6 +57,7 @@ public final class TelegramUser: Peer {
public let username: String?
public let phone: String?
public let photo: [TelegramMediaImageRepresentation]
public let botInfo: BotUserInfo?
public var name: String {
if let firstName = self.firstName {
@ -32,7 +77,7 @@ public final class TelegramUser: Peer {
return .personName(first: self.firstName ?? "", last: self.lastName ?? "")
}
public init(id: PeerId, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: [TelegramMediaImageRepresentation]) {
public init(id: PeerId, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: [TelegramMediaImageRepresentation], botInfo: BotUserInfo?) {
self.id = id
self.accessHash = accessHash
self.firstName = firstName
@ -40,6 +85,7 @@ public final class TelegramUser: Peer {
self.username = username
self.phone = phone
self.photo = photo
self.botInfo = botInfo
}
public init(decoder: Decoder) {
@ -59,6 +105,12 @@ public final class TelegramUser: Peer {
self.phone = decoder.decodeStringForKey("p")
self.photo = decoder.decodeObjectArrayForKey("ph")
if let botInfo = decoder.decodeObjectForKey("bi", decoder: { return BotUserInfo(decoder: $0) }) as? BotUserInfo {
self.botInfo = botInfo
} else {
self.botInfo = nil
}
}
public func encode(_ encoder: Encoder) {
@ -83,6 +135,12 @@ public final class TelegramUser: Peer {
}
encoder.encodeObjectArray(self.photo, forKey: "ph")
if let botInfo = self.botInfo {
encoder.encodeObject(botInfo, forKey: "bi")
} else {
encoder.encodeNil(forKey: "bi")
}
}
public func isEqual(_ other: Peer) -> Bool {
@ -110,6 +168,9 @@ public final class TelegramUser: Peer {
return false
}
}
if self.botInfo != other.botInfo {
return false
}
return true
} else {
@ -118,10 +179,13 @@ public final class TelegramUser: Peer {
}
}
//user#d10d979a flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string = User;
public extension TelegramUser {
public convenience init(user: Api.User) {
switch user {
case let .user(_, id, accessHash, firstName, lastName, username, phone, photo, _, _, _, _):
case let .user(flags, id, accessHash, firstName, lastName, username, phone, photo, _, _, _, botInlinePlaceholder):
var telegramPhoto: [TelegramMediaImageRepresentation] = []
if let photo = photo {
switch photo {
@ -134,15 +198,29 @@ public extension TelegramUser {
break
}
}
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: id), accessHash: accessHash, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: telegramPhoto)
var botInfo: BotUserInfo?
if (flags & (1 << 14)) != 0 {
var botFlags = BotUserInfoFlags()
if (flags & (1 << 15)) != 0 {
botFlags.insert(.hasAccessToChatHistory)
}
if (flags & (1 << 16)) == 0 {
botFlags.insert(.worksWithGroups)
}
if (flags & (1 << 21)) == 0 {
botFlags.insert(.requiresGeolocationForInlineRequests)
}
botInfo = BotUserInfo(flags: botFlags, inlinePlaceholder: botInlinePlaceholder)
}
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: id), accessHash: accessHash, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: telegramPhoto, botInfo: botInfo)
case let .userEmpty(id):
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: id), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [])
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: id), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil)
}
}
public static func merge(_ lhs: TelegramUser?, rhs: Api.User) -> TelegramUser? {
switch rhs {
case let .user(_, _, accessHash, _, _, username, _, photo, _, _, _, _):
case let .user(flags, _, accessHash, _, _, username, _, photo, _, _, _, botInlinePlaceholder):
if let _ = accessHash {
return TelegramUser(user: rhs)
} else {
@ -159,7 +237,22 @@ public extension TelegramUser {
}
}
if let lhs = lhs {
return TelegramUser(id: lhs.id, accessHash: lhs.accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: username, phone: lhs.phone, photo: telegramPhoto)
var botInfo: BotUserInfo?
if (flags & (1 << 14)) != 0 {
var botFlags = BotUserInfoFlags()
if (flags & (1 << 15)) != 0 {
botFlags.insert(.hasAccessToChatHistory)
}
if (flags & (1 << 16)) == 0 {
botFlags.insert(.worksWithGroups)
}
if (flags & (1 << 21)) == 0 {
botFlags.insert(.requiresGeolocationForInlineRequests)
}
botInfo = BotUserInfo(flags: botFlags, inlinePlaceholder: botInlinePlaceholder)
}
return TelegramUser(id: lhs.id, accessHash: lhs.accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo)
} else {
return TelegramUser(user: rhs)
}

View File

@ -5,7 +5,7 @@ import Foundation
import Postbox
#endif
public enum MessageTextEntityType {
public enum MessageTextEntityType: Equatable {
case Unknown
case Mention
case Hashtag
@ -18,9 +18,86 @@ public enum MessageTextEntityType {
case Pre
case TextUrl(url: String)
case TextMention(peerId: PeerId)
public static func ==(lhs: MessageTextEntityType, rhs: MessageTextEntityType) -> Bool {
switch lhs {
case .Unknown:
if case .Unknown = rhs {
return true
} else {
return false
}
case .Mention:
if case .Mention = rhs {
return true
} else {
return false
}
case .Hashtag:
if case .Hashtag = rhs {
return true
} else {
return false
}
case .BotCommand:
if case .BotCommand = rhs {
return true
} else {
return false
}
case .Url:
if case .Url = rhs {
return true
} else {
return false
}
case .Email:
if case .Email = rhs {
return true
} else {
return false
}
case .Bold:
if case .Bold = rhs {
return true
} else {
return false
}
case .Italic:
if case .Italic = rhs {
return true
} else {
return false
}
case .Code:
if case .Code = rhs {
return true
} else {
return false
}
case .Pre:
if case .Pre = rhs {
return true
} else {
return false
}
case let .TextUrl(url):
if case let .TextUrl(url) = rhs {
return true
} else {
return false
}
case let .TextMention(peerId):
if case .TextMention(peerId) = rhs {
return true
} else {
return false
}
}
}
}
public struct MessageTextEntity: Coding {
public struct MessageTextEntity: Coding, Equatable {
public let range: Range<Int>
public let type: MessageTextEntityType
@ -92,9 +169,13 @@ public struct MessageTextEntity: Coding {
encoder.encodeInt64(peerId.toInt64(), forKey: "peerId")
}
}
public static func ==(lhs: MessageTextEntity, rhs: MessageTextEntity) -> Bool {
return lhs.range == rhs.range && lhs.type == rhs.type
}
}
public class TextEntitiesMessageAttribute: MessageAttribute {
public class TextEntitiesMessageAttribute: MessageAttribute, Equatable {
public let entities: [MessageTextEntity]
public var associatedPeerIds: [PeerId] {
@ -121,4 +202,8 @@ public class TextEntitiesMessageAttribute: MessageAttribute {
public func encode(_ encoder: Encoder) {
encoder.encodeObjectArray(self.entities, forKey: "entities")
}
public static func ==(lhs: TextEntitiesMessageAttribute, rhs: TextEntitiesMessageAttribute) -> Bool {
return lhs.entities == rhs.entities
}
}

View File

@ -17,7 +17,11 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos
return postbox.modify { modifier -> Void in
switch result {
case let .userFull(_, _, _, _, _, notifySettings, _, commonChatCount):
case let .userFull(_, user, _, _, _, notifySettings, _, commonChatCount):
let telegramUser = TelegramUser(user: user)
modifier.updatePeers([telegramUser], update: { _, updated -> Peer in
return updated
})
modifier.updatePeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)])
}
modifier.updatePeerCachedData(peerIds: [peerId], update: { peerId, _ in

View File

@ -232,6 +232,12 @@ extension Api.Update {
return [PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)]
case let .updateUserPhoto(userId, _, _, _):
return [PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)]
case let .updateServiceNotification(_, inboxDate, _, _, _, _):
if let _ = inboxDate {
return [PeerId(namespace: Namespaces.Peer.CloudUser, id: 777000)]
} else {
return []
}
default:
return []
}