Revert Watch App removal

This commit is contained in:
Kylmakalle 2025-07-04 14:58:10 +03:00
parent d183f786e0
commit d574500fdb
132 changed files with 10843 additions and 4 deletions

View File

@ -1451,6 +1451,7 @@ public protocol AccountContext: AnyObject {
var downloadedMediaStoreManager: DownloadedMediaStoreManager { get }
var peerChannelMemberCategoriesContextsManager: PeerChannelMemberCategoriesContextsManager { get }
var wallpaperUploadManager: WallpaperUploadManager? { get }
var watchManager: WatchManager? { get }
var inAppPurchaseManager: InAppPurchaseManager? { get }
var starsContext: StarsContext? { get }
var tonContext: StarsContext? { get }

View File

@ -0,0 +1,23 @@
import Foundation
import SwiftSignalKit
import TelegramCore
public struct WatchRunningTasks: Equatable {
public let running: Bool
public let version: Int32
public init(running: Bool, version: Int32) {
self.running = running
self.version = version
}
public static func ==(lhs: WatchRunningTasks, rhs: WatchRunningTasks) -> Bool {
return lhs.running == rhs.running && lhs.version == rhs.version
}
}
public protocol WatchManager: AnyObject {
var watchAppInstalled: Signal<Bool, NoError> { get }
var navigateToMessageRequested: Signal<EngineMessage.Id, NoError> { get }
var runningTasks: Signal<WatchRunningTasks?, NoError> { get }
}

View File

@ -895,6 +895,9 @@ private func languageSearchableItems(context: AccountContext, localizations: [Lo
}
func settingsSearchableItems(context: AccountContext, notificationExceptionsList: Signal<NotificationExceptionsList?, NoError>, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, privacySettings: Signal<AccountPrivacySettings?, NoError>, hasTwoStepAuth: Signal<Bool?, NoError>, twoStepAuthData: Signal<TwoStepVerificationAccessConfiguration?, NoError>, activeSessionsContext: Signal<ActiveSessionsContext?, NoError>, webSessionsContext: Signal<WebSessionsContext?, NoError>) -> Signal<[SettingsSearchableItem], NoError> {
let watchAppInstalled = (context.watchManager?.watchAppInstalled ?? .single(false))
|> take(1)
let canAddAccount = activeAccountsAndPeers(context: context)
|> take(1)
|> map { accountsAndPeers -> Bool in
@ -988,8 +991,8 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
}
}
return combineLatest(canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext)
|> map { canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext in
return combineLatest(watchAppInstalled, canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext)
|> map { watchAppInstalled, canAddAccount, localizations, notificationSettings, notificationExceptionsList, archivedStickerPacks, proxyServers, privacySettings, hasTwoStepAuth, twoStepAuthData, activeSessionsContext, activeWebSessionsContext in
let strings = context.sharedContext.currentPresentationData.with { $0 }.strings
var allItems: [SettingsSearchableItem] = []
@ -1040,6 +1043,13 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList
let storiesItems = storiesSearchableItems(context: context)
allItems.append(contentsOf: storiesItems)
if watchAppInstalled {
let watch = SettingsSearchableItem(id: .watch(0), title: strings.Settings_AppleWatch, alternate: synonyms(strings.SettingsSearch_Synonyms_Watch), icon: .watch, breadcrumbs: [], present: { context, _, present in
present(.push, watchSettingsController(context: context))
})
allItems.append(watch)
}
if let hasTwoStepAuth = hasTwoStepAuth, hasTwoStepAuth {
let passport = SettingsSearchableItem(id: .passport(0), title: strings.Settings_Passport, alternate: synonyms(strings.SettingsSearch_Synonyms_Passport), icon: .passport, breadcrumbs: [], present: { context, _, present in
present(.modal, SecureIdAuthController(context: context, mode: .list))

View File

@ -96,6 +96,7 @@ swift_library(
"//submodules/TelegramVoip:TelegramVoip",
"//submodules/DeviceAccess:DeviceAccess",
"//submodules/Utils/DeviceModel",
"//submodules/WatchCommon/Host:WatchCommon",
"//submodules/BuildConfig:BuildConfig",
"//submodules/BuildConfigExtra:BuildConfigExtra",
"//submodules/rlottie:RLottieBinding",
@ -217,6 +218,8 @@ swift_library(
"//submodules/RaiseToListen:RaiseToListen",
"//submodules/OpusBinding:OpusBinding",
"//third-party/opus:opus",
"//submodules/WatchBridgeAudio:WatchBridgeAudio",
"//submodules/WatchBridge:WatchBridge",
"//submodules/ShareItems:ShareItems",
"//submodules/ShareItems/Impl:ShareItemsImpl",
"//submodules/SettingsUI:SettingsUI",

View File

@ -914,6 +914,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
combineLatest(notificationExceptions, notificationsAuthorizationStatus.get(), notificationsWarningSuppressed.get()),
combineLatest(context.account.viewTracker.featuredStickerPacks(), archivedStickerPacks),
hasPassport,
(context.watchManager?.watchAppInstalled ?? .single(false)),
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]),
context.engine.notices.getServerProvidedSuggestions(),
context.engine.data.get(
@ -932,7 +933,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
starsState,
tonState
)
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel, starsState, tonState -> PeerInfoScreenData in
|> map { peerView, accountsAndPeers, accountSessions, privacySettings, sharedPreferences, notifications, stickerPacks, hasPassport, hasWatchApp, accountPreferences, suggestions, limits, hasPassword, isPowerSavingEnabled, hasStories, bots, personalChannel, starsState, tonState -> PeerInfoScreenData in
let (notificationExceptions, notificationsAuthorizationStatus, notificationsWarningSuppressed) = notifications
let (featuredStickerPacks, archivedStickerPacks) = stickerPacks
@ -979,7 +980,7 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id,
userLimits: peer?.isPremium == true ? limits.1 : limits.0,
bots: bots,
hasPassport: hasPassport,
hasWatchApp: false,
hasWatchApp: hasWatchApp,
enableQRLogin: enableQRLogin
)

View File

@ -159,6 +159,8 @@ public final class AccountContextImpl: AccountContext {
return self._countriesConfiguration.get()
}
public var watchManager: WatchManager?
private var storedPassword: (String, CFAbsoluteTime, SwiftSignalKit.Timer)?
private var limitsConfigurationDisposable: Disposable?
private var contentSettingsDisposable: Disposable?

View File

@ -31,6 +31,7 @@ import OverlayStatusController
import UndoUI
import LegacyUI
import PassportUI
import WatchBridge
import SettingsUI
import AppBundle
import UrlHandling

View File

@ -22,6 +22,7 @@ import TelegramPermissionsUI
import PasscodeUI
import ImageBlur
import FastBlur
import WatchBridge
import SettingsUI
import AppLock
import AccountUtils
@ -813,6 +814,54 @@ final class AuthorizedApplicationContext {
}
})
let _ = (watchManagerArguments
|> deliverOnMainQueue).start(next: { [weak self] arguments in
guard let strongSelf = self else {
return
}
let watchManager = WatchManagerImpl(arguments: arguments)
strongSelf.context.watchManager = watchManager
strongSelf.watchNavigateToMessageDisposable.set((strongSelf.context.sharedContext.applicationBindings.applicationInForeground |> mapToSignal({ applicationInForeground -> Signal<(Bool, MessageId), NoError> in
return watchManager.navigateToMessageRequested
|> map { messageId in
return (applicationInForeground, messageId)
}
|> deliverOnMainQueue
})).start(next: { [weak self] applicationInForeground, messageId in
if let strongSelf = self {
if applicationInForeground {
var chatIsVisible = false
if let controller = strongSelf.rootController.viewControllers.last as? ChatControllerImpl, case .peer(messageId.peerId) = controller.chatLocation {
chatIsVisible = true
}
let navigateToMessage = {
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: messageId.peerId))
|> deliverOnMainQueue).start(next: { peer in
guard let peer = peer else {
return
}
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: strongSelf.rootController, context: strongSelf.context, chatLocation: .peer(peer), subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false)))
})
}
if chatIsVisible {
navigateToMessage()
} else {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let controller = textAlertController(context: strongSelf.context, title: presentationData.strings.WatchRemote_AlertTitle, text: presentationData.strings.WatchRemote_AlertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.WatchRemote_AlertOpen, action:navigateToMessage)])
(strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
}
} else {
//strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId)
}
}
}))
})
self.rootController.setForceInCallStatusBar((self.context.sharedContext as! SharedAccountContextImpl).currentCallStatusBarNode)
if let groupCallController = self.context.sharedContext.currentGroupCallController as? VoiceChatController {
if let overlayController = groupCallController.currentOverlayController {

View File

@ -0,0 +1,25 @@
import Foundation
import SwiftSignalKit
import TelegramCore
import AccountContext
import WatchBridge
public final class WatchManagerImpl: WatchManager {
private let arguments: WatchManagerArguments?
public init(arguments: WatchManagerArguments?) {
self.arguments = arguments
}
public var watchAppInstalled: Signal<Bool, NoError> {
return self.arguments?.appInstalled ?? .single(false)
}
public var navigateToMessageRequested: Signal<EngineMessage.Id, NoError> {
return self.arguments?.navigateToMessageRequested ?? .never()
}
public var runningTasks: Signal<WatchRunningTasks?, NoError> {
return self.arguments?.runningTasks ?? .single(nil)
}
}

View File

@ -0,0 +1,33 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "WatchBridge",
module_name = "WatchBridge",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/SSignalKit/SSignalKit:SSignalKit",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/WatchCommon/Host:WatchCommon",
"//submodules/WatchBridgeAudio:WatchBridgeAudio",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/AccountContext:AccountContext",
"//submodules/AvatarNode:AvatarNode",
"//submodules/StickerResources:StickerResources",
"//submodules/PhotoResources:PhotoResources",
"//submodules/LegacyComponents:LegacyComponents",
"//submodules/LegacyUI:LegacyUI",
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
"//submodules/WatchBridge/Impl:WatchBridgeImpl",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,27 @@
objc_library(
name = "WatchBridgeImpl",
enable_modules = True,
module_name = "WatchBridgeImpl",
srcs = glob([
"Sources/**/*.m",
"Sources/**/*.h",
], allow_empty=True),
hdrs = glob([
"PublicHeaders/**/*.h",
]),
includes = [
"PublicHeaders",
],
deps = [
"//submodules/LegacyComponents:LegacyComponents",
"//submodules/WatchCommon/Host:WatchCommon",
],
sdk_frameworks = [
"Foundation",
"WatchConnectivity",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,29 @@
#import <SSignalKit/SSignalKit.h>
#import <UIKit/UIKit.h>
@class TGBridgeSubscription;
@interface TGBridgeServer : NSObject
@property (nonatomic, readonly) NSURL * _Nullable temporaryFilesURL;
@property (nonatomic, readonly) bool isRunning;
- (instancetype _Nonnull)initWithHandler:(SSignal * _Nullable (^ _Nonnull)(TGBridgeSubscription * _Nullable))handler fileHandler:(void (^ _Nonnull)(NSString * _Nullable, NSDictionary * _Nullable))fileHandler dispatchOnQueue:(void (^ _Nonnull)(void (^ _Nonnull)(void)))dispatchOnQueue logFunction:(void (^ _Nonnull)(NSString * _Nullable))logFunction allowBackgroundTimeExtension:(void (^ _Nonnull)())allowBackgroundTimeExtension;
- (void)startRunning;
- (SSignal * _Nonnull)watchAppInstalledSignal;
- (SSignal * _Nonnull)runningRequestsSignal;
- (void)setAuthorized:(bool)authorized userId:(int64_t)userId;
- (void)setMicAccessAllowed:(bool)allowed;
- (void)setStartupData:(NSDictionary * _Nullable)data;
- (void)pushContext;
- (void)sendFileWithURL:(NSURL * _Nonnull)url metadata:(NSDictionary * _Nullable)metadata asMessageData:(bool)asMessageData;
- (void)sendFileWithData:(NSData * _Nonnull)data metadata:(NSDictionary * _Nullable)metadata errorHandler:(void (^ _Nullable)(void))errorHandler;
- (NSInteger)wakeupNetwork;
- (void)suspendNetworkIfReady:(NSInteger)token;
@end

View File

@ -0,0 +1,3 @@
#import <UIKit/UIKit.h>
#import <WatchBridgeImpl/TGBridgeServer.h>

View File

@ -0,0 +1,770 @@
#import <WatchBridgeImpl/TGBridgeServer.h>
#import <LegacyComponents/LegacyComponents.h>
#import <WatchConnectivity/WatchConnectivity.h>
#import <os/lock.h>
#import <WatchCommon/WatchCommon.h>
@interface TGBridgeSignalManager : NSObject
- (bool)startSignalForKey:(NSString *)key producer:(SSignal *(^)())producer;
- (void)haltSignalForKey:(NSString *)key;
- (void)haltAllSignals;
@end
@interface TGBridgeServer () <WCSessionDelegate>
{
SSignal *(^_handler)(TGBridgeSubscription *);
void (^_fileHandler)(NSString *, NSDictionary *);
void (^_logFunction)(NSString *);
void (^_dispatch)(void (^)(void));
bool _pendingStart;
bool _processingNotification;
int32_t _sessionId;
volatile int32_t _tasksVersion;
TGBridgeContext *_activeContext;
TGBridgeSignalManager *_signalManager;
os_unfair_lock _incomingQueueLock;
NSMutableArray *_incomingMessageQueue;
bool _requestSubscriptionList;
NSArray *_initialSubscriptionList;
os_unfair_lock _outgoingQueueLock;
NSMutableArray *_outgoingMessageQueue;
os_unfair_lock _replyHandlerMapLock;
NSMutableDictionary *_replyHandlerMap;
SPipe *_appInstalled;
NSMutableDictionary *_runningTasks;
SVariable *_hasRunningTasks;
void (^_allowBackgroundTimeExtension)();
}
@property (nonatomic, readonly) WCSession *session;
@end
@implementation TGBridgeServer
- (instancetype)initWithHandler:(SSignal *(^)(TGBridgeSubscription *))handler fileHandler:(void (^)(NSString *, NSDictionary *))fileHandler dispatchOnQueue:(void (^)(void (^)(void)))dispatchOnQueue logFunction:(void (^)(NSString *))logFunction allowBackgroundTimeExtension:(void (^)())allowBackgroundTimeExtension
{
self = [super init];
if (self != nil)
{
_handler = [handler copy];
_fileHandler = [fileHandler copy];
_dispatch = [dispatchOnQueue copy];
_logFunction = [logFunction copy];
_allowBackgroundTimeExtension = [allowBackgroundTimeExtension copy];
_runningTasks = [[NSMutableDictionary alloc] init];
_hasRunningTasks = [[SVariable alloc] init];
[_hasRunningTasks set:[SSignal single:@false]];
_signalManager = [[TGBridgeSignalManager alloc] init];
_incomingMessageQueue = [[NSMutableArray alloc] init];
self.session.delegate = self;
[self.session activateSession];
_replyHandlerMap = [[NSMutableDictionary alloc] init];
_appInstalled = [[SPipe alloc] init];
_activeContext = [[TGBridgeContext alloc] initWithDictionary:[self.session applicationContext]];
}
return self;
}
- (void)log:(NSString *)message
{
_logFunction(message);
}
- (void)dispatch:(void (^)(void))action
{
_dispatch(action);
}
- (void)startRunning
{
if (self.isRunning)
return;
os_unfair_lock_lock(&_incomingQueueLock);
_isRunning = true;
for (id message in _incomingMessageQueue)
[self handleMessage:message replyHandler:nil finishTask:nil completion:nil];
[_incomingMessageQueue removeAllObjects];
os_unfair_lock_unlock(&_incomingQueueLock);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dispatch:^{
_appInstalled.sink(@(self.session.isWatchAppInstalled));
}];
});
}
- (NSURL *)temporaryFilesURL
{
return self.session.watchDirectoryURL;
}
- (SSignal *)watchAppInstalledSignal
{
return [[SSignal single:@(self.session.watchAppInstalled)] then:_appInstalled.signalProducer()];
}
- (SSignal *)runningRequestsSignal
{
return _hasRunningTasks.signal;
}
#pragma mark -
- (void)setAuthorized:(bool)authorized userId:(int64_t)userId
{
_activeContext = [_activeContext updatedWithAuthorized:authorized peerId:userId];
}
- (void)setMicAccessAllowed:(bool)allowed
{
_activeContext = [_activeContext updatedWithMicAccessAllowed:allowed];
}
- (void)setStartupData:(NSDictionary *)data
{
_activeContext = [_activeContext updatedWithPreheatData:data];
}
- (void)pushContext
{
NSError *error;
[self.session updateApplicationContext:[_activeContext dictionary] error:&error];
//if (error != nil)
//TGLog(@"[BridgeServer][ERROR] Failed to push active application context: %@", error.localizedDescription);
}
#pragma mark -
- (void)handleMessageData:(NSData *)messageData task:(id<SDisposable>)task replyHandler:(void (^)(NSData *))replyHandler completion:(void (^)(void))completion
{
if (_allowBackgroundTimeExtension) {
_allowBackgroundTimeExtension();
}
__block id<SDisposable> runningTask = task;
void (^finishTask)(NSTimeInterval) = ^(NSTimeInterval delay)
{
if (runningTask == nil)
return;
void (^block)(void) = ^
{
[self dispatch:^{
[runningTask dispose];
//TGLog(@"[BridgeServer]: ended taskid: %d", runningTask);
runningTask = nil;
}];
};
if (delay > DBL_EPSILON)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((delay) * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
else
block();
};
id message = [NSKeyedUnarchiver unarchiveObjectWithData:messageData];
os_unfair_lock_lock(&_incomingQueueLock);
if (!self.isRunning)
{
[_incomingMessageQueue addObject:message];
if (replyHandler != nil)
replyHandler([NSData data]);
finishTask(4.0);
os_unfair_lock_unlock(&_incomingQueueLock);
return;
}
os_unfair_lock_unlock(&_incomingQueueLock);
[self handleMessage:message replyHandler:replyHandler finishTask:finishTask completion:completion];
}
- (void)handleMessage:(id)message replyHandler:(void (^)(NSData *))replyHandler finishTask:(void (^)(NSTimeInterval))finishTask completion:(void (^)(void))completion
{
if ([message isKindOfClass:[TGBridgeSubscription class]])
{
TGBridgeSubscription *subcription = (TGBridgeSubscription *)message;
[self _createSubscription:subcription replyHandler:replyHandler finishTask:finishTask completion:completion];
//TGLog(@"[BridgeServer] Create subscription: %@", subcription);
}
else if ([message isKindOfClass:[TGBridgeDisposal class]])
{
TGBridgeDisposal *disposal = (TGBridgeDisposal *)message;
[_signalManager haltSignalForKey:[NSString stringWithFormat:@"%lld", disposal.identifier]];
if (replyHandler != nil)
replyHandler([NSData data]);
if (completion != nil)
completion();
//TGLog(@"[BridgeServer] Dispose subscription %lld", disposal.identifier);
if (finishTask != nil)
finishTask(0);
}
else if ([message isKindOfClass:[TGBridgeSubscriptionList class]])
{
TGBridgeSubscriptionList *list = (TGBridgeSubscriptionList *)message;
for (TGBridgeSubscription *subscription in list.subscriptions)
[self _createSubscription:subscription replyHandler:nil finishTask:nil completion:nil];
//TGLog(@"[BridgeServer] Received subscription list, applying");
if (replyHandler != nil)
replyHandler([NSData data]);
if (finishTask != nil)
finishTask(4.0);
if (completion != nil)
completion();
}
else if ([message isKindOfClass:[TGBridgePing class]])
{
TGBridgePing *ping = (TGBridgePing *)message;
if (_sessionId != ping.sessionId)
{
//TGLog(@"[BridgeServer] Session id mismatch");
if (_sessionId != 0)
{
//TGLog(@"[BridgeServer] Halt all active subscriptions");
[_signalManager haltAllSignals];
os_unfair_lock_lock(&_outgoingQueueLock);
[_outgoingMessageQueue removeAllObjects];
os_unfair_lock_unlock(&_outgoingQueueLock);
}
_sessionId = ping.sessionId;
if (self.session.isReachable)
[self _requestSubscriptionList];
else
_requestSubscriptionList = true;
}
else
{
if (_requestSubscriptionList)
{
_requestSubscriptionList = false;
[self _requestSubscriptionList];
}
[self _sendQueuedResponses];
if (replyHandler != nil)
replyHandler([NSData data]);
}
if (completion != nil)
completion();
if (finishTask != nil)
finishTask(4.0);
}
else
{
if (completion != nil)
completion();
if (finishTask != nil)
finishTask(1.0);
}
}
- (void)_createSubscription:(TGBridgeSubscription *)subscription replyHandler:(void (^)(NSData *))replyHandler finishTask:(void (^)(NSTimeInterval))finishTask completion:(void (^)(void))completion
{
SSignal *subscriptionHandler = _handler(subscription);
if (replyHandler != nil)
{
os_unfair_lock_lock(&_replyHandlerMapLock);
_replyHandlerMap[@(subscription.identifier)] = replyHandler;
os_unfair_lock_unlock(&_replyHandlerMapLock);
}
if (subscriptionHandler != nil)
{
[_signalManager startSignalForKey:[NSString stringWithFormat:@"%lld", subscription.identifier] producer:^SSignal *
{
STimer *timer = [[STimer alloc] initWithTimeout:2.0 repeat:false completion:^(__unused STimer *timer)
{
os_unfair_lock_lock(&_replyHandlerMapLock);
void (^reply)(NSData *) = _replyHandlerMap[@(subscription.identifier)];
if (reply == nil)
{
os_unfair_lock_unlock(&_replyHandlerMapLock);
if (finishTask != nil)
finishTask(2.0);
return;
}
reply([NSData data]);
[_replyHandlerMap removeObjectForKey:@(subscription.identifier)];
os_unfair_lock_unlock(&_replyHandlerMapLock);
if (finishTask != nil)
finishTask(4.0);
//TGLog(@"[BridgeServer]: subscription 0x%x hit 2.0s timeout, releasing reply handler", subscription.identifier);
} queue:[SQueue mainQueue]];
[timer start];
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(__unused SSubscriber *subscriber)
{
return [subscriptionHandler startWithNext:^(id next)
{
[timer invalidate];
[self _responseToSubscription:subscription message:next type:TGBridgeResponseTypeNext completion:completion];
if (finishTask != nil)
finishTask(4.0);
} error:^(id error)
{
[timer invalidate];
[self _responseToSubscription:subscription message:error type:TGBridgeResponseTypeFailed completion:completion];
if (finishTask != nil)
finishTask(4.0);
} completed:^
{
[timer invalidate];
[self _responseToSubscription:subscription message:nil type:TGBridgeResponseTypeCompleted completion:completion];
if (finishTask != nil)
finishTask(4.0);
}];
}];
}];
}
else
{
os_unfair_lock_lock(&_replyHandlerMapLock);
void (^reply)(NSData *) = _replyHandlerMap[@(subscription.identifier)];
if (reply == nil)
{
os_unfair_lock_unlock(&_replyHandlerMapLock);
if (finishTask != nil)
finishTask(2.0);
return;
}
reply([NSData data]);
[_replyHandlerMap removeObjectForKey:@(subscription.identifier)];
os_unfair_lock_unlock(&_replyHandlerMapLock);
if (finishTask != nil)
finishTask(2.0);
}
}
- (void)_responseToSubscription:(TGBridgeSubscription *)subscription message:(id<NSCoding>)message type:(TGBridgeResponseType)type completion:(void (^)(void))completion
{
TGBridgeResponse *response = nil;
switch (type)
{
case TGBridgeResponseTypeNext:
response = [TGBridgeResponse single:message forSubscription:subscription];
break;
case TGBridgeResponseTypeFailed:
response = [TGBridgeResponse fail:message forSubscription:subscription];
break;
case TGBridgeResponseTypeCompleted:
response = [TGBridgeResponse completeForSubscription:subscription];
break;
default:
break;
}
os_unfair_lock_lock(&_replyHandlerMapLock);
void (^reply)(NSData *) = _replyHandlerMap[@(subscription.identifier)];
if (reply != nil)
[_replyHandlerMap removeObjectForKey:@(subscription.identifier)];
os_unfair_lock_unlock(&_replyHandlerMapLock);
if (_processingNotification)
{
[self _enqueueResponse:response forSubscription:subscription];
if (completion != nil)
completion();
return;
}
NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:response];
if (reply != nil && messageData.length < 64000)
{
reply(messageData);
if (completion != nil)
completion();
}
else
{
if (reply != nil)
reply([NSData data]);
if (self.session.isReachable)
{
[self.session sendMessageData:messageData replyHandler:nil errorHandler:^(NSError *error)
{
//if (error != nil)
// TGLog(@"[BridgeServer]: send response for subscription %lld failed with error %@", subscription.identifier, error);
}];
}
else
{
//TGLog(@"[BridgeServer]: client out of reach, queueing response for subscription %lld", subscription.identifier);
[self _enqueueResponse:response forSubscription:subscription];
}
if (completion != nil)
completion();
}
}
- (void)_enqueueResponse:(TGBridgeResponse *)response forSubscription:(TGBridgeSubscription *)subscription
{
os_unfair_lock_lock(&_outgoingQueueLock);
NSMutableArray *updatedResponses = (_outgoingMessageQueue != nil) ? [_outgoingMessageQueue mutableCopy] : [[NSMutableArray alloc] init];
if (subscription.dropPreviouslyQueued)
{
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
[updatedResponses enumerateObjectsUsingBlock:^(TGBridgeResponse *queuedResponse, NSUInteger index, __unused BOOL *stop)
{
if (queuedResponse.subscriptionIdentifier == subscription.identifier)
[indexSet addIndex:index];
}];
[updatedResponses removeObjectsAtIndexes:indexSet];
}
[updatedResponses addObject:response];
_outgoingMessageQueue = updatedResponses;
os_unfair_lock_unlock(&_outgoingQueueLock);
}
- (void)_sendQueuedResponses
{
if (_processingNotification)
return;
os_unfair_lock_lock(&_outgoingQueueLock);
if (_outgoingMessageQueue.count > 0)
{
//TGLog(@"[BridgeServer] Sending queued responses");
for (TGBridgeResponse *response in _outgoingMessageQueue)
{
NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:response];
[self.session sendMessageData:messageData replyHandler:nil errorHandler:nil];
}
[_outgoingMessageQueue removeAllObjects];
}
os_unfair_lock_unlock(&_outgoingQueueLock);
}
- (void)_requestSubscriptionList
{
TGBridgeSubscriptionListRequest *request = [[TGBridgeSubscriptionListRequest alloc] initWithSessionId:_sessionId];
NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:request];
[self.session sendMessageData:messageData replyHandler:nil errorHandler:nil];
}
- (void)sendFileWithURL:(NSURL *)url metadata:(NSDictionary *)metadata asMessageData:(bool)asMessageData
{
//TGLog(@"[BridgeServer] Sent file with metadata %@", metadata);
if (asMessageData && self.session.isReachable) {
NSData *data = [NSData dataWithContentsOfURL:url];
[self sendFileWithData:data metadata:metadata errorHandler:^{
[self.session transferFile:url metadata:metadata];
}];
} else {
[self.session transferFile:url metadata:metadata];
}
}
- (void)sendFileWithData:(NSData *)data metadata:(NSDictionary *)metadata errorHandler:(void (^)(void))errorHandler
{
TGBridgeFile *file = [[TGBridgeFile alloc] initWithData:data metadata:metadata];
NSData *messageData = [NSKeyedArchiver archivedDataWithRootObject:file];
[self.session sendMessageData:messageData replyHandler:nil errorHandler:^(NSError *error) {
if (errorHandler != nil)
errorHandler();
}];
}
#pragma mark - Tasks
- (id<SDisposable>)beginTask
{
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
NSNumber *taskId = @(randomId);
_runningTasks[taskId] = @true;
[_hasRunningTasks set:[SSignal single:@{@"version": @(_tasksVersion++), @"running": @true}]];
SBlockDisposable *taskDisposable = [[SBlockDisposable alloc] initWithBlock:^{
[_runningTasks removeObjectForKey:taskId];
[_hasRunningTasks set:[SSignal single:@{@"version": @(_tasksVersion++), @"running": @(_runningTasks.count > 0)}]];
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((4.0) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dispatch:^{
[taskDisposable dispose];
}];
});
return taskDisposable;
}
#pragma mark - Session Delegate
- (void)handleReceivedData:(NSData *)messageData replyHandler:(void (^)(NSData *))replyHandler
{
if (messageData.length == 0)
{
if (replyHandler != nil)
replyHandler([NSData data]);
return;
}
// __block UIBackgroundTaskIdentifier backgroundTask;
// backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
// {
// if (replyHandler != nil)
// replyHandler([NSData data]);
// [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
// }];
//
[self handleMessageData:messageData task:[self beginTask] replyHandler:replyHandler completion:^{}];
}
- (void)session:(WCSession *)__unused session didReceiveMessageData:(NSData *)messageData
{
[self dispatch:^{
[self handleReceivedData:messageData replyHandler:nil];
}];
}
- (void)session:(WCSession *)__unused session didReceiveMessageData:(NSData *)messageData replyHandler:(void (^)(NSData *))replyHandler
{
[self dispatch:^{
[self handleReceivedData:messageData replyHandler:replyHandler];
}];
}
- (void)session:(WCSession *)__unused session didReceiveFile:(WCSessionFile *)file
{
NSDictionary *metadata = file.metadata;
if (metadata == nil || ![metadata[TGBridgeIncomingFileTypeKey] isEqualToString:TGBridgeIncomingFileTypeAudio])
return;
NSError *error;
NSURL *tempURL = [NSURL URLWithString:file.fileURL.lastPathComponent relativeToURL:self.temporaryFilesURL];
[[NSFileManager defaultManager] createDirectoryAtPath:self.temporaryFilesURL.path withIntermediateDirectories:true attributes:nil error:&error];
[[NSFileManager defaultManager] moveItemAtURL:file.fileURL toURL:tempURL error:&error];
[self dispatch:^{
_fileHandler(tempURL.path, file.metadata);
}];
}
- (void)session:(WCSession *)__unused session didFinishFileTransfer:(WCSessionFileTransfer *)__unused fileTransfer error:(NSError *)__unused error
{
}
- (void)session:(nonnull WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error {
}
- (void)sessionDidBecomeInactive:(nonnull WCSession *)session {
}
- (void)sessionDidDeactivate:(nonnull WCSession *)session {
}
- (void)sessionWatchStateDidChange:(WCSession *)session
{
[self dispatch:^{
if (session.isWatchAppInstalled)
[self pushContext];
_appInstalled.sink(@(session.isWatchAppInstalled));
}];
}
- (void)sessionReachabilityDidChange:(WCSession *)session
{
NSLog(@"[TGBridgeServer] Reachability changed: %d", session.isReachable);
}
#pragma mark -
- (NSInteger)wakeupNetwork
{
return 0;
}
- (void)suspendNetworkIfReady:(NSInteger)token
{
}
#pragma mark -
- (WCSession *)session
{
return [WCSession defaultSession];
}
@end
@interface TGBridgeSignalManager()
{
os_unfair_lock _lock;
NSMutableDictionary *_disposables;
}
@end
@implementation TGBridgeSignalManager
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_disposables = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc
{
NSArray *disposables = nil;
os_unfair_lock_lock(&_lock);
disposables = [_disposables allValues];
os_unfair_lock_unlock(&_lock);
for (id<SDisposable> disposable in disposables)
{
[disposable dispose];
}
}
- (bool)startSignalForKey:(NSString *)key producer:(SSignal *(^)())producer
{
if (key == nil)
return false;
bool produce = false;
os_unfair_lock_lock(&_lock);
if (_disposables[key] == nil)
{
_disposables[key] = [[SMetaDisposable alloc] init];
produce = true;
}
os_unfair_lock_unlock(&_lock);
if (produce)
{
__weak TGBridgeSignalManager *weakSelf = self;
id<SDisposable> disposable = [producer() startWithNext:nil error:^(__unused id error)
{
__strong TGBridgeSignalManager *strongSelf = weakSelf;
if (strongSelf != nil)
{
os_unfair_lock_lock(&strongSelf->_lock);
[strongSelf->_disposables removeObjectForKey:key];
os_unfair_lock_unlock(&strongSelf->_lock);
}
} completed:^
{
__strong TGBridgeSignalManager *strongSelf = weakSelf;
if (strongSelf != nil)
{
os_unfair_lock_lock(&strongSelf->_lock);
[strongSelf->_disposables removeObjectForKey:key];
os_unfair_lock_unlock(&strongSelf->_lock);
}
}];
os_unfair_lock_lock(&_lock);
[(SMetaDisposable *)_disposables[key] setDisposable:disposable];
os_unfair_lock_unlock(&_lock);
}
return produce;
}
- (void)haltSignalForKey:(NSString *)key
{
if (key == nil)
return;
os_unfair_lock_lock(&_lock);
if (_disposables[key] != nil)
{
[_disposables[key] dispose];
[_disposables removeObjectForKey:key];
}
os_unfair_lock_unlock(&_lock);
}
- (void)haltAllSignals
{
os_unfair_lock_lock(&_lock);
for (NSObject <SDisposable> *disposable in _disposables.allValues)
[disposable dispose];
[_disposables removeAllObjects];
os_unfair_lock_unlock(&_lock);
}
@end

View File

@ -0,0 +1,219 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import WatchCommon
import SSignalKit
import TelegramUIPreferences
import AccountContext
import WatchBridgeImpl
public final class WatchCommunicationManagerContext {
public let context: AccountContext
public init(context: AccountContext) {
self.context = context
}
}
public final class WatchManagerArguments {
public let appInstalled: Signal<Bool, NoError>
public let navigateToMessageRequested: Signal<MessageId, NoError>
public let runningTasks: Signal<WatchRunningTasks?, NoError>
public init(appInstalled: Signal<Bool, NoError>, navigateToMessageRequested: Signal<MessageId, NoError>, runningTasks: Signal<WatchRunningTasks?, NoError>) {
self.appInstalled = appInstalled
self.navigateToMessageRequested = navigateToMessageRequested
self.runningTasks = runningTasks
}
}
public final class WatchCommunicationManager {
private let queue: Queue
private let allowBackgroundTimeExtension: (Double) -> Void
private var server: TGBridgeServer!
private let contextDisposable = MetaDisposable()
private let presetsDisposable = MetaDisposable()
let accountContext = Promise<AccountContext?>(nil)
private let presets = Promise<WatchPresetSettings?>(nil)
private let navigateToMessagePipe = ValuePipe<MessageId>()
public init(queue: Queue, context: Signal<WatchCommunicationManagerContext?, NoError>, allowBackgroundTimeExtension: @escaping (Double) -> Void) {
self.queue = queue
self.allowBackgroundTimeExtension = allowBackgroundTimeExtension
let handlers = allWatchRequestHandlers.reduce([String : AnyClass]()) { (map, handler) -> [String : AnyClass] in
var map = map
if let handler = handler as? WatchRequestHandler.Type {
for case let subscription as TGBridgeSubscription.Type in handler.handledSubscriptions {
if let name = subscription.subscriptionName() {
map[name] = handler
}
}
}
return map
}
self.server = TGBridgeServer(handler: { [weak self] subscription -> SSignal? in
guard let strongSelf = self, let subscription = subscription, let handler = handlers[subscription.name] as? WatchRequestHandler.Type else {
return nil
}
return handler.handle(subscription: subscription, manager: strongSelf)
}, fileHandler: { [weak self] path, metadata in
guard let strongSelf = self, let path = path, let metadata = metadata as? [String : Any] else {
return
}
if metadata[TGBridgeIncomingFileTypeKey] as? String == TGBridgeIncomingFileTypeAudio {
let _ = WatchAudioHandler.handleFile(path: path, metadata: metadata, manager: strongSelf).start()
}
}, dispatchOnQueue: { [weak self] block in
if let strongSelf = self {
strongSelf.queue.justDispatch(block)
}
}, logFunction: { value in
if let value = value {
Logger.shared.log("WatchBridge", value)
}
}, allowBackgroundTimeExtension: {
allowBackgroundTimeExtension(4.0)
})
self.server.startRunning()
self.contextDisposable.set((combineLatest(self.watchAppInstalled, context |> deliverOn(self.queue))).start(next: { [weak self] appInstalled, appContext in
guard let strongSelf = self, appInstalled else {
return
}
if let context = appContext {
strongSelf.accountContext.set(.single(context.context))
strongSelf.server.setAuthorized(true, userId: context.context.account.peerId.id._internalGetInt64Value())
strongSelf.server.setMicAccessAllowed(false)
strongSelf.server.pushContext()
strongSelf.server.setMicAccessAllowed(true)
strongSelf.server.pushContext()
strongSelf.presets.set(context.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.watchPresetSettings])
|> map({ sharedData -> WatchPresetSettings in
return sharedData.entries[ApplicationSpecificSharedDataKeys.watchPresetSettings]?.get(WatchPresetSettings.self) ?? WatchPresetSettings.defaultSettings
}))
} else {
strongSelf.accountContext.set(.single(nil))
strongSelf.server.setAuthorized(false, userId: 0)
strongSelf.server.pushContext()
strongSelf.presets.set(.single(nil))
}
}))
self.presetsDisposable.set((combineLatest(self.watchAppInstalled, self.presets.get() |> distinctUntilChanged |> deliverOn(self.queue), context |> deliverOn(self.queue))).start(next: { [weak self] appInstalled, presets, appContext in
guard let strongSelf = self, let presets = presets, let context = appContext, appInstalled, let tempPath = strongSelf.watchTemporaryStorePath else {
return
}
let presentationData = context.context.sharedContext.currentPresentationData.with { $0 }
let defaultSuggestions: [String : String] = [
"OK": presentationData.strings.Watch_Suggestion_OK,
"Thanks": presentationData.strings.Watch_Suggestion_Thanks,
"WhatsUp": presentationData.strings.Watch_Suggestion_WhatsUp,
"TalkLater": presentationData.strings.Watch_Suggestion_TalkLater,
"CantTalk": presentationData.strings.Watch_Suggestion_CantTalk,
"HoldOn": presentationData.strings.Watch_Suggestion_HoldOn,
"BRB": presentationData.strings.Watch_Suggestion_BRB,
"OnMyWay": presentationData.strings.Watch_Suggestion_OnMyWay
]
var suggestions: [String : String] = [:]
for (key, defaultValue) in defaultSuggestions {
suggestions[key] = presets.customPresets[key] ?? defaultValue
}
let fileManager = FileManager.default
let presetsFileUrl = URL(fileURLWithPath: tempPath + "/presets.dat")
if fileManager.fileExists(atPath: presetsFileUrl.path) {
try? fileManager.removeItem(atPath: presetsFileUrl.path)
}
let data = try? NSKeyedArchiver.archivedData(withRootObject: suggestions, requiringSecureCoding: false)
try? data?.write(to: presetsFileUrl)
let _ = strongSelf.sendFile(url: presetsFileUrl, metadata: [TGBridgeIncomingFileIdentifierKey: "presets"]).start()
}))
}
deinit {
self.contextDisposable.dispose()
self.presetsDisposable.dispose()
}
public var arguments: WatchManagerArguments {
return WatchManagerArguments(appInstalled: self.watchAppInstalled, navigateToMessageRequested: self.navigateToMessagePipe.signal(), runningTasks: self.runningTasks)
}
public func requestNavigateToMessage(messageId: MessageId) {
self.navigateToMessagePipe.putNext(messageId)
}
private var watchAppInstalled: Signal<Bool, NoError> {
return Signal { subscriber in
let disposable = self.server.watchAppInstalledSignal().start(next: { value in
if let value = value as? NSNumber {
subscriber.putNext(value.boolValue)
}
})
return ActionDisposable {
disposable?.dispose()
}
} |> deliverOn(self.queue)
}
private var runningTasks: Signal<WatchRunningTasks?, NoError> {
return Signal { subscriber in
let disposable = self.server.runningRequestsSignal().start(next: { value in
if let value = value as? Dictionary<String, Any> {
if let running = value["running"] as? Bool, let version = value["version"] as? Int32 {
subscriber.putNext(WatchRunningTasks(running: running, version: version))
}
}
})
return ActionDisposable {
disposable?.dispose()
}
} |> deliverOn(self.queue)
}
public var watchTemporaryStorePath: String? {
return self.server.temporaryFilesURL?.path
}
public func sendFile(url: URL, metadata: Dictionary<AnyHashable, Any>, asMessageData: Bool = false) -> Signal<Void, NoError> {
return Signal { subscriber in
self.server.sendFile(with: url, metadata: metadata, asMessageData: asMessageData)
subscriber.putCompletion()
return EmptyDisposable
} |> runOn(self.queue)
}
public func sendFile(data: Data, metadata: Dictionary<AnyHashable, Any>) -> Signal<Void, NoError> {
return Signal { subscriber in
self.server.sendFile(with: data, metadata: metadata, errorHandler: {})
subscriber.putCompletion()
return EmptyDisposable
} |> runOn(self.queue)
}
}
public func watchCommunicationManager(context: Signal<WatchCommunicationManagerContext?, NoError>, allowBackgroundTimeExtension: @escaping (Double) -> Void) -> Signal<WatchCommunicationManager?, NoError> {
return Signal { subscriber in
let queue = Queue()
queue.async {
if #available(iOSApplicationExtension 9.0, *) {
subscriber.putNext(WatchCommunicationManager(queue: queue, context: context, allowBackgroundTimeExtension: allowBackgroundTimeExtension))
} else {
subscriber.putNext(nil)
}
subscriber.putCompletion()
}
return EmptyDisposable
}
}

View File

@ -0,0 +1,884 @@
import Foundation
import SwiftSignalKit
import Postbox
import Display
import TelegramCore
import LegacyComponents
import WatchCommon
import TelegramPresentationData
import AvatarNode
import StickerResources
import PhotoResources
import AccountContext
import WatchBridgeAudio
let allWatchRequestHandlers: [AnyClass] = [
WatchChatListHandler.self,
WatchChatMessagesHandler.self,
WatchSendMessageHandler.self,
WatchPeerInfoHandler.self,
WatchMediaHandler.self,
WatchStickersHandler.self,
WatchAudioHandler.self,
WatchLocationHandler.self,
WatchPeerSettingsHandler.self,
WatchContinuationHandler.self,
]
protocol WatchRequestHandler: AnyObject {
static var handledSubscriptions: [Any] { get }
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal
}
final class WatchChatListHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [TGBridgeChatListSubscription.self]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeChatListSubscription {
let limit = Int(args.limit)
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<(ChatListView, PresentationData), NoError> in
if let context = context {
return context.account.viewTracker.tailChatListView(groupId: .root, count: limit)
|> map { chatListView, _ -> (ChatListView, PresentationData) in
return (chatListView, context.sharedContext.currentPresentationData.with { $0 })
}
} else {
return .complete()
}
})
let disposable = signal.start(next: { chatListView, presentationData in
var chats: [TGBridgeChat] = []
var users: [Int64 : TGBridgeUser] = [:]
for entry in chatListView.entries.reversed() {
if let (chat, chatUsers) = makeBridgeChat(entry, strings: presentationData.strings) {
chats.append(chat)
users = users.merging(chatUsers, uniquingKeysWith: { (_, last) in last })
}
}
subscriber.putNext([ TGBridgeChatsArrayKey: chats, TGBridgeUsersDictionaryKey: users ] as [String: Any])
})
return SBlockDisposable {
disposable.dispose()
}
}
} else {
return SSignal.fail(nil)
}
}
}
final class WatchChatMessagesHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [
TGBridgeChatMessageListSubscription.self,
TGBridgeChatMessageSubscription.self,
TGBridgeReadChatMessageListSubscription.self
]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeChatMessageListSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
return SSignal { subscriber in
let limit = Int(args.rangeMessageCount)
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<(MessageHistoryView, Bool, PresentationData), NoError> in
if let context = context {
return context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(peerId: peerId, threadId: nil), index: .upperBound, anchorIndex: .upperBound, count: limit, fixedCombinedReadStates: nil)
|> map { messageHistoryView, _, _ -> (MessageHistoryView, Bool, PresentationData) in
return (messageHistoryView, peerId == context.account.peerId, context.sharedContext.currentPresentationData.with { $0 })
}
} else {
return .complete()
}
})
let disposable = signal.start(next: { messageHistoryView, savedMessages, presentationData in
var messages: [TGBridgeMessage] = []
var users: [Int64 : TGBridgeUser] = [:]
for entry in messageHistoryView.entries.reversed() {
if let (message, messageUsers) = makeBridgeMessage(entry, strings: presentationData.strings) {
messages.append(message)
users = users.merging(messageUsers, uniquingKeysWith: { (_, last) in last })
}
}
subscriber.putNext([ TGBridgeMessagesArrayKey: messages, TGBridgeUsersDictionaryKey: users ] as [String: Any])
})
return SBlockDisposable {
disposable.dispose()
}
}
} else if let args = subscription as? TGBridgeReadChatMessageListSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<Void, NoError> in
if let context = context {
let messageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId)
return context.engine.messages.applyMaxReadIndexInteractively(index: MessageIndex(id: messageId, timestamp: 0))
} else {
return .complete()
}
})
let disposable = signal.start(next: { _ in
subscriber.putNext(true)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
} else if let args = subscription as? TGBridgeChatMessageSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<(Message, PresentationData)?, NoError> in
if let context = context {
let messageSignal = context.engine.messages.downloadMessage(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId))
|> map { message -> (Message, PresentationData)? in
if let message = message {
return (message, context.sharedContext.currentPresentationData.with { $0 })
} else {
return nil
}
}
return messageSignal |> timeout(3.5, queue: Queue.concurrentDefaultQueue(), alternate: .single(nil))
} else {
return .single(nil)
}
})
let disposable = signal.start(next: { messageAndPresentationData in
if let (message, presentationData) = messageAndPresentationData, let bridgeMessage = makeBridgeMessage(message, strings: presentationData.strings) {
let peers = makeBridgePeers(message)
var response: [String : Any] = [TGBridgeMessageKey: bridgeMessage, TGBridgeUsersDictionaryKey: peers]
if peerId.namespace != Namespaces.Peer.CloudUser {
response[TGBridgeChatKey] = peers[makeBridgeIdentifier(peerId)]
}
subscriber.putNext(response)
}
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
}
return SSignal.fail(nil)
}
}
final class WatchSendMessageHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [
TGBridgeSendTextMessageSubscription.self,
TGBridgeSendLocationMessageSubscription.self,
TGBridgeSendStickerMessageSubscription.self,
TGBridgeSendForwardedMessageSubscription.self
]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<Bool, NoError> in
if let context = context {
var messageSignal: Signal<(EnqueueMessage?, PeerId?), NoError>?
if let args = subscription as? TGBridgeSendTextMessageSubscription {
let peerId = makePeerIdFromBridgeIdentifier(args.peerId)
var replyMessageId: MessageId?
if args.replyToMid != 0, let peerId = peerId {
replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.replyToMid)
}
messageSignal = .single((.message(text: args.text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []), peerId))
} else if let args = subscription as? TGBridgeSendLocationMessageSubscription, let location = args.location {
let peerId = makePeerIdFromBridgeIdentifier(args.peerId)
let map = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, venue: makeVenue(from: location.venue), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)
messageSignal = .single((.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: map), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []), peerId))
} else if let args = subscription as? TGBridgeSendStickerMessageSubscription {
let peerId = makePeerIdFromBridgeIdentifier(args.peerId)
messageSignal = mediaForSticker(documentId: args.document.documentId, account: context.account)
|> map({ media -> (EnqueueMessage?, PeerId?) in
if let media = media {
return (.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []), peerId)
} else {
return (nil, nil)
}
})
} else if let args = subscription as? TGBridgeSendForwardedMessageSubscription {
let peerId = makePeerIdFromBridgeIdentifier(args.targetPeerId)
if let forwardPeerId = makePeerIdFromBridgeIdentifier(args.peerId) {
messageSignal = .single((.forward(source: MessageId(peerId: forwardPeerId, namespace: Namespaces.Message.Cloud, id: args.messageId), threadId: nil, grouping: .none, attributes: [], correlationId: nil), peerId))
}
}
if let messageSignal = messageSignal {
return messageSignal |> mapToSignal({ message, peerId -> Signal<Bool, NoError> in
if let message = message, let peerId = peerId {
return enqueueMessages(account: context.account, peerId: peerId, messages: [message]) |> mapToSignal({ _ in
return .single(true)
})
} else {
return .complete()
}
})
}
}
return .complete()
})
let disposable = signal.start(next: { _ in
subscriber.putNext(true)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
}
}
final class WatchPeerInfoHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [
TGBridgeUserInfoSubscription.self,
TGBridgeUserBotInfoSubscription.self,
TGBridgeConversationSubscription.self
]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeUserInfoSubscription {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<PeerView, NoError> in
if let context = context, let userId = args.userIds.first as? Int64, let peerId = makePeerIdFromBridgeIdentifier(userId) {
return context.account.viewTracker.peerView(peerId)
} else {
return .complete()
}
})
let disposable = signal.start(next: { view in
if let user = makeBridgeUser(peerViewMainPeer(view), presence: view.peerPresences[view.peerId], cachedData: view.cachedData) {
subscriber.putNext([user.identifier: user])
} else {
subscriber.putCompletion()
}
})
return SBlockDisposable {
disposable.dispose()
}
}
} else if let _ = subscription as? TGBridgeUserBotInfoSubscription {
return SSignal.complete()
} else if let args = subscription as? TGBridgeConversationSubscription {
return SSignal { subscriber in
let signal = manager.accountContext.get() |> take(1) |> mapToSignal({ context -> Signal<PeerView, NoError> in
if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
return context.account.viewTracker.peerView(peerId)
} else {
return .complete()
}
})
let disposable = signal.start(next: { view in
let (chat, users) = makeBridgeChat(peerViewMainPeer(view), view: view)
subscriber.putNext([ TGBridgeChatKey: chat, TGBridgeUsersDictionaryKey: users ] as [String: Any])
})
return SBlockDisposable {
disposable.dispose()
}
}
}
return SSignal.fail(nil)
}
}
private func mediaForSticker(documentId: Int64, account: Account) -> Signal<TelegramMediaFile?, NoError> {
return account.postbox.itemCollectionsView(orderedItemListCollectionIds: [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers], namespaces: [Namespaces.ItemCollection.CloudStickerPacks], aroundIndex: nil, count: 50)
|> take(1)
|> map { view -> TelegramMediaFile? in
for view in view.orderedItemListsViews {
for entry in view.items {
if let file = entry.contents.get(SavedStickerItem.self)?.file {
if file.id.id == documentId {
return file._parse()
}
} else if let file = entry.contents.get(RecentMediaItem.self)?.media {
if file.id.id == documentId {
return file._parse()
}
}
}
}
return nil
}
}
private let roundCorners = { () -> UIImage in
let diameter: CGFloat = 44.0
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0)
let context = UIGraphicsGetCurrentContext()!
context.setBlendMode(.copy)
context.setFillColor(UIColor.black.cgColor)
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: diameter, height: diameter)))
context.setBlendMode(.clear)
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: diameter, height: diameter)))
let image = UIGraphicsGetImageFromCurrentImageContext()!.stretchableImage(withLeftCapWidth: Int(diameter / 2.0), topCapHeight: Int(diameter / 2.0))
UIGraphicsEndImageContext()
return image
}()
private func sendData(manager: WatchCommunicationManager, data: Data, key: String, ext: String, type: String, forceAsData: Bool = false) {
if let tempPath = manager.watchTemporaryStorePath, !forceAsData {
let tempFileUrl = URL(fileURLWithPath: tempPath + "/\(key)\(ext)")
let _ = try? data.write(to: tempFileUrl)
let _ = manager.sendFile(url: tempFileUrl, metadata: [TGBridgeIncomingFileTypeKey: type, TGBridgeIncomingFileIdentifierKey: key]).start()
} else {
let _ = manager.sendFile(data: data, metadata: [TGBridgeIncomingFileTypeKey: type, TGBridgeIncomingFileIdentifierKey: key]).start()
}
}
final class WatchMediaHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [
TGBridgeMediaThumbnailSubscription.self,
TGBridgeMediaAvatarSubscription.self,
TGBridgeMediaStickerSubscription.self
]
}
static private let disposable = DisposableSet()
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeMediaAvatarSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
let key = "\(args.url!)_\(args.type.rawValue)"
let targetSize: CGSize
var compressionRate: CGFloat = 0.5
var round = false
switch args.type {
case .small:
targetSize = CGSize(width: 19, height: 19);
compressionRate = 0.5
case .profile:
targetSize = CGSize(width: 44, height: 44);
round = true
case .large:
targetSize = CGSize(width: 150, height: 150);
@unknown default:
fatalError()
}
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<UIImage?, NoError> in
if let context = context {
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> mapToSignal { peer -> Signal<EnginePeer?, NoError> in
if let peer = peer, case let .secretChat(secretChat) = peer {
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: secretChat.regularPeerId))
} else {
return .single(peer)
}
}
|> mapToSignal({ peer -> Signal<UIImage?, NoError> in
if let peer = peer, let representation = peer.smallProfileImage {
let imageData = peerAvatarImageData(account: context.account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: nil, representation: representation, synchronousLoad: false)
if let imageData = imageData {
return imageData
|> map { data -> UIImage? in
if let (data, _) = data, let image = generateImage(targetSize, contextGenerator: { size, context -> Void in
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
context.setBlendMode(.copy)
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: targetSize))
if round {
context.setBlendMode(.normal)
context.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: targetSize))
}
}
}, scale: 2.0) {
return image
}
return nil
}
}
}
return .single(nil)
})
} else {
return .complete()
}
})
let disposable = signal.start(next: { image in
if let image = image, let imageData = image.jpegData(compressionQuality: compressionRate) {
sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: true)
}
subscriber.putNext(key)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
} else if let args = subscription as? TGBridgeMediaStickerSubscription {
let key = "sticker_\(args.documentId)_\(Int(args.size.width))x\(Int(args.size.height))_\(args.notification ? 1 : 0)"
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<UIImage?, NoError> in
if let context = context {
var mediaSignal: Signal<(TelegramMediaFile, FileMediaReference)?, NoError>? = nil
if args.stickerPackId != 0 {
mediaSignal = mediaForSticker(documentId: args.documentId, account: context.account)
|> map { media -> (TelegramMediaFile, FileMediaReference)? in
if let media = media {
return (media, .standalone(media: media))
} else {
return nil
}
}
} else if args.stickerPeerId != 0, let peerId = makePeerIdFromBridgeIdentifier(args.stickerPeerId) {
mediaSignal = context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.stickerMessageId)))
|> map { message -> (TelegramMediaFile, FileMediaReference)? in
if let message = message {
for media in message.media {
if let media = media as? TelegramMediaFile {
return (media, .message(message: MessageReference(message._asMessage()), media: media))
}
}
}
return nil
}
}
var size: CGSize = args.size
if let mediaSignal = mediaSignal {
return mediaSignal
|> mapToSignal { mediaAndFileReference -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
if let (media, fileReference) = mediaAndFileReference {
if let dimensions = media.dimensions {
size = dimensions.cgSize
}
self.disposable.add(freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).start())
return chatMessageSticker(account: context.account, userLocation: .other, file: media, small: false, fetched: true, onlyFullSize: true)
}
return .complete()
}
|> map{ f -> UIImage? in
let context = f(TransformImageArguments(corners: ImageCorners(), imageSize: size.fitted(args.size), boundingSize: args.size, intrinsicInsets: UIEdgeInsets(), emptyColor: args.notification ? UIColor(rgb: 0xe5e5ea) : .black, scale: 2.0))
return context?.generateImage()
}
}
}
return .complete()
})
let disposable = signal.start(next: { image in
if let image = image, let imageData = image.jpegData(compressionQuality: 0.2) {
sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: args.notification)
}
subscriber.putNext(key)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
} else if let args = subscription as? TGBridgeMediaThumbnailSubscription {
let key = "\(args.peerId)_\(args.messageId)"
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<UIImage?, NoError> in
if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
var roundVideo = false
return context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId)))
|> mapToSignal { message -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
if let message = message, !message._asMessage().containsSecretMedia {
var imageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>?
var updatedMediaReference: AnyMediaReference?
var candidateMediaReference: AnyMediaReference?
var imageDimensions: CGSize?
for media in message.media {
if let image = media as? TelegramMediaImage, let resource = largestImageRepresentation(image.representations)?.resource {
self.disposable.add(messageMediaImageInteractiveFetched(context: context, message: message._asMessage(), image: image, resource: resource, storeToDownloadsPeerId: nil).start())
candidateMediaReference = .message(message: MessageReference(message._asMessage()), media: media)
break
} else if let _ = media as? TelegramMediaFile {
candidateMediaReference = .message(message: MessageReference(message._asMessage()), media: media)
break
} else if let webPage = media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content, let image = content.image, let resource = largestImageRepresentation(image.representations)?.resource {
self.disposable.add(messageMediaImageInteractiveFetched(context: context, message: message._asMessage(), image: image, resource: resource, storeToDownloadsPeerId: nil).start())
candidateMediaReference = .webPage(webPage: WebpageReference(webPage), media: image)
break
}
}
if let imageReference = candidateMediaReference?.concrete(TelegramMediaImage.self) {
updatedMediaReference = imageReference.abstract
if let representation = largestRepresentationForPhoto(imageReference.media) {
imageDimensions = representation.dimensions.cgSize
}
} else if let fileReference = candidateMediaReference?.concrete(TelegramMediaFile.self) {
updatedMediaReference = fileReference.abstract
if let representation = largestImageRepresentation(fileReference.media.previewRepresentations), !fileReference.media.isSticker {
imageDimensions = representation.dimensions.cgSize
}
}
if let updatedMediaReference = updatedMediaReference, imageDimensions != nil {
if let imageReference = updatedMediaReference.concrete(TelegramMediaImage.self) {
imageSignal = chatMessagePhotoThumbnail(account: context.account, userLocation: .other, photoReference: imageReference, onlyFullSize: true)
} else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) {
if fileReference.media.isVideo {
imageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .other, fileReference: fileReference)
roundVideo = fileReference.media.isInstantVideo
} else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) {
imageSignal = chatWebpageSnippetFile(account: context.account, userLocation: .other, mediaReference: fileReference.abstract, representation: iconImageRepresentation)
}
}
}
if let signal = imageSignal {
return signal
}
}
return .complete()
} |> map{ f -> UIImage? in
var insets = UIEdgeInsets()
if roundVideo {
insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
}
let context = f(TransformImageArguments(corners: ImageCorners(), imageSize: args.size, boundingSize: args.size, intrinsicInsets: insets, scale: 2.0))
return context?.generateImage()
}
} else {
return .complete()
}
})
let disposable = signal.start(next: { image in
if let image = image, let imageData = image.jpegData(compressionQuality: 0.5) {
sendData(manager: manager, data: imageData, key: key, ext: ".jpg", type: TGBridgeIncomingFileTypeImage, forceAsData: args.notification)
}
subscriber.putNext(key)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
}
return SSignal.fail(nil)
}
}
final class WatchStickersHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [TGBridgeRecentStickersSubscription.self]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeRecentStickersSubscription {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<ItemCollectionsView, NoError> in
if let context = context {
return context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers], namespaces: [Namespaces.ItemCollection.CloudStickerPacks], aroundIndex: nil, count: 50) |> take(1)
} else {
return .complete()
}
})
let disposable = signal.start(next: { view in
var stickers: [TGBridgeDocumentMediaAttachment] = []
var added: Set<Int64> = []
outer: for view in view.orderedItemListsViews {
for entry in view.items {
if let file = entry.contents.get(SavedStickerItem.self)?.file {
if let sticker = makeBridgeDocument(file._parse()), !added.contains(sticker.documentId) {
stickers.append(sticker)
added.insert(sticker.documentId)
}
} else if let file = entry.contents.get(RecentMediaItem.self)?.media {
if let sticker = makeBridgeDocument(file._parse()), !added.contains(sticker.documentId) {
stickers.append(sticker)
added.insert(sticker.documentId)
}
}
if stickers.count == args.limit {
break outer
}
}
}
subscriber.putNext(stickers)
})
return SBlockDisposable {
disposable.dispose()
}
}
}
return SSignal.fail(nil)
}
}
final class WatchAudioHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [
TGBridgeAudioSubscription.self,
TGBridgeAudioSentSubscription.self
]
}
static private let disposable = DisposableSet()
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeAudioSubscription {
let key = "audio_\(args.peerId)_\(args.messageId)"
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<String, NoError> in
if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
return context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId)))
|> mapToSignal { message -> Signal<String, NoError> in
if let message = message {
for media in message.media {
if let file = media as? TelegramMediaFile {
self.disposable.add(messageMediaFileInteractiveFetched(context: context, message: message._asMessage(), file: file, userInitiated: true).start())
return context.account.postbox.mediaBox.resourceData(file.resource)
|> mapToSignal({ data -> Signal<String, NoError> in
if let tempPath = manager.watchTemporaryStorePath, data.complete {
let outputPath = tempPath + "/\(key).m4a"
return legacyDecodeOpusAudio(path: data.path, outputPath: outputPath)
} else {
return .complete()
}
})
}
}
}
return .complete()
}
} else {
return .complete()
}
})
let disposable = signal.start(next: { path in
let _ = manager.sendFile(url: URL(fileURLWithPath: path), metadata: [TGBridgeIncomingFileTypeKey: TGBridgeIncomingFileTypeAudio, TGBridgeIncomingFileIdentifierKey: key]).start()
subscriber.putNext(key)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
//let outputPath = manager.watchTemporaryStorePath + "/\(key).opus"
} else if let _ = subscription as? TGBridgeAudioSentSubscription {
}
return SSignal.fail(nil)
}
static func handleFile(path: String, metadata: Dictionary<String, Any>, manager: WatchCommunicationManager) -> Signal<Void, NoError> {
let randomId = metadata[TGBridgeIncomingFileRandomIdKey] as? Int64
let peerId = metadata[TGBridgeIncomingFilePeerIdKey] as? Int64
let replyToMid = metadata[TGBridgeIncomingFileReplyToMidKey] as? Int32
if let randomId = randomId, let id = peerId, let peerId = makePeerIdFromBridgeIdentifier(id) {
return combineLatest(manager.accountContext.get() |> take(1), legacyEncodeOpusAudio(path: path))
|> map({ context, pathAndDuration -> Void in
let (path, duration) = pathAndDuration
if let context = context, let path = path, let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
let resource = LocalFileMediaResource(fileId: randomId)
context.account.postbox.mediaBox.storeResourceData(resource.id, data: data)
var replyMessageId: MessageId? = nil
if let replyToMid = replyToMid, replyToMid != 0 {
replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMid)
}
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.count), attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)], alternativeRepresentations: [])), threadId: nil, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]).start()
}
})
} else {
return .complete()
}
}
}
final class WatchLocationHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [TGBridgeNearbyVenuesSubscription.self]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeNearbyVenuesSubscription {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<[ChatContextResultMessage], NoError> in
if let context = context {
return context.engine.peers.resolvePeerByName(name: "foursquare", referrer: nil)
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
guard case let .result(result) = result else {
return .complete()
}
return .single(result)
}
|> take(1)
|> mapToSignal { peer -> Signal<ChatContextResultCollection?, NoError> in
guard let peer = peer?._asPeer() else {
return .single(nil)
}
return context.engine.messages.requestChatContextResults(botId: peer.id, peerId: context.account.peerId, query: "", location: .single((args.coordinate.latitude, args.coordinate.longitude)), offset: "")
|> map { results -> ChatContextResultCollection? in
return results?.results
}
|> `catch` { error -> Signal<ChatContextResultCollection?, NoError> in
return .single(nil)
}
}
|> mapToSignal { contextResult -> Signal<[ChatContextResultMessage], NoError> in
guard let contextResult = contextResult else {
return .single([])
}
return .single(contextResult.results.map { $0.message })
}
} else {
return .complete()
}
})
let disposable = signal.start(next: { results in
var venues: [TGBridgeLocationVenue] = []
for result in results {
if let venue = makeBridgeLocationVenue(result) {
venues.append(venue)
}
}
subscriber.putNext(venues)
})
return SBlockDisposable {
disposable.dispose()
}
}
}
return SSignal.fail(nil)
}
}
final class WatchPeerSettingsHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [
TGBridgePeerSettingsSubscription.self,
TGBridgePeerUpdateNotificationSettingsSubscription.self,
TGBridgePeerUpdateBlockStatusSubscription.self
]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgePeerSettingsSubscription {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<PeerView, NoError> in
if let context = context, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
return context.account.viewTracker.peerView(peerId)
} else {
return .complete()
}
})
let disposable = signal.start(next: { view in
var muted = false
var blocked = false
if let notificationSettings = view.notificationSettings as? TelegramPeerNotificationSettings, case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) {
muted = true
}
if let cachedData = view.cachedData as? CachedUserData {
blocked = cachedData.isBlocked
}
subscriber.putNext([ "muted": muted, "blocked": blocked ])
})
return SBlockDisposable {
disposable.dispose()
}
}
} else {
return SSignal { subscriber in
let signal = manager.accountContext.get()
|> take(1)
|> mapToSignal({ context -> Signal<Bool, NoError> in
if let context = context {
var signal: Signal<Void, NoError>?
if let args = subscription as? TGBridgePeerUpdateNotificationSettingsSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
signal = context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil)
} else if let args = subscription as? TGBridgePeerUpdateBlockStatusSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
signal = context.engine.privacy.requestUpdatePeerIsBlocked(peerId: peerId, isBlocked: args.blocked)
}
if let signal = signal {
return signal |> mapToSignal({ _ in
return .single(true)
})
} else {
return .complete()
}
} else {
return .complete()
}
})
let disposable = signal.start(next: { _ in
subscriber.putNext(true)
}, completed: {
subscriber.putCompletion()
})
return SBlockDisposable {
disposable.dispose()
}
}
}
}
}
final class WatchContinuationHandler: WatchRequestHandler {
static var handledSubscriptions: [Any] {
return [TGBridgeRemoteSubscription.self]
}
static func handle(subscription: TGBridgeSubscription, manager: WatchCommunicationManager) -> SSignal {
if let args = subscription as? TGBridgeRemoteSubscription, let peerId = makePeerIdFromBridgeIdentifier(args.peerId) {
manager.requestNavigateToMessage(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.messageId))
}
return SSignal.fail(nil)
}
}

View File

@ -0,0 +1,19 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "WatchBridgeAudio",
module_name = "WatchBridgeAudio",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/WatchBridgeAudio/Impl:WatchBridgeAudioImpl",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,27 @@
objc_library(
name = "WatchBridgeAudioImpl",
enable_modules = True,
module_name = "WatchBridgeAudioImpl",
srcs = glob([
"Sources/**/*.m",
"Sources/**/*.mm",
"Sources/**/*.h",
], allow_empty=True),
hdrs = glob([
"PublicHeaders/**/*.h",
]),
includes = [
"PublicHeaders",
],
deps = [
"//submodules/SSignalKit/SSignalKit:SSignalKit",
"//submodules/OpusBinding:OpusBinding",
],
sdk_frameworks = [
"Foundation",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
@interface TGBridgeAudioDecoder : NSObject
- (instancetype)initWithURL:(NSURL *)url outputUrl:(NSURL *)outputURL;
- (void)startWithCompletion:(void (^)(void))completion;
@end

View File

@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
@interface TGBridgeAudioEncoder : NSObject
- (instancetype)initWithURL:(NSURL *)url;
- (void)startWithCompletion:(void (^)(NSString *, int32_t))completion;
@end

View File

@ -0,0 +1,6 @@
#import <UIKit/UIKit.h>
#import <WatchBridgeAudioImpl/TGBridgeAudioEncoder.h>
#import <WatchBridgeAudioImpl/TGBridgeAudioDecoder.h>

View File

@ -0,0 +1,197 @@
#import <WatchBridgeAudioImpl/TGBridgeAudioDecoder.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <SSignalKit/SSignalKit.h>
#import <OpusBinding/OpusBinding.h>
const NSInteger TGBridgeAudioDecoderInputSampleRate = 48000;
const NSInteger TGBridgeAudioDecoderResultSampleRate = 24000;
const NSUInteger TGBridgeAudioDecoderBufferSize = 32768;
#define checkResult(result,operation) (_checkResultLite((result),(operation),__FILE__,__LINE__))
struct TGAudioBuffer
{
NSUInteger capacity;
uint8_t *data;
NSUInteger size;
int64_t pcmOffset;
};
inline TGAudioBuffer *TGAudioBufferWithCapacity(NSUInteger capacity)
{
TGAudioBuffer *audioBuffer = (TGAudioBuffer *)malloc(sizeof(TGAudioBuffer));
audioBuffer->capacity = capacity;
audioBuffer->data = (uint8_t *)malloc(capacity);
audioBuffer->size = 0;
audioBuffer->pcmOffset = 0;
return audioBuffer;
}
inline void TGAudioBufferDispose(TGAudioBuffer *audioBuffer)
{
if (audioBuffer != NULL)
{
free(audioBuffer->data);
free(audioBuffer);
}
}
static inline bool _checkResultLite(OSStatus result, const char *operation, const char* file, int line)
{
if ( result != noErr )
{
NSLog(@"%s:%d: %s result %d %08X %4.4s\n", file, line, operation, (int)result, (int)result, (char*)&result);
return NO;
}
return YES;
}
@interface TGBridgeAudioDecoder ()
{
NSURL *_url;
NSURL *_resultURL;
OggOpusReader *_opusReader;
bool _finished;
bool _cancelled;
}
@end
@implementation TGBridgeAudioDecoder
- (instancetype)initWithURL:(NSURL *)url outputUrl:(NSURL *)outputUrl
{
self = [super init];
if (self != nil)
{
_url = url;
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
_resultURL = outputUrl;
}
return self;
}
- (void)startWithCompletion:(void (^)(void))completion
{
[[TGBridgeAudioDecoder processingQueue] dispatch:^
{
_opusReader = [[OggOpusReader alloc] initWithPath:_url.path];
if (_opusReader == NULL) {
return;
}
AudioStreamBasicDescription sourceFormat;
sourceFormat.mSampleRate = TGBridgeAudioDecoderInputSampleRate;
sourceFormat.mFormatID = kAudioFormatLinearPCM;
sourceFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
sourceFormat.mFramesPerPacket = 1;
sourceFormat.mChannelsPerFrame = 1;
sourceFormat.mBitsPerChannel = 16;
sourceFormat.mBytesPerPacket = 2;
sourceFormat.mBytesPerFrame = 2;
AudioStreamBasicDescription destFormat;
memset(&destFormat, 0, sizeof(destFormat));
destFormat.mChannelsPerFrame = sourceFormat.mChannelsPerFrame;
destFormat.mFormatID = kAudioFormatMPEG4AAC;
destFormat.mSampleRate = TGBridgeAudioDecoderResultSampleRate;
UInt32 size = sizeof(destFormat);
if (!checkResult(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destFormat),
"AudioFormatGetProperty(kAudioFormatProperty_FormatInfo)"))
{
return;
}
ExtAudioFileRef destinationFile;
if (!checkResult(ExtAudioFileCreateWithURL((__bridge CFURLRef)_resultURL, kAudioFileM4AType, &destFormat, NULL, kAudioFileFlags_EraseFile, &destinationFile), "ExtAudioFileCreateWithURL"))
{
return;
}
if (!checkResult(ExtAudioFileSetProperty(destinationFile, kExtAudioFileProperty_ClientDataFormat, size, &sourceFormat),
"ExtAudioFileSetProperty(destinationFile, kExtAudioFileProperty_ClientDataFormat"))
{
return;
}
bool canResumeAfterInterruption = false;
AudioConverterRef converter;
size = sizeof(converter);
if (checkResult(ExtAudioFileGetProperty(destinationFile, kExtAudioFileProperty_AudioConverter, &size, &converter),
"ExtAudioFileGetProperty(kExtAudioFileProperty_AudioConverter;)"))
{
UInt32 canResume = 0;
size = sizeof(canResume);
if (AudioConverterGetProperty(converter, kAudioConverterPropertyCanResumeFromInterruption, &size, &canResume) == noErr)
canResumeAfterInterruption = canResume;
}
uint8_t srcBuffer[TGBridgeAudioDecoderBufferSize];
while (!_cancelled)
{
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mNumberChannels = sourceFormat.mChannelsPerFrame;
bufferList.mBuffers[0].mDataByteSize = TGBridgeAudioDecoderBufferSize;
bufferList.mBuffers[0].mData = srcBuffer;
uint32_t writtenOutputBytes = 0;
while (writtenOutputBytes < TGBridgeAudioDecoderBufferSize)
{
int32_t readSamples = [_opusReader read:(uint16_t *)(srcBuffer + writtenOutputBytes) bufSize:(TGBridgeAudioDecoderBufferSize - writtenOutputBytes) / sourceFormat.mBytesPerFrame];
if (readSamples > 0)
writtenOutputBytes += readSamples * sourceFormat.mBytesPerFrame;
else
break;
}
bufferList.mBuffers[0].mDataByteSize = writtenOutputBytes;
int32_t nFrames = writtenOutputBytes / sourceFormat.mBytesPerFrame;
if (nFrames == 0)
break;
OSStatus status = ExtAudioFileWrite(destinationFile, nFrames, &bufferList);
if (status == kExtAudioFileError_CodecUnavailableInputConsumed)
{
//TGLog(@"1");
}
else if (status == kExtAudioFileError_CodecUnavailableInputNotConsumed)
{
//TGLog(@"2");
}
else if (!checkResult(status, "ExtAudioFileWrite"))
{
//TGLog(@"3");
}
}
ExtAudioFileDispose(destinationFile);
if (completion != nil)
completion();
}];
}
+ (SQueue *)processingQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
static const char *queueSpecific = "org.telegram.opusAudioDecoderQueue";
dispatch_queue_t dispatchQueue = dispatch_queue_create("org.telegram.opusAudioDecoderQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(dispatchQueue, queueSpecific, (void *)queueSpecific, NULL);
queue = [SQueue wrapConcurrentNativeQueue:dispatchQueue];
});
return queue;
}
@end

View File

@ -0,0 +1,564 @@
#import <WatchBridgeAudioImpl/TGBridgeAudioEncoder.h>
#import <AVFoundation/AVFoundation.h>
#import <OpusBinding/OpusBinding.h>
static const char *AMQueueSpecific = "AMQueueSpecific";
const NSInteger TGBridgeAudioEncoderSampleRate = 48000;
typedef enum {
ATQueuePriorityLow,
ATQueuePriorityDefault,
ATQueuePriorityHigh
} ATQueuePriority;
@interface ATQueue : NSObject
+ (ATQueue *)mainQueue;
+ (ATQueue *)concurrentDefaultQueue;
+ (ATQueue *)concurrentBackgroundQueue;
- (instancetype)init;
- (instancetype)initWithName:(NSString *)name;
- (instancetype)initWithPriority:(ATQueuePriority)priority;
- (void)dispatch:(dispatch_block_t)block;
- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous;
- (void)dispatchAfter:(NSTimeInterval)seconds block:(dispatch_block_t)block;
- (dispatch_queue_t)nativeQueue;
@end
@interface TGFileDataItem : TGDataItem
- (instancetype)initWithTempFile;
- (void)appendData:(NSData *)data;
- (NSData *)readDataAtOffset:(NSUInteger)offset length:(NSUInteger)length;
- (NSUInteger)length;
- (NSString *)path;
@end
@interface TGBridgeAudioEncoder ()
{
AVAssetReader *_assetReader;
AVAssetReaderOutput *_readerOutput;
NSMutableData *_audioBuffer;
TGFileDataItem *_tempFileItem;
TGOggOpusWriter *_oggWriter;
int _tailLength;
}
@end
@implementation TGBridgeAudioEncoder
- (instancetype)initWithURL:(NSURL *)url
{
self = [super init];
if (self != nil)
{
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
if (asset == nil || asset.tracks.count == 0)
{
return nil;
}
NSError *error;
_assetReader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
NSDictionary *outputSettings = @
{
AVFormatIDKey: @(kAudioFormatLinearPCM),
AVSampleRateKey: @(TGBridgeAudioEncoderSampleRate),
AVNumberOfChannelsKey: @1,
AVLinearPCMBitDepthKey: @16,
AVLinearPCMIsFloatKey: @false,
AVLinearPCMIsBigEndianKey: @false,
AVLinearPCMIsNonInterleaved: @false
};
_readerOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:asset.tracks audioSettings:outputSettings];
[_assetReader addOutput:_readerOutput];
_tempFileItem = [[TGFileDataItem alloc] initWithTempFile];
}
return self;
}
- (void)dealloc
{
[self cleanup];
}
- (void)cleanup
{
_oggWriter = nil;
}
+ (ATQueue *)processingQueue
{
static ATQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[ATQueue alloc] initWithName:@"org.telegram.opusAudioEncoderQueue"];
});
return queue;
}
static const int encoderPacketSizeInBytes = 16000 / 1000 * 60 * 2;
- (void)startWithCompletion:(void (^)(NSString *, int32_t))completion
{
[[TGBridgeAudioEncoder processingQueue] dispatch:^
{
_oggWriter = [[TGOggOpusWriter alloc] init];
if (![_oggWriter beginWithDataItem:_tempFileItem])
{
[self cleanup];
return;
}
[_assetReader startReading];
while (_assetReader.status != AVAssetReaderStatusCompleted)
{
if (_assetReader.status == AVAssetReaderStatusReading)
{
CMSampleBufferRef nextBuffer = [_readerOutput copyNextSampleBuffer];
if (nextBuffer)
{
AudioBufferList abl;
CMBlockBufferRef blockBuffer;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer);
[[TGBridgeAudioEncoder processingQueue] dispatch:^
{
[self _processBuffer:&abl.mBuffers[0]];
CFRelease(nextBuffer);
CFRelease(blockBuffer);
}];
}
else
{
[[TGBridgeAudioEncoder processingQueue] dispatch:^
{
if (_tailLength > 0) {
[_oggWriter writeFrame:(uint8_t *)_audioBuffer.bytes frameByteCount:(NSUInteger)_tailLength];
}
}];
break;
}
}
}
[[TGBridgeAudioEncoder processingQueue] dispatch:^
{
TGFileDataItem *dataItemResult = nil;
NSTimeInterval durationResult = 0.0;
NSUInteger totalBytes = 0;
if (_assetReader.status == AVAssetReaderStatusCompleted)
{
NSLog(@"finished");
if (_oggWriter != nil && [_oggWriter writeFrame:NULL frameByteCount:0])
{
dataItemResult = _tempFileItem;
durationResult = [_oggWriter encodedDuration];
totalBytes = [_oggWriter encodedBytes];
}
[self cleanup];
}
//TGLog(@"[TGBridgeAudioEncoder#%x convert time: %f ms]", self, (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0);
if (completion != nil)
completion(dataItemResult.path, (int32_t)durationResult);
}];
}];
}
- (void)_processBuffer:(AudioBuffer const *)buffer
{
@autoreleasepool
{
if (_oggWriter == nil)
return;
unsigned char currentEncoderPacket[encoderPacketSizeInBytes];
int bufferOffset = 0;
while (true)
{
int currentEncoderPacketSize = 0;
while (currentEncoderPacketSize < encoderPacketSizeInBytes)
{
if (_audioBuffer.length != 0)
{
int takenBytes = MIN((int)_audioBuffer.length, encoderPacketSizeInBytes - currentEncoderPacketSize);
if (takenBytes != 0)
{
memcpy(currentEncoderPacket + currentEncoderPacketSize, _audioBuffer.bytes, takenBytes);
[_audioBuffer replaceBytesInRange:NSMakeRange(0, takenBytes) withBytes:NULL length:0];
currentEncoderPacketSize += takenBytes;
}
}
else if (bufferOffset < (int)buffer->mDataByteSize)
{
int takenBytes = MIN((int)buffer->mDataByteSize - bufferOffset, encoderPacketSizeInBytes - currentEncoderPacketSize);
if (takenBytes != 0)
{
memcpy(currentEncoderPacket + currentEncoderPacketSize, ((const char *)buffer->mData) + bufferOffset, takenBytes);
bufferOffset += takenBytes;
currentEncoderPacketSize += takenBytes;
}
}
else {
break;
}
}
_tailLength = currentEncoderPacketSize;
if (currentEncoderPacketSize < encoderPacketSizeInBytes)
{
if (_audioBuffer == nil)
_audioBuffer = [[NSMutableData alloc] initWithCapacity:encoderPacketSizeInBytes];
[_audioBuffer appendBytes:currentEncoderPacket length:currentEncoderPacketSize];
break;
}
else
{
[_oggWriter writeFrame:currentEncoderPacket frameByteCount:(NSUInteger)currentEncoderPacketSize];
_tailLength = 0;
}
}
}
}
@end
@interface TGFileDataItem ()
{
NSUInteger _length;
NSString *_fileName;
bool _fileExists;
NSMutableData *_data;
}
@end
@implementation TGFileDataItem
{
ATQueue *_queue;
}
- (void)_commonInit
{
_queue = [[ATQueue alloc] initWithPriority:ATQueuePriorityLow];
_data = [[NSMutableData alloc] init];
}
- (instancetype)initWithTempFile
{
self = [super init];
if (self != nil)
{
[self _commonInit];
[_queue dispatch:^
{
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
_fileName = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%" PRIx64 "", randomId]];
_fileExists = false;
}];
}
return self;
}
- (instancetype)initWithFilePath:(NSString *)filePath
{
self = [super init];
if (self != nil)
{
[self _commonInit];
[_queue dispatch:^
{
_fileName = filePath;
_length = [[[NSFileManager defaultManager] attributesOfItemAtPath:_fileName error:nil][NSFileSize] unsignedIntegerValue];
_fileExists = [[NSFileManager defaultManager] fileExistsAtPath:_fileName];
}];
}
return self;
}
- (void)noop
{
}
- (void)moveToPath:(NSString *)path
{
[_queue dispatch:^
{
[[NSFileManager defaultManager] moveItemAtPath:_fileName toPath:path error:nil];
_fileName = path;
}];
}
- (void)remove
{
[_queue dispatch:^
{
[[NSFileManager defaultManager] removeItemAtPath:_fileName error:nil];
}];
}
- (void)appendData:(NSData *)data
{
[_queue dispatch:^
{
if (!_fileExists)
{
[[NSFileManager defaultManager] createFileAtPath:_fileName contents:nil attributes:nil];
_fileExists = true;
}
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName];
[file seekToEndOfFile];
[file writeData:data];
[file synchronizeFile];
[file closeFile];
_length += data.length;
[_data appendData:data];
}];
}
- (NSData *)readDataAtOffset:(NSUInteger)offset length:(NSUInteger)length
{
__block NSData *data = nil;
[_queue dispatch:^
{
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName];
[file seekToFileOffset:(unsigned long long)offset];
data = [file readDataOfLength:length];
if (data.length != length)
//TGLog(@"Read data length mismatch");
[file closeFile];
} synchronous:true];
return data;
}
- (NSUInteger)length
{
__block NSUInteger result = 0;
[_queue dispatch:^
{
result = _length;
} synchronous:true];
return result;
}
- (NSString *)path {
return _fileName;
}
@end
@interface ATQueue ()
{
dispatch_queue_t _nativeQueue;
bool _isMainQueue;
int32_t _noop;
}
@end
@implementation ATQueue
+ (NSString *)applicationPrefix
{
static NSString *prefix = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
prefix = [[NSBundle mainBundle] bundleIdentifier];
});
return prefix;
}
+ (ATQueue *)mainQueue
{
static ATQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[ATQueue alloc] init];
queue->_nativeQueue = dispatch_get_main_queue();
queue->_isMainQueue = true;
});
return queue;
}
+ (ATQueue *)concurrentDefaultQueue
{
static ATQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
});
return queue;
}
+ (ATQueue *)concurrentBackgroundQueue
{
static ATQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)];
});
return queue;
}
- (instancetype)init
{
return [self initWithName:[[ATQueue applicationPrefix] stringByAppendingFormat:@".%ld", lrand48()]];
}
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if (self != nil)
{
_nativeQueue = dispatch_queue_create([name UTF8String], DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(_nativeQueue, AMQueueSpecific, (__bridge void *)self, NULL);
}
return self;
}
- (instancetype)initWithPriority:(ATQueuePriority)priority
{
self = [super init];
if (self != nil)
{
_nativeQueue = dispatch_queue_create([[[ATQueue applicationPrefix] stringByAppendingFormat:@".%ld", lrand48()] UTF8String], DISPATCH_QUEUE_SERIAL);
long targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_DEFAULT;
switch (priority)
{
case ATQueuePriorityLow:
targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_LOW;
break;
case ATQueuePriorityDefault:
targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_DEFAULT;
break;
case ATQueuePriorityHigh:
targetQueueIdentifier = DISPATCH_QUEUE_PRIORITY_HIGH;
break;
}
dispatch_set_target_queue(_nativeQueue, dispatch_get_global_queue(targetQueueIdentifier, 0));
dispatch_queue_set_specific(_nativeQueue, AMQueueSpecific, (__bridge void *)self, NULL);
}
return self;
}
- (instancetype)initWithNativeQueue:(dispatch_queue_t)queue
{
self = [super init];
if (self != nil)
{
#if !OS_OBJECT_USE_OBJC
_nativeQueue = dispatch_retain(queue);
#else
_nativeQueue = queue;
#endif
}
return self;
}
- (void)dealloc
{
if (_nativeQueue != nil)
{
#if !OS_OBJECT_USE_OBJC
dispatch_release(_nativeQueue);
#endif
_nativeQueue = nil;
}
}
- (void)dispatch:(dispatch_block_t)block
{
[self dispatch:block synchronous:false];
}
- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous
{
__block ATQueue *strongSelf = self;
dispatch_block_t blockWithSelf = ^
{
block();
[strongSelf noop];
strongSelf = nil;
};
if (_isMainQueue)
{
if ([NSThread isMainThread])
blockWithSelf();
else if (synchronous)
dispatch_sync(_nativeQueue, blockWithSelf);
else
dispatch_async(_nativeQueue, blockWithSelf);
}
else
{
if (dispatch_get_specific(AMQueueSpecific) == (__bridge void *)self)
block();
else if (synchronous)
dispatch_sync(_nativeQueue, blockWithSelf);
else
dispatch_async(_nativeQueue, blockWithSelf);
}
}
- (void)dispatchAfter:(NSTimeInterval)seconds block:(dispatch_block_t)block
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)), _nativeQueue, block);
}
- (dispatch_queue_t)nativeQueue
{
return _nativeQueue;
}
- (void)noop
{
}
@end

View File

@ -0,0 +1,25 @@
import Foundation
import SwiftSignalKit
import WatchBridgeAudioImpl
public func legacyDecodeOpusAudio(path: String, outputPath: String) -> Signal<String, NoError> {
return Signal { subscriber in
let decoder = TGBridgeAudioDecoder(url: URL(fileURLWithPath: path), outputUrl: URL(fileURLWithPath: outputPath))
decoder?.start(completion: {
subscriber.putNext(outputPath)
subscriber.putCompletion()
})
return EmptyDisposable
}
}
public func legacyEncodeOpusAudio(path: String) -> Signal<(String?, Int32), NoError> {
return Signal { subscriber in
let encoder = TGBridgeAudioEncoder(url: URL(fileURLWithPath: path))
encoder?.start(completion: { (path, duration) in
subscriber.putNext((path, duration))
subscriber.putCompletion()
})
return EmptyDisposable
}
}

View File

View File

@ -0,0 +1,25 @@
objc_library(
name = "WatchCommon",
enable_modules = True,
module_name = "WatchCommon",
srcs = glob([
"Sources/**/*.m",
"Sources/**/*.h",
], allow_empty=True),
hdrs = glob([
"PublicHeaders/**/*.h",
]),
copts = [
"-I{}/PublicHeaders/WatchCommon".format(package_name()),
],
includes = [
"PublicHeaders",
],
sdk_frameworks = [
"Foundation",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,36 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
typedef NS_ENUM(NSUInteger, TGBridgeMessageAction) {
TGBridgeMessageActionNone = 0,
TGBridgeMessageActionChatEditTitle = 1,
TGBridgeMessageActionChatAddMember = 2,
TGBridgeMessageActionChatDeleteMember = 3,
TGBridgeMessageActionCreateChat = 4,
TGBridgeMessageActionChatEditPhoto = 5,
TGBridgeMessageActionContactRequest = 6,
TGBridgeMessageActionAcceptContactRequest = 7,
TGBridgeMessageActionContactRegistered = 8,
TGBridgeMessageActionUserChangedPhoto = 9,
TGBridgeMessageActionEncryptedChatRequest = 10,
TGBridgeMessageActionEncryptedChatAccept = 11,
TGBridgeMessageActionEncryptedChatDecline = 12,
TGBridgeMessageActionEncryptedChatMessageLifetime = 13,
TGBridgeMessageActionEncryptedChatScreenshot = 14,
TGBridgeMessageActionEncryptedChatMessageScreenshot = 15,
TGBridgeMessageActionCreateBroadcastList = 16,
TGBridgeMessageActionJoinedByLink = 17,
TGBridgeMessageActionChannelCreated = 18,
TGBridgeMessageActionChannelCommentsStatusChanged = 19,
TGBridgeMessageActionChannelInviter = 20,
TGBridgeMessageActionGroupMigratedTo = 21,
TGBridgeMessageActionGroupDeactivated = 22,
TGBridgeMessageActionGroupActivated = 23,
TGBridgeMessageActionChannelMigratedFrom = 24
};
@interface TGBridgeActionMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) TGBridgeMessageAction actionType;
@property (nonatomic, strong) NSDictionary *actionData;
@end

View File

@ -0,0 +1,16 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeAudioMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t audioId;
@property (nonatomic, assign) int64_t accessHash;
@property (nonatomic, assign) int32_t datacenterId;
@property (nonatomic, assign) int64_t localAudioId;
@property (nonatomic, assign) int32_t duration;
@property (nonatomic, assign) int32_t fileSize;
- (int64_t)identifier;
@end

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface TGBridgeBotCommandInfo : NSObject <NSCoding>
{
NSString *_command;
NSString *_commandDescription;
}
@property (nonatomic, readonly) NSString *command;
@property (nonatomic, readonly) NSString *commandDescription;
@end

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface TGBridgeBotInfo : NSObject <NSCoding>
{
NSString *_shortDescription;
NSArray *_commandList;
}
@property (nonatomic, readonly) NSString *shortDescription;
@property (nonatomic, readonly) NSArray *commandList;
@end

View File

@ -0,0 +1,46 @@
#import <WatchCommon/TGBridgeCommon.h>
#import <WatchCommon/TGBridgeMessage.h>
@interface TGBridgeChat : NSObject <NSCoding>
@property (nonatomic) int64_t identifier;
@property (nonatomic) NSTimeInterval date;
@property (nonatomic) int32_t fromUid;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) NSArray *media;
@property (nonatomic) bool outgoing;
@property (nonatomic) bool unread;
@property (nonatomic) bool deliveryError;
@property (nonatomic) TGBridgeMessageDeliveryState deliveryState;
@property (nonatomic) int32_t unreadCount;
@property (nonatomic) bool isBroadcast;
@property (nonatomic, strong) NSString *groupTitle;
@property (nonatomic, strong) NSString *groupPhotoSmall;
@property (nonatomic, strong) NSString *groupPhotoBig;
@property (nonatomic) bool isGroup;
@property (nonatomic) bool hasLeftGroup;
@property (nonatomic) bool isKickedFromGroup;
@property (nonatomic) bool isChannel;
@property (nonatomic) bool isChannelGroup;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *about;
@property (nonatomic) bool verified;
@property (nonatomic) int32_t participantsCount;
@property (nonatomic, strong) NSArray *participants;
- (NSArray<NSNumber *> *)involvedUserIds;
- (NSArray<NSNumber *> *)participantsUserIds;
@end
extern NSString *const TGBridgeChatKey;
extern NSString *const TGBridgeChatsArrayKey;

View File

@ -0,0 +1,14 @@
#import <WatchCommon/TGBridgeCommon.h>
@class SSignal;
@interface TGBridgeChatMessages : NSObject <NSCoding>
{
NSArray *_messages;
}
@property (nonatomic, readonly) NSArray *messages;
@end
extern NSString *const TGBridgeChatMessageListViewKey;

View File

@ -0,0 +1,95 @@
#import <Foundation/Foundation.h>
extern NSString *const TGBridgeIncomingFileTypeKey;
extern NSString *const TGBridgeIncomingFileIdentifierKey;
extern NSString *const TGBridgeIncomingFileRandomIdKey;
extern NSString *const TGBridgeIncomingFilePeerIdKey;
extern NSString *const TGBridgeIncomingFileReplyToMidKey;
extern NSString *const TGBridgeIncomingFileTypeAudio;
extern NSString *const TGBridgeIncomingFileTypeImage;
@interface TGBridgeSubscription : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t identifier;
@property (nonatomic, readonly, strong) NSString *name;
@property (nonatomic, readonly) bool isOneTime;
@property (nonatomic, readonly) bool renewable;
@property (nonatomic, readonly) bool dropPreviouslyQueued;
@property (nonatomic, readonly) bool synchronous;
- (void)_serializeParametersWithCoder:(NSCoder *)aCoder;
- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder;
+ (NSString *)subscriptionName;
@end
@interface TGBridgeDisposal : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t identifier;
- (instancetype)initWithIdentifier:(int64_t)identifier;
@end
@interface TGBridgeFile : NSObject <NSCoding>
@property (nonatomic, readonly, strong) NSData *data;
@property (nonatomic, readonly, strong) NSDictionary *metadata;
- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata;
@end
@interface TGBridgePing : NSObject <NSCoding>
@property (nonatomic, readonly) int32_t sessionId;
- (instancetype)initWithSessionId:(int32_t)sessionId;
@end
@interface TGBridgeSubscriptionListRequest : NSObject <NSCoding>
@property (nonatomic, readonly) int32_t sessionId;
- (instancetype)initWithSessionId:(int32_t)sessionId;
@end
@interface TGBridgeSubscriptionList : NSObject <NSCoding>
@property (nonatomic, readonly, strong) NSArray *subscriptions;
- (instancetype)initWithArray:(NSArray *)array;
@end
typedef NS_ENUM(int32_t, TGBridgeResponseType) {
TGBridgeResponseTypeUndefined,
TGBridgeResponseTypeNext,
TGBridgeResponseTypeFailed,
TGBridgeResponseTypeCompleted
};
@interface TGBridgeResponse : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t subscriptionIdentifier;
@property (nonatomic, readonly) TGBridgeResponseType type;
@property (nonatomic, readonly, strong) id next;
@property (nonatomic, readonly, strong) NSString *error;
+ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription;
+ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription;
+ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription;
@end

View File

@ -0,0 +1,13 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeContactMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int32_t uid;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *phoneNumber;
@property (nonatomic, strong) NSString *prettyPhoneNumber;
- (NSString *)displayName;
@end

View File

@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
@interface TGBridgeContext : NSObject
@property (nonatomic, readonly) bool authorized;
@property (nonatomic, readonly) int32_t userId;
@property (nonatomic, readonly) bool micAccessAllowed;
@property (nonatomic, readonly) NSDictionary *preheatData;
@property (nonatomic, readonly) NSInteger preheatVersion;
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
- (NSDictionary *)dictionary;
- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId;
- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data;
- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed;
@end

View File

@ -0,0 +1,23 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeDocumentMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t documentId;
@property (nonatomic, assign) int64_t localDocumentId;
@property (nonatomic, assign) int32_t fileSize;
@property (nonatomic, strong) NSString *fileName;
@property (nonatomic, strong) NSValue *imageSize;
@property (nonatomic, assign) bool isAnimated;
@property (nonatomic, assign) bool isSticker;
@property (nonatomic, strong) NSString *stickerAlt;
@property (nonatomic, assign) int64_t stickerPackId;
@property (nonatomic, assign) int64_t stickerPackAccessHash;
@property (nonatomic, assign) bool isVoice;
@property (nonatomic, assign) bool isAudio;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *performer;
@property (nonatomic, assign) int32_t duration;
@end

View File

@ -0,0 +1,9 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeForwardedMessageMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t peerId;
@property (nonatomic, assign) int32_t mid;
@property (nonatomic, assign) int32_t date;
@end

View File

@ -0,0 +1,10 @@
#import <CoreGraphics/CoreGraphics.h>
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeImageMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t imageId;
@property (nonatomic, assign) CGSize dimensions;
@end

View File

@ -0,0 +1,19 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeVenueAttachment : NSObject <NSCoding>
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *address;
@property (nonatomic, strong) NSString *provider;
@property (nonatomic, strong) NSString *venueId;
@end
@interface TGBridgeLocationMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) double latitude;
@property (nonatomic, assign) double longitude;
@property (nonatomic, strong) TGBridgeVenueAttachment *venue;
@end

View File

@ -0,0 +1,15 @@
#import <CoreLocation/CoreLocation.h>
@class TGBridgeLocationMediaAttachment;
@interface TGBridgeLocationVenue : NSObject <NSCoding>
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, strong) NSString *provider;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *address;
- (TGBridgeLocationMediaAttachment *)locationAttachment;
@end

View File

@ -0,0 +1,11 @@
#import <WatchCommon/TGBridgeCommon.h>
@interface TGBridgeMediaAttachment : NSObject <NSCoding>
@property (nonatomic, readonly) NSInteger mediaType;
+ (NSInteger)mediaType;
@end
extern NSString *const TGBridgeMediaAttachmentTypeKey;

View File

@ -0,0 +1,65 @@
#import <WatchCommon/TGBridgeCommon.h>
#import <WatchCommon/TGBridgeImageMediaAttachment.h>
#import <WatchCommon/TGBridgeVideoMediaAttachment.h>
#import <WatchCommon/TGBridgeAudioMediaAttachment.h>
#import <WatchCommon/TGBridgeDocumentMediaAttachment.h>
#import <WatchCommon/TGBridgeLocationMediaAttachment.h>
#import <WatchCommon/TGBridgeContactMediaAttachment.h>
#import <WatchCommon/TGBridgeActionMediaAttachment.h>
#import <WatchCommon/TGBridgeReplyMessageMediaAttachment.h>
#import <WatchCommon/TGBridgeForwardedMessageMediaAttachment.h>
#import <WatchCommon/TGBridgeWebPageMediaAttachment.h>
#import <WatchCommon/TGBridgeMessageEntitiesAttachment.h>
#import <WatchCommon/TGBridgeUnsupportedMediaAttachment.h>
typedef enum {
TGBridgeTextCheckingResultTypeUndefined,
TGBridgeTextCheckingResultTypeBold,
TGBridgeTextCheckingResultTypeItalic,
TGBridgeTextCheckingResultTypeCode,
TGBridgeTextCheckingResultTypePre
} TGBridgeTextCheckingResultType;
@interface TGBridgeTextCheckingResult : NSObject
@property (nonatomic, assign) TGBridgeTextCheckingResultType type;
@property (nonatomic, assign) NSRange range;
@end
typedef NS_ENUM(NSUInteger, TGBridgeMessageDeliveryState) {
TGBridgeMessageDeliveryStateDelivered = 0,
TGBridgeMessageDeliveryStatePending = 1,
TGBridgeMessageDeliveryStateFailed = 2
};
@interface TGBridgeMessage : NSObject <NSCoding>
@property (nonatomic) int32_t identifier;
@property (nonatomic) NSTimeInterval date;
@property (nonatomic) int64_t randomId;
@property (nonatomic) bool unread;
@property (nonatomic) bool deliveryError;
@property (nonatomic) TGBridgeMessageDeliveryState deliveryState;
@property (nonatomic) bool outgoing;
@property (nonatomic) int64_t fromUid;
@property (nonatomic) int64_t toUid;
@property (nonatomic) int64_t cid;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) NSArray *media;
@property (nonatomic) bool forceReply;
- (NSArray<NSNumber *> *)involvedUserIds;
- (NSArray *)textCheckingResults;
+ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId;
+ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId replyToMessage:(TGBridgeMessage *)replyToMessage;
+ (instancetype)temporaryNewMessageForSticker:(TGBridgeDocumentMediaAttachment *)sticker userId:(int32_t)userId;
+ (instancetype)temporaryNewMessageForLocation:(TGBridgeLocationMediaAttachment *)location userId:(int32_t)userId;
+ (instancetype)temporaryNewMessageForAudioWithDuration:(int32_t)duration userId:(int32_t)userId localAudioId:(int64_t)localAudioId;
@end
extern NSString *const TGBridgeMessageKey;
extern NSString *const TGBridgeMessagesArrayKey;

View File

@ -0,0 +1,59 @@
#import <Foundation/Foundation.h>
@interface TGBridgeMessageEntity : NSObject <NSCoding>
@property (nonatomic, assign) NSRange range;
+ (instancetype)entitityWithRange:(NSRange)range;
@end
@interface TGBridgeMessageEntityUrl : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityEmail : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityTextUrl : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityMention : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityHashtag : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityBotCommand : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityBold : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityItalic : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityCode : TGBridgeMessageEntity
@end
@interface TGBridgeMessageEntityPre : TGBridgeMessageEntity
@end

View File

@ -0,0 +1,8 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
#import <WatchCommon/TGBridgeMessageEntities.h>
@interface TGBridgeMessageEntitiesAttachment : TGBridgeMediaAttachment
@property (nonatomic, strong) NSArray *entities;
@end

View File

@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
@interface TGBridgePeerNotificationSettings : NSObject <NSCoding>
@property (nonatomic, assign) int32_t muteFor;
@end

View File

@ -0,0 +1,9 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@class TGBridgeBotReplyMarkup;
@interface TGBridgeReplyMarkupMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, strong) TGBridgeBotReplyMarkup *replyMarkup;
@end

View File

@ -0,0 +1,10 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@class TGBridgeMessage;
@interface TGBridgeReplyMessageMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int32_t mid;
@property (nonatomic, strong) TGBridgeMessage *message;
@end

View File

@ -0,0 +1,268 @@
#import <WatchCommon/TGBridgeCommon.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreGraphics/CoreGraphics.h>
@class TGBridgeMediaAttachment;
@class TGBridgeImageMediaAttachment;
@class TGBridgeVideoMediaAttachment;
@class TGBridgeDocumentMediaAttachment;
@class TGBridgeLocationMediaAttachment;
@class TGBridgePeerNotificationSettings;
@interface TGBridgeAudioSubscription : TGBridgeSubscription
@property (nonatomic, readonly) TGBridgeMediaAttachment *attachment;
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
- (instancetype)initWithAttachment:(TGBridgeMediaAttachment *)attachment peerId:(int64_t)peerId messageId:(int32_t)messageId;
@end
@interface TGBridgeAudioSentSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t conversationId;
- (instancetype)initWithConversationId:(int64_t)conversationId;
@end
@interface TGBridgeChatListSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int32_t limit;
- (instancetype)initWithLimit:(int32_t)limit;
@end
@interface TGBridgeChatMessageListSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t atMessageId;
@property (nonatomic, readonly) NSUInteger rangeMessageCount;
- (instancetype)initWithPeerId:(int64_t)peerId atMessageId:(int32_t)messageId rangeMessageCount:(NSUInteger)rangeMessageCount;
@end
@interface TGBridgeChatMessageSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId;
@end
@interface TGBridgeReadChatMessageListSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId;
@end
@interface TGBridgeContactsSubscription : TGBridgeSubscription
@property (nonatomic, readonly) NSString *query;
- (instancetype)initWithQuery:(NSString *)query;
@end
@interface TGBridgeConversationSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
- (instancetype)initWithPeerId:(int64_t)peerId;
@end
@interface TGBridgeNearbyVenuesSubscription : TGBridgeSubscription
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, readonly) int32_t limit;
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate limit:(int32_t)limit;
@end
@interface TGBridgeMediaThumbnailSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
@property (nonatomic, readonly) CGSize size;
@property (nonatomic, readonly) bool notification;
- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId size:(CGSize)size notification:(bool)notification;
@end
typedef NS_ENUM(NSUInteger, TGBridgeMediaAvatarType) {
TGBridgeMediaAvatarTypeSmall,
TGBridgeMediaAvatarTypeProfile,
TGBridgeMediaAvatarTypeLarge
};
@interface TGBridgeMediaAvatarSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) NSString *url;
@property (nonatomic, readonly) TGBridgeMediaAvatarType type;
- (instancetype)initWithPeerId:(int64_t)peerId url:(NSString *)url type:(TGBridgeMediaAvatarType)type;
@end
@interface TGBridgeMediaStickerSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t documentId;
@property (nonatomic, readonly) int64_t stickerPackId;
@property (nonatomic, readonly) int64_t stickerPackAccessHash;
@property (nonatomic, readonly) int64_t stickerPeerId;
@property (nonatomic, readonly) int32_t stickerMessageId;
@property (nonatomic, readonly) bool notification;
@property (nonatomic, readonly) CGSize size;
- (instancetype)initWithDocumentId:(int64_t)documentId stickerPackId:(int64_t)stickerPackId stickerPackAccessHash:(int64_t)stickerPackAccessHash stickerPeerId:(int64_t)stickerPeerId stickerMessageId:(int32_t)stickerMessageId notification:(bool)notification size:(CGSize)size;
@end
@interface TGBridgePeerSettingsSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
- (instancetype)initWithPeerId:(int64_t)peerId;
@end
@interface TGBridgePeerUpdateNotificationSettingsSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
- (instancetype)initWithPeerId:(int64_t)peerId;
@end
@interface TGBridgePeerUpdateBlockStatusSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) bool blocked;
- (instancetype)initWithPeerId:(int64_t)peerId blocked:(bool)blocked;
@end
@interface TGBridgeRemoteSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
@property (nonatomic, readonly) int32_t type;
@property (nonatomic, readonly) bool autoPlay;
- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId type:(int32_t)type autoPlay:(bool)autoPlay;
@end
@interface TGBridgeSendTextMessageSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) NSString *text;
@property (nonatomic, readonly) int32_t replyToMid;
- (instancetype)initWithPeerId:(int64_t)peerId text:(NSString *)text replyToMid:(int32_t)replyToMid;
@end
@interface TGBridgeSendStickerMessageSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) TGBridgeDocumentMediaAttachment *document;
@property (nonatomic, readonly) int32_t replyToMid;
- (instancetype)initWithPeerId:(int64_t)peerId document:(TGBridgeDocumentMediaAttachment *)document replyToMid:(int32_t)replyToMid;
@end
@interface TGBridgeSendLocationMessageSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) TGBridgeLocationMediaAttachment *location;
@property (nonatomic, readonly) int32_t replyToMid;
- (instancetype)initWithPeerId:(int64_t)peerId location:(TGBridgeLocationMediaAttachment *)location replyToMid:(int32_t)replyToMid;
@end
@interface TGBridgeSendForwardedMessageSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
@property (nonatomic, readonly) int32_t messageId;
@property (nonatomic, readonly) int64_t targetPeerId;
- (instancetype)initWithPeerId:(int64_t)peerId messageId:(int32_t)messageId targetPeerId:(int64_t)targetPeerId;
@end
@interface TGBridgeStateSubscription : TGBridgeSubscription
@end
@interface TGBridgeStickerPacksSubscription : TGBridgeSubscription
@end
@interface TGBridgeRecentStickersSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int32_t limit;
- (instancetype)initWithLimit:(int32_t)limit;
@end
@interface TGBridgeUserInfoSubscription : TGBridgeSubscription
@property (nonatomic, readonly) NSArray *userIds;
- (instancetype)initWithUserIds:(NSArray *)userIds;
@end
@interface TGBridgeUserBotInfoSubscription : TGBridgeSubscription
@property (nonatomic, readonly) NSArray *userIds;
- (instancetype)initWithUserIds:(NSArray *)userIds;
@end
@interface TGBridgeBotReplyMarkupSubscription : TGBridgeSubscription
@property (nonatomic, readonly) int64_t peerId;
- (instancetype)initWithPeerId:(int64_t)peerId;
@end

View File

@ -0,0 +1,9 @@
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeUnsupportedMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, strong) NSString *compactTitle;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;
@end

View File

@ -0,0 +1,59 @@
#import <WatchCommon/TGBridgeCommon.h>
@class TGBridgeBotInfo;
@class TGBridgeUserChange;
typedef NS_ENUM(NSUInteger, TGBridgeUserKind) {
TGBridgeUserKindGeneric,
TGBridgeUserKindBot,
TGBridgeUserKindSmartBot
};
typedef NS_ENUM(NSUInteger, TGBridgeBotKind) {
TGBridgeBotKindGeneric,
TGBridgeBotKindPrivate
};
@interface TGBridgeUser : NSObject <NSCoding, NSCopying>
@property (nonatomic) int64_t identifier;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *phoneNumber;
@property (nonatomic, strong) NSString *prettyPhoneNumber;
@property (nonatomic, strong) NSString *about;
@property (nonatomic) bool online;
@property (nonatomic) NSTimeInterval lastSeen;
@property (nonatomic, strong) NSString *photoSmall;
@property (nonatomic, strong) NSString *photoBig;
@property (nonatomic) TGBridgeUserKind kind;
@property (nonatomic) TGBridgeBotKind botKind;
@property (nonatomic) int32_t botVersion;
@property (nonatomic) bool verified;
@property (nonatomic) int32_t userVersion;
- (NSString *)displayName;
- (TGBridgeUserChange *)changeFromUser:(TGBridgeUser *)user;
- (TGBridgeUser *)userByApplyingChange:(TGBridgeUserChange *)change;
- (bool)isBot;
@end
@interface TGBridgeUserChange : NSObject <NSCoding>
@property (nonatomic, readonly) int32_t userIdentifier;
@property (nonatomic, readonly) NSDictionary *fields;
- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields;
@end
extern NSString *const TGBridgeUsersDictionaryKey;

View File

@ -0,0 +1,12 @@
#import <CoreGraphics/CoreGraphics.h>
#import <WatchCommon/TGBridgeMediaAttachment.h>
@interface TGBridgeVideoMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t videoId;
@property (nonatomic, assign) int32_t duration;
@property (nonatomic, assign) CGSize dimensions;
@property (nonatomic, assign) bool round;
@end

View File

@ -0,0 +1,23 @@
#import <CoreGraphics/CoreGraphics.h>
#import <WatchCommon/TGBridgeMediaAttachment.h>
@class TGBridgeImageMediaAttachment;
@interface TGBridgeWebPageMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t webPageId;
@property (nonatomic, strong) NSString *url;
@property (nonatomic, strong) NSString *displayUrl;
@property (nonatomic, strong) NSString *pageType;
@property (nonatomic, strong) NSString *siteName;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *pageDescription;
@property (nonatomic, strong) TGBridgeImageMediaAttachment *photo;
@property (nonatomic, strong) NSString *embedUrl;
@property (nonatomic, strong) NSString *embedType;
@property (nonatomic, assign) CGSize embedSize;
@property (nonatomic, strong) NSNumber *duration;
@property (nonatomic, strong) NSString *author;
@end

View File

@ -0,0 +1,29 @@
#import <UIKit/UIKit.h>
#import <WatchCommon/TGBridgeActionMediaAttachment.h>
#import <WatchCommon/TGBridgeAudioMediaAttachment.h>
#import <WatchCommon/TGBridgeBotCommandInfo.h>
#import <WatchCommon/TGBridgeBotInfo.h>
#import <WatchCommon/TGBridgeChat.h>
#import <WatchCommon/TGBridgeChatMessages.h>
#import <WatchCommon/TGBridgeCommon.h>
#import <WatchCommon/TGBridgeContactMediaAttachment.h>
#import <WatchCommon/TGBridgeContext.h>
#import <WatchCommon/TGBridgeDocumentMediaAttachment.h>
#import <WatchCommon/TGBridgeForwardedMessageMediaAttachment.h>
#import <WatchCommon/TGBridgeImageMediaAttachment.h>
#import <WatchCommon/TGBridgeLocationMediaAttachment.h>
#import <WatchCommon/TGBridgeLocationVenue.h>
#import <WatchCommon/TGBridgeMediaAttachment.h>
#import <WatchCommon/TGBridgeMessage.h>
#import <WatchCommon/TGBridgeMessageEntities.h>
#import <WatchCommon/TGBridgeMessageEntitiesAttachment.h>
#import <WatchCommon/TGBridgeReplyMarkupMediaAttachment.h>
#import <WatchCommon/TGBridgeReplyMessageMediaAttachment.h>
#import <WatchCommon/TGBridgeSubscriptions.h>
#import <WatchCommon/TGBridgeUnsupportedMediaAttachment.h>
#import <WatchCommon/TGBridgeUser.h>
#import <WatchCommon/TGBridgeVideoMediaAttachment.h>
#import <WatchCommon/TGBridgeWebPageMediaAttachment.h>
#import <WatchCommon/TGBridgePeerIdAdapter.h>
#import <WatchCommon/TGBridgePeerNotificationSettings.h>

View File

@ -0,0 +1,33 @@
#import "TGBridgeActionMediaAttachment.h"
#import "TGBridgeImageMediaAttachment.h"
const NSInteger TGBridgeActionMediaAttachmentType = 0x1167E28B;
NSString *const TGBridgeActionMediaTypeKey = @"actionType";
NSString *const TGBridgeActionMediaDataKey = @"actionData";
@implementation TGBridgeActionMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_actionType = (TGBridgeMessageAction)[aDecoder decodeInt32ForKey:TGBridgeActionMediaTypeKey];
_actionData = [aDecoder decodeObjectForKey:TGBridgeActionMediaDataKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.actionType forKey:TGBridgeActionMediaTypeKey];
[aCoder encodeObject:self.actionData forKey:TGBridgeActionMediaDataKey];
}
+ (NSInteger)mediaType
{
return TGBridgeActionMediaAttachmentType;
}
@end

View File

@ -0,0 +1,65 @@
#import "TGBridgeAudioMediaAttachment.h"
const NSInteger TGBridgeAudioMediaAttachmentType = 0x3A0E7A32;
NSString *const TGBridgeAudioMediaAudioIdKey = @"audioId";
NSString *const TGBridgeAudioMediaAccessHashKey = @"accessHash";
NSString *const TGBridgeAudioMediaLocalIdKey = @"localId";
NSString *const TGBridgeAudioMediaDatacenterIdKey = @"datacenterId";
NSString *const TGBridgeAudioMediaDurationKey = @"duration";
NSString *const TGBridgeAudioMediaFileSizeKey = @"fileSize";
@implementation TGBridgeAudioMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_audioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAudioIdKey];
_accessHash = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAccessHashKey];
_localAudioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaLocalIdKey];
_datacenterId = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDatacenterIdKey];
_duration = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDurationKey];
_fileSize = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaFileSizeKey];
}
return self;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder
{
[aCoder encodeInt64:self.audioId forKey:TGBridgeAudioMediaAudioIdKey];
[aCoder encodeInt64:self.accessHash forKey:TGBridgeAudioMediaAccessHashKey];
[aCoder encodeInt64:self.localAudioId forKey:TGBridgeAudioMediaLocalIdKey];
[aCoder encodeInt32:self.datacenterId forKey:TGBridgeAudioMediaDatacenterIdKey];
[aCoder encodeInt32:self.duration forKey:TGBridgeAudioMediaDurationKey];
[aCoder encodeInt32:self.fileSize forKey:TGBridgeAudioMediaFileSizeKey];
}
- (int64_t)identifier
{
if (self.localAudioId != 0)
return self.localAudioId;
return self.audioId;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeAudioMediaAttachment *audio = (TGBridgeAudioMediaAttachment *)object;
return (self.audioId == audio.audioId || self.localAudioId == audio.localAudioId);
}
+ (NSInteger)mediaType
{
return TGBridgeAudioMediaAttachmentType;
}
@end

View File

@ -0,0 +1,25 @@
#import "TGBridgeBotCommandInfo.h"
NSString *const TGBridgeBotCommandInfoCommandKey = @"command";
NSString *const TGBridgeBotCommandDescriptionKey = @"commandDescription";
@implementation TGBridgeBotCommandInfo
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_command = [aDecoder decodeObjectForKey:TGBridgeBotCommandInfoCommandKey];
_commandDescription = [aDecoder decodeObjectForKey:TGBridgeBotCommandDescriptionKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.command forKey:TGBridgeBotCommandInfoCommandKey];
[aCoder encodeObject:self.commandDescription forKey:TGBridgeBotCommandDescriptionKey];
}
@end

View File

@ -0,0 +1,25 @@
#import "TGBridgeBotInfo.h"
NSString *const TGBridgeBotInfoShortDescriptionKey = @"shortDescription";
NSString *const TGBridgeBotInfoCommandListKey = @"commandList";
@implementation TGBridgeBotInfo
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_shortDescription = [aDecoder decodeObjectForKey:TGBridgeBotInfoShortDescriptionKey];
_commandList = [aDecoder decodeObjectForKey:TGBridgeBotInfoCommandListKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.shortDescription forKey:TGBridgeBotInfoShortDescriptionKey];
[aCoder encodeObject:self.commandList forKey:TGBridgeBotInfoCommandListKey];
}
@end

View File

@ -0,0 +1,148 @@
#import "TGBridgeChat.h"
#import "TGBridgePeerIdAdapter.h"
NSString *const TGBridgeChatIdentifierKey = @"identifier";
NSString *const TGBridgeChatDateKey = @"date";
NSString *const TGBridgeChatFromUidKey = @"fromUid";
NSString *const TGBridgeChatTextKey = @"text";
NSString *const TGBridgeChatOutgoingKey = @"outgoing";
NSString *const TGBridgeChatUnreadKey = @"unread";
NSString *const TGBridgeChatMediaKey = @"media";
NSString *const TGBridgeChatUnreadCountKey = @"unreadCount";
NSString *const TGBridgeChatGroupTitleKey = @"groupTitle";
NSString *const TGBridgeChatGroupPhotoSmallKey = @"groupPhotoSmall";
NSString *const TGBridgeChatGroupPhotoBigKey = @"groupPhotoBig";
NSString *const TGBridgeChatIsGroupKey = @"isGroup";
NSString *const TGBridgeChatHasLeftGroupKey = @"hasLeftGroup";
NSString *const TGBridgeChatIsKickedFromGroupKey = @"isKickedFromGroup";
NSString *const TGBridgeChatIsChannelKey = @"isChannel";
NSString *const TGBridgeChatIsChannelGroupKey = @"isChannelGroup";
NSString *const TGBridgeChatUserNameKey = @"userName";
NSString *const TGBridgeChatAboutKey = @"about";
NSString *const TGBridgeChatVerifiedKey = @"verified";
NSString *const TGBridgeChatGroupParticipantsCountKey = @"participantsCount";
NSString *const TGBridgeChatGroupParticipantsKey = @"participants";
NSString *const TGBridgeChatDeliveryStateKey = @"deliveryState";
NSString *const TGBridgeChatDeliveryErrorKey = @"deliveryError";
NSString *const TGBridgeChatKey = @"chat";
NSString *const TGBridgeChatsArrayKey = @"chats";
@implementation TGBridgeChat
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeChatIdentifierKey];
_date = [aDecoder decodeDoubleForKey:TGBridgeChatDateKey];
_fromUid = [aDecoder decodeInt32ForKey:TGBridgeChatFromUidKey];
_text = [aDecoder decodeObjectForKey:TGBridgeChatTextKey];
_outgoing = [aDecoder decodeBoolForKey:TGBridgeChatOutgoingKey];
_unread = [aDecoder decodeBoolForKey:TGBridgeChatUnreadKey];
_unreadCount = [aDecoder decodeInt32ForKey:TGBridgeChatUnreadCountKey];
_deliveryState = [aDecoder decodeInt32ForKey:TGBridgeChatDeliveryStateKey];
_deliveryError = [aDecoder decodeBoolForKey:TGBridgeChatDeliveryErrorKey];
_media = [aDecoder decodeObjectForKey:TGBridgeChatMediaKey];
_groupTitle = [aDecoder decodeObjectForKey:TGBridgeChatGroupTitleKey];
_groupPhotoSmall = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoSmallKey];
_groupPhotoBig = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoBigKey];
_isGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsGroupKey];
_hasLeftGroup = [aDecoder decodeBoolForKey:TGBridgeChatHasLeftGroupKey];
_isKickedFromGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsKickedFromGroupKey];
_isChannel = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelKey];
_isChannelGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelGroupKey];
_userName = [aDecoder decodeObjectForKey:TGBridgeChatUserNameKey];
_about = [aDecoder decodeObjectForKey:TGBridgeChatAboutKey];
_verified = [aDecoder decodeBoolForKey:TGBridgeChatVerifiedKey];
_participantsCount = [aDecoder decodeInt32ForKey:TGBridgeChatGroupParticipantsCountKey];
_participants = [aDecoder decodeObjectForKey:TGBridgeChatGroupParticipantsKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeChatIdentifierKey];
[aCoder encodeDouble:self.date forKey:TGBridgeChatDateKey];
[aCoder encodeInt32:self.fromUid forKey:TGBridgeChatFromUidKey];
[aCoder encodeObject:self.text forKey:TGBridgeChatTextKey];
[aCoder encodeBool:self.outgoing forKey:TGBridgeChatOutgoingKey];
[aCoder encodeBool:self.unread forKey:TGBridgeChatUnreadKey];
[aCoder encodeInt32:self.unreadCount forKey:TGBridgeChatUnreadCountKey];
[aCoder encodeInt32:self.deliveryState forKey:TGBridgeChatDeliveryStateKey];
[aCoder encodeBool:self.deliveryError forKey:TGBridgeChatDeliveryErrorKey];
[aCoder encodeObject:self.media forKey:TGBridgeChatMediaKey];
[aCoder encodeObject:self.groupTitle forKey:TGBridgeChatGroupTitleKey];
[aCoder encodeObject:self.groupPhotoSmall forKey:TGBridgeChatGroupPhotoSmallKey];
[aCoder encodeObject:self.groupPhotoBig forKey:TGBridgeChatGroupPhotoBigKey];
[aCoder encodeBool:self.isGroup forKey:TGBridgeChatIsGroupKey];
[aCoder encodeBool:self.hasLeftGroup forKey:TGBridgeChatHasLeftGroupKey];
[aCoder encodeBool:self.isKickedFromGroup forKey:TGBridgeChatIsKickedFromGroupKey];
[aCoder encodeBool:self.isChannel forKey:TGBridgeChatIsChannelKey];
[aCoder encodeBool:self.isChannelGroup forKey:TGBridgeChatIsChannelGroupKey];
[aCoder encodeObject:self.userName forKey:TGBridgeChatUserNameKey];
[aCoder encodeObject:self.about forKey:TGBridgeChatAboutKey];
[aCoder encodeBool:self.verified forKey:TGBridgeChatVerifiedKey];
[aCoder encodeInt32:self.participantsCount forKey:TGBridgeChatGroupParticipantsCountKey];
[aCoder encodeObject:self.participants forKey:TGBridgeChatGroupParticipantsKey];
}
- (NSArray<NSNumber *> *)involvedUserIds
{
NSMutableSet<NSNumber *> *userIds = [[NSMutableSet alloc] init];
if (!self.isGroup && !self.isChannel && self.identifier != 0)
[userIds addObject:[NSNumber numberWithLongLong:self.identifier]];
if ((!self.isChannel || self.isChannelGroup) && self.fromUid != self.identifier && self.fromUid != 0 && !TGPeerIdIsChannel(self.fromUid) && self.fromUid > 0)
[userIds addObject:[NSNumber numberWithLongLong:self.fromUid]];
for (TGBridgeMediaAttachment *attachment in self.media)
{
if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]])
{
TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment;
if (actionAttachment.actionData[@"uid"] != nil)
[userIds addObject:[NSNumber numberWithLongLong:[actionAttachment.actionData[@"uid"] longLongValue]]];
}
}
NSMutableArray *result = [[NSMutableArray alloc] init];
for (NSNumber *object in userIds) {
[result addObject:object];
}
return result;
}
- (NSArray<NSNumber *> *)participantsUserIds
{
NSMutableSet<NSNumber *> *userIds = [[NSMutableSet alloc] init];
for (NSNumber *uid in self.participants) {
[userIds addObject:[NSNumber numberWithLongLong:uid.longLongValue]];
}
NSMutableArray *result = [[NSMutableArray alloc] init];
for (NSNumber *object in userIds) {
[result addObject:object];
}
return result;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
return self.identifier == ((TGBridgeChat *)object).identifier;
}
@end

View File

@ -0,0 +1,27 @@
#import "TGBridgeChatMessages.h"
#import "TGBridgeMessage.h"
NSString *const TGBridgeChatMessageListViewMessagesKey = @"messages";
NSString *const TGBridgeChatMessageListViewEarlierMessageIdKey = @"earlier";
NSString *const TGBridgeChatMessageListViewLaterMessageIdKey = @"later";
NSString *const TGBridgeChatMessageListViewKey = @"messageListView";
@implementation TGBridgeChatMessages
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_messages = [aDecoder decodeObjectForKey:TGBridgeChatMessageListViewMessagesKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.messages forKey:TGBridgeChatMessageListViewMessagesKey];
}
@end

View File

@ -0,0 +1,295 @@
#import "TGBridgeCommon.h"
NSString *const TGBridgeIncomingFileTypeKey = @"type";
NSString *const TGBridgeIncomingFileIdentifierKey = @"identifier";
NSString *const TGBridgeIncomingFileRandomIdKey = @"randomId";
NSString *const TGBridgeIncomingFilePeerIdKey = @"peerId";
NSString *const TGBridgeIncomingFileReplyToMidKey = @"replyToMid";
NSString *const TGBridgeIncomingFileTypeAudio = @"audio";
NSString *const TGBridgeIncomingFileTypeImage = @"image";
NSString *const TGBridgeResponseSubscriptionIdentifier = @"identifier";
NSString *const TGBridgeResponseTypeKey = @"type";
NSString *const TGBridgeResponseNextKey = @"next";
NSString *const TGBridgeResponseErrorKey = @"error";
@implementation TGBridgeResponse
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_subscriptionIdentifier = [aDecoder decodeInt64ForKey:TGBridgeResponseSubscriptionIdentifier];
_type = [aDecoder decodeInt32ForKey:TGBridgeResponseTypeKey];
_next = [aDecoder decodeObjectForKey:TGBridgeResponseNextKey];
_error = [aDecoder decodeObjectForKey:TGBridgeResponseErrorKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.subscriptionIdentifier forKey:TGBridgeResponseSubscriptionIdentifier];
[aCoder encodeInt32:self.type forKey:TGBridgeResponseTypeKey];
[aCoder encodeObject:self.next forKey:TGBridgeResponseNextKey];
[aCoder encodeObject:self.error forKey:TGBridgeResponseErrorKey];
}
+ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription
{
TGBridgeResponse *response = [[TGBridgeResponse alloc] init];
response->_subscriptionIdentifier = subscription.identifier;
response->_type = TGBridgeResponseTypeNext;
response->_next = next;
return response;
}
+ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription
{
TGBridgeResponse *response = [[TGBridgeResponse alloc] init];
response->_subscriptionIdentifier = subscription.identifier;
response->_type = TGBridgeResponseTypeFailed;
response->_error = error;
return response;
}
+ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription
{
TGBridgeResponse *response = [[TGBridgeResponse alloc] init];
response->_subscriptionIdentifier = subscription.identifier;
response->_type = TGBridgeResponseTypeCompleted;
return response;
}
@end
NSString *const TGBridgeSubscriptionIdentifierKey = @"identifier";
NSString *const TGBridgeSubscriptionNameKey = @"name";
NSString *const TGBridgeSubscriptionParametersKey = @"parameters";
@implementation TGBridgeSubscription
- (instancetype)init
{
self = [super init];
if (self != nil)
{
int64_t randomId = 0;
arc4random_buf(&randomId, sizeof(int64_t));
_identifier = randomId;
_name = [[self class] subscriptionName];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey];
_name = [aDecoder decodeObjectForKey:TGBridgeSubscriptionNameKey];
[self _unserializeParametersWithCoder:aDecoder];
}
return self;
}
- (bool)synchronous
{
return false;
}
- (bool)renewable
{
return true;
}
- (bool)dropPreviouslyQueued
{
return false;
}
- (void)_serializeParametersWithCoder:(NSCoder *)__unused aCoder
{
}
- (void)_unserializeParametersWithCoder:(NSCoder *)__unused aDecoder
{
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey];
[aCoder encodeObject:self.name forKey:TGBridgeSubscriptionNameKey];
[self _serializeParametersWithCoder:aCoder];
}
+ (NSString *)subscriptionName
{
return nil;
}
@end
@implementation TGBridgeDisposal
- (instancetype)initWithIdentifier:(int64_t)identifier
{
self = [super init];
if (self != nil)
{
_identifier = identifier;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey];
}
@end
NSString *const TGBridgeFileDataKey = @"data";
NSString *const TGBridgeFileMetadataKey = @"metadata";
@implementation TGBridgeFile
- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata
{
self = [super init];
if (self != nil)
{
_data = data;
_metadata = metadata;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_data = [aDecoder decodeObjectForKey:TGBridgeFileDataKey];
_metadata = [aDecoder decodeObjectForKey:TGBridgeFileMetadataKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.data forKey:TGBridgeFileDataKey];
[aCoder encodeObject:self.metadata forKey:TGBridgeFileMetadataKey];
}
@end
NSString *const TGBridgeSessionIdKey = @"sessionId";
@implementation TGBridgePing
- (instancetype)initWithSessionId:(int32_t)sessionId
{
self = [super init];
if (self != nil)
{
_sessionId = sessionId;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey];
}
@end
@implementation TGBridgeSubscriptionListRequest
- (instancetype)initWithSessionId:(int32_t)sessionId
{
self = [super init];
if (self != nil)
{
_sessionId = sessionId;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey];
}
@end
NSString *const TGBridgeSubscriptionListSubscriptionsKey = @"subscriptions";
@implementation TGBridgeSubscriptionList
- (instancetype)initWithArray:(NSArray *)array
{
self = [super init];
if (self != nil)
{
_subscriptions = array;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_subscriptions = [aDecoder decodeObjectForKey:TGBridgeSubscriptionListSubscriptionsKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.subscriptions forKey:TGBridgeSubscriptionListSubscriptionsKey];
}
@end

View File

@ -0,0 +1,60 @@
#import "TGBridgeContactMediaAttachment.h"
//#import "../Extension/TGStringUtils.h"
const NSInteger TGBridgeContactMediaAttachmentType = 0xB90A5663;
NSString *const TGBridgeContactMediaUidKey = @"uid";
NSString *const TGBridgeContactMediaFirstNameKey = @"firstName";
NSString *const TGBridgeContactMediaLastNameKey = @"lastName";
NSString *const TGBridgeContactMediaPhoneNumberKey = @"phoneNumber";
NSString *const TGBridgeContactMediaPrettyPhoneNumberKey = @"prettyPhoneNumber";
@implementation TGBridgeContactMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_uid = [aDecoder decodeInt32ForKey:TGBridgeContactMediaUidKey];
_firstName = [aDecoder decodeObjectForKey:TGBridgeContactMediaFirstNameKey];
_lastName = [aDecoder decodeObjectForKey:TGBridgeContactMediaLastNameKey];
_phoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPhoneNumberKey];
_prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPrettyPhoneNumberKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.uid forKey:TGBridgeContactMediaUidKey];
[aCoder encodeObject:self.firstName forKey:TGBridgeContactMediaFirstNameKey];
[aCoder encodeObject:self.lastName forKey:TGBridgeContactMediaLastNameKey];
[aCoder encodeObject:self.phoneNumber forKey:TGBridgeContactMediaPhoneNumberKey];
[aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeContactMediaPrettyPhoneNumberKey];
}
- (NSString *)displayName
{
NSString *firstName = self.firstName;
NSString *lastName = self.lastName;
if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0)
{
return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];
}
else if (firstName != nil && firstName.length != 0)
return firstName;
else if (lastName != nil && lastName.length != 0)
return lastName;
return @"";
}
+ (NSInteger)mediaType
{
return TGBridgeContactMediaAttachmentType;
}
@end

View File

@ -0,0 +1,101 @@
#import "TGBridgeContext.h"
#import "TGBridgeCommon.h"
//#import "TGWatchCommon.h"
NSString *const TGBridgeContextAuthorized = @"authorized";
NSString *const TGBridgeContextUserId = @"userId";
NSString *const TGBridgeContextMicAccessAllowed = @"micAccessAllowed";
NSString *const TGBridgeContextStartupData = @"startupData";
NSString *const TGBridgeContextStartupDataVersion = @"version";
@implementation TGBridgeContext
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self != nil)
{
_authorized = [dictionary[TGBridgeContextAuthorized] boolValue];
_userId = (int32_t)[dictionary[TGBridgeContextUserId] intValue];
_micAccessAllowed = [dictionary[TGBridgeContextMicAccessAllowed] boolValue];
if (dictionary[TGBridgeContextStartupData] != nil) {
_preheatData = [NSKeyedUnarchiver unarchiveObjectWithData:dictionary[TGBridgeContextStartupData]];
_preheatVersion = [dictionary[TGBridgeContextStartupDataVersion] integerValue];
}
}
return self;
}
- (NSDictionary *)dictionary
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[TGBridgeContextAuthorized] = @(self.authorized);
dictionary[TGBridgeContextUserId] = @(self.userId);
dictionary[TGBridgeContextMicAccessAllowed] = @(self.micAccessAllowed);
if (self.preheatData != nil) {
dictionary[TGBridgeContextStartupData] = [NSKeyedArchiver archivedDataWithRootObject:self.preheatData];
dictionary[TGBridgeContextStartupDataVersion] = @(self.preheatVersion);
}
return dictionary;
}
- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId
{
TGBridgeContext *context = [[TGBridgeContext alloc] init];
context->_authorized = authorized;
context->_userId = peerId;
context->_micAccessAllowed = self.micAccessAllowed;
if (authorized) {
context->_preheatData = self.preheatData;
context->_preheatVersion = self.preheatVersion;
}
return context;
}
- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data
{
TGBridgeContext *context = [[TGBridgeContext alloc] init];
context->_authorized = self.authorized;
context->_userId = self.userId;
context->_micAccessAllowed = self.micAccessAllowed;
if (data != nil) {
context->_preheatData = data;
context->_preheatVersion = (int32_t)[NSDate date].timeIntervalSinceReferenceDate;
}
return context;
}
- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed
{
TGBridgeContext *context = [[TGBridgeContext alloc] init];
context->_authorized = self.authorized;
context->_userId = self.userId;
context->_micAccessAllowed = allowed;
context->_preheatData = self.preheatData;
context->_preheatVersion = self.preheatVersion;
return context;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return true;
if (!object || ![object isKindOfClass:[self class]])
return false;
TGBridgeContext *context = (TGBridgeContext *)object;
if (context.authorized != self.authorized)
return false;
if (context.userId != self.userId)
return false;
if (context.micAccessAllowed != self.micAccessAllowed)
return false;
if (context.preheatVersion != self.preheatVersion)
return false;
return true;
}
@end

View File

@ -0,0 +1,84 @@
#import "TGBridgeDocumentMediaAttachment.h"
const NSInteger TGBridgeDocumentMediaAttachmentType = 0xE6C64318;
NSString *const TGBridgeDocumentMediaDocumentIdKey = @"documentId";
NSString *const TGBridgeDocumentMediaLocalDocumentIdKey = @"localDocumentId";
NSString *const TGBridgeDocumentMediaFileSizeKey = @"fileSize";
NSString *const TGBridgeDocumentMediaFileNameKey = @"fileName";
NSString *const TGBridgeDocumentMediaImageSizeKey = @"imageSize";
NSString *const TGBridgeDocumentMediaAnimatedKey = @"animated";
NSString *const TGBridgeDocumentMediaStickerKey = @"sticker";
NSString *const TGBridgeDocumentMediaStickerAltKey = @"stickerAlt";
NSString *const TGBridgeDocumentMediaStickerPackIdKey = @"stickerPackId";
NSString *const TGBridgeDocumentMediaStickerPackAccessHashKey = @"stickerPackAccessHash";
NSString *const TGBridgeDocumentMediaAudioKey = @"audio";
NSString *const TGBridgeDocumentMediaAudioTitleKey = @"title";
NSString *const TGBridgeDocumentMediaAudioPerformerKey = @"performer";
NSString *const TGBridgeDocumentMediaAudioVoice = @"voice";
NSString *const TGBridgeDocumentMediaAudioDuration = @"duration";
@implementation TGBridgeDocumentMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_documentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaDocumentIdKey];
_localDocumentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaLocalDocumentIdKey];
_fileSize = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaFileSizeKey];
_fileName = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaFileNameKey];
_imageSize = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaImageSizeKey];
_isAnimated = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAnimatedKey];
_isSticker = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaStickerKey];
_stickerAlt = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaStickerAltKey];
_stickerPackId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackIdKey];
_stickerPackAccessHash = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackAccessHashKey];
_isAudio = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioKey];
_title = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioTitleKey];
_performer = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioPerformerKey];
_isVoice = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioVoice];
_duration = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaAudioDuration];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.documentId forKey:TGBridgeDocumentMediaDocumentIdKey];
[aCoder encodeInt64:self.localDocumentId forKey:TGBridgeDocumentMediaLocalDocumentIdKey];
[aCoder encodeInt32:self.fileSize forKey:TGBridgeDocumentMediaFileSizeKey];
[aCoder encodeObject:self.fileName forKey:TGBridgeDocumentMediaFileNameKey];
[aCoder encodeObject:self.imageSize forKey:TGBridgeDocumentMediaImageSizeKey];
[aCoder encodeBool:self.isAnimated forKey:TGBridgeDocumentMediaAnimatedKey];
[aCoder encodeBool:self.isSticker forKey:TGBridgeDocumentMediaStickerKey];
[aCoder encodeObject:self.stickerAlt forKey:TGBridgeDocumentMediaStickerAltKey];
[aCoder encodeInt64:self.stickerPackId forKey:TGBridgeDocumentMediaStickerPackIdKey];
[aCoder encodeInt64:self.stickerPackAccessHash forKey:TGBridgeDocumentMediaStickerPackAccessHashKey];
[aCoder encodeBool:self.isAudio forKey:TGBridgeDocumentMediaAudioKey];
[aCoder encodeObject:self.title forKey:TGBridgeDocumentMediaAudioTitleKey];
[aCoder encodeObject:self.performer forKey:TGBridgeDocumentMediaAudioPerformerKey];
[aCoder encodeBool:self.isVoice forKey:TGBridgeDocumentMediaAudioVoice];
[aCoder encodeInt32:self.duration forKey:TGBridgeDocumentMediaAudioDuration];
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeDocumentMediaAttachment *document = (TGBridgeDocumentMediaAttachment *)object;
return (self.localDocumentId == 0 && self.documentId == document.documentId) || (self.localDocumentId != 0 && self.localDocumentId == document.localDocumentId);
}
+ (NSInteger)mediaType
{
return TGBridgeDocumentMediaAttachmentType;
}
@end

View File

@ -0,0 +1,35 @@
#import "TGBridgeForwardedMessageMediaAttachment.h"
const NSInteger TGBridgeForwardedMessageMediaAttachmentType = 0xAA1050C1;
NSString *const TGBridgeForwardedMessageMediaPeerIdKey = @"peerId";
NSString *const TGBridgeForwardedMessageMediaMidKey = @"mid";
NSString *const TGBridgeForwardedMessageMediaDateKey = @"date";
@implementation TGBridgeForwardedMessageMediaAttachment
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_peerId = [aDecoder decodeInt64ForKey:TGBridgeForwardedMessageMediaPeerIdKey];
_mid = [aDecoder decodeInt32ForKey:TGBridgeForwardedMessageMediaMidKey];
_date = [aDecoder decodeInt32ForKey:TGBridgeForwardedMessageMediaDateKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.peerId forKey:TGBridgeForwardedMessageMediaPeerIdKey];
[aCoder encodeInt32:self.mid forKey:TGBridgeForwardedMessageMediaMidKey];
[aCoder encodeInt32:self.date forKey:TGBridgeForwardedMessageMediaDateKey];
}
+ (NSInteger)mediaType
{
return TGBridgeForwardedMessageMediaAttachmentType;
}
@end

View File

@ -0,0 +1,33 @@
#import "TGBridgeImageMediaAttachment.h"
#import <UIKit/UIKit.h>
const NSInteger TGBridgeImageMediaAttachmentType = 0x269BD8A8;
NSString *const TGBridgeImageMediaImageIdKey = @"imageId";
NSString *const TGBridgeImageMediaDimensionsKey = @"dimensions";
@implementation TGBridgeImageMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_imageId = [aDecoder decodeInt64ForKey:TGBridgeImageMediaImageIdKey];
_dimensions = [aDecoder decodeCGSizeForKey:TGBridgeImageMediaDimensionsKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.imageId forKey:TGBridgeImageMediaImageIdKey];
[aCoder encodeCGSize:self.dimensions forKey:TGBridgeImageMediaDimensionsKey];
}
+ (NSInteger)mediaType
{
return TGBridgeImageMediaAttachmentType;
}
@end

View File

@ -0,0 +1,95 @@
#import "TGBridgeLocationMediaAttachment.h"
const NSInteger TGBridgeLocationMediaAttachmentType = 0x0C9ED06E;
NSString *const TGBridgeLocationMediaLatitudeKey = @"lat";
NSString *const TGBridgeLocationMediaLongitudeKey = @"lon";
NSString *const TGBridgeLocationMediaVenueKey = @"venue";
NSString *const TGBridgeVenueTitleKey = @"title";
NSString *const TGBridgeVenueAddressKey = @"address";
NSString *const TGBridgeVenueProviderKey = @"provider";
NSString *const TGBridgeVenueIdKey = @"venueId";
@implementation TGBridgeVenueAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_title = [aDecoder decodeObjectForKey:TGBridgeVenueTitleKey];
_address = [aDecoder decodeObjectForKey:TGBridgeVenueAddressKey];
_provider = [aDecoder decodeObjectForKey:TGBridgeVenueProviderKey];
_venueId = [aDecoder decodeObjectForKey:TGBridgeVenueIdKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.title forKey:TGBridgeVenueTitleKey];
[aCoder encodeObject:self.address forKey:TGBridgeVenueAddressKey];
[aCoder encodeObject:self.provider forKey:TGBridgeVenueProviderKey];
[aCoder encodeObject:self.venueId forKey:TGBridgeVenueIdKey];
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeVenueAttachment *venue = (TGBridgeVenueAttachment *)object;
return [self.title isEqualToString:venue.title] && [self.address isEqualToString:venue.address] && [self.provider isEqualToString:venue.provider] && [self.venueId isEqualToString:venue.venueId];
}
@end
@implementation TGBridgeLocationMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_latitude = [aDecoder decodeDoubleForKey:TGBridgeLocationMediaLatitudeKey];
_longitude = [aDecoder decodeDoubleForKey:TGBridgeLocationMediaLongitudeKey];
_venue = [aDecoder decodeObjectForKey:TGBridgeLocationMediaVenueKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeDouble:self.latitude forKey:TGBridgeLocationMediaLatitudeKey];
[aCoder encodeDouble:self.longitude forKey:TGBridgeLocationMediaLongitudeKey];
[aCoder encodeObject:self.venue forKey:TGBridgeLocationMediaVenueKey];
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeLocationMediaAttachment *location = (TGBridgeLocationMediaAttachment *)object;
bool equalCoord = (fabs(self.latitude - location.latitude) < DBL_EPSILON && fabs(self.longitude - location.longitude) < DBL_EPSILON);
bool equalVenue = (self.venue == nil && location.venue == nil) || ([self.venue isEqual:location.venue]);
return equalCoord || equalVenue;
}
+ (NSInteger)mediaType
{
return TGBridgeLocationMediaAttachmentType;
}
@end

View File

@ -0,0 +1,66 @@
#import "TGBridgeLocationVenue.h"
#import "TGBridgeLocationMediaAttachment.h"
NSString *const TGBridgeLocationVenueLatitudeKey = @"lat";
NSString *const TGBridgeLocationVenueLongitudeKey = @"lon";
NSString *const TGBridgeLocationVenueIdentifierKey = @"identifier";
NSString *const TGBridgeLocationVenueProviderKey = @"provider";
NSString *const TGBridgeLocationVenueNameKey = @"name";
NSString *const TGBridgeLocationVenueAddressKey = @"address";
@implementation TGBridgeLocationVenue
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_coordinate = CLLocationCoordinate2DMake([aDecoder decodeDoubleForKey:TGBridgeLocationVenueLatitudeKey], [aDecoder decodeDoubleForKey:TGBridgeLocationVenueLongitudeKey]);
_identifier = [aDecoder decodeObjectForKey:TGBridgeLocationVenueIdentifierKey];
_provider = [aDecoder decodeObjectForKey:TGBridgeLocationVenueProviderKey];
_name = [aDecoder decodeObjectForKey:TGBridgeLocationVenueNameKey];
_address = [aDecoder decodeObjectForKey:TGBridgeLocationVenueAddressKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeDouble:self.coordinate.latitude forKey:TGBridgeLocationVenueLatitudeKey];
[aCoder encodeDouble:self.coordinate.longitude forKey:TGBridgeLocationVenueLongitudeKey];
[aCoder encodeObject:self.identifier forKey:TGBridgeLocationVenueIdentifierKey];
[aCoder encodeObject:self.provider forKey:TGBridgeLocationVenueProviderKey];
[aCoder encodeObject:self.name forKey:TGBridgeLocationVenueNameKey];
[aCoder encodeObject:self.address forKey:TGBridgeLocationVenueAddressKey];
}
- (TGBridgeLocationMediaAttachment *)locationAttachment
{
TGBridgeLocationMediaAttachment *attachment = [[TGBridgeLocationMediaAttachment alloc] init];
attachment.latitude = self.coordinate.latitude;
attachment.longitude = self.coordinate.longitude;
TGBridgeVenueAttachment *venueAttachment = [[TGBridgeVenueAttachment alloc] init];
venueAttachment.title = self.name;
venueAttachment.address = self.address;
venueAttachment.provider = self.provider;
venueAttachment.venueId = self.identifier;
attachment.venue = venueAttachment;
return attachment;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
return [self.identifier isEqualToString:((TGBridgeLocationVenue *)object).identifier];
}
@end

View File

@ -0,0 +1,32 @@
#import "TGBridgeMediaAttachment.h"
NSString *const TGBridgeMediaAttachmentTypeKey = @"type";
@implementation TGBridgeMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder
{
self = [super init];
if (self != nil)
{
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)__unused aCoder
{
}
- (NSInteger)mediaType
{
return 0;
}
+ (NSInteger)mediaType
{
return 0;
}
@end

View File

@ -0,0 +1,242 @@
#import "TGBridgeMessage.h"
//#import "TGWatchCommon.h"
#import "TGBridgePeerIdAdapter.h"
NSString *const TGBridgeMessageIdentifierKey = @"identifier";
NSString *const TGBridgeMessageDateKey = @"date";
NSString *const TGBridgeMessageRandomIdKey = @"randomId";
NSString *const TGBridgeMessageFromUidKey = @"fromUid";
NSString *const TGBridgeMessageCidKey = @"cid";
NSString *const TGBridgeMessageTextKey = @"text";
NSString *const TGBridgeMessageUnreadKey = @"unread";
NSString *const TGBridgeMessageOutgoingKey = @"outgoing";
NSString *const TGBridgeMessageMediaKey = @"media";
NSString *const TGBridgeMessageDeliveryStateKey = @"deliveryState";
NSString *const TGBridgeMessageForceReplyKey = @"forceReply";
NSString *const TGBridgeMessageKey = @"message";
NSString *const TGBridgeMessagesArrayKey = @"messages";
@interface TGBridgeMessage ()
{
NSArray *_textCheckingResults;
}
@end
@implementation TGBridgeMessage
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt32ForKey:TGBridgeMessageIdentifierKey];
_date = [aDecoder decodeDoubleForKey:TGBridgeMessageDateKey];
_randomId = [aDecoder decodeInt64ForKey:TGBridgeMessageRandomIdKey];
_fromUid = [aDecoder decodeInt64ForKey:TGBridgeMessageFromUidKey];
_cid = [aDecoder decodeInt64ForKey:TGBridgeMessageCidKey];
_text = [aDecoder decodeObjectForKey:TGBridgeMessageTextKey];
_outgoing = [aDecoder decodeBoolForKey:TGBridgeMessageOutgoingKey];
_unread = [aDecoder decodeBoolForKey:TGBridgeMessageUnreadKey];
_deliveryState = [aDecoder decodeInt32ForKey:TGBridgeMessageDeliveryStateKey];
_media = [aDecoder decodeObjectForKey:TGBridgeMessageMediaKey];
_forceReply = [aDecoder decodeBoolForKey:TGBridgeMessageForceReplyKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.identifier forKey:TGBridgeMessageIdentifierKey];
[aCoder encodeDouble:self.date forKey:TGBridgeMessageDateKey];
[aCoder encodeInt64:self.randomId forKey:TGBridgeMessageRandomIdKey];
[aCoder encodeInt64:self.fromUid forKey:TGBridgeMessageFromUidKey];
[aCoder encodeInt64:self.cid forKey:TGBridgeMessageCidKey];
[aCoder encodeObject:self.text forKey:TGBridgeMessageTextKey];
[aCoder encodeBool:self.outgoing forKey:TGBridgeMessageOutgoingKey];
[aCoder encodeBool:self.unread forKey:TGBridgeMessageUnreadKey];
[aCoder encodeInt32:self.deliveryState forKey:TGBridgeMessageDeliveryStateKey];
[aCoder encodeObject:self.media forKey:TGBridgeMessageMediaKey];
[aCoder encodeBool:self.forceReply forKey:TGBridgeMessageForceReplyKey];
}
- (NSArray<NSNumber *> *)involvedUserIds
{
NSMutableSet<NSNumber *> *userIds = [[NSMutableSet alloc] init];
if (!TGPeerIdIsChannel(self.fromUid))
[userIds addObject:[NSNumber numberWithLongLong:self.fromUid]];
for (TGBridgeMediaAttachment *attachment in self.media)
{
if ([attachment isKindOfClass:[TGBridgeContactMediaAttachment class]])
{
TGBridgeContactMediaAttachment *contactAttachment = (TGBridgeContactMediaAttachment *)attachment;
if (contactAttachment.uid != 0)
[userIds addObject:[NSNumber numberWithLongLong:contactAttachment.uid]];
}
else if ([attachment isKindOfClass:[TGBridgeForwardedMessageMediaAttachment class]])
{
TGBridgeForwardedMessageMediaAttachment *forwardAttachment = (TGBridgeForwardedMessageMediaAttachment *)attachment;
if (forwardAttachment.peerId != 0 && !TGPeerIdIsChannel(forwardAttachment.peerId))
[userIds addObject:[NSNumber numberWithLongLong:forwardAttachment.peerId]];
}
else if ([attachment isKindOfClass:[TGBridgeReplyMessageMediaAttachment class]])
{
TGBridgeReplyMessageMediaAttachment *replyAttachment = (TGBridgeReplyMessageMediaAttachment *)attachment;
if (replyAttachment.message != nil && !TGPeerIdIsChannel(replyAttachment.message.fromUid))
[userIds addObject:[NSNumber numberWithLongLong:replyAttachment.message.fromUid]];
}
else if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]])
{
TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment;
if (actionAttachment.actionData[@"uid"] != nil)
[userIds addObject:[NSNumber numberWithLongLong:[actionAttachment.actionData[@"uid"] intValue]]];
}
}
NSMutableArray *result = [[NSMutableArray alloc] init];
for (NSNumber *object in userIds) {
[result addObject:object];
}
return result;
}
- (NSArray *)textCheckingResults
{
if (_textCheckingResults == nil)
{
NSMutableArray *results = [[NSMutableArray alloc] init];
NSArray *entities = nil;
for (TGBridgeMediaAttachment *attachment in self.media)
{
if ([attachment isKindOfClass:[TGBridgeMessageEntitiesAttachment class]])
{
entities = ((TGBridgeMessageEntitiesAttachment *)attachment).entities;
break;
}
}
for (TGBridgeMessageEntity *entity in entities)
{
TGBridgeTextCheckingResult *result = [[TGBridgeTextCheckingResult alloc] init];
result.range = entity.range;
if ([entity isKindOfClass:[TGBridgeMessageEntityBold class]])
result.type = TGBridgeTextCheckingResultTypeBold;
else if ([entity isKindOfClass:[TGBridgeMessageEntityItalic class]])
result.type = TGBridgeTextCheckingResultTypeItalic;
else if ([entity isKindOfClass:[TGBridgeMessageEntityCode class]])
result.type = TGBridgeTextCheckingResultTypeCode;
else if ([entity isKindOfClass:[TGBridgeMessageEntityPre class]])
result.type = TGBridgeTextCheckingResultTypePre;
if (result.type != TGBridgeTextCheckingResultTypeUndefined)
[results addObject:result];
}
_textCheckingResults = results;
}
return _textCheckingResults;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeMessage *message = (TGBridgeMessage *)object;
if (self.randomId != 0)
return self.randomId == message.randomId;
else
return self.identifier == message.identifier;
}
+ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId
{
return [self temporaryNewMessageForText:text userId:userId replyToMessage:nil];
}
+ (instancetype)temporaryNewMessageForText:(NSString *)text userId:(int32_t)userId replyToMessage:(TGBridgeMessage *)replyToMessage
{
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
int32_t messageId = 0;
arc4random_buf(&messageId, 4);
TGBridgeMessage *message = [[TGBridgeMessage alloc] init];
message->_identifier = -abs(messageId);
message->_fromUid = userId;
message->_randomId = randomId;
message->_unread = true;
message->_outgoing = true;
message->_deliveryState = TGBridgeMessageDeliveryStatePending;
message->_text = text;
message->_date = [[NSDate date] timeIntervalSince1970];
if (replyToMessage != nil)
{
TGBridgeReplyMessageMediaAttachment *replyAttachment = [[TGBridgeReplyMessageMediaAttachment alloc] init];
replyAttachment.mid = replyToMessage.identifier;
replyAttachment.message = replyToMessage;
message->_media = @[ replyToMessage ];
}
return message;
}
+ (instancetype)temporaryNewMessageForSticker:(TGBridgeDocumentMediaAttachment *)sticker userId:(int32_t)userId
{
return [self _temporaryNewMessageForMediaAttachment:sticker userId:userId];
}
+ (instancetype)temporaryNewMessageForLocation:(TGBridgeLocationMediaAttachment *)location userId:(int32_t)userId
{
return [self _temporaryNewMessageForMediaAttachment:location userId:userId];
}
+ (instancetype)temporaryNewMessageForAudioWithDuration:(int32_t)duration userId:(int32_t)userId localAudioId:(int64_t)localAudioId
{
TGBridgeDocumentMediaAttachment *document = [[TGBridgeDocumentMediaAttachment alloc] init];
document.isAudio = true;
document.isVoice = true;
document.localDocumentId = localAudioId;
document.duration = duration;
return [self _temporaryNewMessageForMediaAttachment:document userId:userId];
}
+ (instancetype)_temporaryNewMessageForMediaAttachment:(TGBridgeMediaAttachment *)attachment userId:(int32_t)userId
{
int64_t randomId = 0;
arc4random_buf(&randomId, 8);
int32_t messageId = 0;
arc4random_buf(&messageId, 4);
TGBridgeMessage *message = [[TGBridgeMessage alloc] init];
message->_identifier = -abs(messageId);
message->_fromUid = userId;
message->_unread = true;
message->_outgoing = true;
message->_deliveryState = TGBridgeMessageDeliveryStatePending;
message->_date = [[NSDate date] timeIntervalSince1970];
message->_media = @[ attachment ];
return message;
}
@end
@implementation TGBridgeTextCheckingResult
@end

View File

@ -0,0 +1,83 @@
#import "TGBridgeMessageEntities.h"
NSString *const TGBridgeMessageEntityLocationKey = @"loc";
NSString *const TGBridgeMessageEntityLengthKey = @"len";
@implementation TGBridgeMessageEntity
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
NSUInteger loc = [aDecoder decodeIntegerForKey:TGBridgeMessageEntityLocationKey];
NSUInteger len = [aDecoder decodeIntegerForKey:TGBridgeMessageEntityLengthKey];
_range = NSMakeRange(loc, len);
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInteger:self.range.location forKey:TGBridgeMessageEntityLocationKey];
[aCoder encodeInteger:self.range.length forKey:TGBridgeMessageEntityLengthKey];
}
+ (instancetype)entitityWithRange:(NSRange)range
{
TGBridgeMessageEntity *entity = [[self alloc] init];
entity.range = range;
return entity;
}
@end
@implementation TGBridgeMessageEntityUrl
@end
@implementation TGBridgeMessageEntityEmail
@end
@implementation TGBridgeMessageEntityTextUrl
@end
@implementation TGBridgeMessageEntityMention
@end
@implementation TGBridgeMessageEntityHashtag
@end
@implementation TGBridgeMessageEntityBotCommand
@end
@implementation TGBridgeMessageEntityBold
@end
@implementation TGBridgeMessageEntityItalic
@end
@implementation TGBridgeMessageEntityCode
@end
@implementation TGBridgeMessageEntityPre
@end

View File

@ -0,0 +1,30 @@
#import "TGBridgeMessageEntitiesAttachment.h"
const NSInteger TGBridgeMessageEntitiesAttachmentType = 0x8c2e3cce;
NSString *const TGBridgeMessageEntitiesKey = @"entities";
@implementation TGBridgeMessageEntitiesAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_entities = [aDecoder decodeObjectForKey:TGBridgeMessageEntitiesKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.entities forKey:TGBridgeMessageEntitiesKey];
}
+ (NSInteger)mediaType
{
return TGBridgeMessageEntitiesAttachmentType;
}
@end

View File

@ -0,0 +1,22 @@
#import "TGBridgePeerNotificationSettings.h"
NSString *const TGBridgePeerNotificationSettingsMuteForKey = @"muteFor";
@implementation TGBridgePeerNotificationSettings
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_muteFor = [aDecoder decodeInt32ForKey:TGBridgePeerNotificationSettingsMuteForKey];
}
return self;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder
{
[aCoder encodeInt32:self.muteFor forKey:TGBridgePeerNotificationSettingsMuteForKey];
}
@end

View File

@ -0,0 +1,29 @@
#import "TGBridgeReplyMarkupMediaAttachment.h"
const NSInteger TGBridgeReplyMarkupMediaAttachmentType = 0x5678acc1;
NSString *const TGBridgeReplyMarkupMediaMessageKey = @"replyMarkup";
@implementation TGBridgeReplyMarkupMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_replyMarkup = [aDecoder decodeObjectForKey:TGBridgeReplyMarkupMediaMessageKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.replyMarkup forKey:TGBridgeReplyMarkupMediaMessageKey];
}
+ (NSInteger)mediaType
{
return TGBridgeReplyMarkupMediaAttachmentType;
}
@end

View File

@ -0,0 +1,33 @@
#import "TGBridgeReplyMessageMediaAttachment.h"
#import "TGBridgeMessage.h"
const NSInteger TGBridgeReplyMessageMediaAttachmentType = 414002169;
NSString *const TGBridgeReplyMessageMediaMidKey = @"mid";
NSString *const TGBridgeReplyMessageMediaMessageKey = @"message";
@implementation TGBridgeReplyMessageMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_mid = [aDecoder decodeInt32ForKey:TGBridgeReplyMessageMediaMidKey];
_message = [aDecoder decodeObjectForKey:TGBridgeReplyMessageMediaMessageKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.mid forKey:TGBridgeReplyMessageMediaMidKey];
[aCoder encodeObject:self.message forKey:TGBridgeReplyMessageMediaMessageKey];
}
+ (NSInteger)mediaType
{
return TGBridgeReplyMessageMediaAttachmentType;
}
@end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
#import "TGBridgeUnsupportedMediaAttachment.h"
const NSInteger TGBridgeUnsupportedMediaAttachmentType = 0x3837BEF7;
NSString *const TGBridgeUnsupportedMediaCompactTitleKey = @"compactTitle";
NSString *const TGBridgeUnsupportedMediaTitleKey = @"title";
NSString *const TGBridgeUnsupportedMediaSubtitleKey = @"subtitle";
@implementation TGBridgeUnsupportedMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_compactTitle = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaCompactTitleKey];
_title = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaTitleKey];
_subtitle = [aDecoder decodeObjectForKey:TGBridgeUnsupportedMediaSubtitleKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.compactTitle forKey:TGBridgeUnsupportedMediaCompactTitleKey];
[aCoder encodeObject:self.title forKey:TGBridgeUnsupportedMediaTitleKey];
[aCoder encodeObject:self.subtitle forKey:TGBridgeUnsupportedMediaSubtitleKey];
}
+ (NSInteger)mediaType
{
return TGBridgeUnsupportedMediaAttachmentType;
}
@end

View File

@ -0,0 +1,286 @@
#import "TGBridgeUser.h"
//#import "TGWatchCommon.h"
#import "TGBridgeBotInfo.h"
//#import "../Extension/TGStringUtils.h"
NSString *const TGBridgeUserIdentifierKey = @"identifier";
NSString *const TGBridgeUserFirstNameKey = @"firstName";
NSString *const TGBridgeUserLastNameKey = @"lastName";
NSString *const TGBridgeUserUserNameKey = @"userName";
NSString *const TGBridgeUserPhoneNumberKey = @"phoneNumber";
NSString *const TGBridgeUserPrettyPhoneNumberKey = @"prettyPhoneNumber";
NSString *const TGBridgeUserOnlineKey = @"online";
NSString *const TGBridgeUserLastSeenKey = @"lastSeen";
NSString *const TGBridgeUserPhotoSmallKey = @"photoSmall";
NSString *const TGBridgeUserPhotoBigKey = @"photoBig";
NSString *const TGBridgeUserKindKey = @"kind";
NSString *const TGBridgeUserBotKindKey = @"botKind";
NSString *const TGBridgeUserBotVersionKey = @"botVersion";
NSString *const TGBridgeUserVerifiedKey = @"verified";
NSString *const TGBridgeUserAboutKey = @"about";
NSString *const TGBridgeUserVersionKey = @"version";
NSString *const TGBridgeUsersDictionaryKey = @"users";
@implementation TGBridgeUser
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeUserIdentifierKey];
_firstName = [aDecoder decodeObjectForKey:TGBridgeUserFirstNameKey];
_lastName = [aDecoder decodeObjectForKey:TGBridgeUserLastNameKey];
_userName = [aDecoder decodeObjectForKey:TGBridgeUserUserNameKey];
_phoneNumber = [aDecoder decodeObjectForKey:TGBridgeUserPhoneNumberKey];
_prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeUserPrettyPhoneNumberKey];
_online = [aDecoder decodeBoolForKey:TGBridgeUserOnlineKey];
_lastSeen = [aDecoder decodeDoubleForKey:TGBridgeUserLastSeenKey];
_photoSmall = [aDecoder decodeObjectForKey:TGBridgeUserPhotoSmallKey];
_photoBig = [aDecoder decodeObjectForKey:TGBridgeUserPhotoBigKey];
_kind = [aDecoder decodeInt32ForKey:TGBridgeUserKindKey];
_botKind = [aDecoder decodeInt32ForKey:TGBridgeUserBotKindKey];
_botVersion = [aDecoder decodeInt32ForKey:TGBridgeUserBotVersionKey];
_verified = [aDecoder decodeBoolForKey:TGBridgeUserVerifiedKey];
_about = [aDecoder decodeObjectForKey:TGBridgeUserAboutKey];
_userVersion = [aDecoder decodeInt32ForKey:TGBridgeUserVersionKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeUserIdentifierKey];
[aCoder encodeObject:self.firstName forKey:TGBridgeUserFirstNameKey];
[aCoder encodeObject:self.lastName forKey:TGBridgeUserLastNameKey];
[aCoder encodeObject:self.userName forKey:TGBridgeUserUserNameKey];
[aCoder encodeObject:self.phoneNumber forKey:TGBridgeUserPhoneNumberKey];
[aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeUserPrettyPhoneNumberKey];
[aCoder encodeBool:self.online forKey:TGBridgeUserOnlineKey];
[aCoder encodeDouble:self.lastSeen forKey:TGBridgeUserLastSeenKey];
[aCoder encodeObject:self.photoSmall forKey:TGBridgeUserPhotoSmallKey];
[aCoder encodeObject:self.photoBig forKey:TGBridgeUserPhotoBigKey];
[aCoder encodeInt32:self.kind forKey:TGBridgeUserKindKey];
[aCoder encodeInt32:self.botKind forKey:TGBridgeUserBotKindKey];
[aCoder encodeInt32:self.botVersion forKey:TGBridgeUserBotVersionKey];
[aCoder encodeBool:self.verified forKey:TGBridgeUserVerifiedKey];
[aCoder encodeObject:self.about forKey:TGBridgeUserAboutKey];
[aCoder encodeInt32:self.userVersion forKey:TGBridgeUserVersionKey];
}
- (instancetype)copyWithZone:(NSZone *)__unused zone
{
TGBridgeUser *user = [[TGBridgeUser alloc] init];
user->_identifier = self.identifier;
user->_firstName = self.firstName;
user->_lastName = self.lastName;
user->_userName = self.userName;
user->_phoneNumber = self.phoneNumber;
user->_prettyPhoneNumber = self.prettyPhoneNumber;
user->_online = self.online;
user->_lastSeen = self.lastSeen;
user->_photoSmall = self.photoSmall;
user->_photoBig = self.photoBig;
user->_kind = self.kind;
user->_botKind = self.botKind;
user->_botVersion = self.botVersion;
user->_verified = self.verified;
user->_about = self.about;
user->_userVersion = self.userVersion;
return user;
}
- (NSString *)displayName
{
NSString *firstName = self.firstName;
NSString *lastName = self.lastName;
if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0)
{
return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];
}
else if (firstName != nil && firstName.length != 0)
return firstName;
else if (lastName != nil && lastName.length != 0)
return lastName;
return @"";
}
- (bool)isBot
{
return (self.kind == TGBridgeUserKindBot || self.kind ==TGBridgeUserKindSmartBot);
}
- (TGBridgeUserChange *)changeFromUser:(TGBridgeUser *)user
{
NSMutableDictionary *fields = [[NSMutableDictionary alloc] init];
[self _compareString:self.firstName oldString:user.firstName dict:fields key:TGBridgeUserFirstNameKey];
[self _compareString:self.lastName oldString:user.lastName dict:fields key:TGBridgeUserLastNameKey];
[self _compareString:self.userName oldString:user.userName dict:fields key:TGBridgeUserUserNameKey];
[self _compareString:self.phoneNumber oldString:user.phoneNumber dict:fields key:TGBridgeUserPhoneNumberKey];
[self _compareString:self.prettyPhoneNumber oldString:user.prettyPhoneNumber dict:fields key:TGBridgeUserPrettyPhoneNumberKey];
if (self.online != user.online)
fields[TGBridgeUserOnlineKey] = @(self.online);
if (fabs(self.lastSeen - user.lastSeen) > DBL_EPSILON)
fields[TGBridgeUserLastSeenKey] = @(self.lastSeen);
[self _compareString:self.photoSmall oldString:user.photoSmall dict:fields key:TGBridgeUserPhotoSmallKey];
[self _compareString:self.photoBig oldString:user.photoBig dict:fields key:TGBridgeUserPhotoBigKey];
if (self.kind != user.kind)
fields[TGBridgeUserKindKey] = @(self.kind);
if (self.botKind != user.botKind)
fields[TGBridgeUserBotKindKey] = @(self.botKind);
if (self.botVersion != user.botVersion)
fields[TGBridgeUserBotVersionKey] = @(self.botVersion);
if (self.verified != user.verified)
fields[TGBridgeUserVerifiedKey] = @(self.verified);
if (fields.count == 0)
return nil;
return [[TGBridgeUserChange alloc] initWithUserIdentifier:user.identifier fields:fields];
}
- (void)_compareString:(NSString *)newString oldString:(NSString *)oldString dict:(NSMutableDictionary *)dict key:(NSString *)key
{
if (newString == nil && oldString == nil)
return;
if (![newString isEqualToString:oldString])
{
if (newString == nil)
dict[key] = [NSNull null];
else
dict[key] = newString;
}
}
- (TGBridgeUser *)userByApplyingChange:(TGBridgeUserChange *)change
{
if (change.userIdentifier != self.identifier)
return nil;
TGBridgeUser *user = [self copy];
NSString *firstNameChange = change.fields[TGBridgeUserFirstNameKey];
if (firstNameChange != nil)
user->_firstName = [self _stringForFieldChange:firstNameChange];
NSString *lastNameChange = change.fields[TGBridgeUserLastNameKey];
if (lastNameChange != nil)
user->_lastName = [self _stringForFieldChange:lastNameChange];
NSString *userNameChange = change.fields[TGBridgeUserUserNameKey];
if (userNameChange != nil)
user->_userName = [self _stringForFieldChange:userNameChange];
NSString *phoneNumberChange = change.fields[TGBridgeUserPhoneNumberKey];
if (phoneNumberChange != nil)
user->_phoneNumber = [self _stringForFieldChange:phoneNumberChange];
NSString *prettyPhoneNumberChange = change.fields[TGBridgeUserPrettyPhoneNumberKey];
if (prettyPhoneNumberChange != nil)
user->_prettyPhoneNumber = [self _stringForFieldChange:prettyPhoneNumberChange];
NSNumber *onlineChange = change.fields[TGBridgeUserOnlineKey];
if (onlineChange != nil)
user->_online = [onlineChange boolValue];
NSNumber *lastSeenChange = change.fields[TGBridgeUserLastSeenKey];
if (lastSeenChange != nil)
user->_lastSeen = [lastSeenChange doubleValue];
NSString *photoSmallChange = change.fields[TGBridgeUserPhotoSmallKey];
if (photoSmallChange != nil)
user->_photoSmall = [self _stringForFieldChange:photoSmallChange];
NSString *photoBigChange = change.fields[TGBridgeUserPhotoBigKey];
if (photoBigChange != nil)
user->_photoBig = [self _stringForFieldChange:photoBigChange];
NSNumber *kindChange = change.fields[TGBridgeUserKindKey];
if (kindChange != nil)
user->_kind = (int32_t)[kindChange intValue];
NSNumber *botKindChange = change.fields[TGBridgeUserBotKindKey];
if (botKindChange != nil)
user->_botKind = (int32_t)[botKindChange intValue];
NSNumber *botVersionChange = change.fields[TGBridgeUserBotVersionKey];
if (botVersionChange != nil)
user->_botVersion = (int32_t)[botVersionChange intValue];
NSNumber *verifiedChange = change.fields[TGBridgeUserVerifiedKey];
if (verifiedChange != nil)
user->_verified = [verifiedChange boolValue];
return user;
}
- (NSString *)_stringForFieldChange:(NSString *)fieldChange
{
if ([fieldChange isKindOfClass:[NSNull class]])
return nil;
return fieldChange;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
return self.identifier == ((TGBridgeUser *)object).identifier;
}
@end
NSString *const TGBridgeUserChangeIdentifierKey = @"userIdentifier";
NSString *const TGBridgeUserChangeFieldsKey = @"fields";
@implementation TGBridgeUserChange
- (instancetype)initWithUserIdentifier:(int32_t)userIdentifier fields:(NSDictionary *)fields
{
self = [super init];
if (self != nil)
{
_userIdentifier = userIdentifier;
_fields = fields;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_userIdentifier = [aDecoder decodeInt32ForKey:TGBridgeUserChangeIdentifierKey];
_fields = [aDecoder decodeObjectForKey:TGBridgeUserChangeFieldsKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.userIdentifier forKey:TGBridgeUserChangeIdentifierKey];
[aCoder encodeObject:self.fields forKey:TGBridgeUserChangeFieldsKey];
}
@end

View File

@ -0,0 +1,39 @@
#import "TGBridgeVideoMediaAttachment.h"
#import <UIKit/UIKit.h>
const NSInteger TGBridgeVideoMediaAttachmentType = 0x338EAA20;
NSString *const TGBridgeVideoMediaVideoIdKey = @"videoId";
NSString *const TGBridgeVideoMediaDimensionsKey = @"dimensions";
NSString *const TGBridgeVideoMediaDurationKey = @"duration";
NSString *const TGBridgeVideoMediaRoundKey = @"round";
@implementation TGBridgeVideoMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_videoId = [aDecoder decodeInt64ForKey:TGBridgeVideoMediaVideoIdKey];
_dimensions = [aDecoder decodeCGSizeForKey:TGBridgeVideoMediaDimensionsKey];
_duration = [aDecoder decodeInt32ForKey:TGBridgeVideoMediaDurationKey];
_round = [aDecoder decodeBoolForKey:TGBridgeVideoMediaRoundKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.videoId forKey:TGBridgeVideoMediaVideoIdKey];
[aCoder encodeCGSize:self.dimensions forKey:TGBridgeVideoMediaDimensionsKey];
[aCoder encodeInt32:self.duration forKey:TGBridgeVideoMediaDurationKey];
[aCoder encodeBool:self.round forKey:TGBridgeVideoMediaRoundKey];
}
+ (NSInteger)mediaType
{
return TGBridgeVideoMediaAttachmentType;
}
@end

View File

@ -0,0 +1,65 @@
#import "TGBridgeWebPageMediaAttachment.h"
#import "TGBridgeImageMediaAttachment.h"
#import <UIKit/UIKit.h>
const NSInteger TGBridgeWebPageMediaAttachmentType = 0x584197af;
NSString *const TGBridgeWebPageMediaWebPageIdKey = @"webPageId";
NSString *const TGBridgeWebPageMediaUrlKey = @"url";
NSString *const TGBridgeWebPageMediaDisplayUrlKey = @"displayUrl";
NSString *const TGBridgeWebPageMediaPageTypeKey = @"pageType";
NSString *const TGBridgeWebPageMediaSiteNameKey = @"siteName";
NSString *const TGBridgeWebPageMediaTitleKey = @"title";
NSString *const TGBridgeWebPageMediaPageDescriptionKey = @"pageDescription";
NSString *const TGBridgeWebPageMediaPhotoKey = @"photo";
NSString *const TGBridgeWebPageMediaEmbedUrlKey = @"embedUrl";
NSString *const TGBridgeWebPageMediaEmbedTypeKey = @"embedType";
NSString *const TGBridgeWebPageMediaEmbedSizeKey = @"embedSize";
NSString *const TGBridgeWebPageMediaDurationKey = @"duration";
NSString *const TGBridgeWebPageMediaAuthorKey = @"author";
@implementation TGBridgeWebPageMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_webPageId = [aDecoder decodeInt64ForKey:TGBridgeWebPageMediaWebPageIdKey];
_url = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaUrlKey];
_displayUrl = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaDisplayUrlKey];
_pageType = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPageTypeKey];
_siteName = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaSiteNameKey];
_title = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaTitleKey];
_pageDescription = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPageDescriptionKey];
_photo = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaPhotoKey];
_embedUrl = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaEmbedUrlKey];
_embedSize = [aDecoder decodeCGSizeForKey:TGBridgeWebPageMediaEmbedSizeKey];
_duration = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaDurationKey];
_author = [aDecoder decodeObjectForKey:TGBridgeWebPageMediaAuthorKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.webPageId forKey:TGBridgeWebPageMediaWebPageIdKey];
[aCoder encodeObject:self.url forKey:TGBridgeWebPageMediaUrlKey];
[aCoder encodeObject:self.displayUrl forKey:TGBridgeWebPageMediaDisplayUrlKey];
[aCoder encodeObject:self.pageType forKey:TGBridgeWebPageMediaPageTypeKey];
[aCoder encodeObject:self.siteName forKey:TGBridgeWebPageMediaSiteNameKey];
[aCoder encodeObject:self.title forKey:TGBridgeWebPageMediaTitleKey];
[aCoder encodeObject:self.pageDescription forKey:TGBridgeWebPageMediaPageDescriptionKey];
[aCoder encodeObject:self.photo forKey:TGBridgeWebPageMediaPhotoKey];
[aCoder encodeObject:self.embedUrl forKey:TGBridgeWebPageMediaEmbedUrlKey];
[aCoder encodeCGSize:self.embedSize forKey:TGBridgeWebPageMediaEmbedSizeKey];
[aCoder encodeObject:self.duration forKey:TGBridgeWebPageMediaDurationKey];
[aCoder encodeObject:self.author forKey:TGBridgeWebPageMediaAuthorKey];
}
+ (NSInteger)mediaType
{
return TGBridgeWebPageMediaAttachmentType;
}
@end

View File

@ -0,0 +1,36 @@
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
typedef NS_ENUM(NSUInteger, TGBridgeMessageAction) {
TGBridgeMessageActionNone = 0,
TGBridgeMessageActionChatEditTitle = 1,
TGBridgeMessageActionChatAddMember = 2,
TGBridgeMessageActionChatDeleteMember = 3,
TGBridgeMessageActionCreateChat = 4,
TGBridgeMessageActionChatEditPhoto = 5,
TGBridgeMessageActionContactRequest = 6,
TGBridgeMessageActionAcceptContactRequest = 7,
TGBridgeMessageActionContactRegistered = 8,
TGBridgeMessageActionUserChangedPhoto = 9,
TGBridgeMessageActionEncryptedChatRequest = 10,
TGBridgeMessageActionEncryptedChatAccept = 11,
TGBridgeMessageActionEncryptedChatDecline = 12,
TGBridgeMessageActionEncryptedChatMessageLifetime = 13,
TGBridgeMessageActionEncryptedChatScreenshot = 14,
TGBridgeMessageActionEncryptedChatMessageScreenshot = 15,
TGBridgeMessageActionCreateBroadcastList = 16,
TGBridgeMessageActionJoinedByLink = 17,
TGBridgeMessageActionChannelCreated = 18,
TGBridgeMessageActionChannelCommentsStatusChanged = 19,
TGBridgeMessageActionChannelInviter = 20,
TGBridgeMessageActionGroupMigratedTo = 21,
TGBridgeMessageActionGroupDeactivated = 22,
TGBridgeMessageActionGroupActivated = 23,
TGBridgeMessageActionChannelMigratedFrom = 24
};
@interface TGBridgeActionMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) TGBridgeMessageAction actionType;
@property (nonatomic, strong) NSDictionary *actionData;
@end

View File

@ -0,0 +1,33 @@
#import "TGBridgeActionMediaAttachment.h"
#import "TGBridgeImageMediaAttachment.h"
const NSInteger TGBridgeActionMediaAttachmentType = 0x1167E28B;
NSString *const TGBridgeActionMediaTypeKey = @"actionType";
NSString *const TGBridgeActionMediaDataKey = @"actionData";
@implementation TGBridgeActionMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_actionType = (TGBridgeMessageAction)[aDecoder decodeInt32ForKey:TGBridgeActionMediaTypeKey];
_actionData = [aDecoder decodeObjectForKey:TGBridgeActionMediaDataKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.actionType forKey:TGBridgeActionMediaTypeKey];
[aCoder encodeObject:self.actionData forKey:TGBridgeActionMediaDataKey];
}
+ (NSInteger)mediaType
{
return TGBridgeActionMediaAttachmentType;
}
@end

View File

@ -0,0 +1,16 @@
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
@interface TGBridgeAudioMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t audioId;
@property (nonatomic, assign) int64_t accessHash;
@property (nonatomic, assign) int32_t datacenterId;
@property (nonatomic, assign) int64_t localAudioId;
@property (nonatomic, assign) int32_t duration;
@property (nonatomic, assign) int32_t fileSize;
- (int64_t)identifier;
@end

View File

@ -0,0 +1,65 @@
#import "TGBridgeAudioMediaAttachment.h"
const NSInteger TGBridgeAudioMediaAttachmentType = 0x3A0E7A32;
NSString *const TGBridgeAudioMediaAudioIdKey = @"audioId";
NSString *const TGBridgeAudioMediaAccessHashKey = @"accessHash";
NSString *const TGBridgeAudioMediaLocalIdKey = @"localId";
NSString *const TGBridgeAudioMediaDatacenterIdKey = @"datacenterId";
NSString *const TGBridgeAudioMediaDurationKey = @"duration";
NSString *const TGBridgeAudioMediaFileSizeKey = @"fileSize";
@implementation TGBridgeAudioMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_audioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAudioIdKey];
_accessHash = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaAccessHashKey];
_localAudioId = [aDecoder decodeInt64ForKey:TGBridgeAudioMediaLocalIdKey];
_datacenterId = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDatacenterIdKey];
_duration = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaDurationKey];
_fileSize = [aDecoder decodeInt32ForKey:TGBridgeAudioMediaFileSizeKey];
}
return self;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder
{
[aCoder encodeInt64:self.audioId forKey:TGBridgeAudioMediaAudioIdKey];
[aCoder encodeInt64:self.accessHash forKey:TGBridgeAudioMediaAccessHashKey];
[aCoder encodeInt64:self.localAudioId forKey:TGBridgeAudioMediaLocalIdKey];
[aCoder encodeInt32:self.datacenterId forKey:TGBridgeAudioMediaDatacenterIdKey];
[aCoder encodeInt32:self.duration forKey:TGBridgeAudioMediaDurationKey];
[aCoder encodeInt32:self.fileSize forKey:TGBridgeAudioMediaFileSizeKey];
}
- (int64_t)identifier
{
if (self.localAudioId != 0)
return self.localAudioId;
return self.audioId;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeAudioMediaAttachment *audio = (TGBridgeAudioMediaAttachment *)object;
return (self.audioId == audio.audioId || self.localAudioId == audio.localAudioId);
}
+ (NSInteger)mediaType
{
return TGBridgeAudioMediaAttachmentType;
}
@end

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface TGBridgeBotCommandInfo : NSObject <NSCoding>
{
NSString *_command;
NSString *_commandDescription;
}
@property (nonatomic, readonly) NSString *command;
@property (nonatomic, readonly) NSString *commandDescription;
@end

View File

@ -0,0 +1,25 @@
#import "TGBridgeBotCommandInfo.h"
NSString *const TGBridgeBotCommandInfoCommandKey = @"command";
NSString *const TGBridgeBotCommandDescriptionKey = @"commandDescription";
@implementation TGBridgeBotCommandInfo
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_command = [aDecoder decodeObjectForKey:TGBridgeBotCommandInfoCommandKey];
_commandDescription = [aDecoder decodeObjectForKey:TGBridgeBotCommandDescriptionKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.command forKey:TGBridgeBotCommandInfoCommandKey];
[aCoder encodeObject:self.commandDescription forKey:TGBridgeBotCommandDescriptionKey];
}
@end

View File

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface TGBridgeBotInfo : NSObject <NSCoding>
{
NSString *_shortDescription;
NSArray *_commandList;
}
@property (nonatomic, readonly) NSString *shortDescription;
@property (nonatomic, readonly) NSArray *commandList;
@end

View File

@ -0,0 +1,25 @@
#import "TGBridgeBotInfo.h"
NSString *const TGBridgeBotInfoShortDescriptionKey = @"shortDescription";
NSString *const TGBridgeBotInfoCommandListKey = @"commandList";
@implementation TGBridgeBotInfo
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_shortDescription = [aDecoder decodeObjectForKey:TGBridgeBotInfoShortDescriptionKey];
_commandList = [aDecoder decodeObjectForKey:TGBridgeBotInfoCommandListKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.shortDescription forKey:TGBridgeBotInfoShortDescriptionKey];
[aCoder encodeObject:self.commandList forKey:TGBridgeBotInfoCommandListKey];
}
@end

View File

@ -0,0 +1,46 @@
#import <WatchCommonWatch/TGBridgeCommon.h>
#import <WatchCommonWatch/TGBridgeMessage.h>
@interface TGBridgeChat : NSObject <NSCoding>
@property (nonatomic) int64_t identifier;
@property (nonatomic) NSTimeInterval date;
@property (nonatomic) int32_t fromUid;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) NSArray *media;
@property (nonatomic) bool outgoing;
@property (nonatomic) bool unread;
@property (nonatomic) bool deliveryError;
@property (nonatomic) TGBridgeMessageDeliveryState deliveryState;
@property (nonatomic) int32_t unreadCount;
@property (nonatomic) bool isBroadcast;
@property (nonatomic, strong) NSString *groupTitle;
@property (nonatomic, strong) NSString *groupPhotoSmall;
@property (nonatomic, strong) NSString *groupPhotoBig;
@property (nonatomic) bool isGroup;
@property (nonatomic) bool hasLeftGroup;
@property (nonatomic) bool isKickedFromGroup;
@property (nonatomic) bool isChannel;
@property (nonatomic) bool isChannelGroup;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *about;
@property (nonatomic) bool verified;
@property (nonatomic) int32_t participantsCount;
@property (nonatomic, strong) NSArray *participants;
- (NSIndexSet *)involvedUserIds;
- (NSIndexSet *)participantsUserIds;
@end
extern NSString *const TGBridgeChatKey;
extern NSString *const TGBridgeChatsArrayKey;

View File

@ -0,0 +1,139 @@
#import "TGBridgeChat.h"
#import "TGBridgePeerIdAdapter.h"
NSString *const TGBridgeChatIdentifierKey = @"identifier";
NSString *const TGBridgeChatDateKey = @"date";
NSString *const TGBridgeChatFromUidKey = @"fromUid";
NSString *const TGBridgeChatTextKey = @"text";
NSString *const TGBridgeChatOutgoingKey = @"outgoing";
NSString *const TGBridgeChatUnreadKey = @"unread";
NSString *const TGBridgeChatMediaKey = @"media";
NSString *const TGBridgeChatUnreadCountKey = @"unreadCount";
NSString *const TGBridgeChatGroupTitleKey = @"groupTitle";
NSString *const TGBridgeChatGroupPhotoSmallKey = @"groupPhotoSmall";
NSString *const TGBridgeChatGroupPhotoBigKey = @"groupPhotoBig";
NSString *const TGBridgeChatIsGroupKey = @"isGroup";
NSString *const TGBridgeChatHasLeftGroupKey = @"hasLeftGroup";
NSString *const TGBridgeChatIsKickedFromGroupKey = @"isKickedFromGroup";
NSString *const TGBridgeChatIsChannelKey = @"isChannel";
NSString *const TGBridgeChatIsChannelGroupKey = @"isChannelGroup";
NSString *const TGBridgeChatUserNameKey = @"userName";
NSString *const TGBridgeChatAboutKey = @"about";
NSString *const TGBridgeChatVerifiedKey = @"verified";
NSString *const TGBridgeChatGroupParticipantsCountKey = @"participantsCount";
NSString *const TGBridgeChatGroupParticipantsKey = @"participants";
NSString *const TGBridgeChatDeliveryStateKey = @"deliveryState";
NSString *const TGBridgeChatDeliveryErrorKey = @"deliveryError";
NSString *const TGBridgeChatKey = @"chat";
NSString *const TGBridgeChatsArrayKey = @"chats";
@implementation TGBridgeChat
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeChatIdentifierKey];
_date = [aDecoder decodeDoubleForKey:TGBridgeChatDateKey];
_fromUid = [aDecoder decodeInt32ForKey:TGBridgeChatFromUidKey];
_text = [aDecoder decodeObjectForKey:TGBridgeChatTextKey];
_outgoing = [aDecoder decodeBoolForKey:TGBridgeChatOutgoingKey];
_unread = [aDecoder decodeBoolForKey:TGBridgeChatUnreadKey];
_unreadCount = [aDecoder decodeInt32ForKey:TGBridgeChatUnreadCountKey];
_deliveryState = [aDecoder decodeInt32ForKey:TGBridgeChatDeliveryStateKey];
_deliveryError = [aDecoder decodeBoolForKey:TGBridgeChatDeliveryErrorKey];
_media = [aDecoder decodeObjectForKey:TGBridgeChatMediaKey];
_groupTitle = [aDecoder decodeObjectForKey:TGBridgeChatGroupTitleKey];
_groupPhotoSmall = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoSmallKey];
_groupPhotoBig = [aDecoder decodeObjectForKey:TGBridgeChatGroupPhotoBigKey];
_isGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsGroupKey];
_hasLeftGroup = [aDecoder decodeBoolForKey:TGBridgeChatHasLeftGroupKey];
_isKickedFromGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsKickedFromGroupKey];
_isChannel = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelKey];
_isChannelGroup = [aDecoder decodeBoolForKey:TGBridgeChatIsChannelGroupKey];
_userName = [aDecoder decodeObjectForKey:TGBridgeChatUserNameKey];
_about = [aDecoder decodeObjectForKey:TGBridgeChatAboutKey];
_verified = [aDecoder decodeBoolForKey:TGBridgeChatVerifiedKey];
_participantsCount = [aDecoder decodeInt32ForKey:TGBridgeChatGroupParticipantsCountKey];
_participants = [aDecoder decodeObjectForKey:TGBridgeChatGroupParticipantsKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeChatIdentifierKey];
[aCoder encodeDouble:self.date forKey:TGBridgeChatDateKey];
[aCoder encodeInt32:self.fromUid forKey:TGBridgeChatFromUidKey];
[aCoder encodeObject:self.text forKey:TGBridgeChatTextKey];
[aCoder encodeBool:self.outgoing forKey:TGBridgeChatOutgoingKey];
[aCoder encodeBool:self.unread forKey:TGBridgeChatUnreadKey];
[aCoder encodeInt32:self.unreadCount forKey:TGBridgeChatUnreadCountKey];
[aCoder encodeInt32:self.deliveryState forKey:TGBridgeChatDeliveryStateKey];
[aCoder encodeBool:self.deliveryError forKey:TGBridgeChatDeliveryErrorKey];
[aCoder encodeObject:self.media forKey:TGBridgeChatMediaKey];
[aCoder encodeObject:self.groupTitle forKey:TGBridgeChatGroupTitleKey];
[aCoder encodeObject:self.groupPhotoSmall forKey:TGBridgeChatGroupPhotoSmallKey];
[aCoder encodeObject:self.groupPhotoBig forKey:TGBridgeChatGroupPhotoBigKey];
[aCoder encodeBool:self.isGroup forKey:TGBridgeChatIsGroupKey];
[aCoder encodeBool:self.hasLeftGroup forKey:TGBridgeChatHasLeftGroupKey];
[aCoder encodeBool:self.isKickedFromGroup forKey:TGBridgeChatIsKickedFromGroupKey];
[aCoder encodeBool:self.isChannel forKey:TGBridgeChatIsChannelKey];
[aCoder encodeBool:self.isChannelGroup forKey:TGBridgeChatIsChannelGroupKey];
[aCoder encodeObject:self.userName forKey:TGBridgeChatUserNameKey];
[aCoder encodeObject:self.about forKey:TGBridgeChatAboutKey];
[aCoder encodeBool:self.verified forKey:TGBridgeChatVerifiedKey];
[aCoder encodeInt32:self.participantsCount forKey:TGBridgeChatGroupParticipantsCountKey];
[aCoder encodeObject:self.participants forKey:TGBridgeChatGroupParticipantsKey];
}
- (NSIndexSet *)involvedUserIds
{
NSMutableIndexSet *userIds = [[NSMutableIndexSet alloc] init];
if (!self.isGroup && !self.isChannel && self.identifier != 0)
[userIds addIndex:(int32_t)self.identifier];
if ((!self.isChannel || self.isChannelGroup) && self.fromUid != self.identifier && self.fromUid != 0 && !TGPeerIdIsChannel(self.fromUid) && self.fromUid > 0)
[userIds addIndex:(int32_t)self.fromUid];
for (TGBridgeMediaAttachment *attachment in self.media)
{
if ([attachment isKindOfClass:[TGBridgeActionMediaAttachment class]])
{
TGBridgeActionMediaAttachment *actionAttachment = (TGBridgeActionMediaAttachment *)attachment;
if (actionAttachment.actionData[@"uid"] != nil)
[userIds addIndex:[actionAttachment.actionData[@"uid"] integerValue]];
}
}
return userIds;
}
- (NSIndexSet *)participantsUserIds
{
NSMutableIndexSet *userIds = [[NSMutableIndexSet alloc] init];
for (NSNumber *uid in self.participants)
[userIds addIndex:uid.unsignedIntegerValue];
return userIds;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
return self.identifier == ((TGBridgeChat *)object).identifier;
}
@end

View File

@ -0,0 +1,14 @@
#import <WatchCommonWatch/TGBridgeCommon.h>
@class SSignal;
@interface TGBridgeChatMessages : NSObject <NSCoding>
{
NSArray *_messages;
}
@property (nonatomic, readonly) NSArray *messages;
@end
extern NSString *const TGBridgeChatMessageListViewKey;

View File

@ -0,0 +1,27 @@
#import "TGBridgeChatMessages.h"
#import "TGBridgeMessage.h"
NSString *const TGBridgeChatMessageListViewMessagesKey = @"messages";
NSString *const TGBridgeChatMessageListViewEarlierMessageIdKey = @"earlier";
NSString *const TGBridgeChatMessageListViewLaterMessageIdKey = @"later";
NSString *const TGBridgeChatMessageListViewKey = @"messageListView";
@implementation TGBridgeChatMessages
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_messages = [aDecoder decodeObjectForKey:TGBridgeChatMessageListViewMessagesKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.messages forKey:TGBridgeChatMessageListViewMessagesKey];
}
@end

View File

@ -0,0 +1,95 @@
#import <Foundation/Foundation.h>
extern NSString *const TGBridgeIncomingFileTypeKey;
extern NSString *const TGBridgeIncomingFileIdentifierKey;
extern NSString *const TGBridgeIncomingFileRandomIdKey;
extern NSString *const TGBridgeIncomingFilePeerIdKey;
extern NSString *const TGBridgeIncomingFileReplyToMidKey;
extern NSString *const TGBridgeIncomingFileTypeAudio;
extern NSString *const TGBridgeIncomingFileTypeImage;
@interface TGBridgeSubscription : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t identifier;
@property (nonatomic, readonly, strong) NSString *name;
@property (nonatomic, readonly) bool isOneTime;
@property (nonatomic, readonly) bool renewable;
@property (nonatomic, readonly) bool dropPreviouslyQueued;
@property (nonatomic, readonly) bool synchronous;
- (void)_serializeParametersWithCoder:(NSCoder *)aCoder;
- (void)_unserializeParametersWithCoder:(NSCoder *)aDecoder;
+ (NSString *)subscriptionName;
@end
@interface TGBridgeDisposal : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t identifier;
- (instancetype)initWithIdentifier:(int64_t)identifier;
@end
@interface TGBridgeFile : NSObject <NSCoding>
@property (nonatomic, readonly, strong) NSData *data;
@property (nonatomic, readonly, strong) NSDictionary *metadata;
- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata;
@end
@interface TGBridgePing : NSObject <NSCoding>
@property (nonatomic, readonly) int32_t sessionId;
- (instancetype)initWithSessionId:(int32_t)sessionId;
@end
@interface TGBridgeSubscriptionListRequest : NSObject <NSCoding>
@property (nonatomic, readonly) int32_t sessionId;
- (instancetype)initWithSessionId:(int32_t)sessionId;
@end
@interface TGBridgeSubscriptionList : NSObject <NSCoding>
@property (nonatomic, readonly, strong) NSArray *subscriptions;
- (instancetype)initWithArray:(NSArray *)array;
@end
typedef NS_ENUM(int32_t, TGBridgeResponseType) {
TGBridgeResponseTypeUndefined,
TGBridgeResponseTypeNext,
TGBridgeResponseTypeFailed,
TGBridgeResponseTypeCompleted
};
@interface TGBridgeResponse : NSObject <NSCoding>
@property (nonatomic, readonly) int64_t subscriptionIdentifier;
@property (nonatomic, readonly) TGBridgeResponseType type;
@property (nonatomic, readonly, strong) id next;
@property (nonatomic, readonly, strong) NSString *error;
+ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription;
+ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription;
+ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription;
@end

View File

@ -0,0 +1,295 @@
#import "TGBridgeCommon.h"
NSString *const TGBridgeIncomingFileTypeKey = @"type";
NSString *const TGBridgeIncomingFileIdentifierKey = @"identifier";
NSString *const TGBridgeIncomingFileRandomIdKey = @"randomId";
NSString *const TGBridgeIncomingFilePeerIdKey = @"peerId";
NSString *const TGBridgeIncomingFileReplyToMidKey = @"replyToMid";
NSString *const TGBridgeIncomingFileTypeAudio = @"audio";
NSString *const TGBridgeIncomingFileTypeImage = @"image";
NSString *const TGBridgeResponseSubscriptionIdentifier = @"identifier";
NSString *const TGBridgeResponseTypeKey = @"type";
NSString *const TGBridgeResponseNextKey = @"next";
NSString *const TGBridgeResponseErrorKey = @"error";
@implementation TGBridgeResponse
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_subscriptionIdentifier = [aDecoder decodeInt64ForKey:TGBridgeResponseSubscriptionIdentifier];
_type = [aDecoder decodeInt32ForKey:TGBridgeResponseTypeKey];
_next = [aDecoder decodeObjectForKey:TGBridgeResponseNextKey];
_error = [aDecoder decodeObjectForKey:TGBridgeResponseErrorKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.subscriptionIdentifier forKey:TGBridgeResponseSubscriptionIdentifier];
[aCoder encodeInt32:self.type forKey:TGBridgeResponseTypeKey];
[aCoder encodeObject:self.next forKey:TGBridgeResponseNextKey];
[aCoder encodeObject:self.error forKey:TGBridgeResponseErrorKey];
}
+ (TGBridgeResponse *)single:(id)next forSubscription:(TGBridgeSubscription *)subscription
{
TGBridgeResponse *response = [[TGBridgeResponse alloc] init];
response->_subscriptionIdentifier = subscription.identifier;
response->_type = TGBridgeResponseTypeNext;
response->_next = next;
return response;
}
+ (TGBridgeResponse *)fail:(id)error forSubscription:(TGBridgeSubscription *)subscription
{
TGBridgeResponse *response = [[TGBridgeResponse alloc] init];
response->_subscriptionIdentifier = subscription.identifier;
response->_type = TGBridgeResponseTypeFailed;
response->_error = error;
return response;
}
+ (TGBridgeResponse *)completeForSubscription:(TGBridgeSubscription *)subscription
{
TGBridgeResponse *response = [[TGBridgeResponse alloc] init];
response->_subscriptionIdentifier = subscription.identifier;
response->_type = TGBridgeResponseTypeCompleted;
return response;
}
@end
NSString *const TGBridgeSubscriptionIdentifierKey = @"identifier";
NSString *const TGBridgeSubscriptionNameKey = @"name";
NSString *const TGBridgeSubscriptionParametersKey = @"parameters";
@implementation TGBridgeSubscription
- (instancetype)init
{
self = [super init];
if (self != nil)
{
int64_t randomId = 0;
arc4random_buf(&randomId, sizeof(int64_t));
_identifier = randomId;
_name = [[self class] subscriptionName];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey];
_name = [aDecoder decodeObjectForKey:TGBridgeSubscriptionNameKey];
[self _unserializeParametersWithCoder:aDecoder];
}
return self;
}
- (bool)synchronous
{
return false;
}
- (bool)renewable
{
return true;
}
- (bool)dropPreviouslyQueued
{
return false;
}
- (void)_serializeParametersWithCoder:(NSCoder *)__unused aCoder
{
}
- (void)_unserializeParametersWithCoder:(NSCoder *)__unused aDecoder
{
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey];
[aCoder encodeObject:self.name forKey:TGBridgeSubscriptionNameKey];
[self _serializeParametersWithCoder:aCoder];
}
+ (NSString *)subscriptionName
{
return nil;
}
@end
@implementation TGBridgeDisposal
- (instancetype)initWithIdentifier:(int64_t)identifier
{
self = [super init];
if (self != nil)
{
_identifier = identifier;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_identifier = [aDecoder decodeInt64ForKey:TGBridgeSubscriptionIdentifierKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.identifier forKey:TGBridgeSubscriptionIdentifierKey];
}
@end
NSString *const TGBridgeFileDataKey = @"data";
NSString *const TGBridgeFileMetadataKey = @"metadata";
@implementation TGBridgeFile
- (instancetype)initWithData:(NSData *)data metadata:(NSDictionary *)metadata
{
self = [super init];
if (self != nil)
{
_data = data;
_metadata = metadata;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_data = [aDecoder decodeObjectForKey:TGBridgeFileDataKey];
_metadata = [aDecoder decodeObjectForKey:TGBridgeFileMetadataKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.data forKey:TGBridgeFileDataKey];
[aCoder encodeObject:self.metadata forKey:TGBridgeFileMetadataKey];
}
@end
NSString *const TGBridgeSessionIdKey = @"sessionId";
@implementation TGBridgePing
- (instancetype)initWithSessionId:(int32_t)sessionId
{
self = [super init];
if (self != nil)
{
_sessionId = sessionId;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey];
}
@end
@implementation TGBridgeSubscriptionListRequest
- (instancetype)initWithSessionId:(int32_t)sessionId
{
self = [super init];
if (self != nil)
{
_sessionId = sessionId;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_sessionId = [aDecoder decodeInt32ForKey:TGBridgeSessionIdKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.sessionId forKey:TGBridgeSessionIdKey];
}
@end
NSString *const TGBridgeSubscriptionListSubscriptionsKey = @"subscriptions";
@implementation TGBridgeSubscriptionList
- (instancetype)initWithArray:(NSArray *)array
{
self = [super init];
if (self != nil)
{
_subscriptions = array;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_subscriptions = [aDecoder decodeObjectForKey:TGBridgeSubscriptionListSubscriptionsKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.subscriptions forKey:TGBridgeSubscriptionListSubscriptionsKey];
}
@end

View File

@ -0,0 +1,13 @@
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
@interface TGBridgeContactMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int32_t uid;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *phoneNumber;
@property (nonatomic, strong) NSString *prettyPhoneNumber;
- (NSString *)displayName;
@end

View File

@ -0,0 +1,60 @@
#import "TGBridgeContactMediaAttachment.h"
//#import "../Extension/TGStringUtils.h"
const NSInteger TGBridgeContactMediaAttachmentType = 0xB90A5663;
NSString *const TGBridgeContactMediaUidKey = @"uid";
NSString *const TGBridgeContactMediaFirstNameKey = @"firstName";
NSString *const TGBridgeContactMediaLastNameKey = @"lastName";
NSString *const TGBridgeContactMediaPhoneNumberKey = @"phoneNumber";
NSString *const TGBridgeContactMediaPrettyPhoneNumberKey = @"prettyPhoneNumber";
@implementation TGBridgeContactMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_uid = [aDecoder decodeInt32ForKey:TGBridgeContactMediaUidKey];
_firstName = [aDecoder decodeObjectForKey:TGBridgeContactMediaFirstNameKey];
_lastName = [aDecoder decodeObjectForKey:TGBridgeContactMediaLastNameKey];
_phoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPhoneNumberKey];
_prettyPhoneNumber = [aDecoder decodeObjectForKey:TGBridgeContactMediaPrettyPhoneNumberKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt32:self.uid forKey:TGBridgeContactMediaUidKey];
[aCoder encodeObject:self.firstName forKey:TGBridgeContactMediaFirstNameKey];
[aCoder encodeObject:self.lastName forKey:TGBridgeContactMediaLastNameKey];
[aCoder encodeObject:self.phoneNumber forKey:TGBridgeContactMediaPhoneNumberKey];
[aCoder encodeObject:self.prettyPhoneNumber forKey:TGBridgeContactMediaPrettyPhoneNumberKey];
}
- (NSString *)displayName
{
NSString *firstName = self.firstName;
NSString *lastName = self.lastName;
if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0)
{
return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName];
}
else if (firstName != nil && firstName.length != 0)
return firstName;
else if (lastName != nil && lastName.length != 0)
return lastName;
return @"";
}
+ (NSInteger)mediaType
{
return TGBridgeContactMediaAttachmentType;
}
@end

View File

@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
@interface TGBridgeContext : NSObject
@property (nonatomic, readonly) bool authorized;
@property (nonatomic, readonly) int32_t userId;
@property (nonatomic, readonly) bool micAccessAllowed;
@property (nonatomic, readonly) NSDictionary *preheatData;
@property (nonatomic, readonly) NSInteger preheatVersion;
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
- (NSDictionary *)dictionary;
- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId;
- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data;
- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed;
@end

View File

@ -0,0 +1,101 @@
#import "TGBridgeContext.h"
#import "TGBridgeCommon.h"
//#import "TGWatchCommon.h"
NSString *const TGBridgeContextAuthorized = @"authorized";
NSString *const TGBridgeContextUserId = @"userId";
NSString *const TGBridgeContextMicAccessAllowed = @"micAccessAllowed";
NSString *const TGBridgeContextStartupData = @"startupData";
NSString *const TGBridgeContextStartupDataVersion = @"version";
@implementation TGBridgeContext
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self != nil)
{
_authorized = [dictionary[TGBridgeContextAuthorized] boolValue];
_userId = (int32_t)[dictionary[TGBridgeContextUserId] intValue];
_micAccessAllowed = [dictionary[TGBridgeContextMicAccessAllowed] boolValue];
if (dictionary[TGBridgeContextStartupData] != nil) {
_preheatData = [NSKeyedUnarchiver unarchiveObjectWithData:dictionary[TGBridgeContextStartupData]];
_preheatVersion = [dictionary[TGBridgeContextStartupDataVersion] integerValue];
}
}
return self;
}
- (NSDictionary *)dictionary
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[TGBridgeContextAuthorized] = @(self.authorized);
dictionary[TGBridgeContextUserId] = @(self.userId);
dictionary[TGBridgeContextMicAccessAllowed] = @(self.micAccessAllowed);
if (self.preheatData != nil) {
dictionary[TGBridgeContextStartupData] = [NSKeyedArchiver archivedDataWithRootObject:self.preheatData];
dictionary[TGBridgeContextStartupDataVersion] = @(self.preheatVersion);
}
return dictionary;
}
- (TGBridgeContext *)updatedWithAuthorized:(bool)authorized peerId:(int32_t)peerId
{
TGBridgeContext *context = [[TGBridgeContext alloc] init];
context->_authorized = authorized;
context->_userId = peerId;
context->_micAccessAllowed = self.micAccessAllowed;
if (authorized) {
context->_preheatData = self.preheatData;
context->_preheatVersion = self.preheatVersion;
}
return context;
}
- (TGBridgeContext *)updatedWithPreheatData:(NSDictionary *)data
{
TGBridgeContext *context = [[TGBridgeContext alloc] init];
context->_authorized = self.authorized;
context->_userId = self.userId;
context->_micAccessAllowed = self.micAccessAllowed;
if (data != nil) {
context->_preheatData = data;
context->_preheatVersion = (int32_t)[NSDate date].timeIntervalSinceReferenceDate;
}
return context;
}
- (TGBridgeContext *)updatedWithMicAccessAllowed:(bool)allowed
{
TGBridgeContext *context = [[TGBridgeContext alloc] init];
context->_authorized = self.authorized;
context->_userId = self.userId;
context->_micAccessAllowed = allowed;
context->_preheatData = self.preheatData;
context->_preheatVersion = self.preheatVersion;
return context;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return true;
if (!object || ![object isKindOfClass:[self class]])
return false;
TGBridgeContext *context = (TGBridgeContext *)object;
if (context.authorized != self.authorized)
return false;
if (context.userId != self.userId)
return false;
if (context.micAccessAllowed != self.micAccessAllowed)
return false;
if (context.preheatVersion != self.preheatVersion)
return false;
return true;
}
@end

View File

@ -0,0 +1,23 @@
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
@interface TGBridgeDocumentMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t documentId;
@property (nonatomic, assign) int64_t localDocumentId;
@property (nonatomic, assign) int32_t fileSize;
@property (nonatomic, strong) NSString *fileName;
@property (nonatomic, strong) NSValue *imageSize;
@property (nonatomic, assign) bool isAnimated;
@property (nonatomic, assign) bool isSticker;
@property (nonatomic, strong) NSString *stickerAlt;
@property (nonatomic, assign) int64_t stickerPackId;
@property (nonatomic, assign) int64_t stickerPackAccessHash;
@property (nonatomic, assign) bool isVoice;
@property (nonatomic, assign) bool isAudio;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *performer;
@property (nonatomic, assign) int32_t duration;
@end

View File

@ -0,0 +1,84 @@
#import "TGBridgeDocumentMediaAttachment.h"
const NSInteger TGBridgeDocumentMediaAttachmentType = 0xE6C64318;
NSString *const TGBridgeDocumentMediaDocumentIdKey = @"documentId";
NSString *const TGBridgeDocumentMediaLocalDocumentIdKey = @"localDocumentId";
NSString *const TGBridgeDocumentMediaFileSizeKey = @"fileSize";
NSString *const TGBridgeDocumentMediaFileNameKey = @"fileName";
NSString *const TGBridgeDocumentMediaImageSizeKey = @"imageSize";
NSString *const TGBridgeDocumentMediaAnimatedKey = @"animated";
NSString *const TGBridgeDocumentMediaStickerKey = @"sticker";
NSString *const TGBridgeDocumentMediaStickerAltKey = @"stickerAlt";
NSString *const TGBridgeDocumentMediaStickerPackIdKey = @"stickerPackId";
NSString *const TGBridgeDocumentMediaStickerPackAccessHashKey = @"stickerPackAccessHash";
NSString *const TGBridgeDocumentMediaAudioKey = @"audio";
NSString *const TGBridgeDocumentMediaAudioTitleKey = @"title";
NSString *const TGBridgeDocumentMediaAudioPerformerKey = @"performer";
NSString *const TGBridgeDocumentMediaAudioVoice = @"voice";
NSString *const TGBridgeDocumentMediaAudioDuration = @"duration";
@implementation TGBridgeDocumentMediaAttachment
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil)
{
_documentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaDocumentIdKey];
_localDocumentId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaLocalDocumentIdKey];
_fileSize = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaFileSizeKey];
_fileName = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaFileNameKey];
_imageSize = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaImageSizeKey];
_isAnimated = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAnimatedKey];
_isSticker = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaStickerKey];
_stickerAlt = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaStickerAltKey];
_stickerPackId = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackIdKey];
_stickerPackAccessHash = [aDecoder decodeInt64ForKey:TGBridgeDocumentMediaStickerPackAccessHashKey];
_isAudio = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioKey];
_title = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioTitleKey];
_performer = [aDecoder decodeObjectForKey:TGBridgeDocumentMediaAudioPerformerKey];
_isVoice = [aDecoder decodeBoolForKey:TGBridgeDocumentMediaAudioVoice];
_duration = [aDecoder decodeInt32ForKey:TGBridgeDocumentMediaAudioDuration];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt64:self.documentId forKey:TGBridgeDocumentMediaDocumentIdKey];
[aCoder encodeInt64:self.localDocumentId forKey:TGBridgeDocumentMediaLocalDocumentIdKey];
[aCoder encodeInt32:self.fileSize forKey:TGBridgeDocumentMediaFileSizeKey];
[aCoder encodeObject:self.fileName forKey:TGBridgeDocumentMediaFileNameKey];
[aCoder encodeObject:self.imageSize forKey:TGBridgeDocumentMediaImageSizeKey];
[aCoder encodeBool:self.isAnimated forKey:TGBridgeDocumentMediaAnimatedKey];
[aCoder encodeBool:self.isSticker forKey:TGBridgeDocumentMediaStickerKey];
[aCoder encodeObject:self.stickerAlt forKey:TGBridgeDocumentMediaStickerAltKey];
[aCoder encodeInt64:self.stickerPackId forKey:TGBridgeDocumentMediaStickerPackIdKey];
[aCoder encodeInt64:self.stickerPackAccessHash forKey:TGBridgeDocumentMediaStickerPackAccessHashKey];
[aCoder encodeBool:self.isAudio forKey:TGBridgeDocumentMediaAudioKey];
[aCoder encodeObject:self.title forKey:TGBridgeDocumentMediaAudioTitleKey];
[aCoder encodeObject:self.performer forKey:TGBridgeDocumentMediaAudioPerformerKey];
[aCoder encodeBool:self.isVoice forKey:TGBridgeDocumentMediaAudioVoice];
[aCoder encodeInt32:self.duration forKey:TGBridgeDocumentMediaAudioDuration];
}
- (BOOL)isEqual:(id)object
{
if (object == self)
return YES;
if (!object || ![object isKindOfClass:[self class]])
return NO;
TGBridgeDocumentMediaAttachment *document = (TGBridgeDocumentMediaAttachment *)object;
return (self.localDocumentId == 0 && self.documentId == document.documentId) || (self.localDocumentId != 0 && self.localDocumentId == document.localDocumentId);
}
+ (NSInteger)mediaType
{
return TGBridgeDocumentMediaAttachmentType;
}
@end

View File

@ -0,0 +1,9 @@
#import <WatchCommonWatch/TGBridgeMediaAttachment.h>
@interface TGBridgeForwardedMessageMediaAttachment : TGBridgeMediaAttachment
@property (nonatomic, assign) int64_t peerId;
@property (nonatomic, assign) int32_t mid;
@property (nonatomic, assign) int32_t date;
@end

Some files were not shown because too many files have changed in this diff Show More