GLEGram 12.5 — Initial public release

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.
This commit is contained in:
Leeksov
2026-04-06 09:48:12 +03:00
commit 4647310322
39685 changed files with 11052678 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "SGAPIToken",
module_name = "SGAPIToken",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramCore:TelegramCore",
"//Swiftgram/SGLogging:SGLogging",
"//Swiftgram/SGWebSettingsScheme:SGWebSettingsScheme",
"//Swiftgram/SGConfig:SGConfig",
"//Swiftgram/SGWebAppExtensions:SGWebAppExtensions",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,133 @@
import Foundation
import SwiftSignalKit
import AccountContext
import TelegramCore
import SGLogging
import SGConfig
import SGWebAppExtensions
private let tokenExpirationTime: TimeInterval = 30 * 60 // 30 minutes
private var tokenCache: [Int64: (token: String, expiration: Date)] = [:]
public enum SGAPITokenError {
case generic(String? = nil)
}
public func getSGApiToken(context: AccountContext, botUsername: String = SG_CONFIG.botUsername) -> Signal<String, SGAPITokenError> {
let userId = context.account.peerId.id._internalGetInt64Value()
if let (token, expiration) = tokenCache[userId], Date() < expiration {
// SGLogger.shared.log("SGAPI", "Using cached token. Expiring at: \(expiration)")
return Signal { subscriber in
subscriber.putNext(token)
subscriber.putCompletion()
return EmptyDisposable
}
}
SGLogger.shared.log("SGAPI", "Requesting new token")
// Workaround for Apple Review
if context.account.testingEnvironment {
return context.account.postbox.transaction { transaction -> String? in
if let testUserPeer = transaction.getPeer(context.account.peerId) as? TelegramUser, let testPhone = testUserPeer.phone {
return testPhone
} else {
return nil
}
}
|> mapToSignalPromotingError { phone -> Signal<String, SGAPITokenError> in
if let phone = phone {
// https://core.telegram.org/api/auth#test-accounts
if phone.starts(with: String(99966)) {
SGLogger.shared.log("SGAPI", "Using demo token")
tokenCache[userId] = (phone, Date().addingTimeInterval(tokenExpirationTime))
return .single(phone)
} else {
return .fail(.generic("Non-demo phone number on test DC"))
}
} else {
return .fail(.generic("Missing test account peer or it's number (how?)"))
}
}
}
return Signal { subscriber in
let getSettingsURLSignal = getSGSettingsURL(context: context, botUsername: botUsername).start(next: { url in
if let hashPart = url.components(separatedBy: "#").last {
let parsedParams = urlParseHashParams(hashPart)
if let token = parsedParams["tgWebAppData"], let token = token {
tokenCache[userId] = (token, Date().addingTimeInterval(tokenExpirationTime))
#if DEBUG
print("[SGAPI]", "API Token: \(token)")
#endif
subscriber.putNext(token)
subscriber.putCompletion()
} else {
subscriber.putError(.generic("Invalid or missing token in response url! \(url)"))
}
} else {
subscriber.putError(.generic("No hash part in URL \(url)"))
}
})
return ActionDisposable {
getSettingsURLSignal.dispose()
}
}
}
public func getSGSettingsURL(context: AccountContext, botUsername: String = SG_CONFIG.botUsername, url: String = SG_CONFIG.webappUrl, themeParams: [String: Any]? = nil) -> Signal<String, SGAPITokenError> {
return Signal { subscriber in
// themeParams = generateWebAppThemeParams(
// context.sharedContext.currentPresentationData.with { $0 }.theme
// )
var requestWebViewSignalDisposable: Disposable? = nil
var requestUpdatePeerIsBlocked: Disposable? = nil
let resolvePeerSignal = (
context.engine.peers.resolvePeerByName(name: botUsername, referrer: nil)
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
guard case let .result(result) = result else {
return .complete()
}
return .single(result)
}).start(next: { botPeer in
if let botPeer = botPeer {
SGLogger.shared.log("SGAPI", "Botpeer found for \(botUsername)")
let requestWebViewSignal = context.engine.messages.requestWebView(peerId: botPeer.id, botId: botPeer.id, url: url, payload: nil, themeParams: themeParams, fromMenu: true, replyToMessageId: nil, threadId: nil)
requestWebViewSignalDisposable = requestWebViewSignal.start(next: { webViewResult in
subscriber.putNext(webViewResult.url)
subscriber.putCompletion()
}, error: { e in
SGLogger.shared.log("SGAPI", "Webview request error, retrying with unblock")
// if e.errorDescription == "YOU_BLOCKED_USER" {
requestUpdatePeerIsBlocked = (context.engine.privacy.requestUpdatePeerIsBlocked(peerId: botPeer.id, isBlocked: false)
|> afterDisposed(
{
requestWebViewSignalDisposable?.dispose()
requestWebViewSignalDisposable = requestWebViewSignal.start(next: { webViewResult in
SGLogger.shared.log("SGAPI", "Webview retry success \(webViewResult)")
subscriber.putNext(webViewResult.url)
subscriber.putCompletion()
}, error: { e in
SGLogger.shared.log("SGAPI", "Webview retry failure \(e)")
subscriber.putError(.generic("Webview retry failure \(e)"))
})
})).start()
// }
})
} else {
SGLogger.shared.log("SGAPI", "Botpeer not found for \(botUsername)")
subscriber.putError(.generic())
}
})
return ActionDisposable {
resolvePeerSignal.dispose()
requestUpdatePeerIsBlocked?.dispose()
requestWebViewSignalDisposable?.dispose()
}
}
}