mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-07-13 08:16:32 +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.
1008 lines
45 KiB
Markdown
1008 lines
45 KiB
Markdown
# Local Premium (Emulate Premium) - Полная документация реализации
|
|
|
|
## 📋 Описание
|
|
|
|
Local Premium — это функция, которая эмулирует Premium подписку Telegram локально на устройстве без реальной подписки.
|
|
|
|
**Важно**: Все функции работают только на стороне клиента и видны только вам. Сервер Telegram не видит эти изменения.
|
|
|
|
---
|
|
|
|
## 🎯 Реализованная функциональность
|
|
|
|
### ✅ Основные функции
|
|
1. **Premium Badge** - Показывает значок Premium рядом с вашим именем везде в приложении
|
|
2. **Безлимитные закрепленные чаты** - Закрепляйте сколько угодно чатов (вместо лимита 5)
|
|
3. **Безлимитные папки** - Создавайте сколько угодно папок (вместо лимита 10)
|
|
4. **Безлимитные чаты в папке** - Добавляйте сколько угодно чатов в папку (вместо лимита 100)
|
|
5. **Безлимитные теги в Избранном** - Добавляйте теги к сообщениям в Saved Messages
|
|
6. **Переупорядочивание папок** - Перемещайте любую папку на первую позицию (включая "Все чаты")
|
|
|
|
### ✅ Отключение серверной синхронизации
|
|
- **Закрепленные чаты**: Сервер не будет откреплять чаты при превышении лимита
|
|
- **Папки**: Сервер не будет удалять папки при превышении лимита
|
|
- **Чаты в папках**: Сервер не будет удалять чаты из папок при превышении лимита
|
|
- **Порядок папок**: Сервер не будет изменять порядок папок
|
|
- **Теги в Избранном**: Сервер не будет синхронизировать теги
|
|
|
|
### ✅ Расположение настроек
|
|
**Settings → IAppsGram → Local Premium:**
|
|
- Один переключатель "Emulate Premium"
|
|
- Подробное описание всех включенных функций
|
|
- Предупреждение о клиентской природе функций
|
|
|
|
---
|
|
|
|
## 🏗️ Архитектура решения
|
|
|
|
### Ключевая инновация: Модификация Peer.isPremium
|
|
|
|
**Главное архитектурное решение** - модификация свойства `Peer.isPremium` в `PeerUtils.swift`:
|
|
|
|
```swift
|
|
var isPremium: Bool {
|
|
switch self {
|
|
case let user as TelegramUser:
|
|
// MARK: Swiftgram Local Premium - Override isPremium for current user
|
|
if user.id.id._internalGetInt64Value() == SGLocalPremium.shared.currentAccountPeerId?.id &&
|
|
user.id.namespace._internalGetInt32Value() == SGLocalPremium.shared.currentAccountPeerId?.namespace {
|
|
return user.flags.contains(.isPremium) || SGLocalPremium.shared.showPremiumBadge
|
|
}
|
|
return user.flags.contains(.isPremium)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Почему это работает:**
|
|
- `Peer.isPremium` используется везде в приложении для проверки Premium статуса
|
|
- Модификация в одном месте автоматически работает во всех UI компонентах
|
|
- Premium Badge отображается везде автоматически
|
|
- Не исчезает после обновлений с сервера (сервер возвращает `isPremium = false`, но наша проверка добавляет `|| SGLocalPremium.shared.showPremiumBadge`)
|
|
|
|
|
|
---
|
|
|
|
## 📦 Модуль SGLocalPremium
|
|
|
|
**Файл**: `Telegram-iOS/Swiftgram/SGLocalPremium/Sources/SGLocalPremium.swift`
|
|
|
|
### Полный код класса
|
|
|
|
```swift
|
|
import Foundation
|
|
import SwiftSignalKit
|
|
|
|
public class SGLocalPremium {
|
|
public static let shared = SGLocalPremium()
|
|
|
|
private var currentAccountId: String?
|
|
public var currentAccountPeerId: (id: Int64, namespace: Int32)?
|
|
|
|
private init() {}
|
|
|
|
// MARK: - Account Configuration
|
|
|
|
public func setAccountPeerId(_ peerId: Int64, namespace: Int32) {
|
|
self.currentAccountId = "\(namespace)_\(peerId)"
|
|
self.currentAccountPeerId = (id: peerId, namespace: namespace)
|
|
}
|
|
|
|
private func accountKey(_ key: String) -> String {
|
|
guard let accountId = currentAccountId else {
|
|
return key
|
|
}
|
|
return "\(key)_\(accountId)"
|
|
}
|
|
|
|
// MARK: - Main Setting (Per-Account)
|
|
|
|
public var emulatePremium: Bool {
|
|
get {
|
|
return UserDefaults.standard.bool(forKey: accountKey("localPremiumEmulate"))
|
|
}
|
|
set {
|
|
UserDefaults.standard.set(newValue, forKey: accountKey("localPremiumEmulate"))
|
|
UserDefaults.standard.synchronize()
|
|
}
|
|
}
|
|
|
|
// MARK: - Computed Properties
|
|
|
|
public var showPremiumBadge: Bool { return emulatePremium }
|
|
public var unlimitedPinnedChats: Bool { return emulatePremium }
|
|
public var unlimitedFolders: Bool { return emulatePremium }
|
|
public var unlimitedChatsPerFolder: Bool { return emulatePremium }
|
|
public var unlimitedSavedMessageTags: Bool { return emulatePremium }
|
|
public var allowFolderReordering: Bool { return emulatePremium }
|
|
public var shouldDisableServerSync: Bool { return emulatePremium }
|
|
}
|
|
```
|
|
|
|
**Ключевые особенности:**
|
|
- **Singleton pattern**: `SGLocalPremium.shared` для глобального доступа
|
|
- **Per-account настройки**: Каждый аккаунт имеет свои настройки через `accountKey()`
|
|
- **Одна главная настройка**: `emulatePremium` контролирует все функции
|
|
- **Computed properties**: Все функции возвращают значение `emulatePremium`
|
|
- **currentAccountPeerId**: Хранит ID текущего аккаунта для проверки в `Peer.isPremium`
|
|
|
|
|
|
---
|
|
|
|
## 🔗 Точки интеграции
|
|
|
|
### 1. Инициализация аккаунта
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramUI/Sources/AccountContext.swift`
|
|
|
|
```swift
|
|
// MARK: Swiftgram Local Premium - Set current account
|
|
SGLocalPremium.shared.setAccountPeerId(
|
|
account.peerId.id._internalGetInt64Value(),
|
|
namespace: account.peerId.namespace._internalGetInt32Value()
|
|
)
|
|
```
|
|
|
|
**Назначение**: Устанавливает текущий аккаунт для per-account настроек и для проверки в `Peer.isPremium`.
|
|
|
|
---
|
|
|
|
### 2. Premium Badge - Ключевая модификация
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/Utils/PeerUtils.swift`
|
|
|
|
```swift
|
|
var isPremium: Bool {
|
|
switch self {
|
|
case let user as TelegramUser:
|
|
// MARK: Swiftgram Local Premium - Override isPremium for current user
|
|
if user.id.id._internalGetInt64Value() == SGLocalPremium.shared.currentAccountPeerId?.id &&
|
|
user.id.namespace._internalGetInt32Value() == SGLocalPremium.shared.currentAccountPeerId?.namespace {
|
|
return user.flags.contains(.isPremium) || SGLocalPremium.shared.showPremiumBadge
|
|
}
|
|
return user.flags.contains(.isPremium)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Почему это критически важно:**
|
|
- Это **единственное место**, где нужно добавить проверку для Premium Badge
|
|
- Все UI компоненты используют `peer.isPremium` для проверки
|
|
- Badge автоматически отображается везде:
|
|
- ✅ PeerInfoScreen - профиль пользователя
|
|
- ✅ PeerInfoHeaderNode - заголовок профиля
|
|
- ✅ ChatListItem - список чатов
|
|
- ✅ ChatTitleView - заголовок чата
|
|
- ✅ ChatTitleComponent - заголовок чата (компонент)
|
|
- ✅ ItemListAvatarAndNameItem - элемент списка с аватаром
|
|
- ✅ ContactsPeerItem - элемент контакта
|
|
- ✅ CallListCallItem - элемент списка звонков
|
|
- ✅ VoiceChatParticipantItem - участник голосового чата
|
|
- ✅ ReactionListContextMenuContent - контекстное меню реакций
|
|
- Badge **не исчезает** после обновлений с сервера (сервер возвращает `isPremium = false`, но проверка `|| SGLocalPremium.shared.showPremiumBadge` сохраняет его)
|
|
|
|
|
|
---
|
|
|
|
### 3. Лимиты закрепленных чатов
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift`
|
|
|
|
```swift
|
|
public var maxPinnedChatCount: Int32 {
|
|
// MARK: Swiftgram Local Premium - Unlimited pinned chats
|
|
return SGLocalPremium.shared.getMaxPinnedChatCount(
|
|
self.isPremium ? self.defaultMaxPremiumPinnedChatCount : self.defaultMaxPinnedChatCount
|
|
)
|
|
}
|
|
```
|
|
|
|
**Метод в SGLocalPremium:**
|
|
```swift
|
|
public func getMaxPinnedChatCount(_ original: Int32) -> Int32 {
|
|
if unlimitedPinnedChats {
|
|
return Int32.max // Практически безлимит
|
|
}
|
|
return original
|
|
}
|
|
```
|
|
|
|
**Назначение**: Возвращает `Int32.max` если `emulatePremium = true`, иначе оригинальный лимит (5 для обычных, 10 для Premium).
|
|
|
|
---
|
|
|
|
### 4. Лимиты папок
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift`
|
|
|
|
```swift
|
|
public var maxFoldersCount: Int32 {
|
|
// MARK: Swiftgram Local Premium - Unlimited folders
|
|
return SGLocalPremium.shared.getMaxFoldersCount(
|
|
self.isPremium ? self.defaultMaxPremiumFoldersCount : self.defaultMaxFoldersCount
|
|
)
|
|
}
|
|
```
|
|
|
|
**Назначение**: Возвращает `Int32.max` если `emulatePremium = true`, иначе оригинальный лимит (10 для обычных, 20 для Premium).
|
|
|
|
---
|
|
|
|
### 5. Лимиты чатов в папке
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift`
|
|
|
|
```swift
|
|
public var maxFolderChatsCount: Int32 {
|
|
// MARK: Swiftgram Local Premium - Unlimited chats per folder
|
|
return SGLocalPremium.shared.getMaxFolderChatsCount(
|
|
self.isPremium ? self.defaultMaxPremiumFolderChatsCount : self.defaultMaxFolderChatsCount
|
|
)
|
|
}
|
|
```
|
|
|
|
**Назначение**: Возвращает `Int32.max` если `emulatePremium = true`, иначе оригинальный лимит (100 для обычных, 200 для Premium).
|
|
|
|
|
|
---
|
|
|
|
### 6. Отключение синхронизации закрепленных чатов
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift`
|
|
|
|
```swift
|
|
func synchronizePinnedChats(
|
|
transaction: Transaction,
|
|
accountPeerId: PeerId
|
|
) -> Signal<Void, NoError> {
|
|
// MARK: Swiftgram Local Premium - Skip server sync if emulate premium is enabled
|
|
if SGLocalPremium.shared.shouldDisableServerSync {
|
|
return .complete()
|
|
}
|
|
|
|
// ... оригинальный код синхронизации
|
|
}
|
|
```
|
|
|
|
**Назначение**:
|
|
- Предотвращает отправку закрепленных чатов на сервер
|
|
- Сервер не будет откреплять чаты при превышении лимита
|
|
- Локальные изменения остаются только на устройстве
|
|
|
|
**Что это дает:**
|
|
- Можно закрепить больше 5 чатов (для обычных пользователей)
|
|
- Чаты не будут автоматически откреплены сервером
|
|
- Закрепленные чаты сохраняются между перезапусками
|
|
|
|
---
|
|
|
|
### 7. Отключение синхронизации папок
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift`
|
|
|
|
```swift
|
|
public func _internal_synchronizeChatListFilters(
|
|
postbox: Postbox,
|
|
network: Network,
|
|
stateManager: AccountStateManager,
|
|
ignoreRemoteUpdates: Bool = false
|
|
) -> Signal<Void, NoError> {
|
|
// MARK: Swiftgram Local Premium - Skip server sync if emulate premium is enabled
|
|
if SGLocalPremium.shared.shouldDisableServerSync {
|
|
return .complete()
|
|
}
|
|
|
|
// ... оригинальный код синхронизации
|
|
}
|
|
```
|
|
|
|
**Назначение**:
|
|
- Предотвращает отправку папок на сервер
|
|
- Сервер не будет удалять папки при превышении лимита
|
|
- Сервер не будет удалять чаты из папок при превышении лимита
|
|
|
|
**Что это дает:**
|
|
- Можно создать больше 10 папок (для обычных пользователей)
|
|
- Можно добавить больше 100 чатов в папку
|
|
- Папки и чаты не будут автоматически удалены сервером
|
|
|
|
|
|
---
|
|
|
|
### 8. Отключение синхронизации порядка папок
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift`
|
|
|
|
```swift
|
|
func _internal_requestUpdateChatListFilterOrder(
|
|
account: Account,
|
|
ids: [Int32]
|
|
) -> Signal<Never, RequestUpdateChatListFilterOrderError> {
|
|
// MARK: Swiftgram Local Premium - Skip server sync if emulate premium is enabled
|
|
if SGLocalPremium.shared.shouldDisableServerSync {
|
|
return .complete()
|
|
}
|
|
|
|
return account.network.request(Api.functions.messages.updateDialogFiltersOrder(order: ids))
|
|
|> mapError { _ -> RequestUpdateChatListFilterOrderError in
|
|
return .generic
|
|
}
|
|
|> mapToSignal { _ -> Signal<Never, RequestUpdateChatListFilterOrderError> in
|
|
return .complete()
|
|
}
|
|
}
|
|
```
|
|
|
|
**Назначение**:
|
|
- Предотвращает отправку порядка папок на сервер
|
|
- Сервер не будет изменять порядок папок
|
|
- Можно переместить любую папку на первую позицию (включая "Все чаты")
|
|
|
|
**Что это дает:**
|
|
- Можно переместить любую папку на первую позицию
|
|
- Папка "Все чаты" может быть не первой
|
|
- Порядок папок сохраняется между перезапусками
|
|
|
|
---
|
|
|
|
### 9. Переупорядочивание папок - UI компонент
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift`
|
|
|
|
```swift
|
|
public final class HorizontalTabsComponent: Component {
|
|
public let canReorderAllChats: Bool
|
|
|
|
public init(
|
|
// ... другие параметры
|
|
canReorderAllChats: Bool = true
|
|
) {
|
|
self.canReorderAllChats = canReorderAllChats
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
**Проверка возможности переупорядочивания:**
|
|
```swift
|
|
// MARK: Swiftgram Local Premium - Check if user can reorder all chats
|
|
if !self.canReorderAllChats, let reorderedItemIds = self.reorderedItemIds {
|
|
if let firstReorderedId = reorderedItemIds.first,
|
|
let firstOriginalTab = component.tabs.first,
|
|
firstReorderedId != firstOriginalTab.id {
|
|
self.reorderedItemIds = self.initialReorderedItemIds
|
|
}
|
|
}
|
|
```
|
|
|
|
**Файл**: `Telegram-iOS/submodules/ChatListUI/Sources/ChatListControllerNode.swift`
|
|
|
|
```swift
|
|
let tabsComponent = HorizontalTabsComponent(
|
|
// ... другие параметры
|
|
canReorderAllChats: SGLocalPremium.shared.canReorderAllChats(isPremium: self.context.isPremium)
|
|
)
|
|
```
|
|
|
|
**Метод в SGLocalPremium:**
|
|
```swift
|
|
public func canReorderAllChats(isPremium: Bool) -> Bool {
|
|
if isPremium {
|
|
return true
|
|
}
|
|
return allowFolderReordering
|
|
}
|
|
```
|
|
|
|
|
|
---
|
|
|
|
## 🎨 UI компоненты
|
|
|
|
### Контроллер настроек IAppsGram
|
|
|
|
**Файл**: `Telegram-iOS/Swiftgram/IAppsGramSettings/Sources/IAppsGramSettingsController.swift`
|
|
|
|
### Секция Local Premium в UI
|
|
|
|
```swift
|
|
// MARK: - Local Premium Section
|
|
case localPremiumHeader(PresentationTheme, String)
|
|
case localPremiumEmulate(PresentationTheme, String, Bool)
|
|
case localPremiumInfo(PresentationTheme, String)
|
|
```
|
|
|
|
### Генерация UI элементов
|
|
|
|
```swift
|
|
// Local Premium Section
|
|
entries.append(.localPremiumHeader(presentationData.theme, "LOCAL PREMIUM"))
|
|
entries.append(.localPremiumEmulate(
|
|
presentationData.theme,
|
|
"Emulate Premium",
|
|
SGLocalPremium.shared.emulatePremium
|
|
))
|
|
entries.append(.localPremiumInfo(
|
|
presentationData.theme,
|
|
"Enables all Premium features locally:\n• Premium badge next to your name\n• Unlimited pinned chats\n• Unlimited folders\n• Unlimited chats per folder\n• Unlimited saved message tags\n• Folder reordering\n• Disables server sync validation\n\n⚠️ Warning: These features are client-side only and visible only to you. Server may reject some changes."
|
|
))
|
|
```
|
|
|
|
### Обработчик переключателя
|
|
|
|
```swift
|
|
let arguments = IAppsGramSettingsControllerArguments(
|
|
context: context,
|
|
// ... другие обработчики
|
|
toggleLocalPremiumEmulate: { value in
|
|
SGLocalPremium.shared.emulatePremium = value
|
|
}
|
|
)
|
|
```
|
|
|
|
**Особенности UI:**
|
|
- Один переключатель для всех функций
|
|
- Подробное описание всех включенных функций
|
|
- Предупреждение о клиентской природе
|
|
- Простой и понятный интерфейс
|
|
- Настройки сохраняются автоматически в UserDefaults
|
|
|
|
|
|
---
|
|
|
|
## 🏷️ Saved Message Tags (Теги в Избранном)
|
|
|
|
### Описание функции
|
|
|
|
Saved Message Tags — это функция, позволяющая добавлять теги (реакции) к сообщениям в "Избранном" для их организации и быстрого поиска. В оригинальном Telegram это Premium-функция.
|
|
|
|
### Точки интеграции
|
|
|
|
#### 1. UI Premium-проверки (4 файла)
|
|
|
|
Добавлена проверка `!SGLocalPremium.shared.unlimitedSavedMessageTags` во все места проверки Premium для тегов:
|
|
|
|
| Файл | Назначение |
|
|
|------|------------|
|
|
| `ChatControllerOpenMessageReactionContextMenu.swift` | Меню выбора тега (реакции) |
|
|
| `ChatController.swift` | Основная Premium-проверка для тегов |
|
|
| `ChatControllerOpenMessageContextMenu.swift` | Контекстное меню сообщения |
|
|
| `ChatSearchTitleAccessoryPanelNode.swift` | Панель поиска по тегам |
|
|
|
|
**Пример изменения:**
|
|
```swift
|
|
// Было:
|
|
if !hasPremium {
|
|
showPremiumPaywall()
|
|
}
|
|
|
|
// Стало:
|
|
if !hasPremium && !SGLocalPremium.shared.unlimitedSavedMessageTags {
|
|
showPremiumPaywall()
|
|
}
|
|
```
|
|
|
|
#### 2. Локальное сохранение тегов
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/MessageReactions.swift`
|
|
|
|
```swift
|
|
// MARK: Swiftgram - Skip server sync for tags in Saved Messages when Local Premium is active
|
|
if isTags && SGLocalPremium.shared.shouldDisableServerSync {
|
|
return postbox.transaction { transaction -> Void in
|
|
// 1. Отменяем pending action (не отправляем на сервер)
|
|
transaction.setPendingMessageAction(type: .updateReaction, id: messageId, action: nil)
|
|
|
|
// 2. Объединяем pending и существующие реакции
|
|
let mergedReactions = mergedMessageReactions(attributes: currentMessage.attributes, isTags: true)
|
|
|
|
// 3. Обновляем сообщение с постоянными ReactionsMessageAttribute
|
|
transaction.updateMessage(messageId, update: { currentMessage in
|
|
// ... обновление атрибутов сообщения
|
|
return .update(StoreMessage(...))
|
|
})
|
|
|
|
// 4. Postbox автоматически индексирует customTags
|
|
// 5. Обновляем SavedMessageTags для UI
|
|
}
|
|
}
|
|
```
|
|
|
|
**Ключевой момент**: Postbox автоматически индексирует теги при вызове `updateMessage`.
|
|
|
|
|
|
#### 3. Отключение серверной синхронизации тегов
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/SavedMessageTags.swift`
|
|
|
|
```swift
|
|
// MARK: Swiftgram - Skip server sync for tags when Local Premium is active
|
|
if SGLocalPremium.shared.shouldDisableServerSync {
|
|
return .complete() // Не делаем polling с сервером
|
|
}
|
|
```
|
|
|
|
#### 4. Bypass синхронизации при изменении тегов
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift`
|
|
|
|
```swift
|
|
// MARK: Swiftgram - When Local Premium is active, skip server sync but mark as cached
|
|
if SGLocalPremium.shared.shouldDisableServerSync {
|
|
return postbox.transaction { transaction -> Void in
|
|
// Помечаем теги как закэшированные, чтобы UI их показывал
|
|
transaction.setPreferencesEntry(
|
|
key: PreferencesKeys.didCacheSavedMessageTags(threadId: threadId),
|
|
value: PreferencesEntry(data: Data())
|
|
)
|
|
}
|
|
|> ignoreValues
|
|
}
|
|
```
|
|
|
|
#### 5. Bypass заполнения "holes" для фильтрации (КЛЮЧЕВОЙ!)
|
|
|
|
**Файл**: `Telegram-iOS/submodules/TelegramCore/Sources/State/Holes.swift`
|
|
|
|
```swift
|
|
// MARK: Swiftgram - Skip server requests for customTag holes when Local Premium is active
|
|
if case .customTag = space {
|
|
switch peerInput {
|
|
case let .direct(peerId, _):
|
|
if peerId == accountPeerId && SGLocalPremium.shared.shouldDisableServerSync {
|
|
return postbox.transaction { transaction -> FetchMessageHistoryHoleResult? in
|
|
// Помечаем hole как заполненный
|
|
transaction.removeHole(peerId: peerId, threadId: nil, namespace: namespace, space: space, range: minMaxRange)
|
|
|
|
// Возвращаем пустой результат - система использует локальные данные
|
|
return FetchMessageHistoryHoleResult(
|
|
removedIndices: IndexSet(integersIn: Int(minMaxRange.lowerBound) ... Int(minMaxRange.upperBound)),
|
|
strictRemovedIndices: IndexSet(),
|
|
actualPeerId: peerId,
|
|
actualThreadId: nil,
|
|
ids: []
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Почему это критически важно:**
|
|
- Без этого bypass система ожидает ответ от сервера на запрос `messages.search` с фильтром по тегу
|
|
- Сервер либо не отвечает, либо возвращает ошибку Premium
|
|
- Результат: бесконечная загрузка
|
|
- С bypass: система сразу использует локально проиндексированные сообщения
|
|
|
|
|
|
---
|
|
|
|
## 📝 BUILD конфигурация
|
|
|
|
### Добавление зависимости SGLocalPremium
|
|
|
|
Для каждого модуля, который использует `SGLocalPremium`, нужно добавить зависимость в BUILD файл:
|
|
|
|
```python
|
|
deps = [
|
|
"//Swiftgram/SGLocalPremium:SGLocalPremium",
|
|
# ... другие зависимости
|
|
]
|
|
```
|
|
|
|
### Список модулей с добавленной зависимостью
|
|
|
|
1. `TelegramCore` - для `PeerUtils.swift`, `UserLimitsConfiguration.swift`, `MessageReactions.swift`, `SavedMessageTags.swift`, `Holes.swift`
|
|
2. `TelegramUI` - для `AccountContext.swift`, `ChatController.swift`, `ChatControllerLoadDisplayNode.swift`, `ChatControllerContentData.swift`
|
|
3. `ChatTitleView` - для `ChatTitleComponent.swift`
|
|
4. `PeerInfoScreen` - для `PeerInfoHeaderNode.swift`, `PeerInfoScreen.swift`
|
|
5. `ItemListAvatarAndNameInfoItem` - для `ItemListAvatarAndNameItem.swift`
|
|
6. `ContactsPeerItem` - для `ContactsPeerItem.swift`
|
|
7. `CallListUI` - для `CallListCallItem.swift`
|
|
8. `TelegramCallsUI` - для `VoiceChatParticipantItem.swift`
|
|
9. `ReactionListContextMenuContent` - для `ReactionListContextMenuContent.swift`
|
|
10. `ChatListUI` - для `ChatListItem.swift`, `ChatListControllerNode.swift`
|
|
11. `HorizontalTabsComponent` - для `HorizontalTabsComponent.swift`
|
|
12. `IAppsGramSettings` - для `IAppsGramSettingsController.swift`
|
|
|
|
### Импорт модуля
|
|
|
|
В каждом файле, использующем `SGLocalPremium`, добавлен импорт:
|
|
|
|
```swift
|
|
import SGLocalPremium
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 Критические детали реализации
|
|
|
|
### 1. Per-Account настройки
|
|
|
|
Настройки хранятся отдельно для каждого аккаунта:
|
|
|
|
```swift
|
|
// Установка текущего аккаунта
|
|
SGLocalPremium.shared.setAccountPeerId(peerId, namespace: namespace)
|
|
|
|
// Ключ в UserDefaults
|
|
"localPremiumEmulate_<namespace>_<peerId>"
|
|
```
|
|
|
|
**Преимущества:**
|
|
- Настройки не сбрасываются при перезапуске
|
|
- Каждый аккаунт имеет независимые настройки
|
|
- Простое переключение между аккаунтами
|
|
|
|
|
|
### 2. Одна настройка для всех функций
|
|
|
|
Вместо отдельных переключателей используется одна настройка `emulatePremium`:
|
|
|
|
```swift
|
|
public var emulatePremium: Bool // Главная настройка
|
|
|
|
// Все функции - computed properties
|
|
public var showPremiumBadge: Bool { return emulatePremium }
|
|
public var unlimitedPinnedChats: Bool { return emulatePremium }
|
|
public var unlimitedFolders: Bool { return emulatePremium }
|
|
public var unlimitedChatsPerFolder: Bool { return emulatePremium }
|
|
public var unlimitedSavedMessageTags: Bool { return emulatePremium }
|
|
public var allowFolderReordering: Bool { return emulatePremium }
|
|
```
|
|
|
|
**Преимущества:**
|
|
- Простой и понятный UI
|
|
- Все функции включаются одновременно
|
|
- Меньше путаницы для пользователей
|
|
|
|
### 3. Отключение серверной синхронизации
|
|
|
|
Ключевая особенность - отключение синхронизации с сервером:
|
|
|
|
```swift
|
|
public var shouldDisableServerSync: Bool {
|
|
return emulatePremium
|
|
}
|
|
```
|
|
|
|
**Что отключается:**
|
|
1. **Синхронизация закрепленных чатов** - сервер не откреплит чаты
|
|
2. **Синхронизация папок** - сервер не удалит папки
|
|
3. **Синхронизация чатов в папках** - сервер не удалит чаты из папок
|
|
4. **Синхронизация порядка папок** - сервер не изменит порядок папок
|
|
5. **Синхронизация тегов** - сервер не будет синхронизировать теги
|
|
|
|
**Как это работает:**
|
|
- Функции синхронизации проверяют `shouldDisableServerSync`
|
|
- Если `true`, функция возвращает `.complete()` без выполнения
|
|
- Локальные изменения остаются только на устройстве
|
|
|
|
### 4. Переопределение лимитов
|
|
|
|
Лимиты переопределяются через методы в `SGLocalPremium`:
|
|
|
|
```swift
|
|
public func getMaxPinnedChatCount(_ original: Int32) -> Int32 {
|
|
if unlimitedPinnedChats {
|
|
return Int32.max // Безлимит
|
|
}
|
|
return original // Оригинальный лимит
|
|
}
|
|
```
|
|
|
|
**Стратегия:**
|
|
- Если `emulatePremium = true`, возвращается `Int32.max`
|
|
- Если `emulatePremium = false`, возвращается оригинальный лимит
|
|
- Применяется ко всем типам лимитов
|
|
|
|
|
|
### 5. Premium Badge - Ключевое архитектурное решение
|
|
|
|
Premium значок работает через модификацию `Peer.isPremium`:
|
|
|
|
```swift
|
|
var isPremium: Bool {
|
|
switch self {
|
|
case let user as TelegramUser:
|
|
// Проверяем, является ли это текущим пользователем
|
|
if user.id.id._internalGetInt64Value() == SGLocalPremium.shared.currentAccountPeerId?.id &&
|
|
user.id.namespace._internalGetInt32Value() == SGLocalPremium.shared.currentAccountPeerId?.namespace {
|
|
// Возвращаем true если реальный Premium ИЛИ Local Premium включен
|
|
return user.flags.contains(.isPremium) || SGLocalPremium.shared.showPremiumBadge
|
|
}
|
|
return user.flags.contains(.isPremium)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Проверки:**
|
|
1. Является ли пользователь текущим аккаунтом?
|
|
2. Есть ли реальный Premium (`user.flags.contains(.isPremium)`)?
|
|
3. Включен ли Local Premium (`SGLocalPremium.shared.showPremiumBadge`)?
|
|
|
|
**Результат:**
|
|
- Значок показывается только рядом с вашим именем
|
|
- Другие пользователи не видят значок
|
|
- Значок виден только на вашем устройстве
|
|
- **Не исчезает после обновлений с сервера** (сервер возвращает `isPremium = false`, но проверка `|| SGLocalPremium.shared.showPremiumBadge` сохраняет его)
|
|
|
|
---
|
|
|
|
## 📊 Полный список измененных файлов
|
|
|
|
### Основной модуль
|
|
1. `Telegram-iOS/Swiftgram/SGLocalPremium/Sources/SGLocalPremium.swift` - Главный менеджер
|
|
|
|
### Core изменения
|
|
2. `Telegram-iOS/submodules/TelegramCore/Sources/Utils/PeerUtils.swift` - **КЛЮЧЕВОЙ** - Модификация `Peer.isPremium`
|
|
3. `Telegram-iOS/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift` - Лимиты (3 места)
|
|
4. `Telegram-iOS/submodules/TelegramCore/Sources/State/ManagedSynchronizePinnedChatsOperations.swift` - Bypass синхронизации закрепленных чатов
|
|
5. `Telegram-iOS/submodules/TelegramCore/Sources/TelegramEngine/Peers/ChatListFiltering.swift` - Bypass синхронизации папок (2 места)
|
|
6. `Telegram-iOS/submodules/TelegramCore/Sources/State/MessageReactions.swift` - Локальное сохранение тегов
|
|
7. `Telegram-iOS/submodules/TelegramCore/Sources/State/SavedMessageTags.swift` - Bypass синхронизации тегов (2 места)
|
|
8. `Telegram-iOS/submodules/TelegramCore/Sources/State/ManagedConsumePersonalMessagesActions.swift` - Bypass с маркером кэширования
|
|
9. `Telegram-iOS/submodules/TelegramCore/Sources/State/Holes.swift` - **КЛЮЧЕВОЙ** - Bypass заполнения holes для тегов
|
|
|
|
### UI изменения
|
|
10. `Telegram-iOS/submodules/TelegramUI/Sources/AccountContext.swift` - Инициализация аккаунта
|
|
11. `Telegram-iOS/submodules/TelegramUI/Sources/ChatController.swift` - Premium-проверка для тегов
|
|
12. `Telegram-iOS/submodules/TelegramUI/Sources/ChatControllerLoadDisplayNode.swift` - Bypass закрытия панели фильтрации
|
|
13. `Telegram-iOS/submodules/TelegramUI/Sources/ChatControllerContentData.swift` - Bypass `hasSearchTags` (2 места)
|
|
14. `Telegram-iOS/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift` - Premium-проверка
|
|
15. `Telegram-iOS/submodules/TelegramUI/Sources/ChatControllerOpenMessageContextMenu.swift` - Premium-проверка
|
|
16. `Telegram-iOS/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift` - Premium-проверка (4 места)
|
|
|
|
|
|
### UI компоненты (продолжение)
|
|
17. `Telegram-iOS/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleComponent.swift` - Premium Badge в заголовке чата
|
|
18. `Telegram-iOS/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift` - Premium Badge в заголовке профиля
|
|
19. `Telegram-iOS/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift` - Premium Badge в профиле
|
|
20. `Telegram-iOS/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift` - Premium Badge в элементе списка
|
|
21. `Telegram-iOS/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift` - Premium Badge в контактах
|
|
22. `Telegram-iOS/submodules/CallListUI/Sources/CallListCallItem.swift` - Premium Badge в списке звонков
|
|
23. `Telegram-iOS/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift` - Premium Badge в голосовом чате
|
|
24. `Telegram-iOS/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift` - Premium Badge в реакциях
|
|
25. `Telegram-iOS/submodules/ChatListUI/Sources/ChatListItem.swift` - Premium Badge в списке чатов (2 места)
|
|
26. `Telegram-iOS/submodules/ChatListUI/Sources/ChatListControllerNode.swift` - Передача `canReorderAllChats`
|
|
27. `Telegram-iOS/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift` - Переупорядочивание папок
|
|
|
|
### Настройки
|
|
28. `Telegram-iOS/Swiftgram/IAppsGramSettings/Sources/IAppsGramSettingsController.swift` - UI настроек
|
|
|
|
### BUILD файлы
|
|
29-40. BUILD файлы для всех модулей выше (добавлена зависимость `//Swiftgram/SGLocalPremium:SGLocalPremium`)
|
|
|
|
**Итого: 40+ файлов изменено**
|
|
|
|
---
|
|
|
|
## ⚠️ Важные предупреждения и ограничения
|
|
|
|
### 1. Клиентская природа функций
|
|
|
|
**Все функции работают только на вашем устройстве:**
|
|
- Premium Badge виден только вам
|
|
- Другие пользователи не видят ваш Premium статус
|
|
- Сервер не знает о ваших локальных изменениях
|
|
|
|
### 2. Серверная валидация
|
|
|
|
**Сервер может отклонить некоторые изменения:**
|
|
- При синхронизации с другими устройствами
|
|
- При восстановлении из резервной копии
|
|
- При переустановке приложения
|
|
|
|
**Что защищено от серверной валидации:**
|
|
- ✅ Закрепленные чаты (синхронизация отключена)
|
|
- ✅ Папки (синхронизация отключена)
|
|
- ✅ Чаты в папках (синхронизация отключена)
|
|
- ✅ Порядок папок (синхронизация отключена)
|
|
- ✅ Теги в Избранном (синхронизация отключена)
|
|
|
|
|
|
### 3. Локальное хранение
|
|
|
|
**Все данные хранятся локально:**
|
|
- Настройки в UserDefaults
|
|
- Закрепленные чаты в Postbox
|
|
- Папки в Postbox
|
|
- Теги в Postbox
|
|
|
|
**При удалении приложения:**
|
|
- ❌ Все локальные данные будут потеряны
|
|
- ❌ Настройки Local Premium будут сброшены
|
|
- ❌ Локальные теги будут удалены
|
|
|
|
### 4. Синхронизация между устройствами
|
|
|
|
**Local Premium НЕ синхронизируется между устройствами:**
|
|
- Настройки нужно включать на каждом устройстве отдельно
|
|
- Закрепленные чаты не синхронизируются (синхронизация отключена)
|
|
- Папки не синхронизируются (синхронизация отключена)
|
|
- Теги не синхронизируются (синхронизация отключена)
|
|
|
|
### 5. Совместимость с реальным Premium
|
|
|
|
**Если у вас есть реальный Premium:**
|
|
- Local Premium не конфликтует с реальным Premium
|
|
- Все функции работают как обычно
|
|
- Можно включить Local Premium для дополнительных функций
|
|
- Отключение серверной синхронизации работает независимо от реального Premium
|
|
|
|
---
|
|
|
|
## 🚀 Руководство по использованию
|
|
|
|
### Включение Local Premium
|
|
|
|
1. Откройте **Settings → IAppsGram → Local Premium**
|
|
2. Включите переключатель **"Emulate Premium"**
|
|
3. Все функции активированы!
|
|
|
|
### Проверка работы Premium Badge
|
|
|
|
1. Включите "Emulate Premium"
|
|
2. Откройте свой профиль
|
|
3. Проверьте наличие Premium значка рядом с именем
|
|
4. Откройте любой чат - значок должен быть в заголовке
|
|
5. Откройте список чатов - значок должен быть рядом с вашим именем
|
|
|
|
### Тестирование безлимитных закрепленных чатов
|
|
|
|
1. Включите "Emulate Premium"
|
|
2. Закрепите 6+ чатов (больше стандартного лимита 5)
|
|
3. Проверьте, что все чаты остаются закрепленными
|
|
4. Перезапустите приложение
|
|
5. Проверьте, что все чаты все еще закреплены
|
|
|
|
### Тестирование безлимитных папок
|
|
|
|
1. Включите "Emulate Premium"
|
|
2. Создайте 11+ папок (больше стандартного лимита 10)
|
|
3. Проверьте, что все папки созданы
|
|
4. Перезапустите приложение
|
|
5. Проверьте, что все папки все еще существуют
|
|
|
|
|
|
### Тестирование тегов в Избранном
|
|
|
|
1. Включите "Emulate Premium"
|
|
2. Откройте "Saved Messages" (Избранное)
|
|
3. Выберите любое сообщение
|
|
4. Нажмите на кнопку реакции
|
|
5. Выберите любую реакцию как тег
|
|
6. Проверьте, что тег добавлен к сообщению
|
|
7. Нажмите на тег в панели поиска
|
|
8. Проверьте, что отображаются только сообщения с этим тегом
|
|
|
|
### Тестирование переупорядочивания папок
|
|
|
|
1. Включите "Emulate Premium"
|
|
2. Создайте несколько папок
|
|
3. Зажмите любую папку и перетащите её на первую позицию
|
|
4. Проверьте, что папка переместилась
|
|
5. Попробуйте переместить папку "Все чаты" на другую позицию
|
|
6. Проверьте, что это работает
|
|
7. Перезапустите приложение
|
|
8. Проверьте, что порядок папок сохранился
|
|
|
|
---
|
|
|
|
## 🔧 Troubleshooting (Решение проблем)
|
|
|
|
### Premium Badge не отображается
|
|
|
|
**Проблема**: Premium Badge не виден в профиле или чатах.
|
|
|
|
**Решение:**
|
|
1. Проверьте, что "Emulate Premium" включен в настройках
|
|
2. Перезапустите приложение
|
|
3. Проверьте, что вы смотрите на свой профиль (не на профиль другого пользователя)
|
|
4. Проверьте, что `SGLocalPremium.shared.currentAccountPeerId` установлен правильно
|
|
|
|
### Закрепленные чаты откреплены после перезапуска
|
|
|
|
**Проблема**: Закрепленные чаты исчезают после перезапуска приложения.
|
|
|
|
**Решение:**
|
|
1. Проверьте, что "Emulate Premium" включен
|
|
2. Проверьте, что синхронизация отключена (`shouldDisableServerSync = true`)
|
|
3. Проверьте логи на наличие ошибок синхронизации
|
|
|
|
### Папки удалены после перезапуска
|
|
|
|
**Проблема**: Созданные папки исчезают после перезапуска.
|
|
|
|
**Решение:**
|
|
1. Проверьте, что "Emulate Premium" включен
|
|
2. Проверьте, что синхронизация отключена
|
|
3. Проверьте, что bypass в `ChatListFiltering.swift` работает
|
|
|
|
### Теги не сохраняются
|
|
|
|
**Проблема**: Добавленные теги исчезают или не отображаются.
|
|
|
|
**Решение:**
|
|
1. Проверьте, что "Emulate Premium" включен
|
|
2. Проверьте, что bypass в `MessageReactions.swift` работает
|
|
3. Проверьте, что bypass в `Holes.swift` работает (критически важно!)
|
|
4. Проверьте логи Postbox на наличие ошибок индексации
|
|
|
|
|
|
### Бесконечная загрузка при фильтрации по тегу
|
|
|
|
**Проблема**: При нажатии на тег в панели поиска появляется бесконечная загрузка.
|
|
|
|
**Решение:**
|
|
1. **КРИТИЧЕСКИ ВАЖНО**: Проверьте, что bypass в `Holes.swift` работает
|
|
2. Этот bypass предотвращает запросы к серверу для заполнения "holes"
|
|
3. Без него система ожидает ответ от сервера, который никогда не придет
|
|
4. Проверьте, что `case .customTag` обрабатывается правильно
|
|
|
|
### Настройки сбрасываются при переключении аккаунтов
|
|
|
|
**Проблема**: Настройки Local Premium сбрасываются при переключении между аккаунтами.
|
|
|
|
**Решение:**
|
|
1. Это нормальное поведение - настройки per-account
|
|
2. Каждый аккаунт имеет свои независимые настройки
|
|
3. Включите "Emulate Premium" для каждого аккаунта отдельно
|
|
|
|
---
|
|
|
|
## 📚 Дополнительные ресурсы
|
|
|
|
### Связанные документы
|
|
|
|
- `LOCAL_PREMIUM_GUIDE.md` - Старая версия документации (для справки)
|
|
- `LOCAL_PREMIUM_IMPLEMENTATION_PROGRESS.md` - История разработки
|
|
|
|
### Ключевые файлы для изучения
|
|
|
|
1. **SGLocalPremium.swift** - Главный менеджер, начните отсюда
|
|
2. **PeerUtils.swift** - Ключевая модификация `Peer.isPremium`
|
|
3. **UserLimitsConfiguration.swift** - Переопределение лимитов
|
|
4. **MessageReactions.swift** - Локальное сохранение тегов
|
|
5. **Holes.swift** - Критический bypass для фильтрации по тегам
|
|
|
|
### Архитектурные решения
|
|
|
|
**Почему модификация `Peer.isPremium` вместо проверок в UI?**
|
|
- ✅ Одно место изменения вместо 10+
|
|
- ✅ Автоматически работает везде
|
|
- ✅ Не исчезает после обновлений с сервера
|
|
- ✅ Проще поддерживать
|
|
- ✅ Меньше кода
|
|
|
|
**Почему одна настройка вместо отдельных переключателей?**
|
|
- ✅ Проще для пользователей
|
|
- ✅ Меньше путаницы
|
|
- ✅ Все функции работают вместе
|
|
- ✅ Проще тестировать
|
|
|
|
**Почему отключение серверной синхронизации?**
|
|
- ✅ Предотвращает откат изменений сервером
|
|
- ✅ Локальные данные остаются на устройстве
|
|
- ✅ Не нужно бороться с серверной валидацией
|
|
- ✅ Проще реализовать
|
|
|
|
---
|
|
|
|
## 🎓 Заключение
|
|
|
|
Local Premium - это комплексная функция, которая эмулирует Premium подписку Telegram локально на устройстве. Ключевые особенности реализации:
|
|
|
|
1. **Модификация `Peer.isPremium`** - главное архитектурное решение для Premium Badge
|
|
2. **Отключение серверной синхронизации** - защита от отката изменений
|
|
3. **Per-account настройки** - независимые настройки для каждого аккаунта
|
|
4. **Одна настройка для всех функций** - простой и понятный UI
|
|
|
|
Все функции работают только на стороне клиента и видны только вам. Сервер не знает о ваших локальных изменениях.
|
|
|