Files
Leeksov 4647310322 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.
2026-04-06 09:48:12 +03:00

102 lines
4.1 KiB
Swift
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// MARK: Swiftgram Load .dylib tweaks at startup (no Python, no .plugin)
import Foundation
import SGSimpleSettings
#if canImport(Darwin)
import Darwin
#else
import Glibc
#endif
/// Directory where user-installed .dylib tweaks are stored. Tweaks are loaded on next app launch.
public enum TweakLoader {
private static let tweaksSubdirectory = "Tweaks"
/// URL to Application Support/Tweaks (call from main thread or after app container is available).
public static var tweaksDirectoryURL: URL {
let fileManager = FileManager.default
guard let support = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else {
fatalError("Application Support directory not available")
}
return support.appendingPathComponent(tweaksSubdirectory, isDirectory: true)
}
/// Ensure Tweaks directory exists; returns its URL.
@discardableResult
public static func ensureTweaksDirectory() -> URL {
let url = tweaksDirectoryURL
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
return url
}
/// List installed tweak filenames (.dylib) in the Tweaks directory.
public static func installedTweakFilenames() -> [String] {
let url = tweaksDirectoryURL
guard let contents = try? FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else {
return []
}
return contents
.filter { $0.pathExtension.lowercased() == "dylib" }
.map { $0.lastPathComponent }
.sorted()
}
/// Copy a .dylib file into the Tweaks directory. Returns destination URL on success.
public static func installTweak(from sourceURL: URL) throws -> URL {
let fileManager = FileManager.default
let dir = ensureTweaksDirectory()
let name = sourceURL.lastPathComponent
guard name.lowercased().hasSuffix(".dylib") else {
throw NSError(domain: "TweakLoader", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not a .dylib file"])
}
let dest = dir.appendingPathComponent(name)
if fileManager.fileExists(atPath: dest.path) {
try fileManager.removeItem(at: dest)
}
try fileManager.copyItem(at: sourceURL, to: dest)
return dest
}
/// Remove a tweak by filename (e.g. "TGExtra.dylib").
public static func removeTweak(filename: String) throws {
let url = tweaksDirectoryURL.appendingPathComponent(filename)
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(at: url)
}
}
/// Load all .dylib files from the Tweaks directory. Call once at app startup when pluginSystemEnabled.
/// On iOS, loading dylibs from a writable path may require jailbreak or special entitlements.
public static func loadTweaks() {
guard SGSimpleSettings.shared.pluginSystemEnabled else { return }
let dir = tweaksDirectoryURL
guard let contents = try? FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else {
return
}
let dylibs = contents.filter { $0.pathExtension.lowercased() == "dylib" }
for url in dylibs {
loadTweak(at: url)
}
}
private static func loadTweak(at url: URL) {
let path = url.path
#if canImport(Darwin)
guard let handle = dlopen(path, RTLD_NOW | RTLD_LOCAL) else {
if let err = dlerror() {
NSLog("[TweakLoader] Failed to load %@: %s", path, err)
}
return
}
// Optional: call an init symbol if the tweak exports it (e.g. GLEGramTweakInit).
if let initSymbol = dlsym(handle, "GLEGramTweakInit") {
typealias InitFn = @convention(c) () -> Void
let fn = unsafeBitCast(initSymbol, to: InitFn.self)
fn()
}
// Keep handle alive (we don't dlclose; tweaks stay loaded for app lifetime).
_ = handle
#endif
}
}