mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
2ebe870735
@ -705,6 +705,8 @@ private final class NotificationServiceHandler {
|
|||||||
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
|
||||||
setupSharedLogger(rootPath: logsPath, path: logsPath)
|
setupSharedLogger(rootPath: logsPath, path: logsPath)
|
||||||
|
|
||||||
|
Logger.shared.log("NotificationService \(episode)", "Started handling notification")
|
||||||
|
|
||||||
initializeAccountManagement()
|
initializeAccountManagement()
|
||||||
|
|
||||||
|
@ -729,9 +729,22 @@ struct WidgetView: View {
|
|||||||
chatUpdateView(size: geometry.size)
|
chatUpdateView(size: geometry.size)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.background(Rectangle().foregroundColor(getBackgroundColor()))
|
|
||||||
.padding(0.0)
|
.padding(0.0)
|
||||||
.unredacted()
|
.unredacted()
|
||||||
|
.widgetBackground(Rectangle().foregroundColor(getBackgroundColor()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOSApplicationExtension 14.0, iOS 14.0, *)
|
||||||
|
extension View {
|
||||||
|
func widgetBackground(_ backgroundView: some View) -> some View {
|
||||||
|
if #available(iOSApplicationExtension 17.0, iOS 17.0, *) {
|
||||||
|
return containerBackground(for: .widget) {
|
||||||
|
backgroundView
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return background(backgroundView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,6 +779,17 @@ struct AvatarsWidgetView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBackgroundColor() -> Color {
|
||||||
|
switch colorScheme {
|
||||||
|
case .light:
|
||||||
|
return .white
|
||||||
|
case .dark:
|
||||||
|
return Color(.sRGB, red: 28.0 / 255.0, green: 28.0 / 255.0, blue: 30.0 / 255.0, opacity: 1.0)
|
||||||
|
@unknown default:
|
||||||
|
return .secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func itemView(index: Int) -> some View {
|
func itemView(index: Int) -> some View {
|
||||||
let peers: ParsedPeers?
|
let peers: ParsedPeers?
|
||||||
var isPlaceholder = false
|
var isPlaceholder = false
|
||||||
@ -821,6 +845,7 @@ struct AvatarsWidgetView: View {
|
|||||||
})
|
})
|
||||||
.padding(EdgeInsets(top: 10.0, leading: 10.0, bottom: 10.0, trailing: 10.0))
|
.padding(EdgeInsets(top: 10.0, leading: 10.0, bottom: 10.0, trailing: 10.0))
|
||||||
.unredacted()
|
.unredacted()
|
||||||
|
.widgetBackground(Rectangle().foregroundColor(getBackgroundColor()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -854,12 +879,22 @@ struct Static_Widget: Widget {
|
|||||||
public var body: some WidgetConfiguration {
|
public var body: some WidgetConfiguration {
|
||||||
let presentationData = WidgetPresentationData.getForExtension()
|
let presentationData = WidgetPresentationData.getForExtension()
|
||||||
|
|
||||||
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
if #available(iOSApplicationExtension 15.0, iOS 15.0, *) {
|
||||||
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
||||||
})
|
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||||
.supportedFamilies([.systemMedium])
|
})
|
||||||
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
|
.supportedFamilies([.systemMedium])
|
||||||
.description(presentationData.widgetChatsGalleryDescription)
|
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
|
||||||
|
.contentMarginsDisabled()
|
||||||
|
.description(presentationData.widgetChatsGalleryDescription)
|
||||||
|
} else {
|
||||||
|
return IntentConfiguration(kind: kind, intent: SelectFriendsIntent.self, provider: Provider(), content: { entry in
|
||||||
|
WidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||||
|
})
|
||||||
|
.supportedFamilies([.systemMedium])
|
||||||
|
.configurationDisplayName(presentationData.widgetChatsGalleryTitle)
|
||||||
|
.description(presentationData.widgetChatsGalleryDescription)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,12 +905,22 @@ struct Static_AvatarsWidget: Widget {
|
|||||||
public var body: some WidgetConfiguration {
|
public var body: some WidgetConfiguration {
|
||||||
let presentationData = WidgetPresentationData.getForExtension()
|
let presentationData = WidgetPresentationData.getForExtension()
|
||||||
|
|
||||||
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
|
if #available(iOSApplicationExtension 15.0, iOS 15.0, *) {
|
||||||
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
|
||||||
})
|
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||||
.supportedFamilies([.systemMedium])
|
})
|
||||||
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
|
.supportedFamilies([.systemMedium])
|
||||||
.description(presentationData.widgetShortcutsGalleryDescription)
|
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
|
||||||
|
.contentMarginsDisabled()
|
||||||
|
.description(presentationData.widgetShortcutsGalleryDescription)
|
||||||
|
} else {
|
||||||
|
return IntentConfiguration(kind: kind, intent: SelectAvatarFriendsIntent.self, provider: AvatarsProvider(), content: { entry in
|
||||||
|
AvatarsWidgetView(data: getWidgetData(contents: entry.contents), presentationData: presentationData)
|
||||||
|
})
|
||||||
|
.supportedFamilies([.systemMedium])
|
||||||
|
.configurationDisplayName(presentationData.widgetShortcutsGalleryTitle)
|
||||||
|
.description(presentationData.widgetShortcutsGalleryDescription)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import LocalAuthentication
|
import LocalAuthentication
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
import Security
|
||||||
|
|
||||||
public enum LocalAuthBiometricAuthentication {
|
public enum LocalAuthBiometricAuthentication {
|
||||||
case touchId
|
case touchId
|
||||||
@ -8,10 +9,65 @@ public enum LocalAuthBiometricAuthentication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct LocalAuth {
|
public struct LocalAuth {
|
||||||
public static let biometricAuthentication: LocalAuthBiometricAuthentication? = {
|
private static let customKeyIdPrefix = "$#_".data(using: .utf8)!
|
||||||
|
|
||||||
|
public enum DecryptionResult {
|
||||||
|
public enum Error {
|
||||||
|
case cancelled
|
||||||
|
case generic
|
||||||
|
}
|
||||||
|
|
||||||
|
case result(Data)
|
||||||
|
case error(Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class PrivateKey {
|
||||||
|
private let privateKey: SecKey
|
||||||
|
private let publicKey: SecKey
|
||||||
|
public let publicKeyRepresentation: Data
|
||||||
|
|
||||||
|
fileprivate init(privateKey: SecKey, publicKey: SecKey, publicKeyRepresentation: Data) {
|
||||||
|
self.privateKey = privateKey
|
||||||
|
self.publicKey = publicKey
|
||||||
|
self.publicKeyRepresentation = publicKeyRepresentation
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encrypt(data: Data) -> Data? {
|
||||||
|
var error: Unmanaged<CFError>?
|
||||||
|
let cipherText = SecKeyCreateEncryptedData(self.publicKey, .eciesEncryptionCofactorVariableIVX963SHA512AESGCM, data as CFData, &error)
|
||||||
|
if let error {
|
||||||
|
error.release()
|
||||||
|
}
|
||||||
|
guard let cipherText else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = cipherText as Data
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func decrypt(data: Data) -> DecryptionResult {
|
||||||
|
var maybeError: Unmanaged<CFError>?
|
||||||
|
let plainText = SecKeyCreateDecryptedData(self.privateKey, .eciesEncryptionCofactorVariableIVX963SHA512AESGCM, data as CFData, &maybeError)
|
||||||
|
let error = maybeError?.takeRetainedValue()
|
||||||
|
|
||||||
|
guard let plainText else {
|
||||||
|
if let error {
|
||||||
|
if CFErrorGetCode(error) == -2 {
|
||||||
|
return .error(.cancelled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .error(.generic)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = plainText as Data
|
||||||
|
return .result(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var biometricAuthentication: LocalAuthBiometricAuthentication? {
|
||||||
let context = LAContext()
|
let context = LAContext()
|
||||||
if context.canEvaluatePolicy(LAPolicy(rawValue: Int(kLAPolicyDeviceOwnerAuthenticationWithBiometrics))!, error: nil) {
|
if context.canEvaluatePolicy(LAPolicy(rawValue: Int(kLAPolicyDeviceOwnerAuthenticationWithBiometrics))!, error: nil) {
|
||||||
#if swift(>=5.9)
|
|
||||||
switch context.biometryType {
|
switch context.biometryType {
|
||||||
case .faceID, .opticID:
|
case .faceID, .opticID:
|
||||||
return .faceId
|
return .faceId
|
||||||
@ -22,22 +78,10 @@ public struct LocalAuth {
|
|||||||
@unknown default:
|
@unknown default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
switch context.biometryType {
|
|
||||||
case .faceID://, .opticID:
|
|
||||||
return .faceId
|
|
||||||
case .touchID:
|
|
||||||
return .touchId
|
|
||||||
case .none:
|
|
||||||
return nil
|
|
||||||
@unknown default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
|
|
||||||
public static let evaluatedPolicyDomainState: Data? = {
|
public static let evaluatedPolicyDomainState: Data? = {
|
||||||
let context = LAContext()
|
let context = LAContext()
|
||||||
@ -78,4 +122,145 @@ public struct LocalAuth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func bundleSeedId() -> String? {
|
||||||
|
let query: [String: Any] = [
|
||||||
|
kSecClass as String: kSecClassGenericPassword as String,
|
||||||
|
kSecAttrAccount as String: "bundleSeedID",
|
||||||
|
kSecAttrService as String: "",
|
||||||
|
kSecReturnAttributes as String: true
|
||||||
|
]
|
||||||
|
var result: CFTypeRef?
|
||||||
|
var status = SecItemCopyMatching(query as CFDictionary, &result)
|
||||||
|
if status == errSecItemNotFound {
|
||||||
|
status = SecItemAdd(query as CFDictionary, &result)
|
||||||
|
}
|
||||||
|
if status != errSecSuccess {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let result = result else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if CFGetTypeID(result) != CFDictionaryGetTypeID() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let resultDict = (result as! CFDictionary) as? [String: Any] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let components = accessGroup.components(separatedBy: ".")
|
||||||
|
guard let seedId = components.first else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return seedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func getPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||||
|
guard let bundleSeedId = self.bundleSeedId() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let applicationTag = customKeyIdPrefix + keyId
|
||||||
|
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||||
|
|
||||||
|
let query: [String: Any] = [
|
||||||
|
kSecClass as String: kSecClassKey as String,
|
||||||
|
kSecAttrApplicationTag as String: applicationTag,
|
||||||
|
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||||
|
kSecAttrAccessGroup as String: accessGroup,
|
||||||
|
kSecReturnRef as String: true
|
||||||
|
]
|
||||||
|
|
||||||
|
var maybePrivateKey: CFTypeRef?
|
||||||
|
let status = SecItemCopyMatching(query as CFDictionary, &maybePrivateKey)
|
||||||
|
if status != errSecSuccess {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let maybePrivateKey else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if CFGetTypeID(maybePrivateKey) != SecKeyGetTypeID() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let privateKey = maybePrivateKey as! SecKey
|
||||||
|
|
||||||
|
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let publicKeyRepresentation = SecKeyCopyExternalRepresentation(publicKey, nil) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func removePrivateKey(baseAppBundleId: String, keyId: Data) -> Bool {
|
||||||
|
guard let bundleSeedId = self.bundleSeedId() else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let applicationTag = customKeyIdPrefix + keyId
|
||||||
|
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||||
|
|
||||||
|
let query: [String: Any] = [
|
||||||
|
kSecClass as String: kSecClassKey as String,
|
||||||
|
kSecAttrApplicationTag as String: applicationTag,
|
||||||
|
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||||
|
kSecAttrIsPermanent as String: true,
|
||||||
|
kSecAttrAccessGroup as String: accessGroup
|
||||||
|
]
|
||||||
|
|
||||||
|
let status = SecItemDelete(query as CFDictionary)
|
||||||
|
if status != errSecSuccess {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func addPrivateKey(baseAppBundleId: String, keyId: Data) -> PrivateKey? {
|
||||||
|
guard let bundleSeedId = self.bundleSeedId() else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let applicationTag = customKeyIdPrefix + keyId
|
||||||
|
let accessGroup = "\(bundleSeedId).\(baseAppBundleId)"
|
||||||
|
|
||||||
|
guard let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, [.userPresence, .privateKeyUsage], nil) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let attributes: [String: Any] = [
|
||||||
|
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom as String,
|
||||||
|
kSecAttrKeySizeInBits as String: 256 as NSNumber,
|
||||||
|
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave as String,
|
||||||
|
kSecPrivateKeyAttrs as String: [
|
||||||
|
kSecAttrIsPermanent as String: true,
|
||||||
|
kSecAttrApplicationTag as String: applicationTag,
|
||||||
|
kSecAttrAccessControl as String: access,
|
||||||
|
kSecAttrAccessGroup as String: accessGroup,
|
||||||
|
] as [String: Any]
|
||||||
|
]
|
||||||
|
var error: Unmanaged<CFError>?
|
||||||
|
let maybePrivateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
|
||||||
|
if let error {
|
||||||
|
error.release()
|
||||||
|
}
|
||||||
|
guard let privateKey = maybePrivateKey else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let publicKeyRepresentation = SecKeyCopyExternalRepresentation(publicKey, nil) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = PrivateKey(privateKey: privateKey, publicKey: publicKey, publicKeyRepresentation: publicKeyRepresentation as Data)
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,7 +495,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) }
|
dict[340088945] = { return Api.MediaArea.parse_mediaAreaSuggestedReaction($0) }
|
||||||
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
|
dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) }
|
||||||
dict[64088654] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
|
dict[64088654] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) }
|
||||||
dict[556221267] = { return Api.Message.parse_message($0) }
|
dict[592953125] = { return Api.Message.parse_message($0) }
|
||||||
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
|
dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) }
|
||||||
dict[721967202] = { return Api.Message.parse_messageService($0) }
|
dict[721967202] = { return Api.Message.parse_messageService($0) }
|
||||||
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
|
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
|
||||||
@ -888,7 +888,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1873947492] = { return Api.Update.parse_updateBotChatBoost($0) }
|
dict[-1873947492] = { return Api.Update.parse_updateBotChatBoost($0) }
|
||||||
dict[299870598] = { return Api.Update.parse_updateBotChatInviteRequester($0) }
|
dict[299870598] = { return Api.Update.parse_updateBotChatInviteRequester($0) }
|
||||||
dict[1299263278] = { return Api.Update.parse_updateBotCommands($0) }
|
dict[1299263278] = { return Api.Update.parse_updateBotCommands($0) }
|
||||||
dict[-1590796039] = { return Api.Update.parse_updateBotDeleteBusinessMessage($0) }
|
dict[-1607821266] = { return Api.Update.parse_updateBotDeleteBusinessMessage($0) }
|
||||||
dict[1420915171] = { return Api.Update.parse_updateBotEditBusinessMessage($0) }
|
dict[1420915171] = { return Api.Update.parse_updateBotEditBusinessMessage($0) }
|
||||||
dict[1232025500] = { return Api.Update.parse_updateBotInlineQuery($0) }
|
dict[1232025500] = { return Api.Update.parse_updateBotInlineQuery($0) }
|
||||||
dict[317794823] = { return Api.Update.parse_updateBotInlineSend($0) }
|
dict[317794823] = { return Api.Update.parse_updateBotInlineSend($0) }
|
||||||
|
@ -628,15 +628,15 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum Message: TypeConstructorDescription {
|
indirect enum Message: TypeConstructorDescription {
|
||||||
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?)
|
case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?)
|
||||||
case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?)
|
case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?)
|
||||||
case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, ttlPeriod: Int32?)
|
case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, ttlPeriod: Int32?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(556221267)
|
buffer.appendInt32(592953125)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(flags2, buffer: buffer, boxed: false)
|
serializeInt32(flags2, buffer: buffer, boxed: false)
|
||||||
@ -647,6 +647,7 @@ public extension Api {
|
|||||||
if Int(flags) & Int(1 << 28) != 0 {savedPeerId!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 28) != 0 {savedPeerId!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 2) != 0 {fwdFrom!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 11) != 0 {serializeInt64(viaBotId!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags2) & Int(1 << 0) != 0 {serializeInt64(viaBusinessBotId!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 3) != 0 {replyTo!.serialize(buffer, true)}
|
||||||
serializeInt32(date, buffer: buffer, boxed: false)
|
serializeInt32(date, buffer: buffer, boxed: false)
|
||||||
serializeString(message, buffer: buffer, boxed: false)
|
serializeString(message, buffer: buffer, boxed: false)
|
||||||
@ -698,8 +699,8 @@ public extension Api {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId):
|
||||||
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any)])
|
return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any)])
|
||||||
case .messageEmpty(let flags, let id, let peerId):
|
case .messageEmpty(let flags, let id, let peerId):
|
||||||
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
|
return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)])
|
||||||
case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let ttlPeriod):
|
case .messageService(let flags, let id, let fromId, let peerId, let replyTo, let date, let action, let ttlPeriod):
|
||||||
@ -734,52 +735,54 @@ public extension Api {
|
|||||||
} }
|
} }
|
||||||
var _9: Int64?
|
var _9: Int64?
|
||||||
if Int(_1!) & Int(1 << 11) != 0 {_9 = reader.readInt64() }
|
if Int(_1!) & Int(1 << 11) != 0 {_9 = reader.readInt64() }
|
||||||
var _10: Api.MessageReplyHeader?
|
var _10: Int64?
|
||||||
|
if Int(_2!) & Int(1 << 0) != 0 {_10 = reader.readInt64() }
|
||||||
|
var _11: Api.MessageReplyHeader?
|
||||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||||
_10 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader
|
_11 = Api.parse(reader, signature: signature) as? Api.MessageReplyHeader
|
||||||
} }
|
} }
|
||||||
var _11: Int32?
|
var _12: Int32?
|
||||||
_11 = reader.readInt32()
|
_12 = reader.readInt32()
|
||||||
var _12: String?
|
var _13: String?
|
||||||
_12 = parseString(reader)
|
_13 = parseString(reader)
|
||||||
var _13: Api.MessageMedia?
|
var _14: Api.MessageMedia?
|
||||||
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
|
||||||
_13 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
_14 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||||
} }
|
} }
|
||||||
var _14: Api.ReplyMarkup?
|
var _15: Api.ReplyMarkup?
|
||||||
if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() {
|
||||||
_14 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
|
_15 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
|
||||||
} }
|
} }
|
||||||
var _15: [Api.MessageEntity]?
|
var _16: [Api.MessageEntity]?
|
||||||
if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() {
|
if Int(_1!) & Int(1 << 7) != 0 {if let _ = reader.readInt32() {
|
||||||
_15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
_16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
|
||||||
} }
|
} }
|
||||||
var _16: Int32?
|
|
||||||
if Int(_1!) & Int(1 << 10) != 0 {_16 = reader.readInt32() }
|
|
||||||
var _17: Int32?
|
var _17: Int32?
|
||||||
if Int(_1!) & Int(1 << 10) != 0 {_17 = reader.readInt32() }
|
if Int(_1!) & Int(1 << 10) != 0 {_17 = reader.readInt32() }
|
||||||
var _18: Api.MessageReplies?
|
var _18: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 10) != 0 {_18 = reader.readInt32() }
|
||||||
|
var _19: Api.MessageReplies?
|
||||||
if Int(_1!) & Int(1 << 23) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 23) != 0 {if let signature = reader.readInt32() {
|
||||||
_18 = Api.parse(reader, signature: signature) as? Api.MessageReplies
|
_19 = Api.parse(reader, signature: signature) as? Api.MessageReplies
|
||||||
} }
|
} }
|
||||||
var _19: Int32?
|
var _20: Int32?
|
||||||
if Int(_1!) & Int(1 << 15) != 0 {_19 = reader.readInt32() }
|
if Int(_1!) & Int(1 << 15) != 0 {_20 = reader.readInt32() }
|
||||||
var _20: String?
|
var _21: String?
|
||||||
if Int(_1!) & Int(1 << 16) != 0 {_20 = parseString(reader) }
|
if Int(_1!) & Int(1 << 16) != 0 {_21 = parseString(reader) }
|
||||||
var _21: Int64?
|
var _22: Int64?
|
||||||
if Int(_1!) & Int(1 << 17) != 0 {_21 = reader.readInt64() }
|
if Int(_1!) & Int(1 << 17) != 0 {_22 = reader.readInt64() }
|
||||||
var _22: Api.MessageReactions?
|
var _23: Api.MessageReactions?
|
||||||
if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() {
|
if Int(_1!) & Int(1 << 20) != 0 {if let signature = reader.readInt32() {
|
||||||
_22 = Api.parse(reader, signature: signature) as? Api.MessageReactions
|
_23 = Api.parse(reader, signature: signature) as? Api.MessageReactions
|
||||||
} }
|
} }
|
||||||
var _23: [Api.RestrictionReason]?
|
var _24: [Api.RestrictionReason]?
|
||||||
if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() {
|
if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() {
|
||||||
_23 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
|
_24 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
|
||||||
} }
|
} }
|
||||||
var _24: Int32?
|
|
||||||
if Int(_1!) & Int(1 << 25) != 0 {_24 = reader.readInt32() }
|
|
||||||
var _25: Int32?
|
var _25: Int32?
|
||||||
if Int(_1!) & Int(1 << 30) != 0 {_25 = reader.readInt32() }
|
if Int(_1!) & Int(1 << 25) != 0 {_25 = reader.readInt32() }
|
||||||
|
var _26: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 30) != 0 {_26 = reader.readInt32() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -789,24 +792,25 @@ public extension Api {
|
|||||||
let _c7 = (Int(_1!) & Int(1 << 28) == 0) || _7 != nil
|
let _c7 = (Int(_1!) & Int(1 << 28) == 0) || _7 != nil
|
||||||
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
|
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
|
||||||
let _c9 = (Int(_1!) & Int(1 << 11) == 0) || _9 != nil
|
let _c9 = (Int(_1!) & Int(1 << 11) == 0) || _9 != nil
|
||||||
let _c10 = (Int(_1!) & Int(1 << 3) == 0) || _10 != nil
|
let _c10 = (Int(_2!) & Int(1 << 0) == 0) || _10 != nil
|
||||||
let _c11 = _11 != nil
|
let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil
|
||||||
let _c12 = _12 != nil
|
let _c12 = _12 != nil
|
||||||
let _c13 = (Int(_1!) & Int(1 << 9) == 0) || _13 != nil
|
let _c13 = _13 != nil
|
||||||
let _c14 = (Int(_1!) & Int(1 << 6) == 0) || _14 != nil
|
let _c14 = (Int(_1!) & Int(1 << 9) == 0) || _14 != nil
|
||||||
let _c15 = (Int(_1!) & Int(1 << 7) == 0) || _15 != nil
|
let _c15 = (Int(_1!) & Int(1 << 6) == 0) || _15 != nil
|
||||||
let _c16 = (Int(_1!) & Int(1 << 10) == 0) || _16 != nil
|
let _c16 = (Int(_1!) & Int(1 << 7) == 0) || _16 != nil
|
||||||
let _c17 = (Int(_1!) & Int(1 << 10) == 0) || _17 != nil
|
let _c17 = (Int(_1!) & Int(1 << 10) == 0) || _17 != nil
|
||||||
let _c18 = (Int(_1!) & Int(1 << 23) == 0) || _18 != nil
|
let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil
|
||||||
let _c19 = (Int(_1!) & Int(1 << 15) == 0) || _19 != nil
|
let _c19 = (Int(_1!) & Int(1 << 23) == 0) || _19 != nil
|
||||||
let _c20 = (Int(_1!) & Int(1 << 16) == 0) || _20 != nil
|
let _c20 = (Int(_1!) & Int(1 << 15) == 0) || _20 != nil
|
||||||
let _c21 = (Int(_1!) & Int(1 << 17) == 0) || _21 != nil
|
let _c21 = (Int(_1!) & Int(1 << 16) == 0) || _21 != nil
|
||||||
let _c22 = (Int(_1!) & Int(1 << 20) == 0) || _22 != nil
|
let _c22 = (Int(_1!) & Int(1 << 17) == 0) || _22 != nil
|
||||||
let _c23 = (Int(_1!) & Int(1 << 22) == 0) || _23 != nil
|
let _c23 = (Int(_1!) & Int(1 << 20) == 0) || _23 != nil
|
||||||
let _c24 = (Int(_1!) & Int(1 << 25) == 0) || _24 != nil
|
let _c24 = (Int(_1!) & Int(1 << 22) == 0) || _24 != nil
|
||||||
let _c25 = (Int(_1!) & Int(1 << 30) == 0) || _25 != nil
|
let _c25 = (Int(_1!) & Int(1 << 25) == 0) || _25 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 {
|
let _c26 = (Int(_1!) & Int(1 << 30) == 0) || _26 != nil
|
||||||
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, replyTo: _10, date: _11!, message: _12!, media: _13, replyMarkup: _14, entities: _15, views: _16, forwards: _17, replies: _18, editDate: _19, postAuthor: _20, groupedId: _21, reactions: _22, restrictionReason: _23, ttlPeriod: _24, quickReplyShortcutId: _25)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 {
|
||||||
|
return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -513,7 +513,7 @@ public extension Api {
|
|||||||
case updateBotChatBoost(peer: Api.Peer, boost: Api.Boost, qts: Int32)
|
case updateBotChatBoost(peer: Api.Peer, boost: Api.Boost, qts: Int32)
|
||||||
case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32)
|
case updateBotChatInviteRequester(peer: Api.Peer, date: Int32, userId: Int64, about: String, invite: Api.ExportedChatInvite, qts: Int32)
|
||||||
case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand])
|
case updateBotCommands(peer: Api.Peer, botId: Int64, commands: [Api.BotCommand])
|
||||||
case updateBotDeleteBusinessMessage(connectionId: String, messages: [Int32], qts: Int32)
|
case updateBotDeleteBusinessMessage(connectionId: String, peer: Api.Peer, messages: [Int32], qts: Int32)
|
||||||
case updateBotEditBusinessMessage(connectionId: String, message: Api.Message, qts: Int32)
|
case updateBotEditBusinessMessage(connectionId: String, message: Api.Message, qts: Int32)
|
||||||
case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String)
|
case updateBotInlineQuery(flags: Int32, queryId: Int64, userId: Int64, query: String, geo: Api.GeoPoint?, peerType: Api.InlineQueryPeerType?, offset: String)
|
||||||
case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?)
|
case updateBotInlineSend(flags: Int32, userId: Int64, query: String, geo: Api.GeoPoint?, id: String, msgId: Api.InputBotInlineMessageID?)
|
||||||
@ -707,11 +707,12 @@ public extension Api {
|
|||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case .updateBotDeleteBusinessMessage(let connectionId, let messages, let qts):
|
case .updateBotDeleteBusinessMessage(let connectionId, let peer, let messages, let qts):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-1590796039)
|
buffer.appendInt32(-1607821266)
|
||||||
}
|
}
|
||||||
serializeString(connectionId, buffer: buffer, boxed: false)
|
serializeString(connectionId, buffer: buffer, boxed: false)
|
||||||
|
peer.serialize(buffer, true)
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
buffer.appendInt32(Int32(messages.count))
|
buffer.appendInt32(Int32(messages.count))
|
||||||
for item in messages {
|
for item in messages {
|
||||||
@ -1827,8 +1828,8 @@ public extension Api {
|
|||||||
return ("updateBotChatInviteRequester", [("peer", peer as Any), ("date", date as Any), ("userId", userId as Any), ("about", about as Any), ("invite", invite as Any), ("qts", qts as Any)])
|
return ("updateBotChatInviteRequester", [("peer", peer as Any), ("date", date as Any), ("userId", userId as Any), ("about", about as Any), ("invite", invite as Any), ("qts", qts as Any)])
|
||||||
case .updateBotCommands(let peer, let botId, let commands):
|
case .updateBotCommands(let peer, let botId, let commands):
|
||||||
return ("updateBotCommands", [("peer", peer as Any), ("botId", botId as Any), ("commands", commands as Any)])
|
return ("updateBotCommands", [("peer", peer as Any), ("botId", botId as Any), ("commands", commands as Any)])
|
||||||
case .updateBotDeleteBusinessMessage(let connectionId, let messages, let qts):
|
case .updateBotDeleteBusinessMessage(let connectionId, let peer, let messages, let qts):
|
||||||
return ("updateBotDeleteBusinessMessage", [("connectionId", connectionId as Any), ("messages", messages as Any), ("qts", qts as Any)])
|
return ("updateBotDeleteBusinessMessage", [("connectionId", connectionId as Any), ("peer", peer as Any), ("messages", messages as Any), ("qts", qts as Any)])
|
||||||
case .updateBotEditBusinessMessage(let connectionId, let message, let qts):
|
case .updateBotEditBusinessMessage(let connectionId, let message, let qts):
|
||||||
return ("updateBotEditBusinessMessage", [("connectionId", connectionId as Any), ("message", message as Any), ("qts", qts as Any)])
|
return ("updateBotEditBusinessMessage", [("connectionId", connectionId as Any), ("message", message as Any), ("qts", qts as Any)])
|
||||||
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
|
case .updateBotInlineQuery(let flags, let queryId, let userId, let query, let geo, let peerType, let offset):
|
||||||
@ -2217,17 +2218,22 @@ public extension Api {
|
|||||||
public static func parse_updateBotDeleteBusinessMessage(_ reader: BufferReader) -> Update? {
|
public static func parse_updateBotDeleteBusinessMessage(_ reader: BufferReader) -> Update? {
|
||||||
var _1: String?
|
var _1: String?
|
||||||
_1 = parseString(reader)
|
_1 = parseString(reader)
|
||||||
var _2: [Int32]?
|
var _2: Api.Peer?
|
||||||
if let _ = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
_2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||||
}
|
}
|
||||||
var _3: Int32?
|
var _3: [Int32]?
|
||||||
_3 = reader.readInt32()
|
if let _ = reader.readInt32() {
|
||||||
|
_3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||||
|
}
|
||||||
|
var _4: Int32?
|
||||||
|
_4 = reader.readInt32()
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
if _c1 && _c2 && _c3 {
|
let _c4 = _4 != nil
|
||||||
return Api.Update.updateBotDeleteBusinessMessage(connectionId: _1!, messages: _2!, qts: _3!)
|
if _c1 && _c2 && _c3 && _c4 {
|
||||||
|
return Api.Update.updateBotDeleteBusinessMessage(connectionId: _1!, peer: _2!, messages: _3!, qts: _4!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -8136,12 +8136,14 @@ public extension Api.functions.messages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.messages {
|
public extension Api.functions.messages {
|
||||||
static func uploadMedia(peer: Api.InputPeer, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
|
static func uploadMedia(flags: Int32, businessConnectionId: String?, peer: Api.InputPeer, media: Api.InputMedia) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.MessageMedia>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(1369162417)
|
buffer.appendInt32(345405816)
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 0) != 0 {serializeString(businessConnectionId!, buffer: buffer, boxed: false)}
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
media.serialize(buffer, true)
|
media.serialize(buffer, true)
|
||||||
return (FunctionDescription(name: "messages.uploadMedia", parameters: [("peer", String(describing: peer)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
|
return (FunctionDescription(name: "messages.uploadMedia", parameters: [("flags", String(describing: flags)), ("businessConnectionId", String(describing: businessConnectionId)), ("peer", String(describing: peer)), ("media", String(describing: media))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.MessageMedia? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.MessageMedia?
|
var result: Api.MessageMedia?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
@ -90,6 +90,7 @@ private var declaredEncodables: Void = {
|
|||||||
declareEncodable(ChannelState.self, f: { ChannelState(decoder: $0) })
|
declareEncodable(ChannelState.self, f: { ChannelState(decoder: $0) })
|
||||||
declareEncodable(RegularChatState.self, f: { RegularChatState(decoder: $0) })
|
declareEncodable(RegularChatState.self, f: { RegularChatState(decoder: $0) })
|
||||||
declareEncodable(InlineBotMessageAttribute.self, f: { InlineBotMessageAttribute(decoder: $0) })
|
declareEncodable(InlineBotMessageAttribute.self, f: { InlineBotMessageAttribute(decoder: $0) })
|
||||||
|
declareEncodable(InlineBusinessBotMessageAttribute.self, f: { InlineBusinessBotMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(TextEntitiesMessageAttribute.self, f: { TextEntitiesMessageAttribute(decoder: $0) })
|
declareEncodable(TextEntitiesMessageAttribute.self, f: { TextEntitiesMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(ReplyMessageAttribute.self, f: { ReplyMessageAttribute(decoder: $0) })
|
declareEncodable(ReplyMessageAttribute.self, f: { ReplyMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(QuotedReplyMessageAttribute.self, f: { QuotedReplyMessageAttribute(decoder: $0) })
|
declareEncodable(QuotedReplyMessageAttribute.self, f: { QuotedReplyMessageAttribute(decoder: $0) })
|
||||||
|
@ -126,7 +126,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
|
|||||||
|
|
||||||
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||||
switch messsage {
|
switch messsage {
|
||||||
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
let chatPeerId = messagePeerId
|
let chatPeerId = messagePeerId
|
||||||
return chatPeerId.peerId
|
return chatPeerId.peerId
|
||||||
case let .messageEmpty(_, _, peerId):
|
case let .messageEmpty(_, _, peerId):
|
||||||
@ -142,7 +142,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
|||||||
|
|
||||||
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||||
switch message {
|
switch message {
|
||||||
case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _):
|
||||||
let peerId: PeerId = chatPeerId.peerId
|
let peerId: PeerId = chatPeerId.peerId
|
||||||
|
|
||||||
var result = [peerId]
|
var result = [peerId]
|
||||||
@ -171,6 +171,9 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||||||
if let viaBotId = viaBotId {
|
if let viaBotId = viaBotId {
|
||||||
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId)))
|
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId)))
|
||||||
}
|
}
|
||||||
|
if let viaBusinessBotId {
|
||||||
|
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBusinessBotId)))
|
||||||
|
}
|
||||||
|
|
||||||
if let savedPeerId = savedPeerId {
|
if let savedPeerId = savedPeerId {
|
||||||
result.append(savedPeerId.peerId)
|
result.append(savedPeerId.peerId)
|
||||||
@ -263,7 +266,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||||||
|
|
||||||
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
|
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? {
|
||||||
switch message {
|
switch message {
|
||||||
case let .message(_, _, id, _, _, chatPeerId, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||||
if let replyTo = replyTo {
|
if let replyTo = replyTo {
|
||||||
let peerId: PeerId = chatPeerId.peerId
|
let peerId: PeerId = chatPeerId.peerId
|
||||||
|
|
||||||
@ -597,7 +600,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
|||||||
extension StoreMessage {
|
extension StoreMessage {
|
||||||
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||||
switch apiMessage {
|
switch apiMessage {
|
||||||
case let .message(flags, _, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId):
|
case let .message(flags, _, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId):
|
||||||
let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId
|
let resolvedFromId = fromId?.peerId ?? chatPeerId.peerId
|
||||||
|
|
||||||
var namespace = namespace
|
var namespace = namespace
|
||||||
@ -799,6 +802,10 @@ extension StoreMessage {
|
|||||||
if let viaBotId = viaBotId {
|
if let viaBotId = viaBotId {
|
||||||
attributes.append(InlineBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId)), title: nil))
|
attributes.append(InlineBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBotId)), title: nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let viaBusinessBotId {
|
||||||
|
attributes.append(InlineBusinessBotMessageAttribute(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(viaBusinessBotId)), title: nil))
|
||||||
|
}
|
||||||
|
|
||||||
if namespace != Namespaces.Message.ScheduledCloud && namespace != Namespaces.Message.QuickReplyCloud {
|
if namespace != Namespaces.Message.ScheduledCloud && namespace != Namespaces.Message.QuickReplyCloud {
|
||||||
if let views = views {
|
if let views = views {
|
||||||
|
@ -40,3 +40,43 @@ public class InlineBotMessageAttribute: MessageAttribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class InlineBusinessBotMessageAttribute: MessageAttribute {
|
||||||
|
public let peerId: PeerId?
|
||||||
|
public let title: String?
|
||||||
|
|
||||||
|
public var associatedPeerIds: [PeerId] {
|
||||||
|
if let peerId = self.peerId {
|
||||||
|
return [peerId]
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(peerId: PeerId?, title: String?) {
|
||||||
|
self.peerId = peerId
|
||||||
|
self.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(decoder: PostboxDecoder) {
|
||||||
|
if let peerId = decoder.decodeOptionalInt64ForKey("i") {
|
||||||
|
self.peerId = PeerId(peerId)
|
||||||
|
} else {
|
||||||
|
self.peerId = nil
|
||||||
|
}
|
||||||
|
self.title = decoder.decodeOptionalStringForKey("t")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
if let peerId = self.peerId {
|
||||||
|
encoder.encodeInt64(peerId.toInt64(), forKey: "i")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "i")
|
||||||
|
}
|
||||||
|
if let title = self.title {
|
||||||
|
encoder.encodeString(title, forKey: "t")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -286,6 +286,7 @@ private enum PreferencesKeyValues: Int32 {
|
|||||||
case displaySavedChatsAsTopics = 35
|
case displaySavedChatsAsTopics = 35
|
||||||
case shortcutMessages = 37
|
case shortcutMessages = 37
|
||||||
case timezoneList = 38
|
case timezoneList = 38
|
||||||
|
case botBiometricsState = 39
|
||||||
}
|
}
|
||||||
|
|
||||||
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
|
||||||
@ -481,6 +482,13 @@ public struct PreferencesKeys {
|
|||||||
key.setInt32(0, value: PreferencesKeyValues.timezoneList.rawValue)
|
key.setInt32(0, value: PreferencesKeyValues.timezoneList.rawValue)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func botBiometricsState(peerId: PeerId) -> ValueBoxKey {
|
||||||
|
let key = ValueBoxKey(length: 4 + 8)
|
||||||
|
key.setInt32(0, value: PreferencesKeyValues.botBiometricsState.rawValue)
|
||||||
|
key.setInt64(4, value: peerId.toInt64())
|
||||||
|
return key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum SharedDataKeyValues: Int32 {
|
private enum SharedDataKeyValues: Int32 {
|
||||||
|
@ -1584,5 +1584,33 @@ public extension TelegramEngine.EngineData.Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct BotBiometricsState: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||||
|
public typealias Result = TelegramBotBiometricsState
|
||||||
|
|
||||||
|
fileprivate var id: EnginePeer.Id
|
||||||
|
public var mapKey: EnginePeer.Id {
|
||||||
|
return self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(id: EnginePeer.Id) {
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
var key: PostboxViewKey {
|
||||||
|
return .preferences(keys: Set([PreferencesKeys.botBiometricsState(peerId: self.id)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func extract(view: PostboxView) -> Result {
|
||||||
|
guard let view = view as? PreferencesView else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
if let state = view.values[PreferencesKeys.botBiometricsState(peerId: self.id)]?.get(TelegramBotBiometricsState.self) {
|
||||||
|
return state
|
||||||
|
} else {
|
||||||
|
return TelegramBotBiometricsState.default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,3 +319,42 @@ func _internal_invokeBotCustomMethod(postbox: Postbox, network: Network, botId:
|
|||||||
|> castError(InvokeBotCustomMethodError.self)
|
|> castError(InvokeBotCustomMethodError.self)
|
||||||
|> switchToLatest
|
|> switchToLatest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct TelegramBotBiometricsState: Codable, Equatable {
|
||||||
|
public struct OpaqueToken: Codable, Equatable {
|
||||||
|
public let publicKey: Data
|
||||||
|
public let data: Data
|
||||||
|
|
||||||
|
public init(publicKey: Data, data: Data) {
|
||||||
|
self.publicKey = publicKey
|
||||||
|
self.data = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var accessRequested: Bool
|
||||||
|
public var accessGranted: Bool
|
||||||
|
public var opaqueToken: OpaqueToken?
|
||||||
|
|
||||||
|
public static var `default`: TelegramBotBiometricsState {
|
||||||
|
return TelegramBotBiometricsState(
|
||||||
|
accessRequested: false,
|
||||||
|
accessGranted: false,
|
||||||
|
opaqueToken: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(accessRequested: Bool, accessGranted: Bool, opaqueToken: OpaqueToken?) {
|
||||||
|
self.accessRequested = accessRequested
|
||||||
|
self.accessGranted = accessGranted
|
||||||
|
self.opaqueToken = opaqueToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _internal_updateBotBiometricsState(account: Account, peerId: EnginePeer.Id, update: @escaping (TelegramBotBiometricsState) -> TelegramBotBiometricsState) -> Signal<Never, NoError> {
|
||||||
|
return account.postbox.transaction { transaction -> Void in
|
||||||
|
let previousState = transaction.getPreferencesEntry(key: PreferencesKeys.botBiometricsState(peerId: peerId))?.get(TelegramBotBiometricsState.self) ?? TelegramBotBiometricsState.default
|
||||||
|
|
||||||
|
transaction.setPreferencesEntry(key: PreferencesKeys.botBiometricsState(peerId: peerId), value: PreferencesEntry(update(previousState)))
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
@ -1490,6 +1490,10 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateBotBiometricsState(peerId: EnginePeer.Id, update: @escaping (TelegramBotBiometricsState) -> TelegramBotBiometricsState) {
|
||||||
|
let _ = _internal_updateBotBiometricsState(account: self.account, peerId: peerId, update: update).startStandalone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ swift_library(
|
|||||||
"//submodules/CheckNode:CheckNode",
|
"//submodules/CheckNode:CheckNode",
|
||||||
"//submodules/Markdown:Markdown",
|
"//submodules/Markdown:Markdown",
|
||||||
"//submodules/TextFormat:TextFormat",
|
"//submodules/TextFormat:TextFormat",
|
||||||
|
"//submodules/LocalAuth",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -23,6 +23,7 @@ import PromptUI
|
|||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
import QrCodeUI
|
import QrCodeUI
|
||||||
import InstantPageUI
|
import InstantPageUI
|
||||||
|
import LocalAuth
|
||||||
|
|
||||||
private let durgerKingBotIds: [Int64] = [5104055776, 2200339955]
|
private let durgerKingBotIds: [Int64] = [5104055776, 2200339955]
|
||||||
|
|
||||||
@ -1064,6 +1065,18 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
if let json = json, let isVisible = json["is_visible"] as? Bool {
|
if let json = json, let isVisible = json["is_visible"] as? Bool {
|
||||||
self.controller?.hasSettings = isVisible
|
self.controller?.hasSettings = isVisible
|
||||||
}
|
}
|
||||||
|
case "web_app_biometry_get_info":
|
||||||
|
self.sendBiometryInfoReceivedEvent()
|
||||||
|
case "web_app_biometry_request_access":
|
||||||
|
self.requestBiometryAccess()
|
||||||
|
case "web_app_biometry_request_auth":
|
||||||
|
self.requestBiometryAuth()
|
||||||
|
case "web_app_biometry_update_token":
|
||||||
|
var tokenData: Data?
|
||||||
|
if let json, let tokenDataValue = json["token"] as? Data {
|
||||||
|
tokenData = tokenDataValue
|
||||||
|
}
|
||||||
|
self.requestBiometryUpdateToken(tokenData: tokenData)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1392,6 +1405,230 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.webView?.sendEvent(name: "custom_method_invoked", data: paramsString)
|
self.webView?.sendEvent(name: "custom_method_invoked", data: paramsString)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func sendBiometryInfoReceivedEvent() {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (self.context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.BotBiometricsState(id: controller.botId)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data: [String: Any] = [:]
|
||||||
|
if let biometricAuthentication = LocalAuth.biometricAuthentication {
|
||||||
|
data["available"] = true
|
||||||
|
switch biometricAuthentication {
|
||||||
|
case .faceId:
|
||||||
|
data["type"] = "face"
|
||||||
|
case .touchId:
|
||||||
|
data["type"] = "finger"
|
||||||
|
}
|
||||||
|
data["access_requested"] = state.accessRequested
|
||||||
|
data["access_granted"] = state.accessGranted
|
||||||
|
data["token_saved"] = state.opaqueToken != nil
|
||||||
|
} else {
|
||||||
|
data["available"] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let jsonDataString = String(data: jsonData, encoding: .utf8) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.webView?.sendEvent(name: "biometry_info_received", data: jsonDataString)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func requestBiometryAccess() {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (self.context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId),
|
||||||
|
TelegramEngine.EngineData.Item.Peer.BotBiometricsState(id: controller.botId)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] botPeer, state in
|
||||||
|
guard let self, let botPeer, let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.accessRequested {
|
||||||
|
self.sendBiometryInfoReceivedEvent()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let updateAccessGranted: (Bool) -> Void = { [weak self] granted in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.context.engine.peers.updateBotBiometricsState(peerId: botPeer.id, update: { state in
|
||||||
|
var state = state
|
||||||
|
state.accessRequested = true
|
||||||
|
state.accessGranted = granted
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
|
||||||
|
self.sendBiometryInfoReceivedEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let alertText = "Do you want to allow \(botPeer.compactDisplayTitle) to use Face ID?"
|
||||||
|
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: alertText, actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_No, action: {
|
||||||
|
updateAccessGranted(false)
|
||||||
|
}),
|
||||||
|
TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Yes, action: {
|
||||||
|
updateAccessGranted(true)
|
||||||
|
})
|
||||||
|
], parseMarkdown: false), in: .window(.root))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func requestBiometryAuth() {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (self.context.engine.data.get(
|
||||||
|
TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId),
|
||||||
|
TelegramEngine.EngineData.Item.Peer.BotBiometricsState(id: controller.botId)
|
||||||
|
)
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] botPeer, state in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.accessRequested && state.accessGranted {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let keyId = "A\(UInt64(bitPattern: self.context.account.id.int64))WebBot\(UInt64(bitPattern: controller.botId.toInt64()))".data(using: .utf8) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let appBundleId = self.context.sharedContext.applicationBindings.appBundleId
|
||||||
|
|
||||||
|
Thread { [weak self] in
|
||||||
|
var key = LocalAuth.getPrivateKey(baseAppBundleId: appBundleId, keyId: keyId)
|
||||||
|
if key == nil {
|
||||||
|
key = LocalAuth.addPrivateKey(baseAppBundleId: appBundleId, keyId: keyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let decryptedData: LocalAuth.DecryptionResult
|
||||||
|
if let key {
|
||||||
|
if let encryptedData = state.opaqueToken {
|
||||||
|
if encryptedData.publicKey == key.publicKeyRepresentation {
|
||||||
|
decryptedData = key.decrypt(data: encryptedData.data)
|
||||||
|
} else {
|
||||||
|
// The local keychain has been reset
|
||||||
|
if let emptyEncryptedData = key.encrypt(data: Data()) {
|
||||||
|
decryptedData = key.decrypt(data: emptyEncryptedData)
|
||||||
|
} else {
|
||||||
|
decryptedData = .error(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let emptyEncryptedData = key.encrypt(data: Data()) {
|
||||||
|
decryptedData = key.decrypt(data: emptyEncryptedData)
|
||||||
|
} else {
|
||||||
|
decryptedData = .error(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decryptedData = .error(.generic)
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch decryptedData {
|
||||||
|
case let .result(token):
|
||||||
|
self.sendBiometryAuthResult(isAuthorized: true, tokenData: state.opaqueToken != nil ? token : nil)
|
||||||
|
case .error:
|
||||||
|
self.sendBiometryAuthResult(isAuthorized: false, tokenData: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
} else {
|
||||||
|
self.sendBiometryAuthResult(isAuthorized: false, tokenData: nil)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func sendBiometryAuthResult(isAuthorized: Bool, tokenData: Data?) {
|
||||||
|
var data: [String: Any] = [:]
|
||||||
|
data["status"] = isAuthorized ? "authorized" : "failed"
|
||||||
|
if isAuthorized {
|
||||||
|
if let tokenData {
|
||||||
|
data["token"] = tokenData
|
||||||
|
} else {
|
||||||
|
data["token"] = Data()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let jsonDataString = String(data: jsonData, encoding: .utf8) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.webView?.sendEvent(name: "biometry_auth_requested", data: jsonDataString)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func requestBiometryUpdateToken(tokenData: Data?) {
|
||||||
|
guard let controller = self.controller else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let keyId = "A\(UInt64(bitPattern: self.context.account.id.int64))WebBot\(UInt64(bitPattern: controller.botId.toInt64()))".data(using: .utf8) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let tokenData {
|
||||||
|
let appBundleId = self.context.sharedContext.applicationBindings.appBundleId
|
||||||
|
Thread { [weak self] in
|
||||||
|
var key = LocalAuth.getPrivateKey(baseAppBundleId: appBundleId, keyId: keyId)
|
||||||
|
if key == nil {
|
||||||
|
key = LocalAuth.addPrivateKey(baseAppBundleId: appBundleId, keyId: keyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptedData: TelegramBotBiometricsState.OpaqueToken?
|
||||||
|
if let key {
|
||||||
|
if let result = key.encrypt(data: tokenData) {
|
||||||
|
encryptedData = TelegramBotBiometricsState.OpaqueToken(
|
||||||
|
publicKey: key.publicKeyRepresentation,
|
||||||
|
data: result
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let encryptedData {
|
||||||
|
self.context.engine.peers.updateBotBiometricsState(peerId: controller.botId, update: { state in
|
||||||
|
var state = state
|
||||||
|
state.opaqueToken = encryptedData
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
} else {
|
||||||
|
self.context.engine.peers.updateBotBiometricsState(peerId: controller.botId, update: { state in
|
||||||
|
var state = state
|
||||||
|
state.opaqueToken = nil
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var controllerNode: Node {
|
fileprivate var controllerNode: Node {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user