mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-04-29 22:37:53 +02:00
4647310322
Based on Swiftgram 12.5 (Telegram iOS 12.5). All GLEGram features ported and organized in GLEGram/ folder. Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass, Font Replacement, Fake Profile, Chat Export, Plugin System, and more. See CHANGELOG_12.5.md for full details.
166 lines
5.9 KiB
Swift
166 lines
5.9 KiB
Swift
import Foundation
|
|
import TelegramApi
|
|
import Postbox
|
|
import SwiftSignalKit
|
|
import MtProtoKit
|
|
#if canImport(SGSimpleSettings)
|
|
import SGSimpleSettings
|
|
#endif
|
|
|
|
private typealias SignalKitTimer = SwiftSignalKit.Timer
|
|
|
|
|
|
private final class AccountPresenceManagerImpl {
|
|
private let queue: Queue
|
|
private let network: Network
|
|
let isPerformingUpdate = ValuePromise<Bool>(false, ignoreRepeated: true)
|
|
|
|
private var shouldKeepOnlinePresenceDisposable: Disposable?
|
|
private let currentRequestDisposable = MetaDisposable()
|
|
private var onlineTimer: SignalKitTimer?
|
|
|
|
// MARK: - GLEGram - AyuGram-style ghost mode: periodic offline packet timer
|
|
private var ghostOfflineTimer: SignalKitTimer?
|
|
|
|
private var wasOnline: Bool = false
|
|
|
|
init(queue: Queue, shouldKeepOnlinePresence: Signal<Bool, NoError>, network: Network) {
|
|
self.queue = queue
|
|
self.network = network
|
|
|
|
self.shouldKeepOnlinePresenceDisposable = (shouldKeepOnlinePresence
|
|
|> distinctUntilChanged
|
|
|> deliverOn(self.queue)).start(next: { [weak self] value in
|
|
guard let `self` = self else {
|
|
return
|
|
}
|
|
if self.wasOnline != value {
|
|
self.wasOnline = value
|
|
self.updatePresence(value)
|
|
}
|
|
})
|
|
}
|
|
|
|
deinit {
|
|
assert(self.queue.isCurrent())
|
|
self.shouldKeepOnlinePresenceDisposable?.dispose()
|
|
self.currentRequestDisposable.dispose()
|
|
self.onlineTimer?.invalidate()
|
|
self.ghostOfflineTimer?.invalidate()
|
|
}
|
|
|
|
// MARK: - GLEGram - Ghost Mode helpers (AyuGram pattern)
|
|
|
|
/// Returns true if any ghost mode option is currently active.
|
|
private func isGhostModeActive() -> Bool {
|
|
#if canImport(SGSimpleSettings)
|
|
return SGSimpleSettings.shared.disableOnlineStatus ||
|
|
SGSimpleSettings.shared.ghostModeMessageSendDelaySeconds > 0
|
|
#else
|
|
return false
|
|
#endif
|
|
}
|
|
|
|
/// Sends a single offline packet to the server (suppresses online appearance).
|
|
private func sendOfflinePacket() {
|
|
let _ = (self.network.request(Api.functions.account.updateStatus(offline: .boolTrue))
|
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|
return .single(.boolFalse)
|
|
}).start()
|
|
}
|
|
|
|
/// Starts the periodic offline-packet timer (AyuGram sends every 3s, we use 5s to be less aggressive).
|
|
/// While this timer is running the user will always appear offline to others,
|
|
/// even if the MTProto layer briefly marks them as online during a message upload.
|
|
private func startGhostOfflineTimer() {
|
|
// Send immediately to cover any online flash
|
|
self.sendOfflinePacket()
|
|
|
|
self.ghostOfflineTimer?.invalidate()
|
|
let timer = SignalKitTimer(timeout: 5.0, repeat: true, completion: { [weak self] in
|
|
self?.sendOfflinePacket()
|
|
}, queue: self.queue)
|
|
self.ghostOfflineTimer = timer
|
|
timer.start()
|
|
}
|
|
|
|
private func stopGhostOfflineTimer() {
|
|
self.ghostOfflineTimer?.invalidate()
|
|
self.ghostOfflineTimer = nil
|
|
}
|
|
|
|
private func updatePresence(_ isOnline: Bool) {
|
|
// MARK: - GLEGram - Ghost Mode: AyuGram-style periodic offline packet
|
|
#if canImport(SGSimpleSettings)
|
|
if self.isGhostModeActive() {
|
|
if isOnline {
|
|
// App came to foreground with ghost mode on:
|
|
// start periodic offline timer to suppress any online appearance,
|
|
// including flashes caused by MTProto during ghost-delay message sends.
|
|
self.startGhostOfflineTimer()
|
|
} else {
|
|
// App went to background: stop timer, send one final offline packet.
|
|
self.stopGhostOfflineTimer()
|
|
self.sendOfflinePacket()
|
|
}
|
|
return
|
|
} else {
|
|
// Ghost mode just disabled — stop timer and fall through to normal logic.
|
|
self.stopGhostOfflineTimer()
|
|
}
|
|
#endif
|
|
|
|
let request: Signal<Api.Bool, MTRpcError>
|
|
if isOnline {
|
|
let timer = SignalKitTimer(timeout: 30.0, repeat: false, completion: { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.updatePresence(true)
|
|
}, queue: self.queue)
|
|
self.onlineTimer = timer
|
|
timer.start()
|
|
request = self.network.request(Api.functions.account.updateStatus(offline: .boolFalse))
|
|
} else {
|
|
self.onlineTimer?.invalidate()
|
|
self.onlineTimer = nil
|
|
request = self.network.request(Api.functions.account.updateStatus(offline: .boolTrue))
|
|
}
|
|
self.isPerformingUpdate.set(true)
|
|
self.currentRequestDisposable.set((request
|
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|
return .single(.boolFalse)
|
|
}
|
|
|> deliverOn(self.queue)).start(completed: { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
strongSelf.isPerformingUpdate.set(false)
|
|
}))
|
|
}
|
|
}
|
|
|
|
final class AccountPresenceManager {
|
|
private let queue = Queue()
|
|
private let impl: QueueLocalObject<AccountPresenceManagerImpl>
|
|
|
|
init(shouldKeepOnlinePresence: Signal<Bool, NoError>, network: Network) {
|
|
let queue = self.queue
|
|
self.impl = QueueLocalObject(queue: self.queue, generate: {
|
|
return AccountPresenceManagerImpl(queue: queue, shouldKeepOnlinePresence: shouldKeepOnlinePresence, network: network)
|
|
})
|
|
}
|
|
|
|
func isPerformingUpdate() -> Signal<Bool, NoError> {
|
|
return Signal { subscriber in
|
|
let disposable = MetaDisposable()
|
|
self.impl.with { impl in
|
|
disposable.set(impl.isPerformingUpdate.get().start(next: { value in
|
|
subscriber.putNext(value)
|
|
}))
|
|
}
|
|
return disposable
|
|
}
|
|
}
|
|
}
|