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,259 @@
// MARK: Swiftgram Fake Profile settings: target user, name/username/phone/ID, badges
import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import AccountContext
import SGItemListUI
import SGSimpleSettings
private enum FakeProfileSection: Int32, SGItemListSection {
case targetUser = 0
case personalData
case badges
}
private enum FakeProfileEntry: ItemListNodeEntry {
case targetHeader(id: Int, text: String)
case targetUserId(id: Int, text: String, placeholder: String)
case targetNotice(id: Int, text: String)
case personalHeader(id: Int, text: String)
case firstName(id: Int, text: String, placeholder: String)
case lastName(id: Int, text: String, placeholder: String)
case username(id: Int, text: String, placeholder: String)
case phone(id: Int, text: String, placeholder: String)
case fakeId(id: Int, text: String, placeholder: String)
case personalNotice(id: Int, text: String)
case badgesHeader(id: Int, text: String)
case premium(id: Int, title: String, subtext: String?, value: Bool)
case verified(id: Int, title: String, subtext: String?, value: Bool)
case scam(id: Int, title: String, subtext: String?, value: Bool)
case fake(id: Int, title: String, subtext: String?, value: Bool)
case support(id: Int, title: String, subtext: String?, value: Bool)
case bot(id: Int, title: String, subtext: String?, value: Bool)
case badgesNotice(id: Int, text: String)
var id: Int { stableId }
var section: ItemListSectionId {
switch self {
case .targetHeader, .targetUserId, .targetNotice: return FakeProfileSection.targetUser.rawValue
case .personalHeader, .firstName, .lastName, .username, .phone, .fakeId, .personalNotice: return FakeProfileSection.personalData.rawValue
default: return FakeProfileSection.badges.rawValue
}
}
var stableId: Int {
switch self {
case .targetHeader(let i, _), .targetUserId(let i, _, _), .targetNotice(let i, _),
.personalHeader(let i, _), .firstName(let i, _, _), .lastName(let i, _, _), .username(let i, _, _),
.phone(let i, _, _), .fakeId(let i, _, _), .personalNotice(let i, _),
.badgesHeader(let i, _), .premium(let i, _, _, _), .verified(let i, _, _, _), .scam(let i, _, _, _),
.fake(let i, _, _, _), .support(let i, _, _, _), .bot(let i, _, _, _), .badgesNotice(let i, _): return i
}
}
static func < (lhs: FakeProfileEntry, rhs: FakeProfileEntry) -> Bool { lhs.stableId < rhs.stableId }
static func == (lhs: FakeProfileEntry, rhs: FakeProfileEntry) -> Bool {
switch (lhs, rhs) {
case let (.targetHeader(a, t1), .targetHeader(b, t2)): return a == b && t1 == t2
case let (.targetUserId(a, t1, p1), .targetUserId(b, t2, p2)): return a == b && t1 == t2 && p1 == p2
case let (.targetNotice(a, t1), .targetNotice(b, t2)): return a == b && t1 == t2
case let (.personalHeader(a, t1), .personalHeader(b, t2)): return a == b && t1 == t2
case let (.firstName(a, t1, p1), .firstName(b, t2, p2)): return a == b && t1 == t2 && p1 == p2
case let (.lastName(a, t1, p1), .lastName(b, t2, p2)): return a == b && t1 == t2 && p1 == p2
case let (.username(a, t1, p1), .username(b, t2, p2)): return a == b && t1 == t2 && p1 == p2
case let (.phone(a, t1, p1), .phone(b, t2, p2)): return a == b && t1 == t2 && p1 == p2
case let (.fakeId(a, t1, p1), .fakeId(b, t2, p2)): return a == b && t1 == t2 && p1 == p2
case let (.personalNotice(a, t1), .personalNotice(b, t2)): return a == b && t1 == t2
case let (.badgesHeader(a, t1), .badgesHeader(b, t2)): return a == b && t1 == t2
case let (.premium(a, t1, s1, v1), .premium(b, t2, s2, v2)): return a == b && t1 == t2 && s1 == s2 && v1 == v2
case let (.verified(a, t1, s1, v1), .verified(b, t2, s2, v2)): return a == b && t1 == t2 && s1 == s2 && v1 == v2
case let (.scam(a, t1, s1, v1), .scam(b, t2, s2, v2)): return a == b && t1 == t2 && s1 == s2 && v1 == v2
case let (.fake(a, t1, s1, v1), .fake(b, t2, s2, v2)): return a == b && t1 == t2 && s1 == s2 && v1 == v2
case let (.support(a, t1, s1, v1), .support(b, t2, s2, v2)): return a == b && t1 == t2 && s1 == s2 && v1 == v2
case let (.bot(a, t1, s1, v1), .bot(b, t2, s2, v2)): return a == b && t1 == t2 && s1 == s2 && v1 == v2
case let (.badgesNotice(a, t1), .badgesNotice(b, t2)): return a == b && t1 == t2
default: return false
}
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let theme = presentationData.theme
let args = arguments as! FakeProfileArguments
switch self {
case .targetHeader(_, let text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: section)
case .targetUserId(_, let text, let placeholder):
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "ID", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), clearType: .always, sectionId: section, textUpdated: { args.updateTargetUserId($0) }, action: {})
case .targetNotice(_, let text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: section)
case .personalHeader(_, let text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: section)
case .firstName(_, let text, let placeholder):
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: (presentationData.strings.baseLanguageCode == "ru" ? "Имя" : "First name"), textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .regular(capitalization: true, autocorrection: false), clearType: .always, sectionId: section, textUpdated: { args.updateFirstName($0) }, action: {})
case .lastName(_, let text, let placeholder):
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: (presentationData.strings.baseLanguageCode == "ru" ? "Фамилия" : "Last name"), textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .regular(capitalization: true, autocorrection: false), clearType: .always, sectionId: section, textUpdated: { args.updateLastName($0) }, action: {})
case .username(_, let text, let placeholder):
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: (presentationData.strings.baseLanguageCode == "ru" ? "Юзернейм (без @)" : "Username (no @)"), textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .regular(capitalization: false, autocorrection: false), clearType: .always, sectionId: section, textUpdated: { args.updateUsername($0) }, action: {})
case .phone(_, let text, let placeholder):
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: (presentationData.strings.baseLanguageCode == "ru" ? "Телефон (без +)" : "Phone (no +)"), textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .number, clearType: .always, sectionId: section, textUpdated: { args.updatePhone($0) }, action: {})
case .fakeId(_, let text, let placeholder):
return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "Telegram ID", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .number, clearType: .always, sectionId: section, textUpdated: { args.updateFakeId($0) }, action: {})
case .personalNotice(_, let text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: section)
case .badgesHeader(_, let text):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: section)
case .premium(_, let title, let subtext, let value):
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, text: subtext, value: value, sectionId: section, style: .blocks, updated: { args.updatePremium($0) })
case .verified(_, let title, let subtext, let value):
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, text: subtext, value: value, sectionId: section, style: .blocks, updated: { args.updateVerified($0) })
case .scam(_, let title, let subtext, let value):
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, text: subtext, value: value, sectionId: section, style: .blocks, updated: { args.updateScam($0) })
case .fake(_, let title, let subtext, let value):
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, text: subtext, value: value, sectionId: section, style: .blocks, updated: { args.updateFake($0) })
case .support(_, let title, let subtext, let value):
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, text: subtext, value: value, sectionId: section, style: .blocks, updated: { args.updateSupport($0) })
case .bot(_, let title, let subtext, let value):
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, text: subtext, value: value, sectionId: section, style: .blocks, updated: { args.updateBot($0) })
case .badgesNotice(_, let text):
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: section)
}
}
}
private final class FakeProfileArguments {
let reload: () -> Void
init(reload: @escaping () -> Void) { self.reload = reload }
func updateTargetUserId(_ value: String) {
SGSimpleSettings.shared.fakeProfileTargetUserId = value
reload()
}
func updateFirstName(_ value: String) {
SGSimpleSettings.shared.fakeProfileFirstName = value
reload()
}
func updateLastName(_ value: String) {
SGSimpleSettings.shared.fakeProfileLastName = value
reload()
}
func updateUsername(_ value: String) {
SGSimpleSettings.shared.fakeProfileUsername = value
reload()
}
func updatePhone(_ value: String) {
SGSimpleSettings.shared.fakeProfilePhone = value
reload()
}
func updateFakeId(_ value: String) {
SGSimpleSettings.shared.fakeProfileId = value
reload()
}
func updatePremium(_ value: Bool) {
SGSimpleSettings.shared.fakeProfilePremium = value
reload()
}
func updateVerified(_ value: Bool) {
SGSimpleSettings.shared.fakeProfileVerified = value
reload()
}
func updateScam(_ value: Bool) {
SGSimpleSettings.shared.fakeProfileScam = value
reload()
}
func updateFake(_ value: Bool) {
SGSimpleSettings.shared.fakeProfileFake = value
reload()
}
func updateSupport(_ value: Bool) {
SGSimpleSettings.shared.fakeProfileSupport = value
reload()
}
func updateBot(_ value: Bool) {
SGSimpleSettings.shared.fakeProfileBot = value
reload()
}
}
private func fakeProfileEntries(presentationData: PresentationData) -> [FakeProfileEntry] {
let lang = presentationData.strings.baseLanguageCode
let s = SGSimpleSettings.shared
var entries: [FakeProfileEntry] = []
var id = 0
entries.append(.targetHeader(id: id, text: lang == "ru" ? "ЦЕЛЕВОЙ ПОЛЬЗОВАТЕЛЬ" : "TARGET USER"))
id += 1
entries.append(.targetUserId(id: id, text: s.fakeProfileTargetUserId, placeholder: lang == "ru" ? "Оставьте пустым для своего профиля" : "Leave empty for your profile"))
id += 1
entries.append(.targetNotice(id: id, text: lang == "ru" ? "Чтобы узнать ID, используйте @userinfobot" : "Use @userinfobot to get user ID"))
id += 1
entries.append(.personalHeader(id: id, text: lang == "ru" ? "ЛИЧНЫЕ ДАННЫЕ" : "PERSONAL DATA"))
id += 1
entries.append(.firstName(id: id, text: s.fakeProfileFirstName, placeholder: lang == "ru" ? "Имя" : "First name"))
id += 1
entries.append(.lastName(id: id, text: s.fakeProfileLastName, placeholder: lang == "ru" ? "Фамилия" : "Last name"))
id += 1
entries.append(.username(id: id, text: s.fakeProfileUsername, placeholder: lang == "ru" ? "без @" : "no @"))
id += 1
entries.append(.phone(id: id, text: s.fakeProfilePhone, placeholder: lang == "ru" ? "без +" : "no +"))
id += 1
entries.append(.fakeId(id: id, text: s.fakeProfileId, placeholder: lang == "ru" ? "Визуально изменить ID" : "Override displayed ID"))
id += 1
entries.append(.personalNotice(id: id, text: lang == "ru" ? "Пустые поля — реальные данные." : "Empty = real data."))
id += 1
entries.append(.badgesHeader(id: id, text: lang == "ru" ? "СТАТУСЫ И ЗНАЧКИ" : "BADGES"))
id += 1
let premiumTitle = lang == "ru" ? "Premium" : "Premium"
let premiumSub = lang == "ru" ? "Визуально добавляет иконку Premium." : "Shows Premium badge."
entries.append(.premium(id: id, title: premiumTitle, subtext: premiumSub, value: s.fakeProfilePremium))
id += 1
let verifiedTitle = lang == "ru" ? "Верификация" : "Verified"
let verifiedSub = lang == "ru" ? "Визуально добавляет галочку." : "Shows verification badge."
entries.append(.verified(id: id, title: verifiedTitle, subtext: verifiedSub, value: s.fakeProfileVerified))
id += 1
let scamTitle = lang == "ru" ? "Scam" : "Scam"
let scamSub = lang == "ru" ? "Помечает как скам." : "Marks as scam."
entries.append(.scam(id: id, title: scamTitle, subtext: scamSub, value: s.fakeProfileScam))
id += 1
let fakeTitle = lang == "ru" ? "Fake" : "Fake"
let fakeSub = lang == "ru" ? "Помечает как фейк." : "Marks as fake."
entries.append(.fake(id: id, title: fakeTitle, subtext: fakeSub, value: s.fakeProfileFake))
id += 1
let supportTitle = lang == "ru" ? "Support" : "Support"
let supportSub = lang == "ru" ? "Официальная поддержка." : "Official support badge."
entries.append(.support(id: id, title: supportTitle, subtext: supportSub, value: s.fakeProfileSupport))
id += 1
let botTitle = lang == "ru" ? "Бот" : "Bot"
let botSub = lang == "ru" ? "Помечает как бота." : "Marks as bot."
entries.append(.bot(id: id, title: botTitle, subtext: botSub, value: s.fakeProfileBot))
id += 1
entries.append(.badgesNotice(id: id, text: lang == "ru" ? "Для полного применения может потребоваться перезапуск." : "Restart may be required for full effect."))
return entries
}
/// Fake Profile settings: target user ID, first/last name, username, phone, fake ID, badge toggles.
public func FakeProfileSettingsController(context: AccountContext, onSave: @escaping () -> Void) -> ViewController {
let reloadPromise = ValuePromise(true, ignoreRepeated: false)
let arguments = FakeProfileArguments(reload: { reloadPromise.set(true) })
let signal = combineLatest(reloadPromise.get(), context.sharedContext.presentationData)
|> map { _, presentationData -> (ItemListControllerState, (ItemListNodeState, FakeProfileArguments)) in
let lang = presentationData.strings.baseLanguageCode
let controllerState = ItemListControllerState(
presentationData: ItemListPresentationData(presentationData),
title: .text(lang == "ru" ? "Настройки Fake Profile" : "Fake Profile settings"),
leftNavigationButton: nil,
rightNavigationButton: nil,
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)
)
let entries = fakeProfileEntries(presentationData: presentationData)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: entries, style: .blocks, ensureVisibleItemTag: nil, initialScrollToItem: nil)
return (controllerState, (listState, arguments))
}
let controller = ItemListController(context: context, state: signal)
return controller
}