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
@@ -0,0 +1,10 @@
import Foundation
import PathKit
import SwiftCLI
extension Path: SwiftCLI.ConvertibleFromString {
public init?(input: String) {
self.init(input)
}
}
@@ -0,0 +1,44 @@
import Foundation
import PathKit
import ProjectSpec
import SwiftCLI
import XcodeGenKit
import XcodeProj
import Version
class CacheCommand: ProjectCommand {
@Key("--cache-path", description: "Where the cache file will be loaded from and save to. Defaults to ~/.xcodegen/cache/{SPEC_PATH_HASH}")
var cacheFilePath: Path?
init(version: Version) {
super.init(version: version,
name: "cache",
shortDescription: "Write the project cache")
}
override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {
let cacheFilePath = self.cacheFilePath ?? Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
var cacheFile: CacheFile?
// generate cache
do {
cacheFile = try specLoader.generateCacheFile()
} catch {
throw GenerationError.projectSpecParsingError(error)
}
// write cache
if let cacheFile = cacheFile {
do {
try cacheFilePath.parent().mkpath()
try cacheFilePath.write(cacheFile.string)
success("Wrote cache to \(cacheFilePath)")
} catch {
info("Failed to write cache: \(error.localizedDescription)")
}
}
}
}
@@ -0,0 +1,64 @@
import Foundation
import SwiftCLI
import PathKit
import ProjectSpec
import Yams
import Version
import XcodeGenKit
class DumpCommand: ProjectCommand {
@Key("--type", "-t", description: "The type of dump to output. Either \(DumpType.allCases.map { "\"\($0.rawValue)\"" }.joined(separator: ", ")). Defaults to \(DumpType.defaultValue.rawValue). The \"parsed\" types parse the project into swift and then back again.")
private var dumpType: DumpType?
@Key("--file", "-f", description: "The path of a file to write to. If not supplied will output to stdout")
private var file: Path?
init(version: Version) {
super.init(version: version,
name: "dump",
shortDescription: "Dumps the resolved project spec to stdout or a file")
}
override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {
let type = dumpType ?? .defaultValue
let output: String
switch type {
case .swiftDump:
var string = ""
dump(project, to: &string)
output = string
case .json:
let data = try JSONSerialization.data(withJSONObject: specLoader.projectDictionary!, options: .prettyPrinted)
output = String(data: data, encoding: .utf8)!
case .yaml:
output = try Yams.dump(object: specLoader.projectDictionary!)
case .parsedJSON:
let data = try JSONSerialization.data(withJSONObject: project.toJSONDictionary(), options: .prettyPrinted)
output = String(data: data, encoding: .utf8)!
case .parsedYaml:
output = try Yams.dump(object: project.toJSONDictionary())
case .summary:
output = project.debugDescription
}
if let file = file {
try file.parent().mkpath()
try file.write(output)
} else {
success(output)
}
}
}
private enum DumpType: String, ConvertibleFromString, CaseIterable {
case swiftDump = "swift-dump"
case json
case yaml
case parsedJSON = "parsed-json"
case parsedYaml = "parsed-yaml"
case summary
static var defaultValue: DumpType { .yaml }
}
@@ -0,0 +1,138 @@
import Foundation
import PathKit
import ProjectSpec
import SwiftCLI
import XcodeGenKit
import XcodeProj
import Version
class GenerateCommand: ProjectCommand {
@Flag("-c", "--use-cache", description: "Use a cache for the xcodegen spec. This will prevent unnecessarily generating the project if nothing has changed")
var useCache: Bool
@Key("--cache-path", description: "Where the cache file will be loaded from and save to. Defaults to ~/.xcodegen/cache/{SPEC_PATH_HASH}")
var cacheFilePath: Path?
@Key("-p", "--project", description: "The path to the directory where the project should be generated. Defaults to the directory the spec is in. The filename is defined in the project spec")
var projectDirectory: Path?
@Flag("--only-plists", description: "Generate only plist files")
var onlyPlists: Bool
init(version: Version) {
super.init(version: version,
name: "generate",
shortDescription: "Generate an Xcode project from a spec")
}
override func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {
let projectDirectory = self.projectDirectory?.absolute() ?? projectSpecPath.parent()
// validate project dictionary
do {
try specLoader.validateProjectDictionaryWarnings()
} catch {
warning("\(error)")
}
let projectPath = projectDirectory + "\(project.name).xcodeproj"
// run pre gen command before we use the cache as the scripts may change it
if let command = project.options.preGenCommand {
try Task.run(bash: command, directory: projectDirectory.absolute().string)
}
let cacheFilePath = self.cacheFilePath ??
Path("~/.xcodegen/cache/\(projectSpecPath.absolute().string.md5)").absolute()
var cacheFile: CacheFile?
// generate cache
if useCache || self.cacheFilePath != nil {
do {
cacheFile = try specLoader.generateCacheFile()
} catch {
throw GenerationError.projectSpecParsingError(error)
}
}
let projectExists = XcodeProj.pbxprojPath(projectPath).exists
// check cache
if let cacheFile = cacheFile,
projectExists,
cacheFilePath.exists {
do {
let existingCacheFile: String = try cacheFilePath.read()
if cacheFile.string == existingCacheFile {
info("Project \(project.name) has not changed since cache was written")
return
}
} catch {
info("Couldn't load cache at \(cacheFile)")
}
}
// validate project
do {
try project.validateMinimumXcodeGenVersion(version)
try project.validate()
} catch let error as SpecValidationError {
throw GenerationError.validationError(error)
}
// generate plists
info("⚙️ Generating plists...")
let fileWriter = FileWriter(project: project)
do {
try fileWriter.writePlists()
if onlyPlists {
return
}
} catch {
throw GenerationError.writingError(error)
}
// generate project
info("⚙️ Generating project...")
let xcodeProject: XcodeProj
do {
let projectGenerator = ProjectGenerator(project: project)
guard let userName = ProcessInfo.processInfo.environment["LOGNAME"] else {
throw GenerationError.missingUsername
}
xcodeProject = try projectGenerator.generateXcodeProject(in: projectDirectory, userName: userName)
} catch {
throw GenerationError.generationError(error)
}
// write project
info("⚙️ Writing project...")
do {
try fileWriter.writeXcodeProject(xcodeProject, to: projectPath)
success("Created project at \(projectPath)")
} catch {
throw GenerationError.writingError(error)
}
// write cache
if let cacheFile = cacheFile {
do {
try cacheFilePath.parent().mkpath()
try cacheFilePath.write(cacheFile.string)
} catch {
info("Failed to write cache: \(error.localizedDescription)")
}
}
// run post gen command
if let command = project.options.postGenCommand {
try Task.run(bash: command, directory: projectDirectory.absolute().string)
}
}
}
@@ -0,0 +1,82 @@
import Foundation
import SwiftCLI
import ProjectSpec
import XcodeGenKit
import PathKit
import XcodeGenCore
import Version
class ProjectCommand: Command {
let version: Version
let name: String
let shortDescription: String
@Flag("-q", "--quiet", description: "Suppress all informational and success output")
var quiet: Bool
@Key("-s", "--spec", description: "The path to the project spec file. Defaults to project.yml. (It is also possible to link to multiple spec files by comma separating them. Note that all other flags will be the same.)")
var spec: String?
@Key("-r", "--project-root", description: "The path to the project root directory. Defaults to the directory containing the project spec.")
var projectRoot: Path?
@Flag("-n", "--no-env", description: "Disable environment variable expansions")
var disableEnvExpansion: Bool
init(version: Version, name: String, shortDescription: String) {
self.version = version
self.name = name
self.shortDescription = shortDescription
}
func execute() throws {
var projectSpecs: [Path] = []
if let spec = spec {
projectSpecs = spec.components(separatedBy: ",").map { Path($0).absolute() }
} else {
projectSpecs = [ Path("project.yml").absolute() ]
}
for projectSpecPath in projectSpecs {
if !projectSpecPath.exists {
throw GenerationError.missingProjectSpec(projectSpecPath)
}
let specLoader = SpecLoader(version: version)
let project: Project
let variables: [String: String] = disableEnvExpansion ? [:] : ProcessInfo.processInfo.environment
do {
project = try specLoader.loadProject(path: projectSpecPath, projectRoot: projectRoot, variables: variables)
} catch {
throw GenerationError.projectSpecParsingError(error)
}
try execute(specLoader: specLoader, projectSpecPath: projectSpecPath, project: project)
}
}
func execute(specLoader: SpecLoader, projectSpecPath: Path, project: Project) throws {}
func info(_ string: String) {
if !quiet {
stdout.print(string)
}
}
func warning(_ string: String) {
if !quiet {
stdout.print(string.yellow)
}
}
func success(_ string: String) {
if !quiet {
stdout.print(string.green)
}
}
}
@@ -0,0 +1,42 @@
import Foundation
import PathKit
import ProjectSpec
import Rainbow
import SwiftCLI
enum GenerationError: Error, CustomStringConvertible, ProcessError {
case missingProjectSpec(Path)
case projectSpecParsingError(Error)
case cacheGenerationError(Error)
case validationError(SpecValidationError)
case generationError(Error)
case missingUsername
case writingError(Error)
var description: String {
switch self {
case let .missingProjectSpec(path):
return "No project spec found at \(path.absolute())"
case let .projectSpecParsingError(error):
return "Parsing project spec failed: \(error)"
case let .cacheGenerationError(error):
return "Couldn't generate cache file: \(error)"
case let .validationError(error):
return error.description
case let .generationError(error):
return String(describing: error)
case .missingUsername:
return "Couldn't find current username"
case let .writingError(error):
return String(describing: error)
}
}
var message: String? {
description.red
}
var exitStatus: Int32 {
1
}
}
@@ -0,0 +1,34 @@
import Foundation
import ProjectSpec
import SwiftCLI
import Version
public class XcodeGenCLI {
let cli: CLI
public init(version: Version) {
let generateCommand = GenerateCommand(version: version)
cli = CLI(
name: "xcodegen",
version: version.description,
description: "Generates Xcode projects",
commands: [
generateCommand,
CacheCommand(version: version),
DumpCommand(version: version),
]
)
cli.parser.routeBehavior = .searchWithFallback(generateCommand)
}
public func execute(arguments: [String]? = nil) {
let status: Int32
if let arguments = arguments {
status = cli.go(with: arguments)
} else {
status = cli.go()
}
exit(status)
}
}