From 85b6a8ffe96bf8a5b1c7c5b5180024bc07770ba2 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 8 Nov 2024 15:45:48 +0100 Subject: [PATCH] Update API [skip ci] --- .../Sources/AccountContext.swift | 8 +- .../Sources/ChatController.swift | 6 +- .../BrowserUI/Sources/BrowserWebContent.swift | 3 +- .../Sources/ChatListSearchListPaneNode.swift | 6 +- submodules/TelegramApi/Sources/Api0.swift | 13 +- submodules/TelegramApi/Sources/Api1.swift | 98 ++-- submodules/TelegramApi/Sources/Api2.swift | 106 ++--- submodules/TelegramApi/Sources/Api25.swift | 22 +- submodules/TelegramApi/Sources/Api3.swift | 46 ++ submodules/TelegramApi/Sources/Api31.swift | 98 ++-- submodules/TelegramApi/Sources/Api32.swift | 320 +++++-------- submodules/TelegramApi/Sources/Api33.swift | 440 ++++++++---------- submodules/TelegramApi/Sources/Api34.swift | 240 ++++++++++ submodules/TelegramApi/Sources/Api36.swift | 88 ++++ .../Sources/ApiUtils/BotInfo.swift | 19 +- .../ReplyMarkupMessageAttribute.swift | 16 + .../State/AccountStateManagementUtils.swift | 8 +- .../Sources/SyncCore/SyncCore_BotInfo.swift | 72 ++- .../SyncCore/SyncCore_CachedUserData.swift | 1 + .../TelegramEngine/Data/PeersData.swift | 28 ++ .../TelegramEngine/Messages/BotWebView.swift | 9 +- .../Messages/PreparedInlineMessages.swift | 40 ++ .../Messages/TelegramEngineMessages.swift | 12 +- .../Peers/TelegramEnginePeers.swift | 4 + .../TelegramEngine/Peers/UpdateBotInfo.swift | 42 +- .../Peers/UpdateCachedPeerData.swift | 10 +- .../Sources/PeerInfoScreen.swift | 16 +- .../Sources/ApplicationContext.swift | 2 +- .../Chat/ChatControllerOpenWebApp.swift | 68 ++- .../TelegramUI/Sources/ChatController.swift | 19 +- .../Sources/ChatHistoryEntriesForView.swift | 6 +- .../Sources/NavigateToChatController.swift | 6 +- .../Sources/SharedAccountContext.swift | 4 +- .../Sources/UndoOverlayController.swift | 1 + .../Sources/UndoOverlayControllerNode.swift | 77 ++- .../UrlHandling/Sources/UrlHandling.swift | 55 ++- submodules/WebUI/Sources/FileDownload.swift | 75 +++ .../WebUI/Sources/WebAppController.swift | 420 +++++++++++------ .../WebAppEmojiStatusAlertController.swift | 1 - .../Sources/WebAppMessagePreviewScreen.swift | 22 +- 40 files changed, 1686 insertions(+), 841 deletions(-) create mode 100644 submodules/TelegramCore/Sources/TelegramEngine/Messages/PreparedInlineMessages.swift create mode 100644 submodules/WebUI/Sources/FileDownload.swift diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 5a701c043b..8ee52a8839 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -278,6 +278,12 @@ public enum StickerPackUrlType { case emoji } +public enum ResolvedStartAppMode { + case generic + case compact + case fullscreen +} + public enum ResolvedUrl { case externalUrl(String) case urlAuth(String) @@ -1044,7 +1050,7 @@ public protocol SharedAccountContext: AnyObject { func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal func makeMiniAppListScreen(context: AccountContext, initialData: MiniAppListScreenInitialData) -> ViewController - func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peer: EnginePeer, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) + func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) func makeDebugSettingsController(context: AccountContext?) -> ViewController? diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 05ab49c8b2..5369f4d437 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -301,13 +301,13 @@ public struct ChatControllerInitialBotAppStart { public let botApp: BotApp? public let payload: String? public let justInstalled: Bool - public let compact: Bool + public let mode: ResolvedStartAppMode - public init(botApp: BotApp?, payload: String?, justInstalled: Bool, compact: Bool) { + public init(botApp: BotApp?, payload: String?, justInstalled: Bool, mode: ResolvedStartAppMode) { self.botApp = botApp self.payload = payload self.justInstalled = justInstalled - self.compact = compact + self.mode = mode } } diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 7e87fd08a2..102cede554 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -1460,8 +1460,7 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU } } - if result.isEmpty, let webViewUrl = self.webView.url { - let schemeAndHostUrl = webViewUrl.deletingPathExtension() + if result.isEmpty, let webViewUrl = self.webView.url, let schemeAndHostUrl = URL(string: "/", relativeTo: webViewUrl) { let url = schemeAndHostUrl.appendingPathComponent("favicon.ico") result.insert(Favicon(url: url.absoluteString, dimensions: nil)) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index d5c691cc9a..ebf73ea3ab 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3816,7 +3816,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { context: self.context, parentController: parentController, updatedPresentationData: nil, - peer: peer, + botPeer: peer, + chatPeer: nil, threadId: nil, buttonText: "", url: "", @@ -3840,7 +3841,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { context: self.context, parentController: parentController, updatedPresentationData: nil, - peer: peer, + botPeer: peer, + chatPeer: nil, threadId: nil, buttonText: "", url: "", diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 0c6f11c5b9..6316a8399f 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -83,6 +83,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1262359766] = { return Api.Boost.parse_boost($0) } dict[-1778593322] = { return Api.BotApp.parse_botApp($0) } dict[1571189943] = { return Api.BotApp.parse_botAppNotModified($0) } + dict[-2103898979] = { return Api.BotAppSettings.parse_botAppSettings($0) } dict[-1989921868] = { return Api.BotBusinessConnection.parse_botBusinessConnection($0) } dict[-1032140601] = { return Api.BotCommand.parse_botCommand($0) } dict[-1180016534] = { return Api.BotCommandScope.parse_botCommandScopeChatAdmins($0) } @@ -92,7 +93,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1071145937] = { return Api.BotCommandScope.parse_botCommandScopePeerAdmins($0) } dict[169026035] = { return Api.BotCommandScope.parse_botCommandScopePeerUser($0) } dict[1011811544] = { return Api.BotCommandScope.parse_botCommandScopeUsers($0) } - dict[-2109505932] = { return Api.BotInfo.parse_botInfo($0) } + dict[912290611] = { return Api.BotInfo.parse_botInfo($0) } dict[1984755728] = { return Api.BotInlineMessage.parse_botInlineMessageMediaAuto($0) } dict[416402882] = { return Api.BotInlineMessage.parse_botInlineMessageMediaContact($0) } dict[85477117] = { return Api.BotInlineMessage.parse_botInlineMessageMediaGeo($0) } @@ -974,7 +975,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[675009298] = { return Api.Update.parse_updateBotPurchasedPaidMedia($0) } dict[-1246823043] = { return Api.Update.parse_updateBotShippingQuery($0) } dict[-997782967] = { return Api.Update.parse_updateBotStopped($0) } - dict[-1464975695] = { return Api.Update.parse_updateBotSubscriptionExpire($0) } + dict[756270830] = { return Api.Update.parse_updateBotSubscriptionExpire($0) } dict[-2095595325] = { return Api.Update.parse_updateBotWebhookJSON($0) } dict[-1684914010] = { return Api.Update.parse_updateBotWebhookJSONQuery($0) } dict[-539401739] = { return Api.Update.parse_updateBroadcastRevenueTransactions($0) } @@ -1266,6 +1267,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1626924713] = { return Api.messages.AvailableReactions.parse_availableReactionsNotModified($0) } dict[-347034123] = { return Api.messages.BotApp.parse_botApp($0) } dict[911761060] = { return Api.messages.BotCallbackAnswer.parse_botCallbackAnswer($0) } + dict[-1899035375] = { return Api.messages.BotPreparedInlineMessage.parse_botPreparedInlineMessage($0) } dict[-534646026] = { return Api.messages.BotResults.parse_botResults($0) } dict[-1231326505] = { return Api.messages.ChatAdminsWithInvites.parse_chatAdminsWithInvites($0) } dict[-438840932] = { return Api.messages.ChatFull.parse_chatFull($0) } @@ -1307,6 +1309,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-83926371] = { return Api.messages.MyStickers.parse_myStickers($0) } dict[863093588] = { return Api.messages.PeerDialogs.parse_peerDialogs($0) } dict[1753266509] = { return Api.messages.PeerSettings.parse_peerSettings($0) } + dict[1636301421] = { return Api.messages.PreparedInlineMessage.parse_preparedInlineMessage($0) } dict[-963811691] = { return Api.messages.QuickReplies.parse_quickReplies($0) } dict[1603398491] = { return Api.messages.QuickReplies.parse_quickRepliesNotModified($0) } dict[-352454890] = { return Api.messages.Reactions.parse_reactions($0) } @@ -1511,6 +1514,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.BotApp: _1.serialize(buffer, boxed) + case let _1 as Api.BotAppSettings: + _1.serialize(buffer, boxed) case let _1 as Api.BotBusinessConnection: _1.serialize(buffer, boxed) case let _1 as Api.BotCommand: @@ -2297,6 +2302,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.BotCallbackAnswer: _1.serialize(buffer, boxed) + case let _1 as Api.messages.BotPreparedInlineMessage: + _1.serialize(buffer, boxed) case let _1 as Api.messages.BotResults: _1.serialize(buffer, boxed) case let _1 as Api.messages.ChatAdminsWithInvites: @@ -2355,6 +2362,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.PeerSettings: _1.serialize(buffer, boxed) + case let _1 as Api.messages.PreparedInlineMessage: + _1.serialize(buffer, boxed) case let _1 as Api.messages.QuickReplies: _1.serialize(buffer, boxed) case let _1 as Api.messages.Reactions: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index bfb77ee42c..3c674d8c8b 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -1112,6 +1112,64 @@ public extension Api { } } +public extension Api { + enum BotAppSettings: TypeConstructorDescription { + case botAppSettings(flags: Int32, placeholderDocument: Api.Document?, backgroundColor: Int32?, backgroundDarkColor: Int32?, headerColor: Int32?, headerDarkColor: Int32?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botAppSettings(let flags, let placeholderDocument, let backgroundColor, let backgroundDarkColor, let headerColor, let headerDarkColor): + if boxed { + buffer.appendInt32(-2103898979) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {placeholderDocument!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(backgroundColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(backgroundDarkColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(headerColor!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt32(headerDarkColor!, buffer: buffer, boxed: false)} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botAppSettings(let flags, let placeholderDocument, let backgroundColor, let backgroundDarkColor, let headerColor, let headerDarkColor): + return ("botAppSettings", [("flags", flags as Any), ("placeholderDocument", placeholderDocument as Any), ("backgroundColor", backgroundColor as Any), ("backgroundDarkColor", backgroundDarkColor as Any), ("headerColor", headerColor as Any), ("headerDarkColor", headerDarkColor as Any)]) + } + } + + public static func parse_botAppSettings(_ reader: BufferReader) -> BotAppSettings? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.Document? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.Document + } } + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + var _5: Int32? + if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() } + var _6: Int32? + if Int(_1!) & Int(1 << 4) != 0 {_6 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.BotAppSettings.botAppSettings(flags: _1!, placeholderDocument: _2, backgroundColor: _3, backgroundDarkColor: _4, headerColor: _5, headerDarkColor: _6) + } + else { + return nil + } + } + + } +} public extension Api { enum BotBusinessConnection: TypeConstructorDescription { case botBusinessConnection(flags: Int32, connectionId: String, userId: Int64, dcId: Int32, date: Int32) @@ -1164,43 +1222,3 @@ public extension Api { } } -public extension Api { - enum BotCommand: TypeConstructorDescription { - case botCommand(command: String, description: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .botCommand(let command, let description): - if boxed { - buffer.appendInt32(-1032140601) - } - serializeString(command, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .botCommand(let command, let description): - return ("botCommand", [("command", command as Any), ("description", description as Any)]) - } - } - - public static func parse_botCommand(_ reader: BufferReader) -> BotCommand? { - var _1: String? - _1 = parseString(reader) - var _2: String? - _2 = parseString(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BotCommand.botCommand(command: _1!, description: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 5407c5f76c..04d777013b 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -1,3 +1,43 @@ +public extension Api { + enum BotCommand: TypeConstructorDescription { + case botCommand(command: String, description: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botCommand(let command, let description): + if boxed { + buffer.appendInt32(-1032140601) + } + serializeString(command, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botCommand(let command, let description): + return ("botCommand", [("command", command as Any), ("description", description as Any)]) + } + } + + public static func parse_botCommand(_ reader: BufferReader) -> BotCommand? { + var _1: String? + _1 = parseString(reader) + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.BotCommand.botCommand(command: _1!, description: _2!) + } + else { + return nil + } + } + + } +} public extension Api { indirect enum BotCommandScope: TypeConstructorDescription { case botCommandScopeChatAdmins @@ -136,13 +176,13 @@ public extension Api { } public extension Api { enum BotInfo: TypeConstructorDescription { - case botInfo(flags: Int32, userId: Int64?, description: String?, descriptionPhoto: Api.Photo?, descriptionDocument: Api.Document?, commands: [Api.BotCommand]?, menuButton: Api.BotMenuButton?, privacyPolicyUrl: String?) + case botInfo(flags: Int32, userId: Int64?, description: String?, descriptionPhoto: Api.Photo?, descriptionDocument: Api.Document?, commands: [Api.BotCommand]?, menuButton: Api.BotMenuButton?, privacyPolicyUrl: String?, appSettings: Api.BotAppSettings?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .botInfo(let flags, let userId, let description, let descriptionPhoto, let descriptionDocument, let commands, let menuButton, let privacyPolicyUrl): + case .botInfo(let flags, let userId, let description, let descriptionPhoto, let descriptionDocument, let commands, let menuButton, let privacyPolicyUrl, let appSettings): if boxed { - buffer.appendInt32(-2109505932) + buffer.appendInt32(912290611) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {serializeInt64(userId!, buffer: buffer, boxed: false)} @@ -156,14 +196,15 @@ public extension Api { }} if Int(flags) & Int(1 << 3) != 0 {menuButton!.serialize(buffer, true)} if Int(flags) & Int(1 << 7) != 0 {serializeString(privacyPolicyUrl!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 8) != 0 {appSettings!.serialize(buffer, true)} break } } public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .botInfo(let flags, let userId, let description, let descriptionPhoto, let descriptionDocument, let commands, let menuButton, let privacyPolicyUrl): - return ("botInfo", [("flags", flags as Any), ("userId", userId as Any), ("description", description as Any), ("descriptionPhoto", descriptionPhoto as Any), ("descriptionDocument", descriptionDocument as Any), ("commands", commands as Any), ("menuButton", menuButton as Any), ("privacyPolicyUrl", privacyPolicyUrl as Any)]) + case .botInfo(let flags, let userId, let description, let descriptionPhoto, let descriptionDocument, let commands, let menuButton, let privacyPolicyUrl, let appSettings): + return ("botInfo", [("flags", flags as Any), ("userId", userId as Any), ("description", description as Any), ("descriptionPhoto", descriptionPhoto as Any), ("descriptionDocument", descriptionDocument as Any), ("commands", commands as Any), ("menuButton", menuButton as Any), ("privacyPolicyUrl", privacyPolicyUrl as Any), ("appSettings", appSettings as Any)]) } } @@ -192,6 +233,10 @@ public extension Api { } } var _8: String? if Int(_1!) & Int(1 << 7) != 0 {_8 = parseString(reader) } + var _9: Api.BotAppSettings? + if Int(_1!) & Int(1 << 8) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.BotAppSettings + } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil @@ -200,8 +245,9 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.BotInfo.botInfo(flags: _1!, userId: _2, description: _3, descriptionPhoto: _4, descriptionDocument: _5, commands: _6, menuButton: _7, privacyPolicyUrl: _8) + let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.BotInfo.botInfo(flags: _1!, userId: _2, description: _3, descriptionPhoto: _4, descriptionDocument: _5, commands: _6, menuButton: _7, privacyPolicyUrl: _8, appSettings: _9) } else { return nil @@ -1164,49 +1210,3 @@ public extension Api { } } -public extension Api { - enum BusinessGreetingMessage: TypeConstructorDescription { - case businessGreetingMessage(shortcutId: Int32, recipients: Api.BusinessRecipients, noActivityDays: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .businessGreetingMessage(let shortcutId, let recipients, let noActivityDays): - if boxed { - buffer.appendInt32(-451302485) - } - serializeInt32(shortcutId, buffer: buffer, boxed: false) - recipients.serialize(buffer, true) - serializeInt32(noActivityDays, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .businessGreetingMessage(let shortcutId, let recipients, let noActivityDays): - return ("businessGreetingMessage", [("shortcutId", shortcutId as Any), ("recipients", recipients as Any), ("noActivityDays", noActivityDays as Any)]) - } - } - - public static func parse_businessGreetingMessage(_ reader: BufferReader) -> BusinessGreetingMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.BusinessRecipients? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.BusinessRecipients - } - var _3: Int32? - _3 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.BusinessGreetingMessage.businessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 1d41cbd8c1..99dd0d3621 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -645,7 +645,7 @@ public extension Api { case updateBotPurchasedPaidMedia(userId: Int64, payload: String, qts: Int32) case updateBotShippingQuery(queryId: Int64, userId: Int64, payload: Buffer, shippingAddress: Api.PostAddress) case updateBotStopped(userId: Int64, date: Int32, stopped: Api.Bool, qts: Int32) - case updateBotSubscriptionExpire(userId: Int64, payload: String, untilDate: Int32, qts: Int32) + case updateBotSubscriptionExpire(userId: Int64, payload: String, invoiceSlug: String, untilDate: Int32, qts: Int32) case updateBotWebhookJSON(data: Api.DataJSON) case updateBotWebhookJSONQuery(queryId: Int64, data: Api.DataJSON, timeout: Int32) case updateBroadcastRevenueTransactions(peer: Api.Peer, balances: Api.BroadcastRevenueBalances) @@ -970,12 +970,13 @@ public extension Api { stopped.serialize(buffer, true) serializeInt32(qts, buffer: buffer, boxed: false) break - case .updateBotSubscriptionExpire(let userId, let payload, let untilDate, let qts): + case .updateBotSubscriptionExpire(let userId, let payload, let invoiceSlug, let untilDate, let qts): if boxed { - buffer.appendInt32(-1464975695) + buffer.appendInt32(756270830) } serializeInt64(userId, buffer: buffer, boxed: false) serializeString(payload, buffer: buffer, boxed: false) + serializeString(invoiceSlug, buffer: buffer, boxed: false) serializeInt32(untilDate, buffer: buffer, boxed: false) serializeInt32(qts, buffer: buffer, boxed: false) break @@ -2051,8 +2052,8 @@ public extension Api { return ("updateBotShippingQuery", [("queryId", queryId as Any), ("userId", userId as Any), ("payload", payload as Any), ("shippingAddress", shippingAddress as Any)]) case .updateBotStopped(let userId, let date, let stopped, let qts): return ("updateBotStopped", [("userId", userId as Any), ("date", date as Any), ("stopped", stopped as Any), ("qts", qts as Any)]) - case .updateBotSubscriptionExpire(let userId, let payload, let untilDate, let qts): - return ("updateBotSubscriptionExpire", [("userId", userId as Any), ("payload", payload as Any), ("untilDate", untilDate as Any), ("qts", qts as Any)]) + case .updateBotSubscriptionExpire(let userId, let payload, let invoiceSlug, let untilDate, let qts): + return ("updateBotSubscriptionExpire", [("userId", userId as Any), ("payload", payload as Any), ("invoiceSlug", invoiceSlug as Any), ("untilDate", untilDate as Any), ("qts", qts as Any)]) case .updateBotWebhookJSON(let data): return ("updateBotWebhookJSON", [("data", data as Any)]) case .updateBotWebhookJSONQuery(let queryId, let data, let timeout): @@ -2749,16 +2750,19 @@ public extension Api { _1 = reader.readInt64() var _2: String? _2 = parseString(reader) - var _3: Int32? - _3 = reader.readInt32() + var _3: String? + _3 = parseString(reader) var _4: Int32? _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateBotSubscriptionExpire(userId: _1!, payload: _2!, untilDate: _3!, qts: _4!) + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateBotSubscriptionExpire(userId: _1!, payload: _2!, invoiceSlug: _3!, untilDate: _4!, qts: _5!) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 2a1c1fb212..600a076078 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -1,3 +1,49 @@ +public extension Api { + enum BusinessGreetingMessage: TypeConstructorDescription { + case businessGreetingMessage(shortcutId: Int32, recipients: Api.BusinessRecipients, noActivityDays: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .businessGreetingMessage(let shortcutId, let recipients, let noActivityDays): + if boxed { + buffer.appendInt32(-451302485) + } + serializeInt32(shortcutId, buffer: buffer, boxed: false) + recipients.serialize(buffer, true) + serializeInt32(noActivityDays, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .businessGreetingMessage(let shortcutId, let recipients, let noActivityDays): + return ("businessGreetingMessage", [("shortcutId", shortcutId as Any), ("recipients", recipients as Any), ("noActivityDays", noActivityDays as Any)]) + } + } + + public static func parse_businessGreetingMessage(_ reader: BufferReader) -> BusinessGreetingMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.BusinessRecipients? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BusinessRecipients + } + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.BusinessGreetingMessage.businessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) + } + else { + return nil + } + } + + } +} public extension Api { enum BusinessIntro: TypeConstructorDescription { case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?) diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index 98d0728567..0c987587ba 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -260,6 +260,46 @@ public extension Api.messages { } } +public extension Api.messages { + enum BotPreparedInlineMessage: TypeConstructorDescription { + case botPreparedInlineMessage(id: String, expireDate: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .botPreparedInlineMessage(let id, let expireDate): + if boxed { + buffer.appendInt32(-1899035375) + } + serializeString(id, buffer: buffer, boxed: false) + serializeInt32(expireDate, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .botPreparedInlineMessage(let id, let expireDate): + return ("botPreparedInlineMessage", [("id", id as Any), ("expireDate", expireDate as Any)]) + } + } + + public static func parse_botPreparedInlineMessage(_ reader: BufferReader) -> BotPreparedInlineMessage? { + var _1: String? + _1 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.BotPreparedInlineMessage.botPreparedInlineMessage(id: _1!, expireDate: _2!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum BotResults: TypeConstructorDescription { case botResults(flags: Int32, queryId: Int64, nextOffset: String?, switchPm: Api.InlineBotSwitchPM?, switchWebview: Api.InlineBotWebView?, results: [Api.BotInlineResult], cacheTime: Int32, users: [Api.User]) @@ -1398,61 +1438,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum FoundStickerSets: TypeConstructorDescription { - case foundStickerSets(hash: Int64, sets: [Api.StickerSetCovered]) - case foundStickerSetsNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .foundStickerSets(let hash, let sets): - if boxed { - buffer.appendInt32(-1963942446) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { - item.serialize(buffer, true) - } - break - case .foundStickerSetsNotModified: - if boxed { - buffer.appendInt32(223655517) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .foundStickerSets(let hash, let sets): - return ("foundStickerSets", [("hash", hash as Any), ("sets", sets as Any)]) - case .foundStickerSetsNotModified: - return ("foundStickerSetsNotModified", []) - } - } - - public static func parse_foundStickerSets(_ reader: BufferReader) -> FoundStickerSets? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerSetCovered]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!) - } - else { - return nil - } - } - public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? { - return Api.messages.FoundStickerSets.foundStickerSetsNotModified - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index ae3204c4a7..cf310b87b3 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -1,3 +1,61 @@ +public extension Api.messages { + enum FoundStickerSets: TypeConstructorDescription { + case foundStickerSets(hash: Int64, sets: [Api.StickerSetCovered]) + case foundStickerSetsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .foundStickerSets(let hash, let sets): + if boxed { + buffer.appendInt32(-1963942446) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sets.count)) + for item in sets { + item.serialize(buffer, true) + } + break + case .foundStickerSetsNotModified: + if boxed { + buffer.appendInt32(223655517) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .foundStickerSets(let hash, let sets): + return ("foundStickerSets", [("hash", hash as Any), ("sets", sets as Any)]) + case .foundStickerSetsNotModified: + return ("foundStickerSetsNotModified", []) + } + } + + public static func parse_foundStickerSets(_ reader: BufferReader) -> FoundStickerSets? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerSetCovered]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!) + } + else { + return nil + } + } + public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? { + return Api.messages.FoundStickerSets.foundStickerSetsNotModified + } + + } +} public extension Api.messages { enum HighScores: TypeConstructorDescription { case highScores(scores: [Api.HighScore], users: [Api.User]) @@ -806,6 +864,68 @@ public extension Api.messages { } } +public extension Api.messages { + enum PreparedInlineMessage: TypeConstructorDescription { + case preparedInlineMessage(queryId: Int64, result: Api.BotInlineResult, peerTypes: [Api.InlineQueryPeerType], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .preparedInlineMessage(let queryId, let result, let peerTypes, let users): + if boxed { + buffer.appendInt32(1636301421) + } + serializeInt64(queryId, buffer: buffer, boxed: false) + result.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peerTypes.count)) + for item in peerTypes { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .preparedInlineMessage(let queryId, let result, let peerTypes, let users): + return ("preparedInlineMessage", [("queryId", queryId as Any), ("result", result as Any), ("peerTypes", peerTypes as Any), ("users", users as Any)]) + } + } + + public static func parse_preparedInlineMessage(_ reader: BufferReader) -> PreparedInlineMessage? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.BotInlineResult? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.BotInlineResult + } + var _3: [Api.InlineQueryPeerType]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InlineQueryPeerType.self) + } + var _4: [Api.User]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.messages.PreparedInlineMessage.preparedInlineMessage(queryId: _1!, result: _2!, peerTypes: _3!, users: _4!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum QuickReplies: TypeConstructorDescription { case quickReplies(quickReplies: [Api.QuickReply], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) @@ -1340,203 +1460,3 @@ public extension Api.messages { } } -public extension Api.messages { - enum SearchResultsCalendar: TypeConstructorDescription { - case searchResultsCalendar(flags: Int32, count: Int32, minDate: Int32, minMsgId: Int32, offsetIdOffset: Int32?, periods: [Api.SearchResultsCalendarPeriod], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): - if boxed { - buffer.appendInt32(343859772) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - serializeInt32(minDate, buffer: buffer, boxed: false) - serializeInt32(minMsgId, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(periods.count)) - for item in periods { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(messages.count)) - for item in messages { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): - return ("searchResultsCalendar", [("flags", flags as Any), ("count", count as Any), ("minDate", minDate as Any), ("minMsgId", minMsgId as Any), ("offsetIdOffset", offsetIdOffset as Any), ("periods", periods as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_searchResultsCalendar(_ reader: BufferReader) -> SearchResultsCalendar? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } - var _6: [Api.SearchResultsCalendarPeriod]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsCalendarPeriod.self) - } - var _7: [Api.Message]? - if let _ = reader.readInt32() { - _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) - } - var _8: [Api.Chat]? - if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _9: [Api.User]? - if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum SearchResultsPositions: TypeConstructorDescription { - case searchResultsPositions(count: Int32, positions: [Api.SearchResultsPosition]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .searchResultsPositions(let count, let positions): - if boxed { - buffer.appendInt32(1404185519) - } - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(positions.count)) - for item in positions { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .searchResultsPositions(let count, let positions): - return ("searchResultsPositions", [("count", count as Any), ("positions", positions as Any)]) - } - } - - public static func parse_searchResultsPositions(_ reader: BufferReader) -> SearchResultsPositions? { - var _1: Int32? - _1 = reader.readInt32() - var _2: [Api.SearchResultsPosition]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsPosition.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SearchResultsPositions.searchResultsPositions(count: _1!, positions: _2!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum SentEncryptedMessage: TypeConstructorDescription { - case sentEncryptedFile(date: Int32, file: Api.EncryptedFile) - case sentEncryptedMessage(date: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .sentEncryptedFile(let date, let file): - if boxed { - buffer.appendInt32(-1802240206) - } - serializeInt32(date, buffer: buffer, boxed: false) - file.serialize(buffer, true) - break - case .sentEncryptedMessage(let date): - if boxed { - buffer.appendInt32(1443858741) - } - serializeInt32(date, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .sentEncryptedFile(let date, let file): - return ("sentEncryptedFile", [("date", date as Any), ("file", file as Any)]) - case .sentEncryptedMessage(let date): - return ("sentEncryptedMessage", [("date", date as Any)]) - } - } - - public static func parse_sentEncryptedFile(_ reader: BufferReader) -> SentEncryptedMessage? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.EncryptedFile? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.EncryptedFile - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) - } - else { - return nil - } - } - public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api33.swift b/submodules/TelegramApi/Sources/Api33.swift index 289b3d5fb5..faaa885d0b 100644 --- a/submodules/TelegramApi/Sources/Api33.swift +++ b/submodules/TelegramApi/Sources/Api33.swift @@ -1,3 +1,203 @@ +public extension Api.messages { + enum SearchResultsCalendar: TypeConstructorDescription { + case searchResultsCalendar(flags: Int32, count: Int32, minDate: Int32, minMsgId: Int32, offsetIdOffset: Int32?, periods: [Api.SearchResultsCalendarPeriod], messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): + if boxed { + buffer.appendInt32(343859772) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + serializeInt32(minDate, buffer: buffer, boxed: false) + serializeInt32(minMsgId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(periods.count)) + for item in periods { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users): + return ("searchResultsCalendar", [("flags", flags as Any), ("count", count as Any), ("minDate", minDate as Any), ("minMsgId", minMsgId as Any), ("offsetIdOffset", offsetIdOffset as Any), ("periods", periods as Any), ("messages", messages as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_searchResultsCalendar(_ reader: BufferReader) -> SearchResultsCalendar? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } + var _6: [Api.SearchResultsCalendarPeriod]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsCalendarPeriod.self) + } + var _7: [Api.Message]? + if let _ = reader.readInt32() { + _7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self) + } + var _8: [Api.Chat]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _9: [Api.User]? + if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum SearchResultsPositions: TypeConstructorDescription { + case searchResultsPositions(count: Int32, positions: [Api.SearchResultsPosition]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .searchResultsPositions(let count, let positions): + if boxed { + buffer.appendInt32(1404185519) + } + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(positions.count)) + for item in positions { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .searchResultsPositions(let count, let positions): + return ("searchResultsPositions", [("count", count as Any), ("positions", positions as Any)]) + } + } + + public static func parse_searchResultsPositions(_ reader: BufferReader) -> SearchResultsPositions? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.SearchResultsPosition]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsPosition.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SearchResultsPositions.searchResultsPositions(count: _1!, positions: _2!) + } + else { + return nil + } + } + + } +} +public extension Api.messages { + enum SentEncryptedMessage: TypeConstructorDescription { + case sentEncryptedFile(date: Int32, file: Api.EncryptedFile) + case sentEncryptedMessage(date: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .sentEncryptedFile(let date, let file): + if boxed { + buffer.appendInt32(-1802240206) + } + serializeInt32(date, buffer: buffer, boxed: false) + file.serialize(buffer, true) + break + case .sentEncryptedMessage(let date): + if boxed { + buffer.appendInt32(1443858741) + } + serializeInt32(date, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .sentEncryptedFile(let date, let file): + return ("sentEncryptedFile", [("date", date as Any), ("file", file as Any)]) + case .sentEncryptedMessage(let date): + return ("sentEncryptedMessage", [("date", date as Any)]) + } + } + + public static func parse_sentEncryptedFile(_ reader: BufferReader) -> SentEncryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.EncryptedFile? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.EncryptedFile + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) + } + else { + return nil + } + } + public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) + } + else { + return nil + } + } + + } +} public extension Api.messages { enum SponsoredMessages: TypeConstructorDescription { case sponsoredMessages(flags: Int32, postsBetween: Int32?, messages: [Api.SponsoredMessage], chats: [Api.Chat], users: [Api.User]) @@ -1330,243 +1530,3 @@ public extension Api.payments { } } -public extension Api.payments { - enum StarsRevenueStats: TypeConstructorDescription { - case starsRevenueStats(revenueGraph: Api.StatsGraph, status: Api.StarsRevenueStatus, usdRate: Double) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starsRevenueStats(let revenueGraph, let status, let usdRate): - if boxed { - buffer.appendInt32(-919881925) - } - revenueGraph.serialize(buffer, true) - status.serialize(buffer, true) - serializeDouble(usdRate, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starsRevenueStats(let revenueGraph, let status, let usdRate): - return ("starsRevenueStats", [("revenueGraph", revenueGraph as Any), ("status", status as Any), ("usdRate", usdRate as Any)]) - } - } - - public static func parse_starsRevenueStats(_ reader: BufferReader) -> StarsRevenueStats? { - var _1: Api.StatsGraph? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _2: Api.StarsRevenueStatus? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StarsRevenueStatus - } - var _3: Double? - _3 = reader.readDouble() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.StarsRevenueStats.starsRevenueStats(revenueGraph: _1!, status: _2!, usdRate: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum StarsRevenueWithdrawalUrl: TypeConstructorDescription { - case starsRevenueWithdrawalUrl(url: String) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starsRevenueWithdrawalUrl(let url): - if boxed { - buffer.appendInt32(497778871) - } - serializeString(url, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starsRevenueWithdrawalUrl(let url): - return ("starsRevenueWithdrawalUrl", [("url", url as Any)]) - } - } - - public static func parse_starsRevenueWithdrawalUrl(_ reader: BufferReader) -> StarsRevenueWithdrawalUrl? { - var _1: String? - _1 = parseString(reader) - let _c1 = _1 != nil - if _c1 { - return Api.payments.StarsRevenueWithdrawalUrl.starsRevenueWithdrawalUrl(url: _1!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum StarsStatus: TypeConstructorDescription { - case starsStatus(flags: Int32, balance: Int64, subscriptions: [Api.StarsSubscription]?, subscriptionsNextOffset: String?, subscriptionsMissingBalance: Int64?, history: [Api.StarsTransaction]?, nextOffset: String?, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users): - if boxed { - buffer.appendInt32(-1141231252) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(balance, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(subscriptions!.count)) - for item in subscriptions! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 2) != 0 {serializeString(subscriptionsNextOffset!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 4) != 0 {serializeInt64(subscriptionsMissingBalance!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(history!.count)) - for item in history! { - item.serialize(buffer, true) - }} - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users): - return ("starsStatus", [("flags", flags as Any), ("balance", balance as Any), ("subscriptions", subscriptions as Any), ("subscriptionsNextOffset", subscriptionsNextOffset as Any), ("subscriptionsMissingBalance", subscriptionsMissingBalance as Any), ("history", history as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_starsStatus(_ reader: BufferReader) -> StarsStatus? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: [Api.StarsSubscription]? - if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsSubscription.self) - } } - var _4: String? - if Int(_1!) & Int(1 << 2) != 0 {_4 = parseString(reader) } - var _5: Int64? - if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt64() } - var _6: [Api.StarsTransaction]? - if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsTransaction.self) - } } - var _7: String? - if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) } - var _8: [Api.Chat]? - if let _ = reader.readInt32() { - _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _9: [Api.User]? - if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, subscriptionsMissingBalance: _5, history: _6, nextOffset: _7, chats: _8!, users: _9!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum UserStarGifts: TypeConstructorDescription { - case userStarGifts(flags: Int32, count: Int32, gifts: [Api.UserStarGift], nextOffset: String?, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .userStarGifts(let flags, let count, let gifts, let nextOffset, let users): - if boxed { - buffer.appendInt32(1801827607) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(gifts.count)) - for item in gifts { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .userStarGifts(let flags, let count, let gifts, let nextOffset, let users): - return ("userStarGifts", [("flags", flags as Any), ("count", count as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)]) - } - } - - public static func parse_userStarGifts(_ reader: BufferReader) -> UserStarGifts? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.UserStarGift]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.UserStarGift.self) - } - var _4: String? - if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } - var _5: [Api.User]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.payments.UserStarGifts.userStarGifts(flags: _1!, count: _2!, gifts: _3!, nextOffset: _4, users: _5!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api34.swift b/submodules/TelegramApi/Sources/Api34.swift index 222cf9735b..e7fe762c2c 100644 --- a/submodules/TelegramApi/Sources/Api34.swift +++ b/submodules/TelegramApi/Sources/Api34.swift @@ -1,3 +1,243 @@ +public extension Api.payments { + enum StarsRevenueStats: TypeConstructorDescription { + case starsRevenueStats(revenueGraph: Api.StatsGraph, status: Api.StarsRevenueStatus, usdRate: Double) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starsRevenueStats(let revenueGraph, let status, let usdRate): + if boxed { + buffer.appendInt32(-919881925) + } + revenueGraph.serialize(buffer, true) + status.serialize(buffer, true) + serializeDouble(usdRate, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starsRevenueStats(let revenueGraph, let status, let usdRate): + return ("starsRevenueStats", [("revenueGraph", revenueGraph as Any), ("status", status as Any), ("usdRate", usdRate as Any)]) + } + } + + public static func parse_starsRevenueStats(_ reader: BufferReader) -> StarsRevenueStats? { + var _1: Api.StatsGraph? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _2: Api.StarsRevenueStatus? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StarsRevenueStatus + } + var _3: Double? + _3 = reader.readDouble() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.payments.StarsRevenueStats.starsRevenueStats(revenueGraph: _1!, status: _2!, usdRate: _3!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum StarsRevenueWithdrawalUrl: TypeConstructorDescription { + case starsRevenueWithdrawalUrl(url: String) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starsRevenueWithdrawalUrl(let url): + if boxed { + buffer.appendInt32(497778871) + } + serializeString(url, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starsRevenueWithdrawalUrl(let url): + return ("starsRevenueWithdrawalUrl", [("url", url as Any)]) + } + } + + public static func parse_starsRevenueWithdrawalUrl(_ reader: BufferReader) -> StarsRevenueWithdrawalUrl? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if _c1 { + return Api.payments.StarsRevenueWithdrawalUrl.starsRevenueWithdrawalUrl(url: _1!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum StarsStatus: TypeConstructorDescription { + case starsStatus(flags: Int32, balance: Int64, subscriptions: [Api.StarsSubscription]?, subscriptionsNextOffset: String?, subscriptionsMissingBalance: Int64?, history: [Api.StarsTransaction]?, nextOffset: String?, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users): + if boxed { + buffer.appendInt32(-1141231252) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(balance, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(subscriptions!.count)) + for item in subscriptions! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 2) != 0 {serializeString(subscriptionsNextOffset!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 4) != 0 {serializeInt64(subscriptionsMissingBalance!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(history!.count)) + for item in history! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users): + return ("starsStatus", [("flags", flags as Any), ("balance", balance as Any), ("subscriptions", subscriptions as Any), ("subscriptionsNextOffset", subscriptionsNextOffset as Any), ("subscriptionsMissingBalance", subscriptionsMissingBalance as Any), ("history", history as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_starsStatus(_ reader: BufferReader) -> StarsStatus? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: [Api.StarsSubscription]? + if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsSubscription.self) + } } + var _4: String? + if Int(_1!) & Int(1 << 2) != 0 {_4 = parseString(reader) } + var _5: Int64? + if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt64() } + var _6: [Api.StarsTransaction]? + if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsTransaction.self) + } } + var _7: String? + if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) } + var _8: [Api.Chat]? + if let _ = reader.readInt32() { + _8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _9: [Api.User]? + if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, subscriptionsMissingBalance: _5, history: _6, nextOffset: _7, chats: _8!, users: _9!) + } + else { + return nil + } + } + + } +} +public extension Api.payments { + enum UserStarGifts: TypeConstructorDescription { + case userStarGifts(flags: Int32, count: Int32, gifts: [Api.UserStarGift], nextOffset: String?, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .userStarGifts(let flags, let count, let gifts, let nextOffset, let users): + if boxed { + buffer.appendInt32(1801827607) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(gifts.count)) + for item in gifts { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {serializeString(nextOffset!, buffer: buffer, boxed: false)} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .userStarGifts(let flags, let count, let gifts, let nextOffset, let users): + return ("userStarGifts", [("flags", flags as Any), ("count", count as Any), ("gifts", gifts as Any), ("nextOffset", nextOffset as Any), ("users", users as Any)]) + } + } + + public static func parse_userStarGifts(_ reader: BufferReader) -> UserStarGifts? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.UserStarGift]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.UserStarGift.self) + } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: [Api.User]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.payments.UserStarGifts.userStarGifts(flags: _1!, count: _2!, gifts: _3!, nextOffset: _4, users: _5!) + } + else { + return nil + } + } + + } +} public extension Api.payments { enum ValidatedRequestedInfo: TypeConstructorDescription { case validatedRequestedInfo(flags: Int32, id: String?, shippingOptions: [Api.ShippingOption]?) diff --git a/submodules/TelegramApi/Sources/Api36.swift b/submodules/TelegramApi/Sources/Api36.swift index 0a6196d7bb..a2de7f78eb 100644 --- a/submodules/TelegramApi/Sources/Api36.swift +++ b/submodules/TelegramApi/Sources/Api36.swift @@ -2574,6 +2574,22 @@ public extension Api.functions.bots { }) } } +public extension Api.functions.bots { + static func toggleUserEmojiStatusPermission(bot: Api.InputUser, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(115237778) + bot.serialize(buffer, true) + enabled.serialize(buffer, true) + return (FunctionDescription(name: "bots.toggleUserEmojiStatusPermission", parameters: [("bot", String(describing: bot)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.bots { static func toggleUsername(bot: Api.InputUser, username: String, active: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -2591,6 +2607,22 @@ public extension Api.functions.bots { }) } } +public extension Api.functions.bots { + static func updateUserEmojiStatus(userId: Api.InputUser, emojiStatus: Api.EmojiStatus) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-308334395) + userId.serialize(buffer, true) + emojiStatus.serialize(buffer, true) + return (FunctionDescription(name: "bots.updateUserEmojiStatus", parameters: [("userId", String(describing: userId)), ("emojiStatus", String(describing: emojiStatus))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.channels { static func checkUsername(channel: Api.InputChannel, username: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -6493,6 +6525,22 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getPreparedInlineMessage(bot: Api.InputUser, id: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-2055291464) + bot.serialize(buffer, true) + serializeString(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.getPreparedInlineMessage", parameters: [("bot", String(describing: bot)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.PreparedInlineMessage? in + let reader = BufferReader(buffer) + var result: Api.messages.PreparedInlineMessage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.PreparedInlineMessage + } + return result + }) + } +} public extension Api.functions.messages { static func getQuickReplies(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -7608,6 +7656,28 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func savePreparedInlineMessage(flags: Int32, result: Api.InputBotInlineResult, userId: Api.InputUser, peerTypes: [Api.InlineQueryPeerType]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-232816849) + serializeInt32(flags, buffer: buffer, boxed: false) + result.serialize(buffer, true) + userId.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peerTypes!.count)) + for item in peerTypes! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "messages.savePreparedInlineMessage", parameters: [("flags", String(describing: flags)), ("result", String(describing: result)), ("userId", String(describing: userId)), ("peerTypes", String(describing: peerTypes))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotPreparedInlineMessage? in + let reader = BufferReader(buffer) + var result: Api.messages.BotPreparedInlineMessage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.BotPreparedInlineMessage + } + return result + }) + } +} public extension Api.functions.messages { static func saveRecentSticker(flags: Int32, id: Api.InputDocument, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -8775,6 +8845,24 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func botCancelStarsSubscription(flags: Int32, userId: Api.InputUser, invoiceSlug: String?, chargeId: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1475996902) + serializeInt32(flags, buffer: buffer, boxed: false) + userId.serialize(buffer, true) + if Int(flags) & Int(1 << 1) != 0 {serializeString(invoiceSlug!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeString(chargeId!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "payments.botCancelStarsSubscription", parameters: [("flags", String(describing: flags)), ("userId", String(describing: userId)), ("invoiceSlug", String(describing: invoiceSlug)), ("chargeId", String(describing: chargeId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.payments { static func canPurchasePremium(purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/BotInfo.swift b/submodules/TelegramCore/Sources/ApiUtils/BotInfo.swift index 92ee56bc26..213f9d8441 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/BotInfo.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/BotInfo.swift @@ -13,10 +13,25 @@ extension BotMenuButton { } } +extension BotAppSettings { + init(apiBotAppSettings: Api.BotAppSettings) { + switch apiBotAppSettings { + case let .botAppSettings(_, placeholderDocument, backgroundColor, backgroundDarkColor, headerColor, headerDarkColor): + self.init( + placeholder: placeholderDocument.flatMap { telegramMediaFileFromApiDocument($0, altDocuments: []) }, + backgroundColor: backgroundColor, + backgroundDarkColor: backgroundDarkColor, + headerColor: headerColor, + headerDarkColor: headerDarkColor + ) + } + } +} + extension BotInfo { convenience init(apiBotInfo: Api.BotInfo) { switch apiBotInfo { - case let .botInfo(_, _, description, descriptionPhoto, descriptionDocument, apiCommands, apiMenuButton, privacyPolicyUrl): + case let .botInfo(_, _, description, descriptionPhoto, descriptionDocument, apiCommands, apiMenuButton, privacyPolicyUrl, appSettings): let photo: TelegramMediaImage? = descriptionPhoto.flatMap(telegramMediaImageFromApiPhoto) let video: TelegramMediaFile? = descriptionDocument.flatMap { telegramMediaFileFromApiDocument($0, altDocuments: []) } var commands: [BotCommand] = [] @@ -32,7 +47,7 @@ extension BotInfo { if let apiMenuButton = apiMenuButton { menuButton = BotMenuButton(apiBotMenuButton: apiMenuButton) } - self.init(description: description ?? "", photo: photo, video: video, commands: commands, menuButton: menuButton, privacyPolicyUrl: privacyPolicyUrl) + self.init(description: description ?? "", photo: photo, video: video, commands: commands, menuButton: menuButton, privacyPolicyUrl: privacyPolicyUrl, appSettings: appSettings.flatMap { BotAppSettings(apiBotAppSettings: $0) }) } } } diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift index 581d20ecfb..4d49aaa5fa 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReplyMarkupMessageAttribute.swift @@ -2,6 +2,22 @@ import Foundation import Postbox import TelegramApi +extension ReplyMarkupButtonAction.PeerTypes { + init?(apiType: Api.InlineQueryPeerType) { + switch apiType { + case .inlineQueryPeerTypePM: + self = .users + case .inlineQueryPeerTypeBotPM: + self = .bots + case .inlineQueryPeerTypeBroadcast: + self = .channels + case .inlineQueryPeerTypeChat, .inlineQueryPeerTypeMegagroup: + self = .groups + case .inlineQueryPeerTypeSameBotPM: + return nil + } + } +} extension ReplyMarkupButton { init(apiButton: Api.KeyboardButton) { diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 9503219ab2..851ad87d6d 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1706,14 +1706,14 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: updatedState.updateCachedPeerData(peer.peerId, { current in if peer.peerId.namespace == Namespaces.Peer.CloudUser, let previous = current as? CachedUserData { if let botInfo = previous.botInfo { - return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, video: botInfo.video, commands: commands, menuButton: botInfo.menuButton, privacyPolicyUrl: botInfo.privacyPolicyUrl)) + return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, video: botInfo.video, commands: commands, menuButton: botInfo.menuButton, privacyPolicyUrl: botInfo.privacyPolicyUrl, appSettings: botInfo.appSettings)) } } else if peer.peerId.namespace == Namespaces.Peer.CloudGroup, let previous = current as? CachedGroupData { if let index = previous.botInfos.firstIndex(where: { $0.peerId == botPeerId }) { var updatedBotInfos = previous.botInfos let previousBotInfo = updatedBotInfos[index] updatedBotInfos.remove(at: index) - updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, video: previousBotInfo.botInfo.video, commands: commands, menuButton: previousBotInfo.botInfo.menuButton, privacyPolicyUrl: previousBotInfo.botInfo.privacyPolicyUrl)), at: index) + updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, video: previousBotInfo.botInfo.video, commands: commands, menuButton: previousBotInfo.botInfo.menuButton, privacyPolicyUrl: previousBotInfo.botInfo.privacyPolicyUrl, appSettings: previousBotInfo.botInfo.appSettings)), at: index) return previous.withUpdatedBotInfos(updatedBotInfos) } } else if peer.peerId.namespace == Namespaces.Peer.CloudChannel, let previous = current as? CachedChannelData { @@ -1721,7 +1721,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: var updatedBotInfos = previous.botInfos let previousBotInfo = updatedBotInfos[index] updatedBotInfos.remove(at: index) - updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, video: previousBotInfo.botInfo.video, commands: commands, menuButton: previousBotInfo.botInfo.menuButton, privacyPolicyUrl: previousBotInfo.botInfo.privacyPolicyUrl)), at: index) + updatedBotInfos.insert(CachedPeerBotInfo(peerId: botPeerId, botInfo: BotInfo(description: previousBotInfo.botInfo.description, photo: previousBotInfo.botInfo.photo, video: previousBotInfo.botInfo.video, commands: commands, menuButton: previousBotInfo.botInfo.menuButton, privacyPolicyUrl: previousBotInfo.botInfo.privacyPolicyUrl, appSettings: previousBotInfo.botInfo.appSettings)), at: index) return previous.withUpdatedBotInfos(updatedBotInfos) } } @@ -1733,7 +1733,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: updatedState.updateCachedPeerData(botPeerId, { current in if let previous = current as? CachedUserData { if let botInfo = previous.botInfo { - return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, video: botInfo.video, commands: botInfo.commands, menuButton: menuButton, privacyPolicyUrl: botInfo.privacyPolicyUrl)) + return previous.withUpdatedBotInfo(BotInfo(description: botInfo.description, photo: botInfo.photo, video: botInfo.video, commands: botInfo.commands, menuButton: menuButton, privacyPolicyUrl: botInfo.privacyPolicyUrl, appSettings: botInfo.appSettings)) } } return current diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_BotInfo.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_BotInfo.swift index cadd5f91bd..632f67a86b 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_BotInfo.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_BotInfo.swift @@ -45,6 +45,62 @@ public enum BotMenuButton: PostboxCoding, Hashable { } } +public struct BotAppSettings: PostboxCoding, Equatable { + public let placeholder: TelegramMediaFile? + public let backgroundColor: Int32? + public let backgroundDarkColor: Int32? + public let headerColor: Int32? + public let headerDarkColor: Int32? + + public init(placeholder: TelegramMediaFile?, backgroundColor: Int32?, backgroundDarkColor: Int32?, headerColor: Int32?, headerDarkColor: Int32?) { + self.placeholder = placeholder + self.backgroundColor = backgroundColor + self.backgroundDarkColor = backgroundDarkColor + self.headerColor = headerColor + self.headerDarkColor = headerDarkColor + } + + public init(decoder: PostboxDecoder) { + if let placeholder = decoder.decodeObjectForKey("p", decoder: { TelegramMediaFile(decoder: $0) }) as? TelegramMediaFile { + self.placeholder = placeholder + } else { + self.placeholder = nil + } + self.backgroundColor = decoder.decodeOptionalInt32ForKey("b") + self.backgroundDarkColor = decoder.decodeOptionalInt32ForKey("bd") + self.headerColor = decoder.decodeOptionalInt32ForKey("h") + self.headerDarkColor = decoder.decodeOptionalInt32ForKey("hd") + } + + public func encode(_ encoder: PostboxEncoder) { + if let placeholder = self.placeholder { + encoder.encodeObject(placeholder, forKey: "p") + } else { + encoder.encodeNil(forKey: "p") + } + if let backgroundColor = self.backgroundColor { + encoder.encodeInt32(backgroundColor, forKey: "b") + } else { + encoder.encodeNil(forKey: "b") + } + if let backgroundDarkColor = self.backgroundDarkColor { + encoder.encodeInt32(backgroundDarkColor, forKey: "bd") + } else { + encoder.encodeNil(forKey: "bd") + } + if let headerColor = self.headerColor { + encoder.encodeInt32(headerColor, forKey: "h") + } else { + encoder.encodeNil(forKey: "h") + } + if let headerDarkColor = self.headerDarkColor { + encoder.encodeInt32(headerDarkColor, forKey: "hd") + } else { + encoder.encodeNil(forKey: "hd") + } + } +} + public final class BotInfo: PostboxCoding, Equatable { public let description: String public let photo: TelegramMediaImage? @@ -52,14 +108,16 @@ public final class BotInfo: PostboxCoding, Equatable { public let commands: [BotCommand] public let menuButton: BotMenuButton public let privacyPolicyUrl: String? + public let appSettings: BotAppSettings? - public init(description: String, photo: TelegramMediaImage?, video: TelegramMediaFile?, commands: [BotCommand], menuButton: BotMenuButton, privacyPolicyUrl: String?) { + public init(description: String, photo: TelegramMediaImage?, video: TelegramMediaFile?, commands: [BotCommand], menuButton: BotMenuButton, privacyPolicyUrl: String?, appSettings: BotAppSettings?) { self.description = description self.photo = photo self.video = video self.commands = commands self.menuButton = menuButton self.privacyPolicyUrl = privacyPolicyUrl + self.appSettings = appSettings } public init(decoder: PostboxDecoder) { @@ -77,6 +135,11 @@ public final class BotInfo: PostboxCoding, Equatable { self.commands = decoder.decodeObjectArrayWithDecoderForKey("c") self.menuButton = (decoder.decodeObjectForKey("b", decoder: { BotMenuButton(decoder: $0) }) as? BotMenuButton) ?? .commands self.privacyPolicyUrl = decoder.decodeOptionalStringForKey("pp") + if let appSettings = decoder.decodeObjectForKey("as", decoder: { BotAppSettings(decoder: $0) }) as? BotAppSettings { + self.appSettings = appSettings + } else { + self.appSettings = nil + } } public func encode(_ encoder: PostboxEncoder) { @@ -98,9 +161,14 @@ public final class BotInfo: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "pp") } + if let appSettings = self.appSettings { + encoder.encodeObject(appSettings, forKey: "as") + } else { + encoder.encodeNil(forKey: "as") + } } public static func ==(lhs: BotInfo, rhs: BotInfo) -> Bool { - return lhs.description == rhs.description && lhs.commands == rhs.commands && lhs.menuButton == rhs.menuButton && lhs.photo == rhs.photo && lhs.privacyPolicyUrl == rhs.privacyPolicyUrl + return lhs.description == rhs.description && lhs.commands == rhs.commands && lhs.menuButton == rhs.menuButton && lhs.photo == rhs.photo && lhs.privacyPolicyUrl == rhs.privacyPolicyUrl && lhs.appSettings == rhs.appSettings } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index 3e5616334f..6d2800ae08 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -299,6 +299,7 @@ public struct CachedUserFlags: OptionSet { public static let premiumRequired = CachedUserFlags(rawValue: 1 << 3) public static let adsEnabled = CachedUserFlags(rawValue: 1 << 4) public static let canViewRevenue = CachedUserFlags(rawValue: 1 << 5) + public static let botCanManageEmojiStatus = CachedUserFlags(rawValue: 1 << 6) } public final class EditableBotInfo: PostboxCoding, Equatable { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift index f8ef324e33..53c6ef5955 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/PeersData.swift @@ -1126,6 +1126,34 @@ public extension TelegramEngine.EngineData.Item { } } + public struct CanManageEmojiStatus: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { + public typealias Result = Bool + + 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 .cachedPeerData(peerId: self.id) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? CachedPeerDataView else { + preconditionFailure() + } + if let cachedData = view.cachedPeerData as? CachedUserData { + return cachedData.flags.contains(.botCanManageEmojiStatus) + } else { + return false + } + } + } + public struct CanViewStarsRevenue: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem { public typealias Result = Bool diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift index 220ef1bdaa..8bbd8de7b4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift @@ -67,7 +67,7 @@ func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: P |> switchToLatest } -func _internal_requestMainWebView(postbox: Postbox, network: Network, botId: PeerId, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal { +func _internal_requestMainWebView(postbox: Postbox, network: Network, peerId: PeerId, botId: PeerId, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal { var serializedThemeParams: Api.DataJSON? if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) { serializedThemeParams = .dataJSON(data: dataString) @@ -76,7 +76,7 @@ func _internal_requestMainWebView(postbox: Postbox, network: Network, botId: Pee guard let bot = transaction.getPeer(botId), let inputUser = apiInputUser(bot) else { return .fail(.generic) } - guard let peer = transaction.getPeer(botId), let inputPeer = apiInputPeer(peer) else { + guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else { return .fail(.generic) } @@ -286,7 +286,7 @@ func _internal_sendWebViewData(postbox: Postbox, network: Network, stateManager: |> switchToLatest } -func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, compact: Bool, allowWrite: Bool) -> Signal { +func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, compact: Bool, fullscreen: Bool, allowWrite: Bool) -> Signal { var serializedThemeParams: Api.DataJSON? if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) { serializedThemeParams = .dataJSON(data: dataString) @@ -321,6 +321,9 @@ func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManage if compact { flags |= (1 << 7) } + if fullscreen { + flags |= (1 << 8) + } return network.request(Api.functions.messages.requestAppWebView(flags: flags, peer: inputPeer, app: app, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform)) |> mapError { _ -> RequestWebViewError in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/PreparedInlineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PreparedInlineMessages.swift new file mode 100644 index 0000000000..d4ce48ffd0 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/PreparedInlineMessages.swift @@ -0,0 +1,40 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +public struct PreparedInlineMessage: Equatable { + let queryId: Int64 + let result: ChatContextResult + let peerTypes: [ReplyMarkupButtonAction.PeerTypes] +} + +func _internal_getPreparedInlineMessage(account: Account, botId: EnginePeer.Id, id: String) -> Signal { + return account.postbox.transaction { transaction -> Api.InputUser? in + return transaction.getPeer(botId).flatMap(apiInputUser) + } + |> mapToSignal { inputBot -> Signal in + guard let inputBot else { + return .single(nil) + } + return account.network.request(Api.functions.messages.getPreparedInlineMessage(bot: inputBot, id: id)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result else { + return .single(nil) + } + return account.postbox.transaction { transaction -> PreparedInlineMessage? in + switch result { + case let .preparedInlineMessage(queryId, result, peerTypes, users): + updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: AccumulatedPeers(users: users)) + + return PreparedInlineMessage(queryId: queryId, result: ChatContextResult(apiResult: result, queryId: queryId), peerTypes: peerTypes.compactMap { ReplyMarkupButtonAction.PeerTypes(apiType: $0) }) + } + } + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index d6170a6de9..955f29e570 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -403,6 +403,10 @@ public extension TelegramEngine { public func messageReadStats(id: MessageId) -> Signal { return _internal_messageReadStats(account: self.account, id: id) } + + public func getPreparedInlineMessage(botId: EnginePeer.Id, id: String) -> Signal { + return _internal_getPreparedInlineMessage(account: self.account, botId: botId, id: id) + } public func requestCancelLiveLocation(ids: [MessageId]) -> Signal { return self.account.postbox.transaction { transaction -> Void in @@ -590,12 +594,12 @@ public extension TelegramEngine { return _internal_requestSimpleWebView(postbox: self.account.postbox, network: self.account.network, botId: botId, url: url, source: source, themeParams: themeParams) } - public func requestMainWebView(botId: PeerId, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal { - return _internal_requestMainWebView(postbox: self.account.postbox, network: self.account.network, botId: botId, source: source, themeParams: themeParams) + public func requestMainWebView(peerId: PeerId, botId: PeerId, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal { + return _internal_requestMainWebView(postbox: self.account.postbox, network: self.account.network, peerId: peerId, botId: botId, source: source, themeParams: themeParams) } - public func requestAppWebView(peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, compact: Bool, allowWrite: Bool) -> Signal { - return _internal_requestAppWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, appReference: appReference, payload: payload, themeParams: themeParams, compact: compact, allowWrite: allowWrite) + public func requestAppWebView(peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, compact: Bool, fullscreen: Bool, allowWrite: Bool) -> Signal { + return _internal_requestAppWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, appReference: appReference, payload: payload, themeParams: themeParams, compact: compact, fullscreen: fullscreen, allowWrite: allowWrite) } public func sendWebViewData(botId: PeerId, buttonText: String, data: String) -> Signal { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 8736e95596..4acd4f6a85 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -811,6 +811,10 @@ public extension TelegramEngine { return _internal_updateBotAbout(account: self.account, peerId: peerId, about: about) } + public func toggleBotEmojiStatusAccess(peerId: PeerId, enabled: Bool) -> Signal { + return _internal_toggleBotEmojiStatusAccess(account: self.account, peerId: peerId, enabled: enabled) + } + public func updatePeerNameColorAndEmoji(peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal { return _internal_updatePeerNameColorAndEmoji(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift index 1ae8ef638f..ef75f3aefc 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateBotInfo.swift @@ -100,7 +100,7 @@ func _internal_updateBotDescription(account: Account, peerId: PeerId, descriptio if let botInfo = current.botInfo { var updatedBotInfo = botInfo if botInfo.description == editableBotInfo.description { - updatedBotInfo = BotInfo(description: description, photo: botInfo.photo, video: botInfo.video, commands: botInfo.commands, menuButton: botInfo.menuButton, privacyPolicyUrl: botInfo.privacyPolicyUrl) + updatedBotInfo = BotInfo(description: description, photo: botInfo.photo, video: botInfo.video, commands: botInfo.commands, menuButton: botInfo.menuButton, privacyPolicyUrl: botInfo.privacyPolicyUrl, appSettings: botInfo.appSettings) } return current.withUpdatedEditableBotInfo(editableBotInfo.withUpdatedDescription(description)).withUpdatedBotInfo(updatedBotInfo) } else { @@ -121,3 +121,43 @@ func _internal_updateBotDescription(account: Account, peerId: PeerId, descriptio |> mapError { _ -> UpdateBotInfoError in } |> switchToLatest } + +public enum ToggleBotEmojiStatusAccessError { + case generic +} + +func _internal_toggleBotEmojiStatusAccess(account: Account, peerId: PeerId, enabled: Bool) -> Signal { + return account.postbox.transaction { transaction -> Signal in + if let peer = transaction.getPeer(peerId), let inputUser = apiInputUser(peer) { + return account.network.request(Api.functions.bots.toggleUserEmojiStatusPermission(bot: inputUser, enabled: enabled ? .boolTrue : .boolFalse)) + |> mapError { _ -> ToggleBotEmojiStatusAccessError in + return .generic + } + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction -> Void in + if case .boolTrue = result { + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + if let current = current as? CachedUserData { + var updatedFlags: CachedUserFlags = current.flags + if enabled { + updatedFlags.insert(.botCanManageEmojiStatus) + } else { + updatedFlags.remove(.botCanManageEmojiStatus) + } + return current.withUpdatedFlags(updatedFlags) + } else { + return current + } + }) + } + } + |> mapError { _ -> ToggleBotEmojiStatusAccessError in } + } + } else { + return .fail(.generic) + } + } + |> mapError { _ -> ToggleBotEmojiStatusAccessError in } + |> switchToLatest + |> ignoreValues +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift index 9121a3cbee..83b3135fa1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift @@ -282,6 +282,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee let translationsDisabled = (userFullFlags & (1 << 23)) != 0 let adsEnabled = (userFullFlags2 & (1 << 7)) != 0 let canViewRevenue = (userFullFlags2 & (1 << 9)) != 0 + let botCanManageEmojiStatus = (userFullFlags2 & (1 << 10)) != 0 var flags: CachedUserFlags = previous.flags if premiumRequired { @@ -309,6 +310,11 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee } else { flags.remove(.canViewRevenue) } + if botCanManageEmojiStatus { + flags.insert(.botCanManageEmojiStatus) + } else { + flags.remove(.botCanManageEmojiStatus) + } let callsPrivate = (userFullFlags & (1 << 5)) != 0 let canPinMessages = (userFullFlags & (1 << 7)) != 0 @@ -446,7 +452,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee var botInfos: [CachedPeerBotInfo] = [] for botInfo in chatFullBotInfo ?? [] { switch botInfo { - case let .botInfo(_, userId, _, _, _, _, _, _): + case let .botInfo(_, userId, _, _, _, _, _, _, _): if let userId = userId { let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) let parsedBotInfo = BotInfo(apiBotInfo: botInfo) @@ -636,7 +642,7 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee var botInfos: [CachedPeerBotInfo] = [] for botInfo in apiBotInfos { switch botInfo { - case let .botInfo(_, userId, _, _, _, _, _, _): + case let .botInfo(_, userId, _, _, _, _, _, _, _): if let userId = userId { let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) let parsedBotInfo = BotInfo(apiBotInfo: botInfo) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 7040216a1e..5a7fc07df5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -1381,7 +1381,8 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese context: context, parentController: parentController, updatedPresentationData: nil, - peer: .user(user), + botPeer: .user(user), + chatPeer: nil, threadId: nil, buttonText: "", url: "", @@ -1545,15 +1546,20 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese if let _ = user.botInfo { //TODO:localize items[.permissions]!.append(PeerInfoScreenHeaderItem(id: 30, text: "ALLOW ACCESS TO")) -// items[.permissions]!.append(PeerInfoScreenSwitchItem(id: 31, text: "Emoji Status", value: false, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in -// -// })) + var canManageEmojiStatus = false + if let cachedData = data.cachedData as? CachedUserData, cachedData.flags.contains(.botCanManageEmojiStatus) { + canManageEmojiStatus = true + } + items[.permissions]!.append(PeerInfoScreenSwitchItem(id: 31, text: "Emoji Status", value: canManageEmojiStatus, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in + let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: user.id, enabled: value) + |> deliverOnMainQueue).startStandalone() + })) if data.webAppPermissions?.location?.isRequested == true { items[.permissions]!.append(PeerInfoScreenSwitchItem(id: 32, text: "Geolocation", value: data.webAppPermissions?.location?.isAllowed ?? false, icon: UIImage(bundleImageName: "Chat/Info/Location"), isLocked: false, toggled: { value in let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: value)) - }.start() + }.startStandalone() })) } } diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 00ef0e1e1e..658482861d 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -946,7 +946,7 @@ final class AuthorizedApplicationContext { } if openAppIfAny, case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp), let parentController = self.rootController.viewControllers.last as? ViewController { - self.context.sharedContext.openWebApp(context: self.context, parentController: parentController, updatedPresentationData: nil, peer: peer, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: true, payload: nil) + self.context.sharedContext.openWebApp(context: self.context, parentController: parentController, updatedPresentationData: nil, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: true, payload: nil) } else { self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: self.rootController, context: self.context, chatLocation: chatLocation, subject: isOutgoingMessage ? messageId.flatMap { .message(id: .id($0), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false) } : nil, activateInput: activateInput ? .text : nil)) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift index 97da9a68e3..cf8220130b 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenWebApp.swift @@ -14,7 +14,20 @@ import UndoUI import UrlHandling import TelegramPresentationData -func openWebAppImpl(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peer: EnginePeer, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) { +func openWebAppImpl( + context: AccountContext, + parentController: ViewController, + updatedPresentationData: (initial: PresentationData, signal: Signal)?, + botPeer: EnginePeer, + chatPeer: EnginePeer?, + threadId: Int64?, + buttonText: String, + url: String, + simple: Bool, + source: ChatOpenWebViewSource, + skipTermsOfService: Bool, + payload: String? +) { let presentationData: PresentationData if let parentController = parentController as? ChatControllerImpl { presentationData = parentController.presentationData @@ -30,9 +43,9 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u botAddress = bot.addressName ?? "" botVerified = bot.isVerified } else { - botName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - botAddress = peer.addressName ?? "" - botVerified = peer.isVerified + botName = botPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + botAddress = botPeer.addressName ?? "" + botVerified = botPeer.isVerified } if source == .generic { @@ -95,7 +108,7 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u if let navigationController = parentController.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer { for controller in minimizedContainer.controllers { - if let controller = controller as? AttachmentController, let mainController = controller.mainController as? WebAppController, mainController.botId == peer.id && mainController.source == .menu { + if let controller = controller as? AttachmentController, let mainController = controller.mainController as? WebAppController, mainController.botId == botPeer.id && mainController.source == .menu { navigationController.maximizeViewController(controller, animated: true) return } @@ -105,22 +118,22 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u var fullSize = false var isFullscreen = false if isTelegramMeLink(url), let internalUrl = parseFullInternalUrl(sharedContext: context.sharedContext, url: url), case .peer(_, .appStart) = internalUrl { - if url.contains("&mode=fullscreen") { + if url.contains("mode=fullscreen") { isFullscreen = true fullSize = true } else { - fullSize = !url.contains("?mode=compact") + fullSize = !url.contains("mode=compact") } } var presentImpl: ((ViewController, Any?) -> Void)? - let params = WebAppParameters(source: .menu, peerId: peer.id, botId: peer.id, botName: botName, botVerified: botVerified, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: fullSize, isFullscreen: isFullscreen) + let params = WebAppParameters(source: .menu, peerId: chatPeer?.id ?? botPeer.id, botId: botPeer.id, botName: botName, botVerified: botVerified, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: fullSize, isFullscreen: isFullscreen) let controller = standaloneWebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, threadId: threadId, openUrl: { [weak parentController] url, concealed, forceUpdate, commit in - ChatControllerImpl.botOpenUrl(context: context, peerId: peer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in + ChatControllerImpl.botOpenUrl(context: context, peerId: chatPeer?.id ?? botPeer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in presentImpl?(c, a) }, commit: commit) }, requestSwitchInline: { [weak parentController] query, chatTypes, completion in - ChatControllerImpl.botRequestSwitchInline(context: context, controller: parentController as? ChatControllerImpl, peerId: peer.id, botAddress: botAddress, query: query, chatTypes: chatTypes, completion: completion) + ChatControllerImpl.botRequestSwitchInline(context: context, controller: parentController as? ChatControllerImpl, peerId: chatPeer?.id ?? botPeer.id, botAddress: botAddress, query: query, chatTypes: chatTypes, completion: completion) }, getInputContainerNode: { [weak parentController] in if let parentController = parentController as? ChatControllerImpl, let layout = parentController.validLayout, case .compact = layout.metrics.widthClass { return (parentController.chatDisplayNode.getWindowInputAccessoryHeight(), parentController.chatDisplayNode.inputPanelContainerNode, { @@ -160,10 +173,10 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u } } else if simple { var isInline = false - var botId = peer.id + var botId = botPeer.id var botName = botName var botAddress = "" - var botVerified = peer.isVerified + var botVerified = botPeer.isVerified if case let .inline(bot) = source { isInline = true botId = bot.id @@ -180,9 +193,14 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u } let webViewSignal: Signal - let webViewSource: RequestSimpleWebViewSource = isInline ? .inline(startParam: payload) : .generic + let webViewSource: RequestSimpleWebViewSource + if let payload { + webViewSource = .inline(startParam: payload) + } else { + webViewSource = .generic + } if url.isEmpty { - webViewSignal = context.engine.messages.requestMainWebView(botId: botId, source: webViewSource, themeParams: generateWebAppThemeParams(presentationData.theme)) + webViewSignal = context.engine.messages.requestMainWebView(peerId: chatPeer?.id ?? botId, botId: botId, source: webViewSource, themeParams: generateWebAppThemeParams(presentationData.theme)) } else { webViewSignal = context.engine.messages.requestSimpleWebView(botId: botId, url: url, source: webViewSource, themeParams: generateWebAppThemeParams(presentationData.theme)) } @@ -202,13 +220,13 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u } else { source = url.isEmpty ? .generic : .simple } - let params = WebAppParameters(source: source, peerId: peer.id, botId: botId, botName: botName, botVerified: botVerified, url: result.url, queryId: nil, payload: payload, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize), isFullscreen: result.flags.contains(.fullScreen)) + let params = WebAppParameters(source: source, peerId: chatPeer?.id ?? botId, botId: botId, botName: botName, botVerified: botVerified, url: result.url, queryId: nil, payload: payload, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize), isFullscreen: result.flags.contains(.fullScreen)) let controller = standaloneWebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, threadId: threadId, openUrl: { [weak parentController] url, concealed, forceUpdate, commit in - ChatControllerImpl.botOpenUrl(context: context, peerId: peer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in + ChatControllerImpl.botOpenUrl(context: context, peerId: chatPeer?.id ?? botId, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in presentImpl?(c, a) }, commit: commit) }, requestSwitchInline: { [weak parentController] query, chatTypes, completion in - ChatControllerImpl.botRequestSwitchInline(context: context, controller: parentController as? ChatControllerImpl, peerId: peer.id, botAddress: botAddress, query: query, chatTypes: chatTypes, completion: completion) + ChatControllerImpl.botRequestSwitchInline(context: context, controller: parentController as? ChatControllerImpl, peerId: chatPeer?.id ?? botId, botAddress: botAddress, query: query, chatTypes: chatTypes, completion: completion) }, getNavigationController: { [weak parentController] in var navigationController: NavigationController? if let parentController = parentController as? ChatControllerImpl { @@ -239,7 +257,7 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u messageActionCallbackDisposable = MetaDisposable() } - messageActionCallbackDisposable.set(((context.engine.messages.requestWebView(peerId: peer.id, botId: peer.id, url: !url.isEmpty ? url : nil, payload: nil, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: false, replyToMessageId: nil, threadId: threadId) + messageActionCallbackDisposable.set(((context.engine.messages.requestWebView(peerId: chatPeer?.id ?? botPeer.id, botId: botPeer.id, url: !url.isEmpty ? url : nil, payload: nil, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: false, replyToMessageId: nil, threadId: threadId) |> afterDisposed { updateProgress() }) @@ -248,9 +266,9 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u return } var presentImpl: ((ViewController, Any?) -> Void)? - let params = WebAppParameters(source: .button, peerId: peer.id, botId: peer.id, botName: botName, botVerified: botVerified, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, forceHasSettings: false, fullSize: result.flags.contains(.fullSize), isFullscreen: result.flags.contains(.fullScreen)) + let params = WebAppParameters(source: .button, peerId: chatPeer?.id ?? botPeer.id, botId: botPeer.id, botName: botName, botVerified: botVerified, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, forceHasSettings: false, fullSize: result.flags.contains(.fullSize), isFullscreen: result.flags.contains(.fullScreen)) let controller = standaloneWebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, threadId: threadId, openUrl: { [weak parentController] url, concealed, forceUpdate, commit in - ChatControllerImpl.botOpenUrl(context: context, peerId: peer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in + ChatControllerImpl.botOpenUrl(context: context, peerId: chatPeer?.id ?? botPeer.id, controller: parentController as? ChatControllerImpl, url: url, concealed: concealed, forceUpdate: forceUpdate, present: { c, a in presentImpl?(c, a) }, commit: commit) }, completion: { [weak parentController] in @@ -285,7 +303,7 @@ func openWebAppImpl(context: AccountContext, parentController: ViewController, u if skipTermsOfService { openWebView() } else { - var botPeer = peer + var botPeer = botPeer if case let .inline(bot) = source { botPeer = bot } @@ -319,7 +337,7 @@ public extension ChatControllerImpl { } self.chatDisplayNode.dismissInput() - self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, peer: EnginePeer(peer), threadId: self.chatLocation.threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: false, payload: nil) + self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: EnginePeer(peer), chatPeer: EnginePeer(peer), threadId: self.chatLocation.threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: false, payload: nil) } static func botRequestSwitchInline(context: AccountContext, controller: ChatControllerImpl?, peerId: EnginePeer.Id, botAddress: String, query: String, chatTypes: [ReplyMarkupButtonRequestPeerType]?, completion: @escaping () -> Void) -> Void { @@ -413,7 +431,7 @@ public extension ChatControllerImpl { } } - func presentBotApp(botApp: BotApp?, botPeer: EnginePeer, payload: String?, compact: Bool, concealed: Bool = false, commit: @escaping () -> Void = {}) { + func presentBotApp(botApp: BotApp?, botPeer: EnginePeer, payload: String?, mode: ResolvedStartAppMode, concealed: Bool = false, commit: @escaping () -> Void = {}) { guard let peerId = self.chatLocation.peerId else { return } @@ -469,7 +487,7 @@ public extension ChatControllerImpl { } let botAddress = botPeer.addressName ?? "" - strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestAppWebView(peerId: peerId, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: payload, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), compact: compact, allowWrite: allowWrite) + strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestAppWebView(peerId: peerId, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: payload, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), compact: mode == .compact, fullscreen: mode == .fullscreen, allowWrite: allowWrite) |> afterDisposed { updateProgress() }) @@ -570,7 +588,7 @@ public extension ChatControllerImpl { } }) } else { - self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, peer: botPeer, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: payload) + self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: botPeer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: payload) } } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6ea48775d8..b44df4b217 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9856,16 +9856,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .withBotApp(botAppStart): let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId.id)) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - if let strongSelf = self, let peer { - if let botApp = botAppStart.botApp { - strongSelf.presentBotApp(botApp: botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact, concealed: concealed, commit: { - dismissWebAppControllers() - commit() - }) - } else { - strongSelf.context.sharedContext.openWebApp(context: strongSelf.context, parentController: strongSelf, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: botAppStart.payload) + guard let self, let peer else { + return + } + if let botApp = botAppStart.botApp { + self.presentBotApp(botApp: botApp, botPeer: peer, payload: botAppStart.payload, mode: botAppStart.mode, concealed: concealed, commit: { + dismissWebAppControllers() commit() - } + }) + } else { + self.context.sharedContext.openWebApp(context: self.context, parentController: self, updatedPresentationData: self.updatedPresentationData, botPeer: peer, chatPeer: nil, threadId: nil, buttonText: "", url: "", simple: true, source: .generic, skipTermsOfService: false, payload: botAppStart.payload) + commit() } }) default: diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index bff0145832..b0364dbd23 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -668,9 +668,9 @@ func chatHistoryEntriesForView( if reverse { return (entries.reversed(), currentState) } else { - #if DEBUG - assert(entries.map(\.stableId) == entries.sorted().map(\.stableId)) - #endif +// #if DEBUG +// assert(entries.map(\.stableId) == entries.sorted().map(\.stableId)) +// #endif return (entries, currentState) } } diff --git a/submodules/TelegramUI/Sources/NavigateToChatController.swift b/submodules/TelegramUI/Sources/NavigateToChatController.swift index 25480b6b89..b2b58e5eae 100644 --- a/submodules/TelegramUI/Sources/NavigateToChatController.swift +++ b/submodules/TelegramUI/Sources/NavigateToChatController.swift @@ -165,7 +165,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) } if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { - controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact) + controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, mode: botAppStart.mode) } params.setupController(controller) found = true @@ -188,7 +188,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam } if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { Queue.mainQueue().after(0.1) { - controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact) + controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, mode: botAppStart.mode) } } } else { @@ -196,7 +196,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { Queue.mainQueue().after(0.1) { - controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact) + controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, mode: botAppStart.mode) } } } diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 30a0afd453..a3cee89117 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2825,8 +2825,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return MiniAppListScreen(context: context, initialData: initialData as! MiniAppListScreen.InitialData) } - public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peer: EnginePeer, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) { - openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, peer: peer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload) + public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) { + openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload) } } diff --git a/submodules/UndoUI/Sources/UndoOverlayController.swift b/submodules/UndoUI/Sources/UndoOverlayController.swift index ca2d9acc38..1744c61bbc 100644 --- a/submodules/UndoUI/Sources/UndoOverlayController.swift +++ b/submodules/UndoUI/Sources/UndoOverlayController.swift @@ -50,6 +50,7 @@ public enum UndoOverlayContent { case peers(context: AccountContext, peers: [EnginePeer], title: String?, text: String, customUndoText: String?) case messageTagged(context: AccountContext, isSingleMessage: Bool, customEmoji: TelegramMediaFile, isBuiltinReaction: Bool, customUndoText: String?) case media(context: AccountContext, file: FileMediaReference, title: String?, text: String, undoText: String?, customAction: (() -> Void)?) + case progress(progress: CGFloat, title: String, text: String, undoText: String?) } public enum UndoOverlayAction { diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index d04b022b4d..6a2c38bbf2 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -1351,6 +1351,45 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { if let updatedImageSignal = updatedImageSignal { stillStickerNode.setSignal(updatedImageSignal) } + case let .progress(progress, title, text, customUndoText): + self.avatarNode = nil + self.iconNode = ASImageNode() + self.iconNode?.displayWithoutProcessing = true + self.iconNode?.displaysAsynchronously = false + self.iconNode?.image = generateTintedImage(image: UIImage(bundleImageName: "Premium/File"), color: .white) + self.iconImageSize = CGSize(width: 14.0, height: 14.0) + self.iconCheckNode = nil + self.animationNode = nil + self.animatedStickerNode = nil + + self.statusNode = RadialStatusNode(backgroundNodeColor: .clear) + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) + + let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) + let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) + let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in + return ("URL", contents) + }), textAlignment: .natural) + self.textNode.attributedText = attributedText + + + if text.contains("](") { + isUserInteractionEnabled = true + } + self.originalRemainingSeconds = 5 + + self.textNode.maximumNumberOfLines = 5 + + if let customUndoText = customUndoText { + undoText = customUndoText + displayUndo = true + } else { + displayUndo = false + } + + self.statusNode?.transitionToState(.progress(color: .white, lineWidth: nil, value: max(progress, 0.027), cancelEnabled: false, animateRotation: true), completion: {}) } self.remainingSeconds = self.originalRemainingSeconds @@ -1394,7 +1433,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } else { self.isUserInteractionEnabled = false } - case .sticker, .customEmoji, .media: + case .sticker, .customEmoji, .media, .progress: self.isUserInteractionEnabled = displayUndo case .dice: self.panelWrapperNode.clipsToBounds = true @@ -1431,6 +1470,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode) self.avatarNode.flatMap(self.panelWrapperNode.addSubnode) self.multiAvatarsNode.flatMap(self.panelWrapperNode.addSubnode) + self.panelWrapperNode.addSubnode(self.buttonNode) self.panelWrapperNode.addSubnode(self.titleNode) self.panelWrapperNode.addSubnode(self.textNode) @@ -1629,6 +1669,15 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.renewWithCurrentContent() transition = .animated(duration: 0.1, curve: .easeInOut) + case let .progress(progress, title, text, _): + let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) + let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) + let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural) + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white) + self.textNode.attributedText = attributedText + + self.statusNode?.transitionToState(.progress(color: .white, lineWidth: nil, value: max(progress, 0.027), cancelEnabled: false, animateRotation: true), completion: {}) default: break } @@ -1667,7 +1716,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { var leftInset: CGFloat = 50.0 if let iconImageSize = self.iconImageSize { - leftInset = 9.0 + iconImageSize.width + 9.0 + if case .progress = self.content { + } else { + leftInset = 9.0 + iconImageSize.width + 9.0 + } } else if let iconSize = preferredSize { if iconSize.width > leftInset { leftInset = iconSize.width - 8.0 @@ -1802,8 +1854,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { iconSize = CGSize() } + var isProgress = false + if case .progress = self.content { + isProgress = true + } + let iconFrame: CGRect - if self.iconImageSize != nil { + if self.iconImageSize != nil && !isProgress { iconFrame = CGRect(origin: CGPoint(x: 9.0, y: floor((contentHeight - iconSize.height) / 2.0) + verticalOffset), size: iconSize) } else { iconFrame = CGRect(origin: CGPoint(x: floor((leftInset - iconSize.width) / 2.0), y: floor((contentHeight - iconSize.height) / 2.0) + verticalOffset), size: iconSize) @@ -1885,13 +1942,17 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } transition.updateFrame(node: statusNode, frame: statusFrame) if !self.didStartStatusNode { - let statusColor: UIColor - if case .starsSent = self.content { - statusColor = self.undoTextColor + if case .progress = self.content { + } else { - statusColor = .white + let statusColor: UIColor + if case .starsSent = self.content { + statusColor = self.undoTextColor + } else { + statusColor = .white + } + statusNode.transitionToState(.secretTimeout(color: statusColor, icon: .none, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) } - statusNode.transitionToState(.secretTimeout(color: statusColor, icon: .none, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) self.didStartStatusNode = true } } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 9f51d5c976..6d879d92f7 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -71,7 +71,7 @@ public enum ParsedInternalPeerUrlParameter { case channelMessage(Int32, Double?) case replyThread(Int32, Int32) case voiceChat(String?) - case appStart(String, String?, Bool) + case appStart(String, String?, ResolvedStartAppMode) case story(Int32) case boost case text(String) @@ -293,18 +293,25 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String) } return .startAttach(peerName, value, choose) } else if queryItem.name == "startapp" { - var compact = false + var mode: ResolvedStartAppMode = .generic if let queryItems = components.queryItems { for queryItem in queryItems { if let value = queryItem.value { - if queryItem.name == "mode", value == "compact" { - compact = true + if queryItem.name == "mode" { + switch value { + case "compact": + mode = .compact + case "fullscreen": + mode = .fullscreen + default: + break + } break } } } } - return .peer(.name(peerName), .appStart("", queryItem.value, compact)) + return .peer(.name(peerName), .appStart("", queryItem.value, mode)) } else if queryItem.name == "story" { if let id = Int32(value) { return .peer(.name(peerName), .story(id)) @@ -335,18 +342,25 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String) } else if queryItem.name == "profile" { return .peer(.name(peerName), .profile) } else if queryItem.name == "startapp" { - var compact = false + var mode: ResolvedStartAppMode = .generic if let queryItems = components.queryItems { for queryItem in queryItems { if let value = queryItem.value { - if queryItem.name == "mode", value == "compact" { - compact = true + if queryItem.name == "mode" { + switch value { + case "compact": + mode = .compact + case "fullscreen": + mode = .fullscreen + default: + break + } break } } } } - return .peer(.name(peerName), .appStart("", nil, compact)) + return .peer(.name(peerName), .appStart("", queryItem.value, mode)) } } } @@ -615,19 +629,28 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String) } else if pathComponents.count == 2 { let appName = pathComponents[1] var startApp: String? - var compact = false + var mode: ResolvedStartAppMode = .generic if let queryItems = components.queryItems { for queryItem in queryItems { if let value = queryItem.value { if queryItem.name == "startapp" { startApp = value - } else if queryItem.name == "mode", value == "compact" { - compact = true + } + if queryItem.name == "mode" { + switch value { + case "compact": + mode = .compact + case "fullscreen": + mode = .fullscreen + default: + break + } + break } } } } - return .peer(.name(peerName), .appStart(appName, startApp, compact)) + return .peer(.name(peerName), .appStart(appName, startApp, mode)) } else { return nil } @@ -745,10 +768,10 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) } } } - case let .appStart(name, payload, compact): + case let .appStart(name, payload, mode): if name.isEmpty { if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) { - return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: nil, payload: payload, justInstalled: false, compact: compact))))) + return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: nil, payload: payload, justInstalled: false, mode: mode))))) } else { return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))) } @@ -760,7 +783,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) } |> mapToSignal { botApp -> Signal in if let botApp { - return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false, compact: compact))))) + return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false, mode: mode))))) } else { return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))) } diff --git a/submodules/WebUI/Sources/FileDownload.swift b/submodules/WebUI/Sources/FileDownload.swift new file mode 100644 index 0000000000..815a109a61 --- /dev/null +++ b/submodules/WebUI/Sources/FileDownload.swift @@ -0,0 +1,75 @@ +import Foundation +import SwiftSignalKit + +final class FileDownload: NSObject, URLSessionDownloadDelegate { + private let fileSize: Int64? + private var urlSession: URLSession! + private var completion: ((URL?, Error?) -> Void)? + private var progressHandler: ((Double) -> Void)? + + init(from url: URL, fileSize: Int64?, progressHandler: @escaping (Double) -> Void, completion: @escaping (URL?, Error?) -> Void) { + self.progressHandler = progressHandler + self.fileSize = fileSize + self.completion = completion + + + super.init() + + let configuration = URLSessionConfiguration.default + urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main) + + let downloadTask = self.urlSession.downloadTask(with: url) + downloadTask.resume() + } + + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { + var totalBytesExpectedToWrite = totalBytesExpectedToWrite + if totalBytesExpectedToWrite == -1, let fileSize = self.fileSize { + totalBytesExpectedToWrite = fileSize + } + let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite) + progressHandler?(progress) + } + + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { + completion?(location, nil) + } + + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + if let error = error { + completion?(nil, error) + } + } + + static func getFileSize(url: String) -> Signal { + if #available(iOS 13.0, *) { + guard let url = URL(string: url) else { + return .single(nil) + } + var request = URLRequest(url: url) + request.httpMethod = "HEAD" + + return Signal { subscriber in + let task = URLSession.shared.dataTask(with: request) { _, response, error in + if let _ = error { + subscriber.putNext(nil) + subscriber.putCompletion() + return + } + var fileSize: Int64? + if let httpResponse = response as? HTTPURLResponse, let contentLength = httpResponse.value(forHTTPHeaderField: "Content-Length"), let size = Int64(contentLength) { + fileSize = size + } + subscriber.putNext(fileSize) + subscriber.putCompletion() + } + task.resume() + return ActionDisposable { + task.cancel() + } + } + } else { + return .single(nil) + } + } +} diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 48c4c656e8..fb09575cd7 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -400,7 +400,7 @@ public final class WebAppController: ViewController, AttachmentContainable { controller.dismiss() return } - let _ = (self.context.engine.messages.requestAppWebView(peerId: peer.id, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: appStart.payload, themeParams: generateWebAppThemeParams(self.presentationData.theme), compact: appStart.compact, allowWrite: true) + let _ = (self.context.engine.messages.requestAppWebView(peerId: peer.id, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: appStart.payload, themeParams: generateWebAppThemeParams(self.presentationData.theme), compact: appStart.mode == .compact, fullscreen: appStart.mode == .fullscreen, allowWrite: true) |> deliverOnMainQueue).startStandalone(next: { [weak self] result in guard let self, let parsedUrl = URL(string: result.url) else { return @@ -626,6 +626,69 @@ public final class WebAppController: ViewController, AttachmentContainable { transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight))) transition.updateFrame(node: self.topOverscrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -1000.0), size: CGSize(width: layout.size.width, height: 1000.0))) + + var contentTopInset: CGFloat = 0.0 + if controller.isFullscreen { + var added = false + let fullscreenControls: ComponentView + if let current = self.fullscreenControls { + fullscreenControls = current + } else { + fullscreenControls = ComponentView() + self.fullscreenControls = fullscreenControls + added = true + } + let controlsMargin: CGFloat = 8.0 + let componentTransition: ComponentTransition = added ? .immediate : ComponentTransition(transition) + let controlsSize = fullscreenControls.update( + transition: componentTransition, + component: AnyComponent( + FullscreenControlsComponent( + context: self.context, + title: controller.botName, + isVerified: controller.botVerified, + insets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), + hasBack: self.hasBackButton, + backPressed: { [weak self] in + guard let self else { + return + } + self.controller?.cancelPressed() + }, + minimizePressed: { [weak self] in + guard let self else { + return + } + self.controller?.requestMinimize(topEdgeOffset: nil, initialVelocity: nil) + }, + morePressed: { [weak self] node, gesture in + guard let self else { + return + } + self.controller?.morePressed(node: node, gesture: gesture) + } + ) + ), + environment: {}, + containerSize: layout.size + ) + if let view = fullscreenControls.view { + if view.superview == nil { + self.view.addSubview(view) + } + transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: (layout.statusBarHeight ?? 0.0) + controlsMargin), size: controlsSize)) + if added { + view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + contentTopInset = controlsSize.height + controlsMargin * 2.0 + } else if let fullscreenControls = self.fullscreenControls { + self.fullscreenControls = nil + fullscreenControls.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + fullscreenControls.view?.removeFromSuperview() + }) + } + if let webView = self.webView { var scrollInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.intrinsicInsets.bottom, right: 0.0) var frameBottomInset: CGFloat = 0.0 @@ -683,6 +746,10 @@ public final class WebAppController: ViewController, AttachmentContainable { if let controller = self.controller { webView.updateMetrics(height: viewportFrame.height, isExpanded: controller.isContainerExpanded(), isStable: !controller.isContainerPanning(), transition: transition) + + let contentInsetsData = "{top:\(contentTopInset), bottom:0.0, left:0.0, right:0.0}" + webView.sendEvent(name: "content_safe_area_changed", data: contentInsetsData) + if self.updateWebViewWhenStable && !controller.isContainerPanning() { self.updateWebViewWhenStable = false webView.setNeedsLayout() @@ -710,66 +777,6 @@ public final class WebAppController: ViewController, AttachmentContainable { placeholderNode.updateAbsoluteRect(placeholderFrame, within: layout.size) } - if controller.isFullscreen { - var added = false - let fullscreenControls: ComponentView - if let current = self.fullscreenControls { - fullscreenControls = current - } else { - fullscreenControls = ComponentView() - self.fullscreenControls = fullscreenControls - added = true - } - - let componentTransition: ComponentTransition = added ? .immediate : ComponentTransition(transition) - let controlsSize = fullscreenControls.update( - transition: componentTransition, - component: AnyComponent( - FullscreenControlsComponent( - context: self.context, - title: controller.botName, - isVerified: controller.botVerified, - insets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: 0.0, right: layout.safeInsets.right), - hasBack: self.hasBackButton, - backPressed: { [weak self] in - guard let self else { - return - } - self.controller?.cancelPressed() - }, - minimizePressed: { [weak self] in - guard let self else { - return - } - self.controller?.requestMinimize(topEdgeOffset: nil, initialVelocity: nil) - }, - morePressed: { [weak self] node, gesture in - guard let self else { - return - } - self.controller?.morePressed(node: node, gesture: gesture) - } - ) - ), - environment: {}, - containerSize: layout.size - ) - if let view = fullscreenControls.view { - if view.superview == nil { - self.view.addSubview(view) - } - transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: (layout.statusBarHeight ?? 0.0) + 8.0), size: controlsSize)) - if added { - view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } - } else if let fullscreenControls = self.fullscreenControls { - self.fullscreenControls = nil - fullscreenControls.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in - fullscreenControls.view?.removeFromSuperview() - }) - } - if let previousLayout = previousLayout, (previousLayout.inputHeight ?? 0.0).isZero, let inputHeight = layout.inputHeight, inputHeight > 44.0 { Queue.mainQueue().justDispatch { self.controller?.requestAttachmentMenuExpansion() @@ -891,6 +898,8 @@ public final class WebAppController: ViewController, AttachmentContainable { self.requestLayout(transition: .immediate) case "web_app_request_safe_area": self.requestLayout(transition: .immediate) + case "web_app_request_content_safe_area": + self.requestLayout(transition: .immediate) case "web_app_request_theme": self.sendThemeChangedEvent() case "web_app_expand": @@ -1370,13 +1379,24 @@ public final class WebAppController: ViewController, AttachmentContainable { case "web_app_add_to_home_screen": self.addToHomeScreen() case "web_app_check_home_screen": - self.webView?.sendEvent(name: "home_screen_checked", data: "{status: \"unknown\"}") + let data: JSON = ["status": "unknown"] + self.webView?.sendEvent(name: "home_screen_checked", data: data.string) case "web_app_request_location": self.requestLocation() case "web_app_check_location": self.checkLocation() case "web_app_open_location_settings": break + case "web_app_send_prepared_message": + if let json = json, let id = json["id"] as? String { + self.sendPreparedMessage(id: id) + } + case "web_app_request_emoji_status_access": + self.requestEmojiStatusAccess() + case "web_app_request_file_download": + if let json = json, let url = json["url"] as? String, let fileName = json["file_name"] as? String { + self.downloadFile(url: url, fileName: fileName) + } default: break } @@ -1612,13 +1632,6 @@ public final class WebAppController: ViewController, AttachmentContainable { } fileprivate func shareAccountContact() { - if "".isEmpty, let controller = self.controller { - let previewController = WebAppMessagePreviewScreen(context: controller.context, completion: { _ in }) - previewController.navigationPresentation = .flatModal - controller.parentController()?.push(previewController) - return - } - guard let context = self.controller?.context, let botId = self.controller?.botId, let botName = self.controller?.botName else { return } @@ -2120,68 +2133,147 @@ public final class WebAppController: ViewController, AttachmentContainable { } } - fileprivate func setEmojiStatus(_ fileId: Int64, expirationDate: Int32? = nil) { + fileprivate func sendPreparedMessage(id: String) { guard let controller = self.controller else { return } - - let botName = controller.botName - if let _ = expirationDate { - let _ = combineLatest( - queue: Queue.mainQueue(), - self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]), - self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)), - self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId)) - ).start(next: { [weak self] files, accountPeer, botPeer in - guard let self, let accountPeer, let controller = self.controller else { + let _ = (self.context.engine.messages.getPreparedInlineMessage(botId: controller.botId, id: id) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] preparedMessage in + guard let self, let controller, let preparedMessage else { + self?.webView?.sendEvent(name: "prepared_message_failed", data: "{error: \"MESSAGE_EXPIRED\"}") + return + } + let previewController = WebAppMessagePreviewScreen(context: controller.context, botName: controller.botName, preparedMessage: preparedMessage, completion: { [weak self] result in + guard let self else { return } - guard let file = files[fileId] else { - self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"SUGGESTED_EMOJI_INVALID\"}") - return + if result { + self.webView?.sendEvent(name: "prepared_message_sent", data: nil) + } else { + self.webView?.sendEvent(name: "prepared_message_failed", data: "{error: \"USER_DECLINED\"}") } - let confirmController = WebAppSetEmojiStatusScreen( - context: self.context, - botName: controller.botName, - accountPeer: accountPeer, - file: file, - completion: { [weak self, weak controller] result in - guard let self else { - return - } - if result { - let _ = (self.context.engine.accountData.setEmojiStatus(file: file, expirationDate: expirationDate) - |> deliverOnMainQueue).start(completed: { [weak self] in - self?.webView?.sendEvent(name: "emoji_status_set", data: nil) - }) - //TODO:localize - let resultController = UndoOverlayController( - presentationData: self.presentationData, - content: .sticker(context: context, file: file, loop: false, title: nil, text: "Your emoji status updated.", undoText: nil, customAction: nil), - elevatedLayout: true, - action: { action in - if case .undo = action { - - } - return true - } - ) - controller?.present(resultController, in: .window(.root)) - } else { - self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"USER_DECLINED\"}") - } - } - ) - controller.parentController()?.push(confirmController) }) - - + previewController.navigationPresentation = .flatModal + controller.parentController()?.push(previewController) + }) + } + + fileprivate func downloadFile(url: String, fileName: String) { + guard let controller = self.controller else { return } + var title: String? + let photoExtensions = [".jpg", ".png", ".gif", ".tiff"] + let videoExtensions = [".mp4", ".mov"] + let lowercasedFilename = fileName.lowercased() + for ext in photoExtensions { + if lowercasedFilename.hasSuffix(ext) { + title = "Download Photo" + break + } + } + if title == nil { + for ext in videoExtensions { + if lowercasedFilename.hasSuffix(ext) { + title = "Download Video" + break + } + } + } + if title == nil { + title = "Download Document" + } + let _ = (FileDownload.getFileSize(url: url) + |> deliverOnMainQueue).start(next: { [weak self] fileSize in + guard let self else { + return + } + + var fileSizeString = "" + if let fileSize { + fileSizeString = " (\(dataSizeString(fileSize, formatting: DataSizeStringFormatting(presentationData: self.presentationData))))" + } + + let text: String = "**\(controller.botName)** suggests you to download **\(fileName)**\(fileSizeString)." + let alertController = standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: title, text: text, actions: [ + TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { [weak self] in + self?.webView?.sendEvent(name: "file_download_requested", data: "{status: \"cancelled\"}") + }), + TextAlertAction(type: .defaultAction, title: "Download", action: { [weak self] in + self?.startDownload(url: url, fileName: fileName, fileSize: fileSize) + }) + ], parseMarkdown: true) + controller.present(alertController, in: .window(.root)) + }) + } + + private var fileDownload: FileDownload? + private weak var fileDownloadTooltip: UndoOverlayController? + fileprivate func startDownload(url: String, fileName: String, fileSize: Int64?) { + guard let controller = self.controller else { + return + } + self.webView?.sendEvent(name: "file_download_requested", data: "{status: \"downloading\"}") + + self.fileDownload = FileDownload( + from: URL(string: url)!, + fileSize: fileSize, + progressHandler: { [weak self] progress in + guard let self else { + return + } + let text: String + if let fileSize { + let downloadedSize = Int64(Double(fileSize) * progress) + text = "\(dataSizeString(downloadedSize, formatting: DataSizeStringFormatting(presentationData: self.presentationData))) / \(dataSizeString(fileSize, formatting: DataSizeStringFormatting(presentationData: self.presentationData)))" + } else { + text = "\(Int32(progress))%" + } + + self.fileDownloadTooltip?.content = .progress( + progress: progress, + title: fileName, + text: text, + undoText: "Cancel" + ) + }, + completion: { resultUrl, _ in + + } + ) + + let text: String + if let fileSize { + text = "0 KB / \(dataSizeString(fileSize, formatting: DataSizeStringFormatting(presentationData: self.presentationData)))" + } else { + text = "0%" + } + + let tooltipController = UndoOverlayController( + presentationData: self.presentationData, + content: .progress( + progress: 0.0, + title: fileName, + text: text, + undoText: "Cancel" + ), + elevatedLayout: false, + position: .top, + action: { _ in + return true + } + ) + controller.present(tooltipController, in: .window(.root)) + self.fileDownloadTooltip = tooltipController + } + + fileprivate func requestEmojiStatusAccess() { + guard let controller = self.controller else { + return + } let _ = combineLatest( queue: Queue.mainQueue(), - self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]), self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)), self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId)), self.context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false) @@ -2194,7 +2286,62 @@ public final class WebAppController: ViewController, AttachmentContainable { } } |> take(1) - ).start(next: { [weak self] files, accountPeer, botPeer, iconStatusEmoji in + ).start(next: { [weak self] accountPeer, botPeer, iconStatusEmoji in + guard let self, let accountPeer, let controller = self.controller else { + return + } + let alertController = webAppEmojiStatusAlertController( + context: self.context, + accountPeer: accountPeer, + botName: controller.botName, + icons: iconStatusEmoji, + completion: { [weak self] result in + guard let self, let controller = self.controller else { + return + } + if result { + let context = self.context + let botId = controller.botId + let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: botId, enabled: true) + |> deliverOnMainQueue).startStandalone(completed: { [weak self] in + self?.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"allowed\"}") + }) + + //TODO:localize + if let botPeer { + let resultController = UndoOverlayController( + presentationData: presentationData, + content: .invitedToVoiceChat(context: self.context, peer: botPeer, title: nil, text: "**\(controller.botName)** can now set your emoji status anytime.", action: "Undo", duration: 5.0), + elevatedLayout: true, + action: { action in + if case .undo = action { + let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: botId, enabled: false) + |> deliverOnMainQueue).startStandalone() + } + return true + } + ) + controller.present(resultController, in: .window(.root)) + } + } else { + self.webView?.sendEvent(name: "emoji_status_access_requested", data: "{status: \"cancelled\"}") + } + } + ) + controller.present(alertController, in: .window(.root)) + }) + } + + fileprivate func setEmojiStatus(_ fileId: Int64, expirationDate: Int32? = nil) { + guard let controller = self.controller else { + return + } + let _ = combineLatest( + queue: Queue.mainQueue(), + self.context.engine.stickers.resolveInlineStickers(fileIds: [fileId]), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)), + self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: controller.botId)) + ).start(next: { [weak self] files, accountPeer, botPeer in guard let self, let accountPeer, let controller = self.controller else { return } @@ -2202,12 +2349,11 @@ public final class WebAppController: ViewController, AttachmentContainable { self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"SUGGESTED_EMOJI_INVALID\"}") return } - let alertController = webAppEmojiStatusAlertController( + let confirmController = WebAppSetEmojiStatusScreen( context: self.context, + botName: controller.botName, accountPeer: accountPeer, - botName: botName, - icons: iconStatusEmoji, - expirationDate: expirationDate, + file: file, completion: { [weak self, weak controller] result in guard let self else { return @@ -2218,26 +2364,24 @@ public final class WebAppController: ViewController, AttachmentContainable { self?.webView?.sendEvent(name: "emoji_status_set", data: nil) }) //TODO:localize - if let botPeer { - let resultController = UndoOverlayController( - presentationData: presentationData, - content: .invitedToVoiceChat(context: context, peer: botPeer, title: nil, text: "**\(botName)** can now set your emoji status anytime.", action: "Undo", duration: 5.0), - elevatedLayout: true, - action: { action in - if case .undo = action { - - } - return true + let resultController = UndoOverlayController( + presentationData: self.presentationData, + content: .sticker(context: context, file: file, loop: false, title: nil, text: "Your emoji status updated.", undoText: nil, customAction: nil), + elevatedLayout: true, + action: { action in + if case .undo = action { + } - ) - controller?.present(resultController, in: .window(.root)) - } + return true + } + ) + controller?.present(resultController, in: .window(.root)) } else { self.webView?.sendEvent(name: "emoji_status_failed", data: "{error: \"USER_DECLINED\"}") } } ) - controller.present(alertController, in: .window(.root)) + controller.parentController()?.push(confirmController) }) } diff --git a/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift b/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift index 9bbdd20916..b0ada96f4a 100644 --- a/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift +++ b/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift @@ -332,7 +332,6 @@ func webAppEmojiStatusAlertController( accountPeer: EnginePeer, botName: String, icons: [TelegramMediaFile], - expirationDate: Int32?, completion: @escaping (Bool) -> Void ) -> AlertController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift b/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift index 39af0f6031..99994bedee 100644 --- a/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift +++ b/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift @@ -26,13 +26,19 @@ private final class SheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let botName: String + let preparedMessage: PreparedInlineMessage let dismiss: () -> Void init( context: AccountContext, + botName: String, + preparedMessage: PreparedInlineMessage, dismiss: @escaping () -> Void ) { self.context = context + self.botName = botName + self.preparedMessage = preparedMessage self.dismiss = dismiss } @@ -225,11 +231,17 @@ private final class WebAppMessagePreviewSheetComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment private let context: AccountContext + private let botName: String + private let preparedMessage: PreparedInlineMessage init( - context: AccountContext + context: AccountContext, + botName: String, + preparedMessage: PreparedInlineMessage ) { self.context = context + self.botName = botName + self.preparedMessage = preparedMessage } static func ==(lhs: WebAppMessagePreviewSheetComponent, rhs: WebAppMessagePreviewSheetComponent) -> Bool { @@ -252,6 +264,8 @@ private final class WebAppMessagePreviewSheetComponent: CombinedComponent { component: SheetComponent( content: AnyComponent(SheetContent( context: context.component.context, + botName: context.component.botName, + preparedMessage: context.component.preparedMessage, dismiss: { animateOut.invoke(Action { _ in if let controller = controller() { @@ -307,6 +321,8 @@ public final class WebAppMessagePreviewScreen: ViewControllerComponentContainer public init( context: AccountContext, + botName: String, + preparedMessage: PreparedInlineMessage, completion: @escaping (Bool) -> Void ) { self.context = context @@ -315,7 +331,9 @@ public final class WebAppMessagePreviewScreen: ViewControllerComponentContainer super.init( context: context, component: WebAppMessagePreviewSheetComponent( - context: context + context: context, + botName: botName, + preparedMessage: preparedMessage ), navigationBarAppearance: .none, statusBarStyle: .ignore,