Files
Swiftgram/submodules/TelegramCore/Sources/TelegramEngine/Peers/UpdateCachedPeerData.swift
2026-01-16 03:00:59 +08:00

1006 lines
69 KiB
Swift

import Foundation
import Postbox
import TelegramApi
import SwiftSignalKit
func fetchAndUpdateSupplementalCachedPeerData(peerId rawPeerId: PeerId, accountPeerId: PeerId, network: Network, postbox: Postbox) -> Signal<Bool, NoError> {
return postbox.combinedView(keys: [.basicPeer(rawPeerId)])
|> mapToSignal { views -> Signal<Peer, NoError> 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<Bool, NoError> in
return postbox.transaction { transaction -> Signal<Bool, NoError> 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
}
if let channel = peer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
return .single(false)
}
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))
|> retryRequestIfNotFrozen
|> mapToSignal { peerSettings -> Signal<Bool, NoError> in
guard let peerSettings else {
return .single(false)
}
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<Bool, NoError> {
return postbox.combinedView(keys: [.basicPeer(rawPeerId)])
|> mapToSignal { views -> Signal<Bool, NoError> 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<Bool, NoError> 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<Bool, NoError> in
if let inputUser = inputUser {
let editableBotInfo: Signal<EditableBotInfo?, NoError>
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<Api.bots.BotInfo?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<EditableBotInfo?, NoError> 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<CachedUserData.BotPreview?, NoError>
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<Api.account.ConnectedBots?, NoError> = .single(nil)
if rawPeerId == accountPeerId {
additionalConnectedBots = network.request(Api.functions.account.getConnectedBots())
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.account.ConnectedBots?, NoError> in
return .single(nil)
}
}
return combineLatest(
network.request(Api.functions.users.getFullUser(id: inputUser))
|> retryRequest,
editableBotInfo,
botPreview,
additionalConnectedBots
)
|> mapToSignal { result, editableBotInfo, botPreview, additionalConnectedBots -> Signal<Bool, NoError> 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(connectedBotData):
let (botId, recipients, rights) = (connectedBotData.botId, connectedBotData.recipients, connectedBotData.rights)
mappedConnectedBot = TelegramAccountConnectedBot(
id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)),
recipients: TelegramBusinessRecipients(apiValue: recipients),
rights: TelegramBusinessBotRights(apiValue: rights)
)
}
}
}
}
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, userFullChatTheme, _, groupAdminRights, channelAdminRights, userWallpaper, _, businessWorkHours, businessLocation, greetingMessage, awayMessage, businessIntro, birthday, personalChannelId, personalChannelMessage, starGiftsCount, starRefProgram, verification, sendPaidMessageStars, disallowedStarGifts, starsRating, starsMyPendingRating, starsMyPendingRatingDate, mainTab, savedMusic, note):
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
let displayGiftButton = (userFullFlags2 & (1 << 16)) != 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)
}
if displayGiftButton {
flags.insert(.displayGiftButton)
} else {
flags.remove(.displayGiftButton)
}
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(channelData) = chat {
let participantsCount = channelData.participantsCount
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) }
let disallowedGifts = TelegramDisallowedGifts(apiDisallowedGifts: disallowedStarGifts)
let botGroupAdminRights = groupAdminRights.flatMap { TelegramChatAdminRights(apiAdminRights: $0) }
let botChannelAdminRights = channelAdminRights.flatMap { TelegramChatAdminRights(apiAdminRights: $0) }
let mappedStarRating = starsRating.flatMap(TelegramStarRating.init(apiRating:))
var pendingRating: TelegramStarPendingRating?
if let starsMyPendingRating, let starsMyPendingRatingDate {
pendingRating = TelegramStarPendingRating(
rating: TelegramStarRating(apiRating: starsMyPendingRating),
timestamp: starsMyPendingRatingDate
)
}
let mappedMainProfileTab = mainTab.flatMap { TelegramProfileTab(apiTab: $0) }
let mappedSavedMusic = savedMusic.flatMap { telegramMediaFileFromApiDocument($0, altDocuments: nil) }
let mappedChatTheme: ChatTheme? = userFullChatTheme.flatMap { ChatTheme(apiChatTheme: $0) }
var mappedNote: CachedUserData.Note?
if let note {
switch note {
case let .textWithEntities(text, entities):
mappedNote = CachedUserData.Note(text: text, entities: messageTextEntitiesFromApiEntities(entities))
}
}
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)
.withUpdatedChatTheme(mappedChatTheme)
.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)
.withUpdatedDisallowedGifts(disallowedGifts)
.withUpdatedBotGroupAdminRights(botGroupAdminRights)
.withUpdatedBotChannelAdminRights(botChannelAdminRights)
.withUpdatedStarRating(mappedStarRating)
.withUpdatedPendingStarRating(pendingRating)
.withUpdatedMainProfileTab(mappedMainProfileTab)
.withUpdatedSavedMusic(mappedSavedMusic)
.withUpdatedNote(mappedNote)
}
})
}
return true
}
}
} else if peerId.namespace == Namespaces.Peer.CloudGroup {
return network.request(Api.functions.messages.getFullChat(chatId: peerId.id._internalGetInt64Value()))
|> retryRequestIfNotFrozen
|> mapToSignal { result -> Signal<Bool, NoError> in
guard let result else {
return .single(false)
}
return postbox.transaction { transaction -> Bool in
switch result {
case let .chatFull(fullChat, chats, users):
switch fullChat {
case let .chatFull(chatFullData):
let (notifySettings) = (chatFullData.notifySettings)
transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)])
case .channelFull:
break
}
switch fullChat {
case let .chatFull(chatFullData):
let (chatFullFlags, chatFullAbout, chatFullParticipants, chatFullChatPhoto, chatFullExportedInvite, chatFullBotInfo, chatFullPinnedMsgId, chatFullCall, chatTtlPeriod, chatFullGroupcallDefaultJoinAs, chatFullThemeEmoticon, chatFullRequestsPending, allowedReactions, reactionsLimit) = (chatFullData.flags, chatFullData.about, chatFullData.participants, chatFullData.chatPhoto, chatFullData.exportedInvite, chatFullData.botInfo, chatFullData.pinnedMsgId, chatFullData.call, chatFullData.ttlPeriod, chatFullData.groupcallDefaultJoinAs, chatFullData.themeEmoticon, chatFullData.requestsPending, chatFullData.availableReactions, chatFullData.reactionsLimit)
var botInfos: [CachedPeerBotInfo] = []
for botInfo in chatFullBotInfo ?? [] {
switch botInfo {
case let .botInfo(botInfoData):
let (_, userId, _, _, _, _, _, _, _, _) = (botInfoData.flags, botInfoData.userId, botInfoData.description, botInfoData.descriptionPhoto, botInfoData.descriptionDocument, botInfoData.commands, botInfoData.menuButton, botInfoData.privacyPolicyUrl, botInfoData.appSettings, botInfoData.verifierSettings)
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(inputGroupCallData):
let (id, accessHash) = (inputGroupCallData.id, inputGroupCallData.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)
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
break
}
}
let mappedAllowedReactions: PeerAllowedReactions
if let allowedReactions = allowedReactions {
switch allowedReactions {
case .chatReactionsAll:
mappedAllowedReactions = .all
case let .chatReactionsSome(chatReactionsSomeData):
let reactions = chatReactionsSomeData.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)
let mappedChatTheme: ChatTheme? = chatFullThemeEmoticon.flatMap { .emoticon($0) }
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)
.withUpdatedChatTheme(mappedChatTheme)
.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<Api.messages.ChatFull?, NoError> in
if error.errorDescription == "CHANNEL_PRIVATE" {
return .single(nil)
}
return .single(nil)
}
let participantSignal: Signal<Api.channels.ChannelParticipant?, NoError>
if let channel = maybePeer as? TelegramChannel, channel.flags.contains(.isMonoforum) {
participantSignal = .single(nil)
} else {
participantSignal = network.request(Api.functions.channels.getParticipant(channel: inputChannel, participant: .inputPeerSelf))
|> map(Optional.init)
|> `catch` { error -> Signal<Api.channels.ChannelParticipant?, NoError> in
return .single(nil)
}
}
return combineLatest(fullChannelSignal, participantSignal)
|> mapToSignal { result, participantResult -> Signal<Bool, NoError> in
return postbox.transaction { transaction -> Bool in
if let result = result {
switch result {
case let .chatFull(fullChat, chats, users):
switch fullChat {
case let .channelFull(channelFullData):
let notifySettings = channelFullData.notifySettings
transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)])
case .chatFull:
break
}
switch fullChat {
case let .channelFull(channelFullData):
let (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, sendPaidMessageStars, mainTab) = (channelFullData.flags, channelFullData.flags2, channelFullData.about, channelFullData.participantsCount, channelFullData.adminsCount, channelFullData.kickedCount, channelFullData.bannedCount, channelFullData.chatPhoto, channelFullData.exportedInvite, channelFullData.botInfo, channelFullData.migratedFromChatId, channelFullData.migratedFromMaxId, channelFullData.pinnedMsgId, channelFullData.stickerset, channelFullData.availableMinId, channelFullData.linkedChatId, channelFullData.location, channelFullData.slowmodeSeconds, channelFullData.slowmodeNextSendDate, channelFullData.statsDc, channelFullData.call, channelFullData.ttlPeriod, channelFullData.pendingSuggestions, channelFullData.groupcallDefaultJoinAs, channelFullData.themeEmoticon, channelFullData.requestsPending, channelFullData.defaultSendAs, channelFullData.availableReactions, channelFullData.reactionsLimit, channelFullData.wallpaper, channelFullData.boostsApplied, channelFullData.boostsUnrestrict, channelFullData.emojiset, channelFullData.botVerification, channelFullData.stargiftsCount, channelFullData.sendPaidMessagesStars, channelFullData.mainTab)
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(botInfoData):
let (_, userId, _, _, _, _, _, _, _, _) = (botInfoData.flags, botInfoData.userId, botInfoData.description, botInfoData.descriptionPhoto, botInfoData.descriptionDocument, botInfoData.commands, botInfoData.menuButton, botInfoData.privacyPolicyUrl, botInfoData.appSettings, botInfoData.verifierSettings)
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(channelParticipantSelfData):
let (flags, inviterId, invitedDate) = (channelParticipantSelfData.flags, channelParticipantSelfData.inviterId, channelParticipantSelfData.date)
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(inputGroupCallData):
let (id, accessHash) = (inputGroupCallData.id, inputGroupCallData.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)
case .inputGroupCallSlug, .inputGroupCallInviteMessage:
break
}
}
let mappedAllowedReactions: PeerAllowedReactions
if let allowedReactions = allowedReactions {
switch allowedReactions {
case .chatReactionsAll:
mappedAllowedReactions = .all
case let .chatReactionsSome(chatReactionsSomeData):
let reactions = chatReactionsSomeData.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) }
let mappedSendPaidMessageStars = sendPaidMessageStars.flatMap { StarsAmount(value: $0, nanos: 0) }
let mappedMainProfileTab = mainTab.flatMap { TelegramProfileTab(apiTab: $0) }
let mappedChatTheme: ChatTheme? = themeEmoticon.flatMap { .emoticon($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 ?? [])
.withUpdatedChatTheme(mappedChatTheme)
.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)
.withUpdatedSendPaidMessageStars(mappedSendPaidMessageStars)
.withUpdatedMainProfileTab(mappedMainProfileTab)
})
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<CachedUserData.BotPreview?, NoError> {
return network.request(Api.functions.bots.getPreviewInfo(bot: inputUser, langCode: language ?? ""))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.bots.PreviewInfo?, NoError> 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(botPreviewMediaData):
let (date, media) = (botPreviewMediaData.date, botPreviewMediaData.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<CachedUserData.BotPreview?, NoError> {
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(botPreviewMediaData):
let (date, media) = (botPreviewMediaData.date, botPreviewMediaData.media)
let value = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let media = value.media {
return CachedUserData.BotPreview.Item(media: media, timestamp: date)
} else {
return nil
}
}
},
alternativeLanguageCodes: []
)
}
}