import Foundation import Postbox import TelegramApi import SwiftSignalKit func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountPeerId: PeerId, network: Network, postbox: Postbox) -> Signal { return postbox.combinedView(keys: [.basicPeer(rawPeerId)]) |> mapToSignal { views -> Signal in guard let view = views.views[.basicPeer(rawPeerId)] as? BasicPeerView else { return .complete() } guard let peer = view.peer else { return .complete() } return .single(peer) } |> take(1) |> mapToSignal { _ -> Signal in return postbox.transaction { transaction -> Signal in guard let rawPeer = transaction.getPeer(rawPeerId) else { return .single(false) } let peer: Peer if let secretChat = rawPeer as? TelegramSecretChat { guard let user = transaction.getPeer(secretChat.regularPeerId) else { return .single(false) } peer = user } else { peer = rawPeer } let cachedData = transaction.getPeerCachedData(peerId: peer.id) if let cachedData = cachedData as? CachedUserData { if cachedData.peerStatusSettings != nil { return .single(true) } } else if let cachedData = cachedData as? CachedGroupData { if cachedData.peerStatusSettings != nil { return .single(true) } } else if let cachedData = cachedData as? CachedChannelData { if cachedData.peerStatusSettings != nil { return .single(true) } } else if let cachedData = cachedData as? CachedSecretChatData { if cachedData.peerStatusSettings != nil { return .single(true) } } if peer.id.namespace == Namespaces.Peer.SecretChat { return postbox.transaction { transaction -> Bool in var peerStatusSettings: PeerStatusSettings if let peer = transaction.getPeer(peer.id), let associatedPeerId = peer.associatedPeerId, !transaction.isPeerContact(peerId: associatedPeerId) { if let peer = peer as? TelegramSecretChat, case .creator = peer.role { peerStatusSettings = PeerStatusSettings(flags: [], managingBot: nil) } else { peerStatusSettings = PeerStatusSettings(flags: [.canReport], managingBot: nil) } } else { peerStatusSettings = PeerStatusSettings(flags: [], managingBot: nil) } transaction.updatePeerCachedData(peerIds: [peer.id], update: { peerId, current in if let current = current as? CachedSecretChatData { return current.withUpdatedPeerStatusSettings(peerStatusSettings) } else { return CachedSecretChatData(peerStatusSettings: peerStatusSettings) } }) return true } } else if let inputPeer = apiInputPeer(peer) { return network.request(Api.functions.messages.getPeerSettings(peer: inputPeer)) |> retryRequest |> mapToSignal { peerSettings -> Signal in return postbox.transaction { transaction -> Bool in let parsedPeers: AccumulatedPeers let peerStatusSettings: PeerStatusSettings switch peerSettings { case let .peerSettings(settings, chats, users): peerStatusSettings = PeerStatusSettings(apiSettings: settings) parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) } updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) transaction.updatePeerCachedData(peerIds: Set([peer.id]), update: { _, current in switch peer.id.namespace { case Namespaces.Peer.CloudUser: let previous: CachedUserData if let current = current as? CachedUserData { previous = current } else { previous = CachedUserData() } return previous.withUpdatedPeerStatusSettings(peerStatusSettings) case Namespaces.Peer.CloudGroup: let previous: CachedGroupData if let current = current as? CachedGroupData { previous = current } else { previous = CachedGroupData() } return previous.withUpdatedPeerStatusSettings(peerStatusSettings) case Namespaces.Peer.CloudChannel: let previous: CachedChannelData if let current = current as? CachedChannelData { previous = current } else { previous = CachedChannelData() } return previous.withUpdatedPeerStatusSettings(peerStatusSettings) default: break } return current }) return true } } } else { return .single(false) } } |> switchToLatest } } func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPeerId: PeerId, network: Network, postbox: Postbox) -> Signal { return postbox.combinedView(keys: [.basicPeer(rawPeerId)]) |> mapToSignal { views -> Signal in if accountPeerId == rawPeerId { return .single(true) } guard let view = views.views[.basicPeer(rawPeerId)] as? BasicPeerView else { return .complete() } guard let _ = view.peer else { return .complete() } return .single(true) } |> take(1) |> mapToSignal { _ -> Signal in return postbox.transaction { transaction -> (Api.InputUser?, Peer?, PeerId) in guard let rawPeer = transaction.getPeer(rawPeerId) else { if rawPeerId == accountPeerId { return (.inputUserSelf, transaction.getPeer(rawPeerId), rawPeerId) } else { return (nil, nil, rawPeerId) } } let peer: Peer if let secretChat = rawPeer as? TelegramSecretChat { guard let user = transaction.getPeer(secretChat.regularPeerId) else { return (nil, nil, rawPeerId) } peer = user } else { peer = rawPeer } if rawPeerId == accountPeerId { return (.inputUserSelf, rawPeer, rawPeerId) } else { return (apiInputUser(peer), peer, peer.id) } } |> mapToSignal { inputUser, maybePeer, peerId -> Signal in if let inputUser = inputUser { let editableBotInfo: Signal if let user = maybePeer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { let flags: Int32 = (1 << 0) editableBotInfo = network.request(Api.functions.bots.getBotInfo(flags: flags, bot: inputUser, langCode: "")) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> mapToSignal { result -> Signal in if let result = result { switch result { case let .botInfo(name, about, description): return .single(EditableBotInfo(name: name, about: about, description: description)) } } else { return .single(nil) } } } else { editableBotInfo = .single(nil) } let botPreview: Signal if let user = maybePeer as? TelegramUser, let botInfo = user.botInfo { if botInfo.flags.contains(.canEdit) { botPreview = _internal_requestBotAdminPreview(network: network, peerId: user.id, inputUser: inputUser, language: nil) } else { botPreview = _internal_requestBotUserPreview(network: network, peerId: user.id, inputUser: inputUser) } } else { botPreview = .single(nil) } var additionalConnectedBots: Signal = .single(nil) if rawPeerId == accountPeerId { additionalConnectedBots = network.request(Api.functions.account.getConnectedBots()) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } } return combineLatest( network.request(Api.functions.users.getFullUser(id: inputUser)) |> retryRequest, editableBotInfo, botPreview, additionalConnectedBots ) |> mapToSignal { result, editableBotInfo, botPreview, additionalConnectedBots -> Signal in return postbox.transaction { transaction -> Bool in switch result { case let .userFull(fullUser, chats, users): var accountUser: Api.User? var parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) for user in users { if user.peerId == accountPeerId { accountUser = user } } let _ = accountUser var mappedConnectedBot: TelegramAccountConnectedBot? if let additionalConnectedBots { switch additionalConnectedBots { case let .connectedBots(connectedBots, users): parsedPeers = parsedPeers.union(with: AccumulatedPeers(transaction: transaction, chats: [], users: users)) if let apiBot = connectedBots.first { switch apiBot { case let .connectedBot(flags, botId, recipients): mappedConnectedBot = TelegramAccountConnectedBot( id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), recipients: TelegramBusinessRecipients(apiValue: recipients), canReply: (flags & (1 << 0)) != 0 ) } } } } switch fullUser { case let .userFull(_, _, _, _, _, _, _, _, userFullNotifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: userFullNotifySettings)]) } transaction.updatePeerCachedData(peerIds: [peerId], update: { peerId, current in let previous: CachedUserData if let current = current as? CachedUserData { previous = current } else { previous = CachedUserData() } switch fullUser { case let .userFull(userFullFlags, userFullFlags2, _, userFullAbout, userFullSettings, personalPhoto, profilePhoto, fallbackPhoto, _, userFullBotInfo, userFullPinnedMsgId, userFullCommonChatsCount, _, userFullTtlPeriod, userFullThemeEmoticon, _, _, _, userWallpaper, _, businessWorkHours, businessLocation, greetingMessage, awayMessage, businessIntro, birthday, personalChannelId, personalChannelMessage, starGiftsCount, starRefProgram, verification, sendPaidMessageStars): let botInfo = userFullBotInfo.flatMap(BotInfo.init(apiBotInfo:)) let isBlocked = (userFullFlags & (1 << 0)) != 0 let voiceCallsAvailable = (userFullFlags & (1 << 4)) != 0 let videoCallsAvailable = (userFullFlags & (1 << 13)) != 0 let voiceMessagesAvailable = (userFullFlags & (1 << 20)) == 0 let readDatesPrivate = (userFullFlags & (1 << 30)) != 0 let premiumRequired = (userFullFlags & (1 << 29)) != 0 let translationsDisabled = (userFullFlags & (1 << 23)) != 0 let adsEnabled = (userFullFlags2 & (1 << 7)) != 0 let canViewRevenue = (userFullFlags2 & (1 << 9)) != 0 let botCanManageEmojiStatus = (userFullFlags2 & (1 << 10)) != 0 var flags: CachedUserFlags = previous.flags if premiumRequired { flags.insert(.premiumRequired) } else { flags.remove(.premiumRequired) } if readDatesPrivate { flags.insert(.readDatesPrivate) } else { flags.remove(.readDatesPrivate) } if translationsDisabled { flags.insert(.translationHidden) } else { flags.remove(.translationHidden) } if adsEnabled { flags.insert(.adsEnabled) } else { flags.remove(.adsEnabled) } if canViewRevenue { flags.insert(.canViewRevenue) } else { flags.remove(.canViewRevenue) } if botCanManageEmojiStatus { flags.insert(.botCanManageEmojiStatus) } else { flags.remove(.botCanManageEmojiStatus) } let callsPrivate = (userFullFlags & (1 << 5)) != 0 let canPinMessages = (userFullFlags & (1 << 7)) != 0 let pinnedMessageId = userFullPinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }) let peerStatusSettings = PeerStatusSettings(apiSettings: userFullSettings) let hasScheduledMessages = (userFullFlags & 1 << 12) != 0 let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(userFullTtlPeriod)) let personalPhoto = personalPhoto.flatMap { telegramMediaImageFromApiPhoto($0) } let photo = profilePhoto.flatMap { telegramMediaImageFromApiPhoto($0) } let fallbackPhoto = fallbackPhoto.flatMap { telegramMediaImageFromApiPhoto($0) } let wallpaper = userWallpaper.flatMap { TelegramWallpaper(apiWallpaper: $0) } var mappedBusinessHours: TelegramBusinessHours? if let businessWorkHours { mappedBusinessHours = TelegramBusinessHours(apiWorkingHours: businessWorkHours) } var mappedBusinessLocation: TelegramBusinessLocation? if let businessLocation { mappedBusinessLocation = TelegramBusinessLocation(apiLocation: businessLocation) } var mappedGreetingMessage: TelegramBusinessGreetingMessage? if let greetingMessage { mappedGreetingMessage = TelegramBusinessGreetingMessage(apiGreetingMessage: greetingMessage) } var mappedAwayMessage: TelegramBusinessAwayMessage? if let awayMessage { mappedAwayMessage = TelegramBusinessAwayMessage(apiAwayMessage: awayMessage) } var mappedBusinessIntro: TelegramBusinessIntro? if let businessIntro { mappedBusinessIntro = TelegramBusinessIntro(apiBusinessIntro: businessIntro) } var mappedBirthday: TelegramBirthday? if let birthday { mappedBirthday = TelegramBirthday(apiBirthday: birthday) } var personalChannel: TelegramPersonalChannel? if let personalChannelId { let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(personalChannelId)) var subscriberCount: Int32? for chat in chats { if chat.peerId == channelPeerId { if case let .channel(_, _, _, _, _, _, _, _, _, _, _, _, participantsCount, _, _, _, _, _, _, _, _, _) = chat { subscriberCount = participantsCount } } } personalChannel = TelegramPersonalChannel( peerId: channelPeerId, subscriberCount: subscriberCount, topMessageId: personalChannelMessage ) } var mappedStarRefProgram: TelegramStarRefProgram? if let starRefProgram { mappedStarRefProgram = TelegramStarRefProgram(apiStarRefProgram: starRefProgram) } let verification = verification.flatMap { PeerVerification(apiBotVerification: $0) } let sendPaidMessageStars = sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) } return previous.withUpdatedAbout(userFullAbout) .withUpdatedBotInfo(botInfo) .withUpdatedEditableBotInfo(editableBotInfo) .withUpdatedCommonGroupCount(userFullCommonChatsCount) .withUpdatedIsBlocked(isBlocked) .withUpdatedVoiceCallsAvailable(voiceCallsAvailable) .withUpdatedVideoCallsAvailable(videoCallsAvailable) .withUpdatedCallsPrivate(callsPrivate) .withUpdatedCanPinMessages(canPinMessages) .withUpdatedPeerStatusSettings(peerStatusSettings) .withUpdatedPinnedMessageId(pinnedMessageId) .withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedThemeEmoticon(userFullThemeEmoticon) .withUpdatedPhoto(.known(photo)) .withUpdatedPersonalPhoto(.known(personalPhoto)) .withUpdatedFallbackPhoto(.known(fallbackPhoto)) .withUpdatedVoiceMessagesAvailable(voiceMessagesAvailable) .withUpdatedWallpaper(wallpaper) .withUpdatedFlags(flags) .withUpdatedBusinessHours(mappedBusinessHours) .withUpdatedBusinessLocation(mappedBusinessLocation) .withUpdatedGreetingMessage(mappedGreetingMessage) .withUpdatedAwayMessage(mappedAwayMessage) .withUpdatedConnectedBot(mappedConnectedBot) .withUpdatedBusinessIntro(mappedBusinessIntro) .withUpdatedBirthday(mappedBirthday) .withUpdatedPersonalChannel(personalChannel) .withUpdatedBotPreview(botPreview) .withUpdatedStarGiftsCount(starGiftsCount) .withUpdatedStarRefProgram(mappedStarRefProgram) .withUpdatedVerification(verification) .withUpdatedSendPaidMessageStars(sendPaidMessageStars) } }) } return true } } } else if peerId.namespace == Namespaces.Peer.CloudGroup { return network.request(Api.functions.messages.getFullChat(chatId: peerId.id._internalGetInt64Value())) |> retryRequest |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> Bool in switch result { case let .chatFull(fullChat, chats, users): switch fullChat { case let .chatFull(_, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _): transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)]) case .channelFull: break } switch fullChat { case let .chatFull(chatFullFlags, _, chatFullAbout, chatFullParticipants, chatFullChatPhoto, _, chatFullExportedInvite, chatFullBotInfo, chatFullPinnedMsgId, _, chatFullCall, chatTtlPeriod, chatFullGroupcallDefaultJoinAs, chatFullThemeEmoticon, chatFullRequestsPending, _, allowedReactions, reactionsLimit): var botInfos: [CachedPeerBotInfo] = [] for botInfo in chatFullBotInfo ?? [] { switch botInfo { case let .botInfo(_, userId, _, _, _, _, _, _, _, _): if let userId = userId { let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) let parsedBotInfo = BotInfo(apiBotInfo: botInfo) botInfos.append(CachedPeerBotInfo(peerId: peerId, botInfo: parsedBotInfo)) } } } let participants = CachedGroupParticipants(apiParticipants: chatFullParticipants) let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(chatTtlPeriod)) var invitedBy: PeerId? if let participants = participants { for participant in participants.participants { if participant.peerId == accountPeerId { if participant.invitedBy != accountPeerId { invitedBy = participant.invitedBy } break } } } let photo: TelegramMediaImage? = chatFullChatPhoto.flatMap(telegramMediaImageFromApiPhoto) let exportedInvitation = chatFullExportedInvite.flatMap { ExportedInvitation(apiExportedInvite: $0) } let pinnedMessageId = chatFullPinnedMsgId.flatMap({ MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }) let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) var flags = CachedGroupFlags() if (chatFullFlags & 1 << 7) != 0 { flags.insert(.canChangeUsername) } var hasScheduledMessages = false if (chatFullFlags & 1 << 8) != 0 { hasScheduledMessages = true } let groupCallDefaultJoinAs = chatFullGroupcallDefaultJoinAs transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in let previous: CachedGroupData if let current = current as? CachedGroupData { previous = current } else { previous = CachedGroupData() } var updatedActiveCall: CachedChannelData.ActiveCall? if let inputCall = chatFullCall { switch inputCall { case let .inputGroupCall(id, accessHash): updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream) } } let mappedAllowedReactions: PeerAllowedReactions if let allowedReactions = allowedReactions { switch allowedReactions { case .chatReactionsAll: mappedAllowedReactions = .all case let .chatReactionsSome(reactions): mappedAllowedReactions = .limited(reactions.compactMap(MessageReaction.Reaction.init(apiReaction:))) case .chatReactionsNone: mappedAllowedReactions = .empty } } else { mappedAllowedReactions = .empty } let mappedReactionSettings = PeerReactionSettings(allowedReactions: mappedAllowedReactions, maxReactionCount: reactionsLimit, starsAllowed: nil) return previous.withUpdatedParticipants(participants) .withUpdatedExportedInvitation(exportedInvitation) .withUpdatedBotInfos(botInfos) .withUpdatedPinnedMessageId(pinnedMessageId) .withUpdatedAbout(chatFullAbout) .withUpdatedFlags(flags) .withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedInvitedBy(invitedBy) .withUpdatedPhoto(photo) .withUpdatedActiveCall(updatedActiveCall) .withUpdatedCallJoinPeerId(groupCallDefaultJoinAs?.peerId) .withUpdatedThemeEmoticon(chatFullThemeEmoticon) .withUpdatedInviteRequestsPending(chatFullRequestsPending) .withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedReactionSettings(.known(mappedReactionSettings)) }) case .channelFull: break } } return true } } } else if let inputChannel = maybePeer.flatMap(apiInputChannel) { let fullChannelSignal = network.request(Api.functions.channels.getFullChannel(channel: inputChannel)) |> map(Optional.init) |> `catch` { error -> Signal in if error.errorDescription == "CHANNEL_PRIVATE" { return .single(nil) } return .single(nil) } let participantSignal = network.request(Api.functions.channels.getParticipant(channel: inputChannel, participant: .inputPeerSelf)) |> map(Optional.init) |> `catch` { error -> Signal in return .single(nil) } return combineLatest(fullChannelSignal, participantSignal) |> mapToSignal { result, participantResult -> Signal in return postbox.transaction { transaction -> Bool in if let result = result { switch result { case let .chatFull(fullChat, chats, users): switch fullChat { case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)]) case .chatFull: break } switch fullChat { case let .channelFull(flags, flags2, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon, requestsPending, _, defaultSendAs, allowedReactions, reactionsLimit, _, wallpaper, appliedBoosts, boostsUnrestrict, emojiSet, verification, starGiftsCount): var channelFlags = CachedChannelFlags() if (flags & (1 << 3)) != 0 { channelFlags.insert(.canDisplayParticipants) } if (flags & (1 << 6)) != 0 { channelFlags.insert(.canChangeUsername) } if (flags & (1 << 10)) == 0 { channelFlags.insert(.preHistoryEnabled) } if (flags & (1 << 20)) != 0 { channelFlags.insert(.canViewStats) } if (flags & (1 << 7)) != 0 { channelFlags.insert(.canSetStickerSet) } if (flags & (1 << 16)) != 0 { channelFlags.insert(.canChangePeerGeoLocation) } if (flags2 & (1 << 0)) != 0 { channelFlags.insert(.canDeleteHistory) } if (flags2 & Int32(1 << 1)) != 0 { channelFlags.insert(.antiSpamEnabled) } if (flags2 & Int32(1 << 3)) != 0 { channelFlags.insert(.translationHidden) } if (flags2 & Int32(1 << 11)) != 0 { channelFlags.insert(.adsRestricted) } if (flags2 & Int32(1 << 12)) != 0 { channelFlags.insert(.canViewRevenue) } if (flags2 & Int32(1 << 14)) != 0 { channelFlags.insert(.paidMediaAllowed) } if (flags2 & Int32(1 << 15)) != 0 { channelFlags.insert(.canViewStarsRevenue) } if (flags2 & Int32(1 << 19)) != 0 { channelFlags.insert(.starGiftsAvailable) } if (flags2 & Int32(1 << 20)) != 0 { channelFlags.insert(.paidMessagesAvailable) } let sendAsPeerId = defaultSendAs?.peerId let linkedDiscussionPeerId: PeerId? if let linkedChatId = linkedChatId, linkedChatId != 0 { linkedDiscussionPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(Int64(linkedChatId))) } else { linkedDiscussionPeerId = nil } let autoremoveTimeout: CachedPeerAutoremoveTimeout = .known(CachedPeerAutoremoveTimeout.Value(ttl)) let peerGeoLocation: PeerGeoLocation? if let location = location { peerGeoLocation = PeerGeoLocation(apiLocation: location) } else { peerGeoLocation = nil } var botInfos: [CachedPeerBotInfo] = [] for botInfo in apiBotInfos { switch botInfo { case let .botInfo(_, userId, _, _, _, _, _, _, _, _): if let userId = userId { let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId)) let parsedBotInfo = BotInfo(apiBotInfo: botInfo) botInfos.append(CachedPeerBotInfo(peerId: peerId, botInfo: parsedBotInfo)) } } } var pinnedMessageId: MessageId? if let pinnedMsgId = pinnedMsgId { pinnedMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: pinnedMsgId) } var minAvailableMessageId: MessageId? if let minAvailableMsgId = minAvailableMsgId { minAvailableMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: minAvailableMsgId) if let pinnedMsgId = pinnedMsgId, pinnedMsgId < minAvailableMsgId { pinnedMessageId = nil } } var migrationReference: ChannelMigrationReference? if let migratedFromChatId = migratedFromChatId, let migratedFromMaxId = migratedFromMaxId { migrationReference = ChannelMigrationReference(maxMessageId: MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudGroup, id: PeerId.Id._internalFromInt64Value(migratedFromChatId)), namespace: Namespaces.Message.Cloud, id: migratedFromMaxId)) } var parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users) if let participantResult = participantResult { switch participantResult { case let .channelParticipant(_, chats, users): parsedPeers = parsedPeers.union(with: AccumulatedPeers(transaction: transaction, chats: chats, users: users)) } } updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers) let stickerPack: StickerPackCollectionInfo? = stickerSet.flatMap { apiSet -> StickerPackCollectionInfo in let namespace: ItemCollectionId.Namespace switch apiSet { case let .stickerSet(flags, _, _, _, _, _, _, _, _, _, _, _): if (flags & (1 << 3)) != 0 { namespace = Namespaces.ItemCollection.CloudMaskPacks } else if (flags & (1 << 7)) != 0 { namespace = Namespaces.ItemCollection.CloudEmojiPacks } else { namespace = Namespaces.ItemCollection.CloudStickerPacks } } return StickerPackCollectionInfo(apiSet: apiSet, namespace: namespace) } var hasScheduledMessages = false if (flags & (1 << 19)) != 0 { hasScheduledMessages = true } var invitedBy: PeerId? var invitedOn: Int32? if let participantResult = participantResult { switch participantResult { case let .channelParticipant(participant, _, _): switch participant { case let .channelParticipantSelf(flags, _, inviterId, invitedDate, _): invitedBy = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(inviterId)) if (flags & (1 << 0)) != 0 { invitedOn = invitedDate } default: break } } } let photo = telegramMediaImageFromApiPhoto(chatPhoto) let emojiPack: StickerPackCollectionInfo? = emojiSet.flatMap { apiSet -> StickerPackCollectionInfo in let namespace: ItemCollectionId.Namespace switch apiSet { case let .stickerSet(flags, _, _, _, _, _, _, _, _, _, _, _): if (flags & (1 << 3)) != 0 { namespace = Namespaces.ItemCollection.CloudMaskPacks } else if (flags & (1 << 7)) != 0 { namespace = Namespaces.ItemCollection.CloudEmojiPacks } else { namespace = Namespaces.ItemCollection.CloudStickerPacks } } return StickerPackCollectionInfo(apiSet: apiSet, namespace: namespace) } var minAvailableMessageIdUpdated = false transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in var previous: CachedChannelData if let current = current as? CachedChannelData { previous = current } else { previous = CachedChannelData() } previous = previous.withUpdatedIsNotAccessible(false) minAvailableMessageIdUpdated = previous.minAvailableMessageId != minAvailableMessageId var updatedActiveCall: CachedChannelData.ActiveCall? if let inputCall = inputCall { switch inputCall { case let .inputGroupCall(id, accessHash): updatedActiveCall = CachedChannelData.ActiveCall(id: id, accessHash: accessHash, title: previous.activeCall?.title, scheduleTimestamp: previous.activeCall?.scheduleTimestamp, subscribedToScheduled: previous.activeCall?.subscribedToScheduled ?? false, isStream: previous.activeCall?.isStream) } } let mappedAllowedReactions: PeerAllowedReactions if let allowedReactions = allowedReactions { switch allowedReactions { case .chatReactionsAll: mappedAllowedReactions = .all case let .chatReactionsSome(reactions): mappedAllowedReactions = .limited(reactions.compactMap(MessageReaction.Reaction.init(apiReaction:))) case .chatReactionsNone: mappedAllowedReactions = .empty } } else { mappedAllowedReactions = .empty } let starsAllowed: Bool = (flags2 & (1 << 16)) != 0 let mappedReactionSettings = PeerReactionSettings(allowedReactions: mappedAllowedReactions, maxReactionCount: reactionsLimit, starsAllowed: starsAllowed) let membersHidden = (flags2 & (1 << 2)) != 0 let forumViewAsMessages = (flags2 & (1 << 6)) != 0 let wallpaper = wallpaper.flatMap { TelegramWallpaper(apiWallpaper: $0) } let verification = verification.flatMap { PeerVerification(apiBotVerification: $0) } return previous.withUpdatedFlags(channelFlags) .withUpdatedAbout(about) .withUpdatedParticipantsSummary(CachedChannelParticipantsSummary(memberCount: participantsCount, adminCount: adminsCount, bannedCount: bannedCount, kickedCount: kickedCount)) .withUpdatedExportedInvitation(apiExportedInvite.flatMap { ExportedInvitation(apiExportedInvite: $0) }) .withUpdatedBotInfos(botInfos) .withUpdatedPinnedMessageId(pinnedMessageId) .withUpdatedStickerPack(stickerPack) .withUpdatedMinAvailableMessageId(minAvailableMessageId) .withUpdatedMigrationReference(migrationReference) .withUpdatedLinkedDiscussionPeerId(.known(linkedDiscussionPeerId)) .withUpdatedPeerGeoLocation(peerGeoLocation) .withUpdatedSlowModeTimeout(slowmodeSeconds) .withUpdatedSlowModeValidUntilTimestamp(slowmodeNextSendDate) .withUpdatedHasScheduledMessages(hasScheduledMessages) .withUpdatedStatsDatacenterId(statsDc ?? 0) .withUpdatedInvitedBy(invitedBy) .withUpdatedInvitedOn(invitedOn) .withUpdatedPhoto(photo) .withUpdatedActiveCall(updatedActiveCall) .withUpdatedCallJoinPeerId(groupcallDefaultJoinAs?.peerId) .withUpdatedAutoremoveTimeout(autoremoveTimeout) .withUpdatedPendingSuggestions(pendingSuggestions ?? []) .withUpdatedThemeEmoticon(themeEmoticon) .withUpdatedInviteRequestsPending(requestsPending) .withUpdatedSendAsPeerId(sendAsPeerId) .withUpdatedReactionSettings(.known(mappedReactionSettings)) .withUpdatedMembersHidden(.known(PeerMembersHidden(value: membersHidden))) .withUpdatedViewForumAsMessages(.known(forumViewAsMessages)) .withUpdatedWallpaper(wallpaper) .withUpdatedBoostsToUnrestrict(boostsUnrestrict) .withUpdatedAppliedBoosts(appliedBoosts) .withUpdatedEmojiPack(emojiPack) .withUpdatedVerification(verification) .withUpdatedStarGiftsCount(starGiftsCount) }) if let minAvailableMessageId = minAvailableMessageId, minAvailableMessageIdUpdated { var resourceIds: [MediaResourceId] = [] transaction.deleteMessagesInRange(peerId: peerId, namespace: minAvailableMessageId.namespace, minId: 1, maxId: minAvailableMessageId.id, forEachMedia: { media in addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) if !resourceIds.isEmpty { let _ = postbox.mediaBox.removeCachedResources(Array(Set(resourceIds))).start() } } case .chatFull: break } } } else { transaction.updatePeerCachedData(peerIds: [peerId], update: { _, _ in var updated = CachedChannelData() updated = updated.withUpdatedIsNotAccessible(true) return updated }) } return true } } } else { return .single(false) } } } } extension CachedPeerAutoremoveTimeout.Value { init?(_ apiValue: Int32?) { if let value = apiValue { self.init(peerValue: value) } else { return nil } } } func _internal_requestBotAdminPreview(network: Network, peerId: PeerId, inputUser: Api.InputUser, language: String?) -> Signal { return network.request(Api.functions.bots.getPreviewInfo(bot: inputUser, langCode: language ?? "")) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) } |> map { result -> CachedUserData.BotPreview? in guard let result else { return nil } switch result { case let .previewInfo(media, langCodes): return CachedUserData.BotPreview( items: media.compactMap { item -> CachedUserData.BotPreview.Item? in switch item { case let .botPreviewMedia(date, media): let value = textMediaAndExpirationTimerFromApiMedia(media, peerId) if let media = value.media { return CachedUserData.BotPreview.Item(media: media, timestamp: date) } else { return nil } } }, alternativeLanguageCodes: langCodes ) } } } func _internal_requestBotUserPreview(network: Network, peerId: PeerId, inputUser: Api.InputUser) -> Signal { return network.request(Api.functions.bots.getPreviewMedias(bot: inputUser)) |> map(Optional.init) |> `catch` { _ -> Signal<[Api.BotPreviewMedia]?, NoError> in return .single(nil) } |> map { result -> CachedUserData.BotPreview? in guard let result else { return nil } return CachedUserData.BotPreview( items: result.compactMap { item -> CachedUserData.BotPreview.Item? in switch item { case let .botPreviewMedia(date, media): let value = textMediaAndExpirationTimerFromApiMedia(media, peerId) if let media = value.media { return CachedUserData.BotPreview.Item(media: media, timestamp: date) } else { return nil } } }, alternativeLanguageCodes: [] ) } }