import Foundation import SwiftSignalKit import Postbox import TelegramApi public enum AddressNameValidationStatus: Equatable { case checking case invalidFormat(AddressNameFormatError) case availability(AddressNameAvailability) } public typealias EngineStringIndexTokenTransliteration = StringIndexTokenTransliteration public final class OpaqueChatInterfaceState { public let opaqueData: Data? public let historyScrollMessageIndex: MessageIndex? public let mediaDraftState: MediaDraftState? public let synchronizeableInputState: SynchronizeableChatInputState? public init( opaqueData: Data?, historyScrollMessageIndex: MessageIndex?, mediaDraftState: MediaDraftState?, synchronizeableInputState: SynchronizeableChatInputState? ) { self.opaqueData = opaqueData self.historyScrollMessageIndex = historyScrollMessageIndex self.mediaDraftState = mediaDraftState self.synchronizeableInputState = synchronizeableInputState } } public final class TelegramCollectibleItemInfo: Equatable { public enum Subject: Equatable { case username(String) case phoneNumber(String) } public let subject: Subject public let purchaseDate: Int32 public let currency: String public let currencyAmount: Int64 public let cryptoCurrency: String public let cryptoCurrencyAmount: Int64 public let url: String public init(subject: Subject, purchaseDate: Int32, currency: String, currencyAmount: Int64, cryptoCurrency: String, cryptoCurrencyAmount: Int64, url: String) { self.subject = subject self.purchaseDate = purchaseDate self.currency = currency self.currencyAmount = currencyAmount self.cryptoCurrency = cryptoCurrency self.cryptoCurrencyAmount = cryptoCurrencyAmount self.url = url } public static func ==(lhs: TelegramCollectibleItemInfo, rhs: TelegramCollectibleItemInfo) -> Bool { if lhs === rhs { return true } if lhs.subject != rhs.subject { return false } if lhs.purchaseDate != rhs.purchaseDate { return false } if lhs.currency != rhs.currency { return false } if lhs.currencyAmount != rhs.currencyAmount { return false } if lhs.cryptoCurrency != rhs.cryptoCurrency { return false } if lhs.cryptoCurrencyAmount != rhs.cryptoCurrencyAmount { return false } if lhs.url != rhs.url { return false } return true } } public final class TelegramResolvedMessageLink { public let peer: EnginePeer public let message: String public let entities: [MessageTextEntity] public init(peer: EnginePeer, message: String, entities: [MessageTextEntity]) { self.peer = peer self.message = message self.entities = entities } } public enum TelegramPaidReactionPrivacy: Equatable, Codable { case `default` case anonymous case peer(PeerId) enum CodingKeys: String, CodingKey { case `default` = "d" case anonymous = "a" case peer = "p" } public init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) if try container.decodeNil(forKey: .default) { self = .default } else if try container.decodeNil(forKey: .anonymous) { self = .anonymous } else { let peerId = PeerId(try container.decode(Int64.self, forKey: .peer)) self = .peer(peerId) } } public func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .default: try container.encodeNil(forKey: .default) case .anonymous: try container.encodeNil(forKey: .anonymous) case let .peer(peerId): try container.encode(peerId.toInt64(), forKey: .peer) } } } public extension TelegramEngine { enum NextUnreadChannelLocation: Equatable { case same case archived case unarchived case folder(id: Int32, title: ChatFolderTitle) } final class Peers { private let account: Account init(account: Account) { self.account = account } public func addressNameAvailability(domain: AddressNameDomain, name: String) -> Signal { return _internal_addressNameAvailability(account: self.account, domain: domain, name: name) } public func updateAddressName(domain: AddressNameDomain, name: String?) -> Signal { return _internal_updateAddressName(account: self.account, domain: domain, name: name) } public func deactivateAllAddressNames(peerId: EnginePeer.Id) -> Signal { return _internal_deactivateAllAddressNames(account: self.account, peerId: peerId) } public func toggleAddressNameActive(domain: AddressNameDomain, name: String, active: Bool) -> Signal { return _internal_toggleAddressNameActive(account: self.account, domain: domain, name: name, active: active) } public func reorderAddressNames(domain: AddressNameDomain, names: [TelegramPeerUsername]) -> Signal { return _internal_reorderAddressNames(account: self.account, domain: domain, names: names) } public func checkPublicChannelCreationAvailability(location: Bool = false) -> Signal { return _internal_checkPublicChannelCreationAvailability(account: self.account, location: location) } public func adminedPublicChannels(scope: AdminedPublicChannelsScope = .all) -> Signal<[TelegramAdminedPublicChannel], NoError> { return _internal_adminedPublicChannels(account: self.account, scope: scope) } public func channelsForStories() -> Signal<[EnginePeer], NoError> { return _internal_channelsForStories(account: self.account) |> map { peers -> [EnginePeer] in return peers.map(EnginePeer.init) } } public func channelsForPublicReaction(useLocalCache: Bool) -> Signal<[EnginePeer], NoError> { return _internal_channelsForPublicReaction(account: self.account, useLocalCache: useLocalCache) |> map { peers -> [EnginePeer] in return peers.map(EnginePeer.init) } } public func channelAddressNameAssignmentAvailability(peerId: PeerId?) -> Signal { return _internal_channelAddressNameAssignmentAvailability(account: self.account, peerId: peerId) } public func validateAddressNameInteractive(domain: AddressNameDomain, name: String) -> Signal { if let error = _internal_checkAddressNameFormat(name) { return .single(.invalidFormat(error)) } else { return .single(.checking) |> then( self.addressNameAvailability(domain: domain, name: name) |> delay(0.3, queue: Queue.concurrentDefaultQueue()) |> map { result -> AddressNameValidationStatus in .availability(result) } ) } } public func findChannelById(channelId: Int64) -> Signal { return _internal_findChannelById(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, channelId: channelId) |> map { peer in return peer.flatMap(EnginePeer.init) } } public func supportPeerId() -> Signal { return _internal_supportPeerId(account: self.account) } public func inactiveChannelList() -> Signal<[InactiveChannel], NoError> { return _internal_inactiveChannelList(network: self.account.network) } public func resolvePeerByName(name: String, referrer: String?, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal { return _internal_resolvePeerByName(account: self.account, name: name, referrer: referrer, ageLimit: ageLimit) |> mapToSignal { result -> Signal in switch result { case .progress: return .single(.progress) case let .result(peerId): guard let peerId = peerId else { return .single(.result(nil)) } return self.account.postbox.transaction { transaction -> ResolvePeerResult in return .result(transaction.getPeer(peerId).flatMap(EnginePeer.init)) } } } } public func resolvePeerByPhone(phone: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal { return _internal_resolvePeerByPhone(account: self.account, phone: phone, ageLimit: ageLimit) |> mapToSignal { peerId -> Signal in guard let peerId = peerId else { return .single(nil) } return self.account.postbox.transaction { transaction -> EnginePeer? in return transaction.getPeer(peerId).flatMap(EnginePeer.init) } } } public func updatedRemotePeer(peer: PeerReference) -> Signal { return _internal_updatedRemotePeer(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, peer: peer) } public func chatOnlineMembers(peerId: PeerId) -> Signal { return _internal_chatOnlineMembers(postbox: self.account.postbox, network: self.account.network, peerId: peerId) } public func convertGroupToSupergroup(peerId: PeerId, additionalProcessing: ((EnginePeer.Id) -> Signal)? = nil) -> Signal { return _internal_convertGroupToSupergroup(account: self.account, peerId: peerId, additionalProcessing: additionalProcessing) } public func createGroup(title: String, peerIds: [PeerId], ttlPeriod: Int32?) -> Signal { return _internal_createGroup(account: self.account, title: title, peerIds: peerIds, ttlPeriod: ttlPeriod) } public func createSecretChat(peerId: PeerId) -> Signal { return _internal_createSecretChat(account: self.account, peerId: peerId) } public func setChatMessageAutoremoveTimeoutInteractively(peerId: PeerId, timeout: Int32?) -> Signal { if peerId.namespace == Namespaces.Peer.SecretChat { return _internal_setSecretChatMessageAutoremoveTimeoutInteractively(account: self.account, peerId: peerId, timeout: timeout) |> ignoreValues |> castError(SetChatMessageAutoremoveTimeoutError.self) } else { return _internal_setChatMessageAutoremoveTimeoutInteractively(account: self.account, peerId: peerId, timeout: timeout) } } public func setChatMessageAutoremoveTimeouts(peerIds: [EnginePeer.Id], timeout: Int32?) -> Signal { return self.account.postbox.transaction { transaction -> Void in for peerId in peerIds { if peerId.namespace == Namespaces.Peer.SecretChat { _internal_setSecretChatMessageAutoremoveTimeoutInteractively(transaction: transaction, account: self.account, peerId: peerId, timeout: timeout) } else { var canManage = false guard let peer = transaction.getPeer(peerId) else { continue } if let user = peer as? TelegramUser { if user.botInfo == nil { canManage = true } } else if let _ = peer as? TelegramSecretChat { canManage = true } else if let group = peer as? TelegramGroup { canManage = !group.hasBannedPermission(.banChangeInfo) } else if let channel = peer as? TelegramChannel { canManage = channel.hasPermission(.changeInfo) } if !canManage { continue } let cachedData = transaction.getPeerCachedData(peerId: peerId) var currentValue: Int32? if let cachedData = cachedData as? CachedUserData { if case let .known(value) = cachedData.autoremoveTimeout { currentValue = value?.effectiveValue } } else if let cachedData = cachedData as? CachedGroupData { if case let .known(value) = cachedData.autoremoveTimeout { currentValue = value?.effectiveValue } } else if let cachedData = cachedData as? CachedChannelData { if case let .known(value) = cachedData.autoremoveTimeout { currentValue = value?.effectiveValue } } if currentValue != timeout { let _ = _internal_setChatMessageAutoremoveTimeoutInteractively(account: self.account, peerId: peerId, timeout: timeout).start() } } } } |> ignoreValues } public func updateChannelSlowModeInteractively(peerId: PeerId, timeout: Int32?) -> Signal { return _internal_updateChannelSlowModeInteractively(postbox: self.account.postbox, network: self.account.network, accountStateManager: self.account.stateManager, peerId: peerId, timeout: timeout) } public func reportPeer(peerId: PeerId) -> Signal { return _internal_reportPeer(account: self.account, peerId: peerId) } public func reportPeer(peerId: PeerId, reason: ReportReason, message: String) -> Signal { return _internal_reportPeer(account: self.account, peerId: peerId, reason: reason, message: message) } public func reportPeerPhoto(peerId: PeerId, reason: ReportReason, message: String) -> Signal { return _internal_reportPeerPhoto(account: self.account, peerId: peerId, reason: reason, message: message) } public func reportPeerMessages(messageIds: [MessageId], reason: ReportReason, message: String) -> Signal { return _internal_reportPeerMessages(account: self.account, messageIds: messageIds, reason: reason, message: message) } public func reportPeerStory(peerId: PeerId, storyId: Int32, reason: ReportReason, message: String) -> Signal { return _internal_reportPeerStory(account: self.account, peerId: peerId, storyId: storyId, reason: reason, message: message) } public func reportPeerReaction(authorId: PeerId, messageId: MessageId) -> Signal { return _internal_reportPeerReaction(account: self.account, authorId: authorId, messageId: messageId) } public func dismissPeerStatusOptions(peerId: PeerId) -> Signal { return _internal_dismissPeerStatusOptions(account: self.account, peerId: peerId) } public func reportRepliesMessage(messageId: MessageId, deleteMessage: Bool, deleteHistory: Bool, reportSpam: Bool) -> Signal { return _internal_reportRepliesMessage(account: self.account, messageId: messageId, deleteMessage: deleteMessage, deleteHistory: deleteHistory, reportSpam: reportSpam) } public func togglePeerMuted(peerId: PeerId, threadId: Int64?) -> Signal { return _internal_togglePeerMuted(account: self.account, peerId: peerId, threadId: threadId) } public func togglePeerStoriesMuted(peerId: EnginePeer.Id) -> Signal { return _internal_togglePeerStoriesMuted(account: self.account, peerId: peerId) |> ignoreValues } public func updatePeerMuteSetting(peerId: PeerId, threadId: Int64?, muteInterval: Int32?) -> Signal { return _internal_updatePeerMuteSetting(account: self.account, peerId: peerId, threadId: threadId, muteInterval: muteInterval) } public func updateMultiplePeerMuteSettings(peerIds: [EnginePeer.Id], muted: Bool) -> Signal { return self.account.postbox.transaction { transaction -> Void in for peerId in peerIds { _internal_updatePeerMuteSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, muteInterval: muted ? Int32.max : nil) } } |> ignoreValues } public func updatePeerDisplayPreviewsSetting(peerId: PeerId, threadId: Int64?, displayPreviews: PeerNotificationDisplayPreviews) -> Signal { return _internal_updatePeerDisplayPreviewsSetting(account: self.account, peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) } public func updatePeerStoriesMutedSetting(peerId: PeerId, mute: PeerStoryNotificationSettings.Mute) -> Signal { return _internal_updatePeerStoriesMutedSetting(account: self.account, peerId: peerId, mute: mute) } public func updatePeerStoriesHideSenderSetting(peerId: PeerId, hideSender: PeerStoryNotificationSettings.HideSender) -> Signal { return self.account.postbox.transaction { transaction -> Void in _internal_updatePeerStoriesHideSenderSetting(account: self.account, transaction: transaction, peerId: peerId, hideSender: hideSender) } } public func updatePeerStorySoundInteractive(peerId: PeerId, sound: PeerMessageSound) -> Signal { return self.account.postbox.transaction { transaction -> Void in _internal_updatePeerStoryNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, sound: sound) } } public func updatePeerNotificationSoundInteractive(peerId: PeerId, threadId: Int64?, sound: PeerMessageSound) -> Signal { return _internal_updatePeerNotificationSoundInteractive(account: self.account, peerId: peerId, threadId: threadId, sound: sound) } public func removeCustomNotificationSettings(peerIds: [PeerId]) -> Signal { return self.account.postbox.transaction { transaction -> Void in for peerId in peerIds { _internal_updatePeerNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, sound: .default) _internal_updatePeerMuteSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, muteInterval: nil) _internal_updatePeerDisplayPreviewsSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: nil, displayPreviews: .default) } } |> ignoreValues } public func removeCustomThreadNotificationSettings(peerId: EnginePeer.Id, threadIds: [Int64]) -> Signal { return self.account.postbox.transaction { transaction -> Void in for threadId in threadIds { _internal_updatePeerNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, threadId: threadId, sound: .default) _internal_updatePeerMuteSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: threadId, muteInterval: nil) _internal_updatePeerDisplayPreviewsSetting(account: self.account, transaction: transaction, peerId: peerId, threadId: threadId, displayPreviews: .default) } } |> ignoreValues } public func removeCustomStoryNotificationSettings(peerIds: [PeerId]) -> Signal { return self.account.postbox.transaction { transaction -> Void in for peerId in peerIds { _internal_updatePeerStoriesMutedSetting(account: self.account, transaction: transaction, peerId: peerId, mute: .default) _internal_updatePeerStoriesHideSenderSetting(account: self.account, transaction: transaction, peerId: peerId, hideSender: .default) _internal_updatePeerStoryNotificationSoundInteractive(account: self.account, transaction: transaction, peerId: peerId, sound: .default) } } |> ignoreValues } public func channelAdminEventLog(peerId: PeerId) -> ChannelAdminEventLogContext { return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId, accountPeerId: self.account.peerId) } public func updateChannelMemberBannedRights(peerId: PeerId, memberId: PeerId, rights: TelegramChatBannedRights?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant?, Bool), NoError> { return _internal_updateChannelMemberBannedRights(account: self.account, peerId: peerId, memberId: memberId, rights: rights) } public func updateDefaultChannelMemberBannedRights(peerId: PeerId, rights: TelegramChatBannedRights) -> Signal { return _internal_updateDefaultChannelMemberBannedRights(account: self.account, peerId: peerId, rights: rights) } public func updateChannelBoostsToUnlockRestrictions(peerId: PeerId, boosts: Int32) -> Signal { return _internal_updateChannelBoostsToUnlockRestrictions(account: self.account, peerId: peerId, boosts: boosts) } public func createChannel(title: String, description: String?, username: String? = nil) -> Signal { return _internal_createChannel(account: self.account, title: title, description: description, username: username) } public func createSupergroup(title: String, description: String?, username: String? = nil, isForum: Bool = false, location: (latitude: Double, longitude: Double, address: String)? = nil, isForHistoryImport: Bool = false, ttlPeriod: Int32? = nil) -> Signal { return _internal_createSupergroup(postbox: self.account.postbox, network: self.account.network, stateManager: account.stateManager, title: title, description: description, username: username, isForum: isForum, location: location, isForHistoryImport: isForHistoryImport, ttlPeriod: ttlPeriod) } public func deleteChannel(peerId: PeerId) -> Signal { return _internal_deleteChannel(account: self.account, peerId: peerId) } public func updateChannelHistoryAvailabilitySettingsInteractively(peerId: PeerId, historyAvailableForNewMembers: Bool) -> Signal { return _internal_updateChannelHistoryAvailabilitySettingsInteractively(postbox: self.account.postbox, network: self.account.network, accountStateManager: self.account.stateManager, peerId: peerId, historyAvailableForNewMembers: historyAvailableForNewMembers) } public func updateChannelRestrictAdMessages(peerId: PeerId, restricted: Bool) -> Signal { return _internal_updateChannelRestrictAdMessages(account: self.account, peerId: peerId, restricted: restricted) } public func channelMembers(peerId: PeerId, category: ChannelMembersCategory = .recent(.all), offset: Int32 = 0, limit: Int32 = 64, hash: Int64 = 0) -> Signal<[RenderedChannelParticipant]?, NoError> { return _internal_channelMembers(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId, peerId: peerId, category: category, offset: offset, limit: limit, hash: hash) } public func checkOwnershipTranfserAvailability(memberId: PeerId) -> Signal { return _internal_checkOwnershipTranfserAvailability(postbox: self.account.postbox, network: self.account.network, accountStateManager: self.account.stateManager, memberId: memberId) } public func updateChannelOwnership(channelId: PeerId, memberId: PeerId, password: String) -> Signal<[(ChannelParticipant?, RenderedChannelParticipant)], ChannelOwnershipTransferError> { return _internal_updateChannelOwnership(account: self.account, channelId: channelId, memberId: memberId, password: password) } public func searchGroupMembers(peerId: PeerId, query: String) -> Signal<[EnginePeer], NoError> { return _internal_searchGroupMembers(postbox: self.account.postbox, network: self.account.network, accountPeerId: self.account.peerId, peerId: peerId, query: query) |> map { peers -> [EnginePeer] in return peers.map { EnginePeer($0) } } } public func toggleShouldChannelMessagesSignatures(peerId: PeerId, signaturesEnabled: Bool, profilesEnabled: Bool) -> Signal { return _internal_toggleShouldChannelMessagesSignatures(account: self.account, peerId: peerId, signaturesEnabled: signaturesEnabled, profilesEnabled: profilesEnabled) } public func toggleMessageCopyProtection(peerId: PeerId, enabled: Bool) -> Signal { return _internal_toggleMessageCopyProtection(account: self.account, peerId: peerId, enabled: enabled) } public func toggleChannelJoinToSend(peerId: PeerId, enabled: Bool) -> Signal { return _internal_toggleChannelJoinToSend(postbox: self.account.postbox, network: self.account.network, accountStateManager: self.account.stateManager, peerId: peerId, enabled: enabled) } public func toggleChannelJoinRequest(peerId: PeerId, enabled: Bool) -> Signal { return _internal_toggleChannelJoinRequest(postbox: self.account.postbox, network: self.account.network, accountStateManager: self.account.stateManager, peerId: peerId, enabled: enabled) } public func toggleAntiSpamProtection(peerId: PeerId, enabled: Bool) -> Signal { return _internal_toggleAntiSpamProtection(account: self.account, peerId: peerId, enabled: enabled) } public func reportAntiSpamFalsePositive(peerId: PeerId, messageId: MessageId) -> Signal { return _internal_reportAntiSpamFalsePositive(account: self.account, peerId: peerId, messageId: messageId) } public func requestPeerPhotos(peerId: PeerId) -> Signal<[TelegramPeerPhoto], NoError> { return _internal_requestPeerPhotos(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, peerId: peerId) } public func updateGroupSpecificStickerset(peerId: PeerId, info: StickerPackCollectionInfo?) -> Signal { return _internal_updateGroupSpecificStickerset(postbox: self.account.postbox, network: self.account.network, peerId: peerId, info: info) } public func updateGroupSpecificEmojiset(peerId: PeerId, info: StickerPackCollectionInfo?) -> Signal { return _internal_updateGroupSpecificEmojiset(postbox: self.account.postbox, network: self.account.network, peerId: peerId, info: info) } public func joinChannel(peerId: PeerId, hash: String?) -> Signal { return _internal_joinChannel(account: self.account, peerId: peerId, hash: hash) } public func removePeerMember(peerId: PeerId, memberId: PeerId) -> Signal { return _internal_removePeerMember(account: self.account, peerId: peerId, memberId: memberId) } public func availableGroupsForChannelDiscussion() -> Signal<[EnginePeer], AvailableChannelDiscussionGroupError> { return _internal_availableGroupsForChannelDiscussion(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network) |> map { peers -> [EnginePeer] in return peers.map(EnginePeer.init) } } public func updateGroupDiscussionForChannel(channelId: PeerId?, groupId: PeerId?) -> Signal { return _internal_updateGroupDiscussionForChannel(network: self.account.network, postbox: self.account.postbox, channelId: channelId, groupId: groupId) } public func peerCommands(id: PeerId) -> Signal { return _internal_peerCommands(account: self.account, id: id) } public func addGroupAdmin(peerId: PeerId, adminId: PeerId) -> Signal { return _internal_addGroupAdmin(account: self.account, peerId: peerId, adminId: adminId) } public func removeGroupAdmin(peerId: PeerId, adminId: PeerId) -> Signal { return _internal_removeGroupAdmin(account: self.account, peerId: peerId, adminId: adminId) } public func fetchChannelParticipant(peerId: PeerId, participantId: PeerId) -> Signal { return _internal_fetchChannelParticipant(account: self.account, peerId: peerId, participantId: participantId) } public func updateChannelAdminRights(peerId: PeerId, adminId: PeerId, rights: TelegramChatAdminRights?, rank: String?) -> Signal<(ChannelParticipant?, RenderedChannelParticipant), UpdateChannelAdminRightsError> { return _internal_updateChannelAdminRights(account: self.account, peerId: peerId, adminId: adminId, rights: rights, rank: rank) } public func peerSpecificStickerPack(peerId: PeerId) -> Signal { return _internal_peerSpecificStickerPack(postbox: self.account.postbox, network: self.account.network, peerId: peerId) } public func peerSpecificEmojiPack(peerId: PeerId) -> Signal { return _internal_peerSpecificEmojiPack(postbox: self.account.postbox, network: self.account.network, peerId: peerId) } public func addRecentlySearchedPeer(peerId: PeerId) -> Signal { return _internal_addRecentlySearchedPeer(postbox: self.account.postbox, peerId: peerId) } public func removeRecentlySearchedPeer(peerId: PeerId) -> Signal { return _internal_removeRecentlySearchedPeer(postbox: self.account.postbox, peerId: peerId) } public func clearRecentlySearchedPeers() -> Signal { return _internal_clearRecentlySearchedPeers(postbox: self.account.postbox) } public func recentlySearchedPeers() -> Signal<[RecentlySearchedPeer], NoError> { return _internal_recentlySearchedPeers(postbox: self.account.postbox) } public func removePeerChat(peerId: PeerId, reportChatSpam: Bool, deleteGloballyIfPossible: Bool = false) -> Signal { return _internal_removePeerChat(account: self.account, peerId: peerId, reportChatSpam: reportChatSpam, deleteGloballyIfPossible: deleteGloballyIfPossible) } public func removePeerChats(peerIds: [PeerId], deleteGloballyIfPossible: Bool = false) -> Signal { return self.account.postbox.transaction { transaction -> Void in for peerId in peerIds { _internal_removePeerChat(account: self.account, transaction: transaction, mediaBox: self.account.postbox.mediaBox, peerId: peerId, reportChatSpam: false, deleteGloballyIfPossible: peerId.namespace == Namespaces.Peer.SecretChat || deleteGloballyIfPossible) } } |> ignoreValues } public func terminateSecretChat(peerId: PeerId, requestRemoteHistoryRemoval: Bool) -> Signal { return self.account.postbox.transaction { transaction -> Void in _internal_terminateSecretChat(transaction: transaction, peerId: peerId, requestRemoteHistoryRemoval: requestRemoteHistoryRemoval) } |> ignoreValues } public func addGroupMember(peerId: PeerId, memberId: PeerId) -> Signal { return _internal_addGroupMember(account: self.account, peerId: peerId, memberId: memberId) } public func addChannelMember(peerId: PeerId, memberId: PeerId) -> Signal<(ChannelParticipant?, RenderedChannelParticipant), AddChannelMemberError> { return _internal_addChannelMember(account: self.account, peerId: peerId, memberId: memberId) } public func sendBotRequestedPeer(messageId: MessageId, buttonId: Int32, requestedPeerIds: [PeerId]) -> Signal { return _internal_sendBotRequestedPeer(account: self.account, peerId: messageId.peerId, messageId: messageId, buttonId: buttonId, requestedPeerIds: requestedPeerIds) } public func addChannelMembers(peerId: PeerId, memberIds: [PeerId]) -> Signal { return _internal_addChannelMembers(account: self.account, peerId: peerId, memberIds: memberIds) } public func recentPeers() -> Signal { return _internal_recentPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox) } public func managedUpdatedRecentPeers() -> Signal { return _internal_managedUpdatedRecentPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network) } public func recentApps() -> Signal<[EnginePeer.Id], NoError> { return _internal_recentApps(accountPeerId: self.account.peerId, postbox: self.account.postbox) } public func managedUpdatedRecentApps() -> Signal { return _internal_managedUpdatedRecentApps(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network) } public func removeRecentPeer(peerId: PeerId) -> Signal { return _internal_removeRecentPeer(account: self.account, peerId: peerId) } public func updateRecentPeersEnabled(enabled: Bool) -> Signal { return _internal_updateRecentPeersEnabled(postbox: self.account.postbox, network: self.account.network, enabled: enabled) } public func addRecentlyUsedInlineBot(peerId: PeerId) -> Signal { return _internal_addRecentlyUsedInlineBot(postbox: self.account.postbox, peerId: peerId) } public func recentlyUsedInlineBots() -> Signal<[(EnginePeer, Double)], NoError> { return _internal_recentlyUsedInlineBots(postbox: self.account.postbox) |> map { list -> [(EnginePeer, Double)] in return list.map { peer, rating in return (EnginePeer(peer), rating) } } } public func removeRecentlyUsedInlineBot(peerId: PeerId) -> Signal { return _internal_removeRecentlyUsedInlineBot(account: self.account, peerId: peerId) } public func removeRecentlyUsedApp(peerId: PeerId) -> Signal { return _internal_removeRecentlyUsedApp(account: self.account, peerId: peerId) } public func uploadedPeerPhoto(resource: MediaResource) -> Signal { return _internal_uploadedPeerPhoto(postbox: self.account.postbox, network: self.account.network, resource: resource) } public func uploadedPeerVideo(resource: MediaResource) -> Signal { return _internal_uploadedPeerVideo(postbox: self.account.postbox, network: self.account.network, messageMediaPreuploadManager: self.account.messageMediaPreuploadManager, resource: resource) } public func updatePeerPhoto(peerId: PeerId, photo: Signal?, video: Signal? = nil, videoStartTimestamp: Double? = nil, markup: UploadPeerPhotoMarkup? = nil, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal { return _internal_updatePeerPhoto(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, accountPeerId: self.account.peerId, peerId: peerId, photo: photo, video: video, videoStartTimestamp: videoStartTimestamp, markup: markup, mapResourceToAvatarSizes: mapResourceToAvatarSizes) } public func requestUpdateChatListFilter(id: Int32, filter: ChatListFilter?) -> Signal { return _internal_requestUpdateChatListFilter(postbox: self.account.postbox, network: self.account.network, id: id, filter: filter) } public func requestUpdateChatListFilterOrder(ids: [Int32]) -> Signal { return _internal_requestUpdateChatListFilterOrder(account: self.account, ids: ids) } public func generateNewChatListFilterId(filters: [ChatListFilter]) -> Int32 { return _internal_generateNewChatListFilterId(filters: filters) } public func updateChatListFiltersInteractively(_ f: @escaping ([ChatListFilter]) -> [ChatListFilter]) -> Signal<[ChatListFilter], NoError> { return _internal_updateChatListFiltersInteractively(postbox: self.account.postbox, f) } public func updateChatListFiltersDisplayTags(isEnabled: Bool) { let _ = _internal_updateChatListFiltersDisplayTagsInteractively(postbox: self.account.postbox, displayTags: isEnabled).startStandalone() } public func updatedChatListFilters() -> Signal<[ChatListFilter], NoError> { return _internal_updatedChatListFilters(postbox: self.account.postbox, hiddenIds: self.account.viewTracker.hiddenChatListFilterIds) } public func chatListFiltersAreSynced() -> Signal { return _internal_chatListFiltersAreSynced(postbox: self.account.postbox) } public func updatedChatListFiltersInfo() -> Signal<(filters: [ChatListFilter], synchronized: Bool), NoError> { return _internal_updatedChatListFiltersInfo(postbox: self.account.postbox) } public func currentChatListFilters() -> Signal<[ChatListFilter], NoError> { return _internal_currentChatListFilters(postbox: self.account.postbox) } public func markChatListFeaturedFiltersAsSeen() -> Signal { return _internal_markChatListFeaturedFiltersAsSeen(postbox: self.account.postbox) } public func updateChatListFeaturedFilters() -> Signal { return _internal_updateChatListFeaturedFilters(postbox: self.account.postbox, network: self.account.network) } public func unmarkChatListFeaturedFiltersAsSeen() -> Signal { return self.account.postbox.transaction { transaction in _internal_unmarkChatListFeaturedFiltersAsSeen(transaction: transaction) } |> ignoreValues } public func checkPeerChatServiceActions(peerId: PeerId) -> Signal { return _internal_checkPeerChatServiceActions(postbox: self.account.postbox, peerId: peerId) } public func createPeerExportedInvitation(peerId: PeerId, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?, subscriptionPricing: StarsSubscriptionPricing?) -> Signal { return _internal_createPeerExportedInvitation(account: self.account, peerId: peerId, title: title, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded, subscriptionPricing: subscriptionPricing) } public func editPeerExportedInvitation(peerId: PeerId, link: String, title: String?, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Bool?) -> Signal { return _internal_editPeerExportedInvitation(account: self.account, peerId: peerId, link: link, title: title, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: requestNeeded) } public func revokePeerExportedInvitation(peerId: PeerId, link: String) -> Signal { return _internal_revokePeerExportedInvitation(account: self.account, peerId: peerId, link: link) } public func deletePeerExportedInvitation(peerId: PeerId, link: String) -> Signal { return _internal_deletePeerExportedInvitation(account: self.account, peerId: peerId, link: link) } public func deleteAllRevokedPeerExportedInvitations(peerId: PeerId, adminId: PeerId) -> Signal { return _internal_deleteAllRevokedPeerExportedInvitations(account: self.account, peerId: peerId, adminId: adminId) } public func peerExportedInvitationsCreators(peerId: PeerId) -> Signal<[ExportedInvitationCreator], NoError> { return _internal_peerExportedInvitationsCreators(account: self.account, peerId: peerId) } public func direct_peerExportedInvitations(peerId: PeerId, revoked: Bool, adminId: PeerId? = nil, offsetLink: ExportedInvitation? = nil) -> Signal { return _internal_peerExportedInvitations(account: self.account, peerId: peerId, revoked: revoked, adminId: adminId, offsetLink: offsetLink) } public func peerExportedInvitations(peerId: PeerId, adminId: PeerId?, revoked: Bool, forceUpdate: Bool) -> PeerExportedInvitationsContext { return PeerExportedInvitationsContext(account: self.account, peerId: peerId, adminId: adminId, revoked: revoked, forceUpdate: forceUpdate) } public func revokePersistentPeerExportedInvitation(peerId: PeerId) -> Signal { return _internal_revokePersistentPeerExportedInvitation(account: self.account, peerId: peerId) } public func peerInvitationImporters(peerId: PeerId, subject: PeerInvitationImportersContext.Subject) -> PeerInvitationImportersContext { return PeerInvitationImportersContext(account: self.account, peerId: peerId, subject: subject) } public func notificationExceptionsList() -> Signal { return combineLatest( _internal_notificationExceptionsList(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, isStories: false), _internal_notificationExceptionsList(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, isStories: true) ) |> map { lhs, rhs in return NotificationExceptionsList( peers: lhs.peers.merging(rhs.peers, uniquingKeysWith: { a, _ in a }), settings: lhs.settings.merging(rhs.settings, uniquingKeysWith: { a, _ in a }) ) } } public func fetchAndUpdateCachedPeerData(peerId: PeerId) -> Signal { return _internal_fetchAndUpdateCachedPeerData(accountPeerId: self.account.peerId, peerId: peerId, network: self.account.network, postbox: self.account.postbox) } public func toggleItemPinned(location: TogglePeerChatPinnedLocation, itemId: PinnedItemId) -> Signal { return _internal_toggleItemPinned(postbox: self.account.postbox, accountPeerId: self.account.peerId, location: location, itemId: itemId) } public func getPinnedItemIds(location: TogglePeerChatPinnedLocation) -> Signal<[PinnedItemId], NoError> { return self.account.postbox.transaction { transaction -> [PinnedItemId] in return _internal_getPinnedItemIds(transaction: transaction, location: location) } } public func reorderPinnedItemIds(location: TogglePeerChatPinnedLocation, itemIds: [PinnedItemId]) -> Signal { return self.account.postbox.transaction { transaction -> Bool in return _internal_reorderPinnedItemIds(transaction: transaction, location: location, itemIds: itemIds) } } public func joinChatInteractively(with hash: String) -> Signal { let account = self.account return _internal_joinChatInteractively(with: hash, account: self.account) |> mapToSignal { id -> Signal in guard let id = id else { return .single(nil) } return account.postbox.transaction { transaction -> EnginePeer? in return transaction.getPeer(id).flatMap(EnginePeer.init) } |> castError(JoinLinkError.self) } } public func joinLinkInformation(_ hash: String) -> Signal { return _internal_joinLinkInformation(hash, account: self.account) } public func joinCallLinkInformation(_ hash: String) -> Signal { return _internal_joinCallLinkInformation(hash, account: self.account) } public func joinCallInvitationInformation(messageId: EngineMessage.Id) -> Signal { return _internal_joinCallInvitationInformation(account: self.account, messageId: messageId) } public func updatePeerTitle(peerId: PeerId, title: String) -> Signal { return _internal_updatePeerTitle(account: self.account, peerId: peerId, title: title) } public func updatePeerDescription(peerId: PeerId, description: String?) -> Signal { return _internal_updatePeerDescription(account: self.account, peerId: peerId, description: description) } public func updateBotName(peerId: PeerId, name: String) -> Signal { return _internal_updateBotName(account: self.account, peerId: peerId, name: name) } public func updateBotAbout(peerId: PeerId, about: String) -> Signal { 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 updateCustomVerification(botId: PeerId, peerId: PeerId, value: UpdateCustomVerificationValue) -> Signal { return _internal_updateCustomVerification(account: self.account, botId: botId, peerId: peerId, value: value) } 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) } public func updatePeerNameColor(peerId: EnginePeer.Id, nameColor: PeerNameColor, backgroundEmojiId: Int64?) -> Signal { return _internal_updatePeerNameColor(account: self.account, peerId: peerId, nameColor: nameColor, backgroundEmojiId: backgroundEmojiId) } public func updatePeerProfileColor(peerId: EnginePeer.Id, profileColor: PeerNameColor?, profileBackgroundEmojiId: Int64?) -> Signal { return _internal_updatePeerProfileColor(account: self.account, peerId: peerId, profileColor: profileColor, profileBackgroundEmojiId: profileBackgroundEmojiId) } public func updatePeerEmojiStatus(peerId: EnginePeer.Id, fileId: Int64?, expirationDate: Int32?) -> Signal { return _internal_updatePeerEmojiStatus(account: self.account, peerId: peerId, fileId: fileId, expirationDate: expirationDate) } public func updatePeerStarGiftStatus(peerId: EnginePeer.Id, starGift: StarGift.UniqueGift, expirationDate: Int32?) -> Signal { return _internal_updatePeerStarGiftStatus(account: self.account, peerId: peerId, starGift: starGift, expirationDate: expirationDate) } public func checkChannelRevenueWithdrawalAvailability() -> Signal { return _internal_checkChannelRevenueWithdrawalAvailability(account: self.account) } public func requestChannelRevenueWithdrawalUrl(peerId: EnginePeer.Id, password: String) -> Signal { return _internal_requestChannelRevenueWithdrawalUrl(account: self.account, peerId: peerId, password: password) } public func checkStarsRevenueWithdrawalAvailability() -> Signal { return _internal_checkStarsRevenueWithdrawalAvailability(account: self.account) } public func requestStarsRevenueWithdrawalUrl(peerId: EnginePeer.Id, amount: Int64, password: String) -> Signal { return _internal_requestStarsRevenueWithdrawalUrl(account: self.account, peerId: peerId, amount: amount, password: password) } public func requestStarsRevenueAdsAccountlUrl(peerId: EnginePeer.Id) -> Signal { return _internal_requestStarsRevenueAdsAccountlUrl(account: self.account, peerId: peerId) } public func getChatListPeers(filterPredicate: ChatListFilterPredicate) -> Signal<[EnginePeer], NoError> { return self.account.postbox.transaction { transaction -> [EnginePeer] in return transaction.getChatListPeers(groupId: .root, filterPredicate: filterPredicate, additionalFilter: nil).map(EnginePeer.init) } } public func getNextUnreadChannel(peerId: PeerId, chatListFilterId: Int32?, getFilterPredicate: @escaping (ChatListFilterData) -> ChatListFilterPredicate) -> Signal<(peer: EnginePeer, unreadCount: Int, location: NextUnreadChannelLocation)?, NoError> { let startTime = CFAbsoluteTimeGetCurrent() return self.account.postbox.transaction { transaction -> (peer: EnginePeer, unreadCount: Int, location: NextUnreadChannelLocation)? in func getForFilter(predicate: ChatListFilterPredicate?, isArchived: Bool) -> (peer: EnginePeer, unreadCount: Int)? { let additionalFilter: (Peer) -> Bool = { peer in if let channel = peer as? TelegramChannel, case .broadcast = channel.info { return true } else { return false } } var peerIds: [PeerId] = [] if predicate != nil { peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: .root, filterPredicate: predicate, additionalFilter: additionalFilter, stopOnFirstMatch: true)) peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: Namespaces.PeerGroup.archive, filterPredicate: predicate, additionalFilter: additionalFilter, stopOnFirstMatch: true)) } else { if isArchived { peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: Namespaces.PeerGroup.archive, filterPredicate: nil, additionalFilter: additionalFilter, stopOnFirstMatch: true)) } else { peerIds.append(contentsOf: transaction.getUnreadChatListPeerIds(groupId: .root, filterPredicate: nil, additionalFilter: additionalFilter, stopOnFirstMatch: true)) } } var results: [(EnginePeer, PeerGroupId, ChatListIndex)] = [] for listId in peerIds { guard let peer = transaction.getPeer(listId) else { continue } guard let channel = peer as? TelegramChannel, case .broadcast = channel.info else { continue } if channel.id == peerId { continue } guard let readState = transaction.getCombinedPeerReadState(channel.id), readState.count != 0 else { continue } guard let (groupId, index) = transaction.getPeerChatListIndex(channel.id) else { continue } results.append((EnginePeer(channel), groupId, index)) } results.sort(by: { $0.2 > $1.2 }) if let peer = results.first?.0 { let unreadCount: Int32 = transaction.getCombinedPeerReadState(peer.id)?.count ?? 0 return (peer: peer, unreadCount: Int(unreadCount)) } else { return nil } } let peerGroupId: PeerGroupId if let peerGroupIdValue = transaction.getPeerChatListIndex(peerId)?.0 { peerGroupId = peerGroupIdValue } else { peerGroupId = .root } if let filterId = chatListFilterId { let filters = _internal_currentChatListFilters(transaction: transaction) guard let index = filters.firstIndex(where: { $0.id == filterId }) else { return nil } var sortedFilters: [ChatListFilter] = [] sortedFilters.append(contentsOf: filters[index...]) sortedFilters.append(contentsOf: filters[0 ..< index]) for i in 0 ..< sortedFilters.count { if case let .filter(id, title, _, data) = sortedFilters[i] { if let value = getForFilter(predicate: getFilterPredicate(data), isArchived: false) { return (peer: value.peer, unreadCount: value.unreadCount, location: i == 0 ? .same : .folder(id: id, title: title)) } } } return nil } else { let folderOrder: [(PeerGroupId, NextUnreadChannelLocation)] if peerGroupId == .root { folderOrder = [ (.root, .same), (Namespaces.PeerGroup.archive, .archived), ] } else { folderOrder = [ (Namespaces.PeerGroup.archive, .same), (.root, .unarchived), ] } for (groupId, location) in folderOrder { if let value = getForFilter(predicate: nil, isArchived: groupId != .root) { return (peer: value.peer, unreadCount: value.unreadCount, location: location) } } return nil } } |> beforeNext { _ in let delayTime = CFAbsoluteTimeGetCurrent() - startTime if delayTime > 0.3 { //Logger.shared.log("getNextUnreadChannel", "took \(delayTime) s") } } } public func getNextUnreadForumTopic(peerId: PeerId, topicId: Int32) -> Signal<(id: Int64, data: MessageHistoryThreadData)?, NoError> { return self.account.postbox.transaction { transaction -> (id: Int64, data: MessageHistoryThreadData)? in var unreadThreads: [(id: Int64, data: MessageHistoryThreadData, index: MessageIndex)] = [] for item in transaction.getMessageHistoryThreadIndex(peerId: peerId, limit: 100) { if item.threadId == Int64(topicId) { continue } guard let data = item.info.data.get(MessageHistoryThreadData.self) else { continue } if data.incomingUnreadCount <= 0 { continue } guard let messageIndex = transaction.getMessageHistoryThreadTopMessage(peerId: peerId, threadId: item.threadId, namespaces: Set([Namespaces.Message.Cloud])) else { continue } unreadThreads.append((item.threadId, data, messageIndex)) } if let result = unreadThreads.min(by: { $0.index > $1.index }) { return (result.id, result.data) } else { return nil } } } public func getOpaqueChatInterfaceState(peerId: PeerId, threadId: Int64?) -> Signal { return self.account.postbox.transaction { transaction -> OpaqueChatInterfaceState? in let storedState: StoredPeerChatInterfaceState? if let threadId = threadId { storedState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId) } else { storedState = transaction.getPeerChatInterfaceState(peerId) } guard let state = storedState, let data = state.data else { return nil } guard let internalState = try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) else { return nil } return OpaqueChatInterfaceState( opaqueData: internalState.opaqueData, historyScrollMessageIndex: internalState.historyScrollMessageIndex, mediaDraftState: internalState.mediaDraftState, synchronizeableInputState: internalState.synchronizeableInputState ) } } public func setOpaqueChatInterfaceState(peerId: PeerId, threadId: Int64?, state: OpaqueChatInterfaceState) -> Signal { return self.account.postbox.transaction { transaction -> Void in guard let data = try? AdaptedPostboxEncoder().encode(InternalChatInterfaceState( synchronizeableInputState: state.synchronizeableInputState, historyScrollMessageIndex: state.historyScrollMessageIndex, mediaDraftState: state.mediaDraftState, opaqueData: state.opaqueData )) else { return } #if DEBUG let _ = try! AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) #endif var overrideChatTimestamp: Int32? if let inputState = state.synchronizeableInputState { overrideChatTimestamp = inputState.timestamp } if let mediaDraftState = state.mediaDraftState { if let current = overrideChatTimestamp, mediaDraftState.timestamp < current { } else { overrideChatTimestamp = mediaDraftState.timestamp } } let storedState = StoredPeerChatInterfaceState( overrideChatTimestamp: overrideChatTimestamp, historyScrollMessageIndex: state.historyScrollMessageIndex, associatedMessageIds: (state.synchronizeableInputState?.replySubject?.messageId).flatMap({ [$0] }) ?? [], data: data ) if let threadId = threadId { var currentInputState: SynchronizeableChatInputState? if let peerChatInterfaceState = transaction.getPeerChatThreadInterfaceState(peerId, threadId: threadId), let data = peerChatInterfaceState.data { currentInputState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState } let updatedInputState = state.synchronizeableInputState if currentInputState != updatedInputState { if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup { addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId, threadId: threadId) } } transaction.setPeerChatThreadInterfaceState(peerId, threadId: threadId, state: storedState) } else { var currentInputState: SynchronizeableChatInputState? if let peerChatInterfaceState = transaction.getPeerChatInterfaceState(peerId), let data = peerChatInterfaceState.data { currentInputState = (try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data))?.synchronizeableInputState } let updatedInputState = state.synchronizeableInputState if currentInputState != updatedInputState { if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudGroup { addSynchronizeChatInputStateOperation(transaction: transaction, peerId: peerId, threadId: nil) } } transaction.setPeerChatInterfaceState( peerId, state: storedState ) } } |> ignoreValues } public func sendAsAvailablePeers(peerId: PeerId) -> Signal<[SendAsPeer], NoError> { return _internal_cachedPeerSendAsAvailablePeers(account: self.account, peerId: peerId) } public func updatePeerSendAsPeer(peerId: PeerId, sendAs: PeerId) -> Signal { return _internal_updatePeerSendAsPeer(account: self.account, peerId: peerId, sendAs: sendAs) } public func updatePeerReactionSettings(peerId: PeerId, reactionSettings: PeerReactionSettings) -> Signal { return _internal_updatePeerReactionSettings(account: account, peerId: peerId, reactionSettings: reactionSettings) } public func notificationSoundList() -> Signal { let key = PostboxViewKey.cachedItem(_internal_cachedNotificationSoundListCacheKey()) return self.account.postbox.combinedView(keys: [key]) |> map { views -> NotificationSoundList? in guard let view = views.views[key] as? CachedItemView else { return nil } return view.value?.get(NotificationSoundList.self) } } public func saveNotificationSound(file: FileMediaReference) -> Signal { return _internal_saveNotificationSound(account: self.account, file: file) } public func removeNotificationSound(file: FileMediaReference) -> Signal { return _internal_saveNotificationSound(account: self.account, file: file, unsave: true) } public func uploadNotificationSound(title: String, data: Data) -> Signal { return _internal_uploadNotificationSound(account: self.account, title: title, data: data) } public func deleteNotificationSound(fileId: Int64) -> Signal { return _internal_deleteNotificationSound(account: self.account, fileId: fileId) } public func ensurePeerIsLocallyAvailable(peer: EnginePeer) -> Signal { return _internal_storedMessageFromSearchPeer(postbox: self.account.postbox, peer: peer._asPeer()) |> map { result -> EnginePeer in return EnginePeer(result) } } public func ensurePeersAreLocallyAvailable(peers: [EnginePeer]) -> Signal { return _internal_storedMessageFromSearchPeers(account: self.account, peers: peers.map { $0._asPeer() }) } public func mostRecentSecretChat(id: EnginePeer.Id) -> Signal { return self.account.postbox.transaction { transaction -> EnginePeer.Id? in let filteredPeerIds = Array(transaction.getAssociatedPeerIds(id)).filter { $0.namespace == Namespaces.Peer.SecretChat } var activeIndices: [ChatListIndex] = [] for associatedId in filteredPeerIds { if let state = (transaction.getPeer(associatedId) as? TelegramSecretChat)?.embeddedState { switch state { case .active, .handshake: if let (_, index) = transaction.getPeerChatListIndex(associatedId) { activeIndices.append(index) } default: break } } } activeIndices.sort() if let index = activeIndices.last { return index.messageIndex.id.peerId } else { return nil } } } public func updatePeersGroupIdInteractively(peerIds: [EnginePeer.Id], groupId: EngineChatList.Group) -> Signal { return self.account.postbox.transaction { transaction -> Void in for peerId in peerIds { _internal_updatePeerGroupIdInteractively(transaction: transaction, peerId: peerId, groupId: groupId._asGroup()) } } |> ignoreValues } public func resetAllPeerNotificationSettings() -> Signal { return self.account.postbox.transaction { transaction -> Void in transaction.resetAllPeerNotificationSettings(TelegramPeerNotificationSettings.defaultSettings) } |> ignoreValues } public func setChannelForumMode(id: EnginePeer.Id, isForum: Bool) -> Signal { return _internal_setChannelForumMode(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: id, isForum: isForum) } public func createForumChannelTopic(id: EnginePeer.Id, title: String, iconColor: Int32, iconFileId: Int64?) -> Signal { return _internal_createForumChannelTopic(account: self.account, peerId: id, title: title, iconColor: iconColor, iconFileId: iconFileId) } public func fetchForumChannelTopic(id: EnginePeer.Id, threadId: Int64) -> Signal { return _internal_fetchForumChannelTopic(account: self.account, peerId: id, threadId: threadId) } public func editForumChannelTopic(id: EnginePeer.Id, threadId: Int64, title: String, iconFileId: Int64?) -> Signal { return _internal_editForumChannelTopic(account: self.account, peerId: id, threadId: threadId, title: title, iconFileId: iconFileId) } public func setForumChannelTopicClosed(id: EnginePeer.Id, threadId: Int64, isClosed: Bool) -> Signal { return _internal_setForumChannelTopicClosed(account: self.account, id: id, threadId: threadId, isClosed: isClosed) } public func setForumChannelTopicHidden(id: EnginePeer.Id, threadId: Int64, isHidden: Bool) -> Signal { return _internal_setForumChannelTopicHidden(account: self.account, id: id, threadId: threadId, isHidden: isHidden) } public func removeForumChannelThread(id: EnginePeer.Id, threadId: Int64) -> Signal { return self.account.postbox.transaction { transaction -> Void in cloudChatAddClearHistoryOperation(transaction: transaction, peerId: id, threadId: threadId, explicitTopMessageId: nil, minTimestamp: nil, maxTimestamp: nil, type: CloudChatClearHistoryType(.forEveryone)) transaction.setMessageHistoryThreadInfo(peerId: id, threadId: threadId, info: nil) _internal_clearHistory(transaction: transaction, mediaBox: self.account.postbox.mediaBox, peerId: id, threadId: threadId, namespaces: .not(Namespaces.Message.allNonRegular)) } |> ignoreValues } public func removeForumChannelThreads(id: EnginePeer.Id, threadIds: [Int64]) -> Signal { return self.account.postbox.transaction { transaction -> Void in for threadId in threadIds { cloudChatAddClearHistoryOperation(transaction: transaction, peerId: id, threadId: threadId, explicitTopMessageId: nil, minTimestamp: nil, maxTimestamp: nil, type: CloudChatClearHistoryType(.forEveryone)) transaction.setMessageHistoryThreadInfo(peerId: id, threadId: threadId, info: nil) _internal_clearHistory(transaction: transaction, mediaBox: self.account.postbox.mediaBox, peerId: id, threadId: threadId, namespaces: .not(Namespaces.Message.allNonRegular)) } } |> ignoreValues } public func toggleForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64) -> Signal { return self.account.postbox.transaction { transaction -> ([Int64], Int) in if id == self.account.peerId { let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue let accountPeer = transaction.getPeer(self.account.peerId) let limitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: accountPeer?.isPremium ?? false) let limit = limitsConfiguration.maxPinnedSavedChatCount return (transaction.getPeerPinnedThreads(peerId: id), Int(limit)) } else { var limit = 5 let appConfiguration: AppConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue if let data = appConfiguration.data, let value = data["topics_pinned_limit"] as? Double { limit = Int(value) } return (transaction.getPeerPinnedThreads(peerId: id), limit) } } |> castError(SetForumChannelTopicPinnedError.self) |> mapToSignal { threadIds, limit -> Signal in var threadIds = threadIds if threadIds.contains(threadId) { threadIds.removeAll(where: { $0 == threadId }) } else { if threadIds.count + 1 > limit { return .fail(.limitReached(limit)) } threadIds.insert(threadId, at: 0) } return _internal_setForumChannelPinnedTopics(account: self.account, id: id, threadIds: threadIds) } } public func getForumChannelPinnedTopics(id: EnginePeer.Id) -> Signal<[Int64], NoError> { return self.account.postbox.transaction { transcation -> [Int64] in return transcation.getPeerPinnedThreads(peerId: id) } } public func setForumChannelPinnedTopics(id: EnginePeer.Id, threadIds: [Int64]) -> Signal { return _internal_setForumChannelPinnedTopics(account: self.account, id: id, threadIds: threadIds) } public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[EngineMessageHistoryThread.NotificationException], NoError> { return _internal_forumChannelTopicNotificationExceptions(account: self.account, id: id) } public func importContactToken(token: String) -> Signal { return _internal_importContactToken(account: self.account, token: token) } public func exportContactToken() -> Signal { return _internal_exportContactToken(account: self.account) } public func updateChannelMembersHidden(peerId: EnginePeer.Id, value: Bool) -> Signal { return self.account.postbox.transaction { transaction -> Api.InputChannel? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if let current = current as? CachedChannelData { return current.withUpdatedMembersHidden(.known(PeerMembersHidden(value: value))) } else { return current } }) return transaction.getPeer(peerId).flatMap(apiInputChannel) } |> mapToSignal { inputChannel -> Signal in guard let inputChannel = inputChannel else { return .complete() } return self.account.network.request(Api.functions.channels.toggleParticipantsHidden(channel: inputChannel, enabled: value ? .boolTrue : .boolFalse)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> beforeNext { updates in if let updates = updates { self.account.stateManager.addUpdates(updates) } } |> ignoreValues } } public func updateForumViewAsMessages(peerId: EnginePeer.Id, value: Bool) -> Signal { return self.account.postbox.transaction { transaction -> Api.InputChannel? in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if let current = current as? CachedChannelData { return current.withUpdatedViewForumAsMessages(.known(value)) } else { return current } }) return transaction.getPeer(peerId).flatMap(apiInputChannel) } |> mapToSignal { inputChannel -> Signal in guard let inputChannel = inputChannel else { return .complete() } return self.account.network.request(Api.functions.channels.toggleViewForumAsMessages(channel: inputChannel, enabled: value ? .boolTrue : .boolFalse)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> beforeNext { updates in if let updates = updates { self.account.stateManager.addUpdates(updates) } } |> ignoreValues } } public func exportChatFolder(filterId: Int32, title: String, peerIds: [PeerId]) -> Signal { return _internal_exportChatFolder(account: self.account, filterId: filterId, title: title, peerIds: peerIds) } public func getExportedChatFolderLinks(id: Int32) -> Signal<[ExportedChatFolderLink]?, NoError> { return _internal_getExportedChatFolderLinks(account: self.account, id: id) } public func editChatFolderLink(filterId: Int32, link: ExportedChatFolderLink, title: String?, peerIds: [EnginePeer.Id]?, revoke: Bool) -> Signal { return _internal_editChatFolderLink(account: self.account, filterId: filterId, link: link, title: title, peerIds: peerIds, revoke: revoke) } public func deleteChatFolderLink(filterId: Int32, link: ExportedChatFolderLink) -> Signal { return _internal_deleteChatFolderLink(account: self.account, filterId: filterId, link: link) } public func checkChatFolderLink(slug: String) -> Signal { return _internal_checkChatFolderLink(account: self.account, slug: slug) } public func joinChatFolderLink(slug: String, peerIds: [EnginePeer.Id]) -> Signal { return _internal_joinChatFolderLink(account: self.account, slug: slug, peerIds: peerIds) } public func pollChatFolderUpdates(folderId: Int32) -> Signal { let signal = _internal_pollChatFolderUpdatesOnce(account: self.account, folderId: folderId) return ( signal |> then( Signal.complete() |> delay(10.0, queue: .concurrentDefaultQueue()) ) ) |> restart } public func subscribedChatFolderUpdates(folderId: Int32) -> Signal { return _internal_subscribedChatFolderUpdates(account: self.account, folderId: folderId) } public func joinAvailableChatsInFolder(updates: ChatFolderUpdates, peerIds: [EnginePeer.Id]) -> Signal { return _internal_joinAvailableChatsInFolder(account: self.account, updates: updates, peerIds: peerIds) } public func hideChatFolderUpdates(folderId: Int32) -> Signal { return _internal_hideChatFolderUpdates(account: self.account, folderId: folderId) } public func leaveChatFolder(folderId: Int32, removePeerIds: [EnginePeer.Id]) -> Signal { return _internal_leaveChatFolder(account: self.account, folderId: folderId, removePeerIds: removePeerIds) } public func requestLeaveChatFolderSuggestions(folderId: Int32) -> Signal<[EnginePeer.Id], NoError> { return _internal_requestLeaveChatFolderSuggestions(account: self.account, folderId: folderId) } public func keepPeerUpdated(id: EnginePeer.Id, forceUpdate: Bool) -> Signal { return self.account.viewTracker.peerView(id, updateData: forceUpdate) |> ignoreValues } public func tokenizeSearchString(string: String, transliteration: EngineStringIndexTokenTransliteration) -> [EngineDataBuffer] { return stringIndexTokens(string, transliteration: transliteration) } public func updatePeerStoriesHidden(id: PeerId, isHidden: Bool) { let _ = _internal_updatePeerStoriesHidden(account: self.account, id: id, isHidden: isHidden).start() } public func getChannelBoostStatus(peerId: EnginePeer.Id) -> Signal { return _internal_getChannelBoostStatus(account: self.account, peerId: peerId) } public func getMyBoostStatus() -> Signal { return _internal_getMyBoostStatus(account: self.account) } public func applyChannelBoost(peerId: EnginePeer.Id, slots: [Int32]) -> Signal { return _internal_applyChannelBoost(account: self.account, peerId: peerId, slots: slots) } public func getPaidMessagesRevenue(peerId: EnginePeer.Id) -> Signal { return _internal_getPaidMessagesRevenue(account: self.account, peerId: peerId) } public func addNoPaidMessagesException(peerId: EnginePeer.Id, refundCharged: Bool) -> Signal { return _internal_addNoPaidMessagesException(account: self.account, peerId: peerId, refundCharged: refundCharged) } public func updateChannelPaidMessagesStars(peerId: EnginePeer.Id, stars: StarsAmount?) -> Signal { return _internal_updateChannelPaidMessagesStars(account: self.account, peerId: peerId, stars: stars) } public func recommendedChannels(peerId: EnginePeer.Id?) -> Signal { return _internal_recommendedChannels(account: self.account, peerId: peerId) } public func recommendedChannelPeerIds(peerId: EnginePeer.Id?) -> Signal<[EnginePeer.Id]?, NoError> { return _internal_recommendedChannelPeerIds(account: self.account, peerId: peerId) } public func toggleRecommendedChannelsHidden(peerId: EnginePeer.Id, hidden: Bool) -> Signal { return _internal_toggleRecommendedChannelsHidden(account: self.account, peerId: peerId, hidden: hidden) } public func requestRecommendedChannels(peerId: EnginePeer.Id, forceUpdate: Bool = false) -> Signal { return _internal_requestRecommendedChannels(account: self.account, peerId: peerId, forceUpdate: forceUpdate) } public func recommendedAppPeerIds() -> Signal<[EnginePeer.Id]?, NoError> { return _internal_recommendedAppPeerIds(account: self.account) } public func requestGlobalRecommendedChannelsIfNeeded() -> Signal { return _internal_requestRecommendedChannels(account: self.account, peerId: nil, forceUpdate: false) } public func requestRecommendedAppsIfNeeded() -> Signal { return _internal_requestRecommendedApps(account: self.account, forceUpdate: false) } public func recommendedBots(peerId: EnginePeer.Id) -> Signal { return _internal_recommendedBots(account: self.account, peerId: peerId) } public func requestRecommendedBots(peerId: EnginePeer.Id, forceUpdate: Bool = false) -> Signal { return _internal_requestRecommendedBots(account: self.account, peerId: peerId, forceUpdate: forceUpdate) } public func searchAdPeers(query: String) -> Signal<[AdPeer], NoError> { return _internal_searchAdPeers(account: self.account, query: query) } public func markAsSeen(ad opaqueId: Data) -> Signal { return _internal_markAsSeen(account: self.account, opaqueId: opaqueId) } public func isPremiumRequiredToContact(_ peerIds: [EnginePeer.Id]) -> Signal<[EnginePeer.Id: RequirementToContact], NoError> { return _internal_updateIsPremiumRequiredToContact(account: self.account, peerIds: peerIds) } public func subscribeIsPremiumRequiredForMessaging(id: EnginePeer.Id) -> Signal { if id.namespace != Namespaces.Peer.CloudUser { return .single(false) } return self.account.postbox.combinedView(keys: [ PostboxViewKey.basicPeer(self.account.peerId), PostboxViewKey.basicPeer(id), PostboxViewKey.cachedPeerData(peerId: id) ]) |> map { views -> Bool in guard let basicAccountPeerView = views.views[PostboxViewKey.basicPeer(self.account.peerId)] as? BasicPeerView else { return false } guard let accountPeer = basicAccountPeerView.peer else { return false } if accountPeer.isPremium { return false } guard let basicPeerView = views.views[PostboxViewKey.basicPeer(id)] as? BasicPeerView else { return false } guard let user = basicPeerView.peer as? TelegramUser else { return false } guard let cachedDataView = views.views[PostboxViewKey.cachedPeerData(peerId: id)] as? CachedPeerDataView else { return false } if !user.flags.contains(.requirePremium) { return false } /*#if DEBUG if "".isEmpty { return true } #endif*/ if let cachedData = cachedDataView.cachedPeerData as? CachedUserData { if cachedData.flags.contains(.premiumRequired) { return true } else { return false } } else { return true } } |> distinctUntilChanged |> mapToSignal { maybeValue -> Signal in if !maybeValue { return .single(false) } return self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId: id, threadId: nil), anchor: .upperBound, ignoreMessagesInTimestampRange: nil, ignoreMessageIds: Set(), count: 44, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tag: nil, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), orderStatistics: []) |> map { view -> Bool in for entry in view.0.entries { if entry.message.flags.contains(.Incoming) { return false } } return true } |> distinctUntilChanged } } public func updateSavedMessagesViewAsTopics(value: Bool) { let _ = (self.account.postbox.transaction { transaction -> Void in transaction.updatePreferencesEntry(key: PreferencesKeys.displaySavedChatsAsTopics(), { _ in return PreferencesEntry(EngineDisplaySavedChatsAsTopics(value: value)) }) }).start() } public func getCollectibleUsernameInfo(username: String) -> Signal { return self.account.network.request(Api.functions.fragment.getCollectibleInfo(collectible: .inputCollectibleUsername(username: username))) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> map { result -> TelegramCollectibleItemInfo? in guard let result else { return nil } switch result { case let .collectibleInfo(purchaseDate, currency, amount, cryptoCurrency, cryptoAmount, url): return TelegramCollectibleItemInfo( subject: .username(username), purchaseDate: purchaseDate, currency: currency, currencyAmount: amount, cryptoCurrency: cryptoCurrency, cryptoCurrencyAmount: cryptoAmount, url: url ) } } } public func getCollectiblePhoneNumberInfo(phoneNumber: String) -> Signal { return self.account.network.request(Api.functions.fragment.getCollectibleInfo(collectible: .inputCollectiblePhone(phone: phoneNumber))) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> map { result -> TelegramCollectibleItemInfo? in guard let result else { return nil } switch result { case let .collectibleInfo(purchaseDate, currency, amount, cryptoCurrency, cryptoAmount, url): return TelegramCollectibleItemInfo( subject: .phoneNumber(phoneNumber), purchaseDate: purchaseDate, currency: currency, currencyAmount: amount, cryptoCurrency: cryptoCurrency, cryptoCurrencyAmount: cryptoAmount, url: url ) } } } public func updateBotBiometricsState(peerId: EnginePeer.Id, update: @escaping (TelegramBotBiometricsState?) -> TelegramBotBiometricsState) { let _ = _internal_updateBotBiometricsState(account: self.account, peerId: peerId, update: update).startStandalone() } public func botsWithBiometricState() -> Signal, NoError> { return _internal_botsWithBiometricState(account: self.account) } public func toggleChatManagingBotIsPaused(chatId: EnginePeer.Id) { let _ = _internal_toggleChatManagingBotIsPaused(account: self.account, chatId: chatId).startStandalone() } public func removeChatManagingBot(chatId: EnginePeer.Id) { let _ = _internal_removeChatManagingBot(account: self.account, chatId: chatId).startStandalone() } public func resolveMessageLink(slug: String) -> Signal { return self.account.network.request(Api.functions.account.resolveBusinessChatLink(slug: slug)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> mapToSignal { result -> Signal in guard let result else { return .single(nil) } return self.account.postbox.transaction { transaction -> TelegramResolvedMessageLink? in switch result { case let .resolvedBusinessChatLinks(_, peer, message, entities, chats, users): updatePeers(transaction: transaction, accountPeerId: self.account.peerId, peers: AccumulatedPeers(transaction: transaction, chats: chats, users: users)) guard let peer = transaction.getPeer(peer.peerId) else { return nil } return TelegramResolvedMessageLink( peer: EnginePeer(peer), message: message, entities: messageTextEntitiesFromApiEntities(entities ?? []) ) } } } } public func setStarsReactionDefaultPrivacy(privacy: TelegramPaidReactionPrivacy) { let _ = self.account.postbox.transaction({ transaction in _internal_setStarsReactionDefaultPrivacy(privacy: privacy, transaction: transaction) }).startStandalone() } public func updateStarRefProgram(id: EnginePeer.Id, program: (commissionPermille: Int32, durationMonths: Int32?)?) -> Signal { return _internal_updateStarRefProgram(account: self.account, id: id, program: program) } public func connectedStarRefBots(id: EnginePeer.Id) -> EngineConnectedStarRefBotsContext { return EngineConnectedStarRefBotsContext(account: self.account, peerId: id) } public func suggestedStarRefBots(id: EnginePeer.Id, sortMode: EngineSuggestedStarRefBotsContext.SortMode) -> EngineSuggestedStarRefBotsContext { return EngineSuggestedStarRefBotsContext(account: self.account, peerId: id, sortMode: sortMode) } public func connectStarRefBot(id: EnginePeer.Id, botId: EnginePeer.Id) -> Signal { return _internal_connectStarRefBot(account: self.account, id: id, botId: botId) } public func getStarRefBotConnection(id: EnginePeer.Id, targetId: EnginePeer.Id) -> Signal { return _internal_getStarRefBotConnection(account: self.account, id: id, targetId: targetId) } public func getPossibleStarRefBotTargets() -> Signal<[EnginePeer], NoError> { return _internal_getPossibleStarRefBotTargets(account: self.account) } } } public func _internal_decodeStoredChatInterfaceState(state: StoredPeerChatInterfaceState) -> OpaqueChatInterfaceState? { guard let data = state.data else { return nil } guard let internalState = try? AdaptedPostboxDecoder().decode(InternalChatInterfaceState.self, from: data) else { return nil } return OpaqueChatInterfaceState( opaqueData: internalState.opaqueData, historyScrollMessageIndex: internalState.historyScrollMessageIndex, mediaDraftState: internalState.mediaDraftState, synchronizeableInputState: internalState.synchronizeableInputState ) }