mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-04-23 19:36:26 +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
|
||
|
||
Все функции работают только на стороне клиента и видны только вам. Сервер не знает о ваших локальных изменениях.
|
||
|