Swiftgram/submodules/TelegramCore/Sources/MacOS/MacInternalUpdater.swift
2021-04-26 02:00:48 +04:00

175 lines
9.7 KiB
Swift

#if os(macOS)
import TelegramApi
import SwiftSignalKit
import Postbox
import SyncCore
public enum InternalUpdaterError {
case generic
case xmlLoad
case archiveLoad
}
public func requestUpdatesXml(account: Account, source: String) -> Signal<Data, InternalUpdaterError> {
return TelegramEngine(account: account).peers.resolvePeerByName(name: source)
|> castError(InternalUpdaterError.self)
|> mapToSignal { peerId -> Signal<Peer?, InternalUpdaterError> in
return account.postbox.transaction { transaction in
return peerId != nil ? transaction.getPeer(peerId!) : nil
} |> castError(InternalUpdaterError.self)
}
|> mapToSignal { peer in
if let peer = peer, let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: 0, addOffset: 0, limit: 1, maxId: Int32.max, minId: 0, hash: 0))
|> retryRequest
|> castError(InternalUpdaterError.self)
|> mapToSignal { result in
switch result {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
if let apiMessage = apiMessages.first, let storeMessage = StoreMessage(apiMessage: apiMessage) {
var peers: [PeerId: Peer] = [:]
for chat in apiChats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers[groupOrChannel.id] = groupOrChannel
}
}
for user in apiUsers {
let telegramUser = TelegramUser(user: user)
peers[telegramUser.id] = telegramUser
}
if let message = locallyRenderedMessage(message: storeMessage, peers: peers), let media = message.media.first as? TelegramMediaFile {
return Signal { subscriber in
let fetchDispsable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: MediaResourceReference.media(media: AnyMediaReference.message(message: MessageReference(message), media: media), resource: media.resource)).start()
let dataDisposable = account.postbox.mediaBox.resourceData(media.resource, option: .complete(waitUntilFetchStatus: true)).start(next: { data in
if data.complete {
if let data = try? Data(contentsOf: URL.init(fileURLWithPath: data.path)) {
subscriber.putNext(data)
subscriber.putCompletion()
} else {
subscriber.putError(.xmlLoad)
}
}
})
return ActionDisposable {
fetchDispsable.dispose()
dataDisposable.dispose()
}
}
}
}
default:
break
}
return .fail(.xmlLoad)
}
} else {
return .fail(.xmlLoad)
}
}
}
public enum AppUpdateDownloadResult {
case started(Int)
case progress(Int, Int)
case finished(String)
}
public func downloadAppUpdate(account: Account, source: String, messageId: Int32) -> Signal<AppUpdateDownloadResult, InternalUpdaterError> {
return TelegramEngine(account: account).peers.resolvePeerByName(name: source)
|> castError(InternalUpdaterError.self)
|> mapToSignal { peerId -> Signal<Peer?, InternalUpdaterError> in
return account.postbox.transaction { transaction in
return peerId != nil ? transaction.getPeer(peerId!) : nil
} |> castError(InternalUpdaterError.self)
}
|> mapToSignal { peer in
if let peer = peer, let inputChannel = apiInputChannel(peer) {
return account.network.request(Api.functions.channels.getMessages(channel: inputChannel, id: [Api.InputMessage.inputMessageID(id: messageId)]))
|> retryRequest
|> castError(InternalUpdaterError.self)
|> mapToSignal { messages in
switch messages {
case let .channelMessages(_, _, _, _, apiMessages, apiChats, apiUsers):
var peers: [PeerId: Peer] = [:]
for chat in apiChats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers[groupOrChannel.id] = groupOrChannel
}
}
for user in apiUsers {
let telegramUser = TelegramUser(user: user)
peers[telegramUser.id] = telegramUser
}
let messageAndFile:(Message, TelegramMediaFile)? = apiMessages.compactMap { value in
return StoreMessage(apiMessage: value)
}.compactMap { value in
return locallyRenderedMessage(message: value, peers: peers)
}.sorted(by: {
$0.id > $1.id
}).first(where: { value -> Bool in
return value.media.first is TelegramMediaFile
}).map { ($0, $0.media.first as! TelegramMediaFile )}
if let (message, media) = messageAndFile {
return Signal { subscriber in
var dataDisposable: Disposable?
var fetchDisposable: Disposable?
var statusDisposable: Disposable?
let removeDisposable = account.postbox.mediaBox.removeCachedResources(Set([WrappedMediaResourceId(media.resource.id)])).start(completed: {
let reference = MediaResourceReference.media(media: .message(message: MessageReference(message), media: media), resource: media.resource)
fetchDisposable = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: reference).start()
statusDisposable = account.postbox.mediaBox.resourceStatus(media.resource).start(next: { status in
switch status {
case let .Fetching(_, progress):
if let size = media.size {
if progress == 0 {
subscriber.putNext(.started(size))
} else {
subscriber.putNext(.progress(Int(progress * Float(size)), Int(size)))
}
}
default:
break
}
})
dataDisposable = account.postbox.mediaBox.resourceData(media.resource, option: .complete(waitUntilFetchStatus: true)).start(next: { data in
if data.complete {
subscriber.putNext(.finished(data.path))
subscriber.putCompletion()
}
})
})
return ActionDisposable {
fetchDisposable?.dispose()
dataDisposable?.dispose()
statusDisposable?.dispose()
removeDisposable.dispose()
}
}
} else {
return .fail(.archiveLoad)
}
default:
break
}
return .fail(.archiveLoad)
}
} else {
return .fail(.archiveLoad)
}
}
}
#endif