Added json app configuration fetch

Added app log events synchronization
Added A/B test API
This commit is contained in:
Ilya Laktyushin 2018-12-04 22:27:51 +04:00
parent b6b1c8f376
commit 008ecf61a1
16 changed files with 1119 additions and 43 deletions

View File

@ -8,6 +8,12 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
09028386218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */; }; 09028386218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */; };
0962E66721B59BAA00245FD9 /* ManagedAppConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66621B59BAA00245FD9 /* ManagedAppConfigurationUpdates.swift */; };
0962E66921B5A11100245FD9 /* SynchronizeAppLogEventsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66821B5A11100245FD9 /* SynchronizeAppLogEventsOperation.swift */; };
0962E66B21B5A41C00245FD9 /* ManagedSynchronizeAppLogEventsOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66A21B5A41C00245FD9 /* ManagedSynchronizeAppLogEventsOperations.swift */; };
0962E66D21B5C56F00245FD9 /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66C21B5C56F00245FD9 /* JSON.swift */; };
0962E66F21B6147600245FD9 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66E21B6147600245FD9 /* AppConfiguration.swift */; };
0962E67521B6437600245FD9 /* SplitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E67421B6437600245FD9 /* SplitTest.swift */; };
9F06831021A40DEC001D8EDB /* NotificationExceptionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830F21A40DEC001D8EDB /* NotificationExceptionsList.swift */; }; 9F06831021A40DEC001D8EDB /* NotificationExceptionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830F21A40DEC001D8EDB /* NotificationExceptionsList.swift */; };
9F06831121A40DEC001D8EDB /* NotificationExceptionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830F21A40DEC001D8EDB /* NotificationExceptionsList.swift */; }; 9F06831121A40DEC001D8EDB /* NotificationExceptionsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F06830F21A40DEC001D8EDB /* NotificationExceptionsList.swift */; };
9F10CE8B20613C78002DD61A /* SecretApiLayer73.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */; }; 9F10CE8B20613C78002DD61A /* SecretApiLayer73.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018EDFF2044939F00CBB130 /* SecretApiLayer73.swift */; };
@ -776,6 +782,12 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedVoipConfigurationUpdates.swift; sourceTree = "<group>"; }; 09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedVoipConfigurationUpdates.swift; sourceTree = "<group>"; };
0962E66621B59BAA00245FD9 /* ManagedAppConfigurationUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAppConfigurationUpdates.swift; sourceTree = "<group>"; };
0962E66821B5A11100245FD9 /* SynchronizeAppLogEventsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizeAppLogEventsOperation.swift; sourceTree = "<group>"; };
0962E66A21B5A41C00245FD9 /* ManagedSynchronizeAppLogEventsOperations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeAppLogEventsOperations.swift; sourceTree = "<group>"; };
0962E66C21B5C56F00245FD9 /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
0962E66E21B6147600245FD9 /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = "<group>"; };
0962E67421B6437600245FD9 /* SplitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitTest.swift; sourceTree = "<group>"; };
9F06830F21A40DEC001D8EDB /* NotificationExceptionsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationExceptionsList.swift; sourceTree = "<group>"; }; 9F06830F21A40DEC001D8EDB /* NotificationExceptionsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationExceptionsList.swift; sourceTree = "<group>"; };
9FC8ADAA206BBFF10094F7B4 /* RecentWebSessions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentWebSessions.swift; sourceTree = "<group>"; }; 9FC8ADAA206BBFF10094F7B4 /* RecentWebSessions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentWebSessions.swift; sourceTree = "<group>"; };
C205FEA71EB3B75900455808 /* ExportMessageLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExportMessageLink.swift; sourceTree = "<group>"; }; C205FEA71EB3B75900455808 /* ExportMessageLink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExportMessageLink.swift; sourceTree = "<group>"; };
@ -1242,6 +1254,7 @@
D0FA08BA2046B37900DD23FC /* ContentPrivacySettings.swift */, D0FA08BA2046B37900DD23FC /* ContentPrivacySettings.swift */,
D026099D20C695AF006C34AC /* Wallpapers.swift */, D026099D20C695AF006C34AC /* Wallpapers.swift */,
D051DB13215EC5A300F30F92 /* AppChangelogState.swift */, D051DB13215EC5A300F30F92 /* AppChangelogState.swift */,
0962E66E21B6147600245FD9 /* AppConfiguration.swift */,
); );
name = Settings; name = Settings;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1340,6 +1353,8 @@
D0F8C39F2017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift */, D0F8C39F2017AF2700236FC5 /* GlobalTelegramCoreConfiguration.swift */,
D0E412E9206AD18E00BEE4A2 /* DecryptedResourceData.swift */, D0E412E9206AD18E00BEE4A2 /* DecryptedResourceData.swift */,
C28D3CEF20D3DA900027F4D6 /* DeepLinkInfo.swift */, C28D3CEF20D3DA900027F4D6 /* DeepLinkInfo.swift */,
0962E66C21B5C56F00245FD9 /* JSON.swift */,
0962E67421B6437600245FD9 /* SplitTest.swift */,
); );
name = Utils; name = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1501,6 +1516,9 @@
09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */, 09028385218E5DBB0067EFBD /* ManagedVoipConfigurationUpdates.swift */,
D0529D2321A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift */, D0529D2321A4123400D7C3C4 /* SynchronizeRecentlyUsedMediaOperations.swift */,
D0529D2621A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift */, D0529D2621A4141800D7C3C4 /* ManagedSynchronizeRecentlyUsedMediaOperations.swift */,
0962E66621B59BAA00245FD9 /* ManagedAppConfigurationUpdates.swift */,
0962E66821B5A11100245FD9 /* SynchronizeAppLogEventsOperation.swift */,
0962E66A21B5A41C00245FD9 /* ManagedSynchronizeAppLogEventsOperations.swift */,
); );
name = State; name = State;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2145,6 +2163,7 @@
D00C7CCF1E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */, D00C7CCF1E3628180080C3D5 /* UpdateCachedChannelParticipants.swift in Sources */,
D03B0CB91D62233400955575 /* Either.swift in Sources */, D03B0CB91D62233400955575 /* Either.swift in Sources */,
D0D748021E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */, D0D748021E7AE98B00F4B1F6 /* StickerPackInteractiveOperations.swift in Sources */,
0962E66D21B5C56F00245FD9 /* JSON.swift in Sources */,
D03B0CBD1D62234300955575 /* Regex.swift in Sources */, D03B0CBD1D62234300955575 /* Regex.swift in Sources */,
D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */, D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */,
D0B843B91DA7FF30005F29E1 /* NBMetadataCoreTest.m in Sources */, D0B843B91DA7FF30005F29E1 /* NBMetadataCoreTest.m in Sources */,
@ -2254,12 +2273,14 @@
D00422D321677F4500719B67 /* ManagedAccountPresence.swift in Sources */, D00422D321677F4500719B67 /* ManagedAccountPresence.swift in Sources */,
D03B0D0A1D62255C00955575 /* Holes.swift in Sources */, D03B0D0A1D62255C00955575 /* Holes.swift in Sources */,
D05464972073872C002ECC1E /* SecureIdBankStatementValue.swift in Sources */, D05464972073872C002ECC1E /* SecureIdBankStatementValue.swift in Sources */,
0962E67521B6437600245FD9 /* SplitTest.swift in Sources */,
D0B843CB1DA7FF30005F29E1 /* NBPhoneNumberUtil.m in Sources */, D0B843CB1DA7FF30005F29E1 /* NBPhoneNumberUtil.m in Sources */,
D03B0D5E1D631A6900955575 /* Network.swift in Sources */, D03B0D5E1D631A6900955575 /* Network.swift in Sources */,
D0B8438E1DA7D296005F29E1 /* CachedGroupParticipants.swift in Sources */, D0B8438E1DA7D296005F29E1 /* CachedGroupParticipants.swift in Sources */,
D0B843BD1DA7FF30005F29E1 /* NBMetadataHelper.m in Sources */, D0B843BD1DA7FF30005F29E1 /* NBMetadataHelper.m in Sources */,
D03B0CF51D62250800955575 /* TelegramMediaContact.swift in Sources */, D03B0CF51D62250800955575 /* TelegramMediaContact.swift in Sources */,
D03B0CFB1D62250800955575 /* TelegramMediaWebpage.swift in Sources */, D03B0CFB1D62250800955575 /* TelegramMediaWebpage.swift in Sources */,
0962E66B21B5A41C00245FD9 /* ManagedSynchronizeAppLogEventsOperations.swift in Sources */,
D09A2FEB1D7CDC320018FB72 /* PeerAccessRestrictionInfo.swift in Sources */, D09A2FEB1D7CDC320018FB72 /* PeerAccessRestrictionInfo.swift in Sources */,
D0E35A101DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */, D0E35A101DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */,
D0E23DDA1E806F7700B9B6D2 /* ManagedSynchronizeMarkFeaturedStickerPacksAsSeenOperations.swift in Sources */, D0E23DDA1E806F7700B9B6D2 /* ManagedSynchronizeMarkFeaturedStickerPacksAsSeenOperations.swift in Sources */,
@ -2267,6 +2288,7 @@
D0BE303A20619EE800FBE6D8 /* SecureIdForm.swift in Sources */, D0BE303A20619EE800FBE6D8 /* SecureIdForm.swift in Sources */,
D0448C991E268F9A005A61A7 /* SecretApiLayer46.swift in Sources */, D0448C991E268F9A005A61A7 /* SecretApiLayer46.swift in Sources */,
D03DC9131F82F89D001D584C /* RegularChatState.swift in Sources */, D03DC9131F82F89D001D584C /* RegularChatState.swift in Sources */,
0962E66721B59BAA00245FD9 /* ManagedAppConfigurationUpdates.swift in Sources */,
D0613FCF1E60520700202CDB /* ChannelMembers.swift in Sources */, D0613FCF1E60520700202CDB /* ChannelMembers.swift in Sources */,
D0B2F7742052DEF700D3BFB9 /* TelegramDeviceContactImportInfo.swift in Sources */, D0B2F7742052DEF700D3BFB9 /* TelegramDeviceContactImportInfo.swift in Sources */,
C2366C891E4F40480097CCFF /* SupportPeerId.swift in Sources */, C2366C891E4F40480097CCFF /* SupportPeerId.swift in Sources */,
@ -2404,6 +2426,7 @@
D0CA3F84207391560042D2B6 /* SecureIdPadding.swift in Sources */, D0CA3F84207391560042D2B6 /* SecureIdPadding.swift in Sources */,
D0FA8BB61E223C16001E855B /* SecretApiLayer8.swift in Sources */, D0FA8BB61E223C16001E855B /* SecretApiLayer8.swift in Sources */,
D0B843C71DA7FF30005F29E1 /* NBPhoneNumberDefines.m in Sources */, D0B843C71DA7FF30005F29E1 /* NBPhoneNumberDefines.m in Sources */,
0962E66921B5A11100245FD9 /* SynchronizeAppLogEventsOperation.swift in Sources */,
D049EAF51E44DF3300A2CD3A /* AccountState.swift in Sources */, D049EAF51E44DF3300A2CD3A /* AccountState.swift in Sources */,
C28725421EF967E700613564 /* NotificationInfoMessageAttribute.swift in Sources */, C28725421EF967E700613564 /* NotificationInfoMessageAttribute.swift in Sources */,
D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */, D03B0D5D1D631A6900955575 /* MultipartFetch.swift in Sources */,
@ -2472,6 +2495,7 @@
D03B0D081D62255C00955575 /* ChannelState.swift in Sources */, D03B0D081D62255C00955575 /* ChannelState.swift in Sources */,
D08984F521187ECA00918162 /* NetworkType.swift in Sources */, D08984F521187ECA00918162 /* NetworkType.swift in Sources */,
C251D7431E65E50500283EDE /* StickerSetInstallation.swift in Sources */, C251D7431E65E50500283EDE /* StickerSetInstallation.swift in Sources */,
0962E66F21B6147600245FD9 /* AppConfiguration.swift in Sources */,
D07047BA1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */, D07047BA1F3DF75500F6A8D4 /* ConsumePersonalMessageAction.swift in Sources */,
D07E413F208A769D00FCA8F0 /* ProxyServersStatuses.swift in Sources */, D07E413F208A769D00FCA8F0 /* ProxyServersStatuses.swift in Sources */,
D0C0B58D1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift in Sources */, D0C0B58D1ED9DC5A000F4D2C /* SynchronizeLocalizationUpdatesOperation.swift in Sources */,

View File

@ -1097,11 +1097,13 @@ public class Account {
})) }))
self.managedOperationsDisposable.add(managedConfigurationUpdates(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedConfigurationUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedVoipConfigurationUpdates(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedVoipConfigurationUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedAppConfigurationUpdates(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedTermsOfServiceUpdates(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start()) self.managedOperationsDisposable.add(managedTermsOfServiceUpdates(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
self.managedOperationsDisposable.add(managedAppChangelog(postbox: self.postbox, network: self.network, stateManager: self.stateManager, appVersion: self.networkArguments.appVersion).start()) self.managedOperationsDisposable.add(managedAppChangelog(postbox: self.postbox, network: self.network, stateManager: self.stateManager, appVersion: self.networkArguments.appVersion).start())
self.managedOperationsDisposable.add(managedProxyInfoUpdates(postbox: self.postbox, network: self.network, viewTracker: self.viewTracker).start()) self.managedOperationsDisposable.add(managedProxyInfoUpdates(postbox: self.postbox, network: self.network, viewTracker: self.viewTracker).start())
self.managedOperationsDisposable.add(managedLocalizationUpdatesOperations(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedLocalizationUpdatesOperations(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedPendingPeerNotificationSettings(postbox: self.postbox, network: self.network).start()) self.managedOperationsDisposable.add(managedPendingPeerNotificationSettings(postbox: self.postbox, network: self.network).start())
self.managedOperationsDisposable.add(managedSynchronizeAppLogEventsOperations(postbox: self.postbox, network: self.network).start())
let storagePreferencesKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.cacheStorageSettings])) let storagePreferencesKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.cacheStorageSettings]))
let mediaBox = postbox.mediaBox let mediaBox = postbox.mediaBox

View File

@ -118,8 +118,11 @@ private var declaredEncodables: Void = {
declareEncodable(CachedStickerQueryResult.self, f: { CachedStickerQueryResult(decoder: $0) }) declareEncodable(CachedStickerQueryResult.self, f: { CachedStickerQueryResult(decoder: $0) })
declareEncodable(TelegramWallpaper.self, f: { TelegramWallpaper(decoder: $0) }) declareEncodable(TelegramWallpaper.self, f: { TelegramWallpaper(decoder: $0) })
declareEncodable(SynchronizeMarkAllUnseenPersonalMessagesOperation.self, f: { SynchronizeMarkAllUnseenPersonalMessagesOperation(decoder: $0) }) declareEncodable(SynchronizeMarkAllUnseenPersonalMessagesOperation.self, f: { SynchronizeMarkAllUnseenPersonalMessagesOperation(decoder: $0) })
declareEncodable(SynchronizeAppLogEventsOperation.self, f: { SynchronizeAppLogEventsOperation(decoder: $0) })
declareEncodable(CachedRecentPeers.self, f: { CachedRecentPeers(decoder: $0) }) declareEncodable(CachedRecentPeers.self, f: { CachedRecentPeers(decoder: $0) })
declareEncodable(AppChangelogState.self, f: { AppChangelogState(decoder: $0) }) declareEncodable(AppChangelogState.self, f: { AppChangelogState(decoder: $0) })
declareEncodable(AppConfiguration.self, f: { AppConfiguration(decoder: $0) })
declareEncodable(JSON.self, f: { JSON(decoder: $0) })
return return

View File

@ -51,6 +51,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-614138572] = { return Api.account.TmpPassword.parse_tmpPassword($0) } dict[-614138572] = { return Api.account.TmpPassword.parse_tmpPassword($0) }
dict[-2103600678] = { return Api.SecureRequiredType.parse_secureRequiredType($0) } dict[-2103600678] = { return Api.SecureRequiredType.parse_secureRequiredType($0) }
dict[41187252] = { return Api.SecureRequiredType.parse_secureRequiredTypeOneOf($0) } dict[41187252] = { return Api.SecureRequiredType.parse_secureRequiredTypeOneOf($0) }
dict[1064139624] = { return Api.JSONValue.parse_jsonNull($0) }
dict[-952869270] = { return Api.JSONValue.parse_jsonBool($0) }
dict[736157604] = { return Api.JSONValue.parse_jsonNumber($0) }
dict[-1222740358] = { return Api.JSONValue.parse_jsonString($0) }
dict[-146520221] = { return Api.JSONValue.parse_jsonArray($0) }
dict[-1715350371] = { return Api.JSONValue.parse_jsonObject($0) }
dict[590459437] = { return Api.Photo.parse_photoEmpty($0) } dict[590459437] = { return Api.Photo.parse_photoEmpty($0) }
dict[-1673036328] = { return Api.Photo.parse_photo($0) } dict[-1673036328] = { return Api.Photo.parse_photo($0) }
dict[-1683826688] = { return Api.Chat.parse_chatEmpty($0) } dict[-1683826688] = { return Api.Chat.parse_chatEmpty($0) }
@ -526,6 +532,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) } dict[1471006352] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonHangup($0) }
dict[-84416311] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonBusy($0) } dict[-84416311] = { return Api.PhoneCallDiscardReason.parse_phoneCallDiscardReasonBusy($0) }
dict[-1910892683] = { return Api.NearestDc.parse_nearestDc($0) } dict[-1910892683] = { return Api.NearestDc.parse_nearestDc($0) }
dict[-1059185703] = { return Api.JSONObjectValue.parse_jsonObjectValue($0) }
dict[-1916114267] = { return Api.photos.Photos.parse_photos($0) } dict[-1916114267] = { return Api.photos.Photos.parse_photos($0) }
dict[352657236] = { return Api.photos.Photos.parse_photosSlice($0) } dict[352657236] = { return Api.photos.Photos.parse_photosSlice($0) }
dict[2010127419] = { return Api.contacts.ImportedContacts.parse_importedContacts($0) } dict[2010127419] = { return Api.contacts.ImportedContacts.parse_importedContacts($0) }
@ -662,7 +669,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1788705589] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) } dict[1788705589] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) } dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) } dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
dict[1996904104] = { return Api.InputAppEvent.parse_inputAppEvent($0) } dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) }
dict[-1148011883] = { return Api.MessageEntity.parse_messageEntityUnknown($0) } dict[-1148011883] = { return Api.MessageEntity.parse_messageEntityUnknown($0) }
dict[-100378723] = { return Api.MessageEntity.parse_messageEntityMention($0) } dict[-100378723] = { return Api.MessageEntity.parse_messageEntityMention($0) }
dict[1868782349] = { return Api.MessageEntity.parse_messageEntityHashtag($0) } dict[1868782349] = { return Api.MessageEntity.parse_messageEntityHashtag($0) }
@ -778,6 +785,8 @@ struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.SecureRequiredType: case let _1 as Api.SecureRequiredType:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.JSONValue:
_1.serialize(buffer, boxed)
case let _1 as Api.Photo: case let _1 as Api.Photo:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.Chat: case let _1 as Api.Chat:
@ -1112,6 +1121,8 @@ struct Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.NearestDc: case let _1 as Api.NearestDc:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.JSONObjectValue:
_1.serialize(buffer, boxed)
case let _1 as Api.photos.Photos: case let _1 as Api.photos.Photos:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.contacts.ImportedContacts: case let _1 as Api.contacts.ImportedContacts:

View File

@ -1242,6 +1242,146 @@ extension Api {
} }
} }
}
enum JSONValue: TypeConstructorDescription {
case jsonNull
case jsonBool(value: Api.Bool)
case jsonNumber(value: Double)
case jsonString(value: String)
case jsonArray(value: [Api.JSONValue])
case jsonObject(value: [Api.JSONObjectValue])
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .jsonNull:
if boxed {
buffer.appendInt32(1064139624)
}
break
case .jsonBool(let value):
if boxed {
buffer.appendInt32(-952869270)
}
value.serialize(buffer, true)
break
case .jsonNumber(let value):
if boxed {
buffer.appendInt32(736157604)
}
serializeDouble(value, buffer: buffer, boxed: false)
break
case .jsonString(let value):
if boxed {
buffer.appendInt32(-1222740358)
}
serializeString(value, buffer: buffer, boxed: false)
break
case .jsonArray(let value):
if boxed {
buffer.appendInt32(-146520221)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(value.count))
for item in value {
item.serialize(buffer, true)
}
break
case .jsonObject(let value):
if boxed {
buffer.appendInt32(-1715350371)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(value.count))
for item in value {
item.serialize(buffer, true)
}
break
}
}
func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .jsonNull:
return ("jsonNull", [])
case .jsonBool(let value):
return ("jsonBool", [("value", value)])
case .jsonNumber(let value):
return ("jsonNumber", [("value", value)])
case .jsonString(let value):
return ("jsonString", [("value", value)])
case .jsonArray(let value):
return ("jsonArray", [("value", value)])
case .jsonObject(let value):
return ("jsonObject", [("value", value)])
}
}
static func parse_jsonNull(_ reader: BufferReader) -> JSONValue? {
return Api.JSONValue.jsonNull
}
static func parse_jsonBool(_ reader: BufferReader) -> JSONValue? {
var _1: Api.Bool?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.Bool
}
let _c1 = _1 != nil
if _c1 {
return Api.JSONValue.jsonBool(value: _1!)
}
else {
return nil
}
}
static func parse_jsonNumber(_ reader: BufferReader) -> JSONValue? {
var _1: Double?
_1 = reader.readDouble()
let _c1 = _1 != nil
if _c1 {
return Api.JSONValue.jsonNumber(value: _1!)
}
else {
return nil
}
}
static func parse_jsonString(_ reader: BufferReader) -> JSONValue? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.JSONValue.jsonString(value: _1!)
}
else {
return nil
}
}
static func parse_jsonArray(_ reader: BufferReader) -> JSONValue? {
var _1: [Api.JSONValue]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.JSONValue.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.JSONValue.jsonArray(value: _1!)
}
else {
return nil
}
}
static func parse_jsonObject(_ reader: BufferReader) -> JSONValue? {
var _1: [Api.JSONObjectValue]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.JSONObjectValue.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.JSONValue.jsonObject(value: _1!)
}
else {
return nil
}
}
} }
enum Photo: TypeConstructorDescription { enum Photo: TypeConstructorDescription {
case photoEmpty(id: Int64) case photoEmpty(id: Int64)
@ -13428,6 +13568,46 @@ extension Api {
} }
} }
}
enum JSONObjectValue: TypeConstructorDescription {
case jsonObjectValue(key: String, value: Api.JSONValue)
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .jsonObjectValue(let key, let value):
if boxed {
buffer.appendInt32(-1059185703)
}
serializeString(key, buffer: buffer, boxed: false)
value.serialize(buffer, true)
break
}
}
func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .jsonObjectValue(let key, let value):
return ("jsonObjectValue", [("key", key), ("value", value)])
}
}
static func parse_jsonObjectValue(_ reader: BufferReader) -> JSONObjectValue? {
var _1: String?
_1 = parseString(reader)
var _2: Api.JSONValue?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.JSONValue
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.JSONObjectValue.jsonObjectValue(key: _1!, value: _2!)
}
else {
return nil
}
}
} }
enum InputWebDocument: TypeConstructorDescription { enum InputWebDocument: TypeConstructorDescription {
case inputWebDocument(url: String, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute]) case inputWebDocument(url: String, size: Int32, mimeType: String, attributes: [Api.DocumentAttribute])
@ -16464,18 +16644,18 @@ extension Api {
} }
enum InputAppEvent: TypeConstructorDescription { enum InputAppEvent: TypeConstructorDescription {
case inputAppEvent(time: Double, type: String, peer: Int64, data: String) case inputAppEvent(time: Double, type: String, peer: Int64, data: Api.JSONValue)
func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .inputAppEvent(let time, let type, let peer, let data): case .inputAppEvent(let time, let type, let peer, let data):
if boxed { if boxed {
buffer.appendInt32(1996904104) buffer.appendInt32(488313413)
} }
serializeDouble(time, buffer: buffer, boxed: false) serializeDouble(time, buffer: buffer, boxed: false)
serializeString(type, buffer: buffer, boxed: false) serializeString(type, buffer: buffer, boxed: false)
serializeInt64(peer, buffer: buffer, boxed: false) serializeInt64(peer, buffer: buffer, boxed: false)
serializeString(data, buffer: buffer, boxed: false) data.serialize(buffer, true)
break break
} }
} }
@ -16494,8 +16674,10 @@ extension Api {
_2 = parseString(reader) _2 = parseString(reader)
var _3: Int64? var _3: Int64?
_3 = reader.readInt64() _3 = reader.readInt64()
var _4: String? var _4: Api.JSONValue?
_4 = parseString(reader) if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.JSONValue
}
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = _3 != nil let _c3 = _3 != nil

View File

@ -3956,24 +3956,6 @@ extension Api {
}) })
} }
static func saveAppLog(events: [Api.InputAppEvent]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(1862465352)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(events.count))
for item in events {
item.serialize(buffer, true)
}
return (FunctionDescription(name: "help.saveAppLog", parameters: [("events", events)]), 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
})
}
static func getInviteText() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.InviteText>) { static func getInviteText() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.InviteText>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(1295590211) buffer.appendInt32(1295590211)
@ -4142,6 +4124,38 @@ extension Api {
return result return result
}) })
} }
static func getAppConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.JSONValue>) {
let buffer = Buffer()
buffer.appendInt32(-1735311088)
return (FunctionDescription(name: "help.getAppConfig", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.JSONValue? in
let reader = BufferReader(buffer)
var result: Api.JSONValue?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.JSONValue
}
return result
})
}
static func saveAppLog(events: [Api.InputAppEvent]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(1862465352)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(events.count))
for item in events {
item.serialize(buffer, true)
}
return (FunctionDescription(name: "help.saveAppLog", parameters: [("events", events)]), 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
})
}
} }
struct updates { struct updates {
static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.updates.State>) { static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.updates.State>) {

View File

@ -0,0 +1,53 @@
import Foundation
#if os(macOS)
import PostboxMac
#else
import Postbox
#endif
public struct AppConfiguration: PreferencesEntry, Equatable {
public var data: JSON?
public static var defaultValue: AppConfiguration {
return AppConfiguration(data: nil)
}
init(data: JSON?) {
self.data = data
}
public init(decoder: PostboxDecoder) {
self.data = decoder.decodeObjectForKey("data", decoder: { JSON(decoder: $0) }) as? JSON
}
public func encode(_ encoder: PostboxEncoder) {
if let data = self.data {
encoder.encodeObject(data, forKey: "data")
} else {
encoder.encodeNil(forKey: "data")
}
}
public func isEqual(to: PreferencesEntry) -> Bool {
guard let to = to as? AppConfiguration else {
return false
}
return self == to
}
}
public func currentAppConfiguration(transaction: Transaction) -> AppConfiguration {
if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration {
return entry
} else {
return AppConfiguration.defaultValue
}
}
func updateAppConfiguration(transaction: Transaction, _ f: (AppConfiguration) -> AppConfiguration) {
let current = currentAppConfiguration(transaction: transaction)
let updated = f(current)
if updated != current {
transaction.setPreferencesEntry(key: PreferencesKeys.appConfiguration, value: updated)
}
}

480
TelegramCore/JSON.swift Normal file
View File

@ -0,0 +1,480 @@
import Foundation
import Postbox
public indirect enum JSON: PostboxCoding, Equatable {
case null
case number(Double)
case string(String)
case bool(Bool)
case array([JSON])
case dictionary([String: JSON])
private enum ValueType: Int32 {
case null = 0
case number = 1
case string = 2
case bool = 3
case array = 4
case dictionary = 5
}
private init?(_ object: Any) {
if let object = object as? JSONValue {
self = object.jsonValue
} else {
return nil
}
}
public init?(data: Data) {
if let object = try? JSONSerialization.jsonObject(with: data, options: []) {
self.init(object)
} else {
return nil
}
}
public init?(string: String) {
if let data = string.data(using: .utf8) {
self.init(data: data)
} else {
return nil
}
}
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("r", orElse: 0) {
case ValueType.null.rawValue:
self = .null
case ValueType.number.rawValue:
self = .number(decoder.decodeDoubleForKey("v", orElse: 0.0))
case ValueType.string.rawValue:
self = .string(decoder.decodeStringForKey("v", orElse: ""))
case ValueType.bool.rawValue:
self = .bool(decoder.decodeBoolForKey("v", orElse: false))
case ValueType.array.rawValue:
self = .array(decoder.decodeObjectArrayForKey("v"))
case ValueType.dictionary.rawValue:
self = .dictionary(decoder.decodeObjectDictionaryForKey("v", keyDecoder: { $0.decodeStringForKey("k", orElse: "")
}, valueDecoder: { JSON(decoder: $0) }))
default:
self = .null
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case .null:
encoder.encodeInt32(ValueType.null.rawValue, forKey: "r")
case let .number(value):
encoder.encodeInt32(ValueType.number.rawValue, forKey: "r")
encoder.encodeDouble(value, forKey: "v")
case let .string(value):
encoder.encodeInt32(ValueType.string.rawValue, forKey: "r")
encoder.encodeString(value, forKey: "v")
case let .bool(value):
encoder.encodeInt32(ValueType.bool.rawValue, forKey: "r")
encoder.encodeBool(value, forKey: "v")
case let .array(value):
encoder.encodeInt32(ValueType.array.rawValue, forKey: "r")
encoder.encodeObjectArray(value, forKey: "v")
case let .dictionary(value):
encoder.encodeInt32(ValueType.dictionary.rawValue, forKey: "r")
encoder.encodeObjectDictionary(value, forKey: "v") { key, encoder in
encoder.encodeString(key, forKey: "k")
}
}
}
public static func ==(lhs: JSON, rhs: JSON) -> Bool {
switch lhs {
case .null:
if case .null = rhs {
return true
} else {
return false
}
case let .number(value):
if case .number(value) = rhs {
return true
} else {
return false
}
case let .string(value):
if case .string(value) = rhs {
return true
} else {
return false
}
case let .bool(value):
if case .bool(value) = rhs {
return true
} else {
return false
}
case let .array(value):
if case .array(value) = rhs {
return true
} else {
return false
}
case let .dictionary(value):
if case .dictionary(value) = rhs {
return true
} else {
return false
}
}
}
public enum Index: Comparable {
case array(Int)
case dictionary(DictionaryIndex<String, JSON>)
case null
static public func ==(lhs: Index, rhs: Index) -> Bool {
switch (lhs, rhs) {
case let (.array(lhs), .array(rhs)):
return lhs == rhs
case let (.dictionary(lhs), .dictionary(rhs)):
return lhs == rhs
case (.null, .null):
return true
default:
return false
}
}
static public func <(lhs: Index, rhs: Index) -> Bool {
switch (lhs, rhs) {
case let (.array(lhs), .array(rhs)):
return lhs < rhs
case let (.dictionary(lhs), .dictionary(rhs)):
return lhs < rhs
default:
return false
}
}
}
}
extension JSON: Collection {
public var startIndex: Index {
switch self {
case let .array(value):
return .array(value.startIndex)
case let .dictionary(value):
return .dictionary(value.startIndex)
default:
return .null
}
}
public var endIndex: Index {
switch self {
case let .array(value):
return .array(value.endIndex)
case let .dictionary(value):
return .dictionary(value.endIndex)
default:
return .null
}
}
public func index(after i: Index) -> Index {
switch (i, self) {
case let (.array(index), .array(value)):
return .array(value.index(after: index))
case let (.dictionary(index), .dictionary(value)):
return .dictionary(value.index(after: index))
default:
return .null
}
}
public subscript (position: Index) -> (String, JSON) {
switch (position, self) {
case let (.array(index), .array(value)):
return (String(index), value[index])
case let (.dictionary(index), .dictionary(value)):
let (key, value) = value[index]
return (key, value)
default:
return ("", .null)
}
}
}
public enum JSONKey {
case index(Int)
case key(String)
}
public protocol JSONSubscriptType {
var jsonKey: JSONKey { get }
}
extension Int: JSONSubscriptType {
public var jsonKey: JSONKey {
return .index(self)
}
}
extension String: JSONSubscriptType {
public var jsonKey: JSONKey {
return .key(self)
}
}
extension JSON {
fileprivate var value: JSONElement {
get {
switch self {
case .null:
return 0
case let .number(value):
return value
case let .string(value):
return value
case let .bool(value):
return value
case let .array(values):
var array: [JSONElement] = []
for value in values {
array.append(value.value)
}
return array
case let .dictionary(values):
var dictionary: [String: JSONElement] = [:]
for (key, value) in values {
dictionary[key] = value.value
}
return dictionary
}
}
}
}
extension JSON {
public subscript(key: JSONSubscriptType) -> JSONElement? {
get {
switch (key.jsonKey, self) {
case let (.index(index), .array(value)):
if value.indices.contains(index) {
return value[index].value
} else {
return nil
}
case let (.key(key), .dictionary(value)):
if let value = value[key] {
return value.value
} else {
return nil
}
default:
return nil
}
}
}
}
extension JSON: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, Any)...) {
self = .dictionary(elements.reduce([String: JSON]()) { (dictionary, element) in
var dictionary = dictionary
if let value = JSON(element.1) {
dictionary[element.0] = value
}
return dictionary
})
}
}
extension JSON: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Any...) {
self = .array(elements.compactMap { JSON($0) })
}
}
public protocol JSONElement {}
private protocol JSONValue {
var jsonValue: JSON { get }
}
extension Int: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension Int8: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension Int16: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension Int32: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension Int64: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension UInt: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension UInt8: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension UInt16: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension UInt32: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension UInt64: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(Double(self))
}
}
extension Double: JSONElement, JSONValue {
var jsonValue: JSON {
return .number(self)
}
}
extension String: JSONElement, JSONValue {
var jsonValue: JSON {
return .string(self)
}
}
extension Bool: JSONElement, JSONValue {
var jsonValue: JSON {
return .bool(self)
}
}
extension Array: JSONElement where Element == JSONElement {
}
extension Array: JSONValue where Element == JSONValue {
var jsonValue: JSON {
return .array(self.map { $0.jsonValue })
}
}
extension Dictionary: JSONElement where Key == String, Value == JSONElement {
}
extension Dictionary: JSONValue where Key == String, Value == JSONValue {
var jsonValue: JSON {
return .dictionary(self.mapValues { $0.jsonValue })
}
}
private extension Bool {
init(apiBool: Api.Bool) {
switch apiBool {
case .boolTrue:
self.init(true)
case .boolFalse:
self.init(false)
}
}
var apiBool: Api.Bool {
if self {
return .boolTrue
} else {
return .boolFalse
}
}
}
extension JSON {
private init?(apiJson: Api.JSONValue, root: Bool) {
switch (apiJson, root) {
case (.jsonNull, false):
self = .null
case let (.jsonNumber(value), false):
self = .number(value)
case let (.jsonString(value), false):
self = .string(value)
case let (.jsonBool(value), false):
self = .bool(Bool(apiBool: value))
case let (.jsonArray(value), _):
self = .array(value.compactMap { JSON(apiJson: $0, root: false) })
case let (.jsonObject(value), _):
self = .dictionary(value.reduce([String: JSON]()) { dictionary, value in
var dictionary = dictionary
switch value {
case let .jsonObjectValue(key, value):
if let value = JSON(apiJson: value, root: false) {
dictionary[key] = value
}
}
return dictionary
})
default:
return nil
}
}
init?(apiJson: Api.JSONValue) {
self.init(apiJson: apiJson, root: true)
}
}
private func apiJson(_ json: JSON, root: Bool) -> Api.JSONValue? {
switch (json, root) {
case (.null, false):
return .jsonNull
case let (.number(value), false):
return .jsonNumber(value: value)
case let (.string(value), false):
return .jsonString(value: value)
case let (.bool(value), false):
return .jsonBool(value: value.apiBool)
case let (.array(value), _):
return .jsonArray(value: value.compactMap { apiJson($0, root: false) })
case let (.dictionary(value), _):
return .jsonObject(value: value.reduce([Api.JSONObjectValue]()) { objectValues, keyAndValue in
var objectValues = objectValues
if let value = apiJson(keyAndValue.value, root: false) {
objectValues.append(.jsonObjectValue(key: keyAndValue.key, value: value))
}
return objectValues
})
default:
return nil
}
}
func apiJson(_ json: JSON) -> Api.JSONValue? {
return apiJson(json, root: true)
}

View File

@ -0,0 +1,29 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
import MtProtoKitMac
#else
import Postbox
import SwiftSignalKit
import MtProtoKitDynamic
#endif
func managedAppConfigurationUpdates(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
let poll = Signal<Void, NoError> { subscriber in
return (network.request(Api.functions.help.getAppConfig())
|> retryRequest
|> mapToSignal { result -> Signal<Void, NoError> in
return postbox.transaction { transaction -> Void in
if let data = JSON(apiJson: result) {
updateAppConfiguration(transaction: transaction, { configuration -> AppConfiguration in
var configuration = configuration
configuration.data = data
return configuration
})
}
}
}).start()
}
return (poll |> then(.complete() |> suspendAwareDelay(12.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
}

View File

@ -0,0 +1,143 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
import MtProtoKitMac
#else
import Postbox
import SwiftSignalKit
import MtProtoKitDynamic
#endif
private final class ManagedSynchronizeAppLogEventsOperationsHelper {
var operationDisposables: [Int32: Disposable] = [:]
func update(_ entries: [PeerMergedOperationLogEntry]) -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) {
var disposeOperations: [Disposable] = []
var beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)] = []
var hasRunningOperationForPeerId = Set<PeerId>()
var validMergedIndices = Set<Int32>()
for entry in entries {
if !hasRunningOperationForPeerId.contains(entry.peerId) {
hasRunningOperationForPeerId.insert(entry.peerId)
validMergedIndices.insert(entry.mergedIndex)
if self.operationDisposables[entry.mergedIndex] == nil {
let disposable = MetaDisposable()
beginOperations.append((entry, disposable))
self.operationDisposables[entry.mergedIndex] = disposable
}
}
}
var removeMergedIndices: [Int32] = []
for (mergedIndex, disposable) in self.operationDisposables {
if !validMergedIndices.contains(mergedIndex) {
removeMergedIndices.append(mergedIndex)
disposeOperations.append(disposable)
}
}
for mergedIndex in removeMergedIndices {
self.operationDisposables.removeValue(forKey: mergedIndex)
}
return (disposeOperations, beginOperations)
}
func reset() -> [Disposable] {
let disposables = Array(self.operationDisposables.values)
self.operationDisposables.removeAll()
return disposables
}
}
private func withTakenOperation(postbox: Postbox, peerId: PeerId, tag: PeerOperationLogTag, tagLocalIndex: Int32, _ f: @escaping (Transaction, PeerMergedOperationLogEntry?) -> Signal<Void, NoError>) -> Signal<Void, NoError> {
return postbox.transaction { transaction -> Signal<Void, NoError> in
var result: PeerMergedOperationLogEntry?
transaction.operationLogUpdateEntry(peerId: peerId, tag: tag, tagLocalIndex: tagLocalIndex, { entry in
if let entry = entry, let _ = entry.mergedIndex, entry.contents is SynchronizeAppLogEventsOperation {
result = entry.mergedEntry!
return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none)
} else {
return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .none)
}
})
return f(transaction, result)
} |> switchToLatest
}
func managedSynchronizeAppLogEventsOperations(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
return Signal { _ in
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeAppLogEvents
let helper = Atomic<ManagedSynchronizeAppLogEventsOperationsHelper>(value: ManagedSynchronizeAppLogEventsOperationsHelper())
let peerId = PeerId(namespace: 0, id: 0)
let _ = (postbox.transaction({ t in
t.operationLogRemoveAllEntries(peerId: peerId, tag: tag)
})).start()
let disposable = postbox.mergedOperationLogView(tag: tag, limit: 50).start(next: { view in
let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerMergedOperationLogEntry, MetaDisposable)]) in
return helper.update(view.entries)
}
for disposable in disposeOperations {
disposable.dispose()
}
for (entry, disposable) in beginOperations {
let signal = withTakenOperation(postbox: postbox, peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex, { transaction, entry -> Signal<Void, NoError> in
if let entry = entry {
if let operation = entry.contents as? SynchronizeAppLogEventsOperation {
return synchronizeAppLogEvents(transaction: transaction, postbox: postbox, network: network, operations: [operation])
} else {
assertionFailure()
}
}
return .complete()
})
|> then(postbox.transaction { transaction -> Void in
let _ = transaction.operationLogRemoveEntry(peerId: entry.peerId, tag: tag, tagLocalIndex: entry.tagLocalIndex)
})
disposable.set(signal.start())
}
})
return ActionDisposable {
let disposables = helper.with { helper -> [Disposable] in
return helper.reset()
}
for disposable in disposables {
disposable.dispose()
}
disposable.dispose()
}
}
}
private func synchronizeAppLogEvents(transaction: Transaction, postbox: Postbox, network: Network, operations: [SynchronizeAppLogEventsOperation]) -> Signal<Void, NoError> {
var events: [Api.InputAppEvent] = []
for operation in operations {
switch operation.content {
case let .add(time, type, peerId, data):
if let data = apiJson(data) {
events.append(.inputAppEvent(time: time, type: type, peer: peerId?.toInt64() ?? 0, data: data))
}
default:
break
}
}
return network.request(Api.functions.help.saveAppLog(events: events))
|> `catch` { _ -> Signal<Api.Bool, NoError> in
return .complete()
}
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
}

View File

@ -126,6 +126,7 @@ public struct OperationLogTags {
static let SynchronizeGroupedPeers = PeerOperationLogTag(value: 15) static let SynchronizeGroupedPeers = PeerOperationLogTag(value: 15)
static let SynchronizeMarkAllUnseenPersonalMessages = PeerOperationLogTag(value: 16) static let SynchronizeMarkAllUnseenPersonalMessages = PeerOperationLogTag(value: 16)
static let SynchronizeRecentlyUsedStickers = PeerOperationLogTag(value: 17) static let SynchronizeRecentlyUsedStickers = PeerOperationLogTag(value: 17)
static let SynchronizeAppLogEvents = PeerOperationLogTag(value: 18)
} }
public extension PeerSummaryCounterTags { public extension PeerSummaryCounterTags {
@ -148,6 +149,7 @@ private enum PreferencesKeyValues: Int32 {
case voipConfiguration = 11 case voipConfiguration = 11
case appChangelogState = 12 case appChangelogState = 12
case localizationListState = 13 case localizationListState = 13
case appConfiguration = 14
} }
public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey {
@ -234,6 +236,12 @@ public struct PreferencesKeys {
key.setInt32(0, value: PreferencesKeyValues.localizationListState.rawValue) key.setInt32(0, value: PreferencesKeyValues.localizationListState.rawValue)
return key return key
}() }()
public static let appConfiguration: ValueBoxKey = {
let key = ValueBoxKey(length: 4)
key.setInt32(0, value: PreferencesKeyValues.appConfiguration.rawValue)
return key
}()
} }
private enum SharedDataKeyValues: Int32 { private enum SharedDataKeyValues: Int32 {

View File

@ -202,7 +202,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization { public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt { public func currentLayer() -> UInt {
return 90 return 91
} }
public func parseMessage(_ data: Data!) -> Any! { public func parseMessage(_ data: Data!) -> Any! {

View File

@ -0,0 +1,29 @@
import Foundation
import Postbox
public protocol SplitTestEvent: RawRepresentable where RawValue == String {
}
public protocol SplitTestConfiguration {
static var defaultValue: Self { get }
}
public protocol SplitTest {
associatedtype Configuration: SplitTestConfiguration
associatedtype Event: SplitTestEvent
var postbox: Postbox { get }
var bucket: String? { get }
var configuration: Configuration { get }
init(postbox: Postbox, bucket: String?, configuration: Configuration)
}
extension SplitTest {
public func addEvent(_ event: Self.Event, data: JSON = []) {
if let bucket = self.bucket {
//TODO: merge additional data
addAppLogEvent(postbox: self.postbox, time: Date().timeIntervalSince1970, type: event.rawValue, peerId: nil, data: ["bucket": bucket])
}
}
}

View File

@ -0,0 +1,98 @@
import Foundation
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
import MtProtoKitMac
#else
import Postbox
import SwiftSignalKit
import MtProtoKitDynamic
#endif
private enum SynchronizeAppLogEventsOperationContentType: Int32 {
case add
case sync
}
enum SynchronizeAppLogEventsOperationContent: PostboxCoding {
case add(time: Double, type: String, peerId: PeerId?, data: JSON)
case sync
init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("r", orElse: 0) {
case SynchronizeAppLogEventsOperationContentType.add.rawValue:
var peerId: PeerId?
if let id = decoder.decodeOptionalInt64ForKey("p") {
peerId = PeerId(id)
}
self = .add(time: decoder.decodeDoubleForKey("tm", orElse: 0.0), type: decoder.decodeStringForKey("t", orElse: ""), peerId: peerId, data: decoder.decodeObjectForKey("d", decoder: { JSON(decoder: $0) }) as! JSON)
case SynchronizeAppLogEventsOperationContentType.sync.rawValue:
self = .sync
default:
assertionFailure()
self = .sync
}
}
func encode(_ encoder: PostboxEncoder) {
switch self {
case let .add(time, type, peerId, data):
encoder.encodeInt32(SynchronizeAppLogEventsOperationContentType.add.rawValue, forKey: "r")
encoder.encodeDouble(time, forKey: "tm")
encoder.encodeString(type, forKey: "t")
if let peerId = peerId {
encoder.encodeInt64(peerId.toInt64(), forKey: "p")
} else {
encoder.encodeNil(forKey: "p")
}
encoder.encodeObject(data, forKey: "d")
case .sync:
encoder.encodeInt32(SynchronizeAppLogEventsOperationContentType.sync.rawValue, forKey: "r")
}
}
}
final class SynchronizeAppLogEventsOperation: PostboxCoding {
let content: SynchronizeAppLogEventsOperationContent
init(content: SynchronizeAppLogEventsOperationContent) {
self.content = content
}
init(decoder: PostboxDecoder) {
self.content = decoder.decodeObjectForKey("c", decoder: { SynchronizeAppLogEventsOperationContent(decoder: $0) }) as! SynchronizeAppLogEventsOperationContent
}
func encode(_ encoder: PostboxEncoder) {
encoder.encodeObject(self.content, forKey: "c")
}
}
public func addAppLogEvent(postbox: Postbox, time: Double, type: String, peerId: PeerId?, data: JSON) {
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeAppLogEvents
let peerId = PeerId(namespace: 0, id: 0)
let _ = (postbox.transaction { transaction in
transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeAppLogEventsOperation(content: .add(time: time, type: type, peerId: peerId, data: data)))
}).start()
}
public func invokeAppLogEventsSynchronization(postbox: Postbox) {
let tag: PeerOperationLogTag = OperationLogTags.SynchronizeAppLogEvents
let peerId = PeerId(namespace: 0, id: 0)
let _ = (postbox.transaction { transaction in
var topOperation: (SynchronizeSavedStickersOperation, Int32)?
transaction.operationLogEnumerateEntries(peerId: peerId, tag: tag, { entry in
if let operation = entry.contents as? SynchronizeSavedStickersOperation, case .sync = operation.content {
topOperation = (operation, entry.tagLocalIndex)
}
return false
})
if let (_, topLocalIndex) = topOperation {
let _ = transaction.operationLogRemoveEntry(peerId: peerId, tag: tag, tagLocalIndex: topLocalIndex)
}
transaction.operationLogAddEntry(peerId: peerId, tag: tag, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SynchronizeAppLogEventsOperation(content: .sync))
}).start()
}