1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGApplyUpdatesActor.mm

1743 lines
74 KiB
Plaintext
Raw Normal View History

2014-07-10 16:11:09 +02:00
#import "TGApplyUpdatesActor.h"
#import "ActionStage.h"
#import "SGraphObjectNode.h"
2015-10-01 18:19:52 +02:00
#import "TGPeerIdAdapter.h"
2014-07-10 16:11:09 +02:00
#import "TGDatabase.h"
#import "TGTelegraph.h"
#import "TGTelegramNetworking.h"
2015-10-01 18:19:52 +02:00
#import "TGAppDelegate.h"
2014-07-10 16:11:09 +02:00
#import "TGUser+Telegraph.h"
#import "TGMessage+Telegraph.h"
#import "TGConversation+Telegraph.h"
#import "TGUserDataRequestBuilder.h"
#import "TGUser+Telegraph.h"
#import "TGTimelineItem.h"
#import "TGConversationAddMessagesActor.h"
#import "TGConversationReadMessagesActor.h"
#import "TGApplyStateRequestBuilder.h"
#import "TGUpdateStateRequestBuilder.h"
#import "TGUpdate.h"
#import "TLUpdate$updateChangePts.h"
2015-10-01 18:19:52 +02:00
#import "TGUpdatesWithSeq.h"
#import "TGUpdatesWithPts.h"
#import "TGUpdatesWithQts.h"
#import "TGUpdatesWithDate.h"
#import "TLMessage$modernMessage.h"
#import "TLMessage$modernMessageService.h"
#import "TLUser$modernUser.h"
#import "TLUpdates+TG.h"
2016-02-25 01:03:51 +01:00
#import "TLMessageFwdHeader$messageFwdHeader.h"
2014-07-10 16:11:09 +02:00
#import <set>
#import <map>
2015-10-01 18:19:52 +02:00
@protocol TGSyntheticUpdateWithPts <NSObject>
- (int32_t)pts;
- (int32_t)pts_count;
@end
@protocol TGSyntheticUpdateWithQts <NSObject>
- (int32_t)qts;
@end
@interface TGWrappedUpdate : NSObject
@property (nonatomic, strong, readonly) id update;
@property (nonatomic, readonly) int32_t date;
@end
@implementation TGWrappedUpdate
- (instancetype)initWithUpdate:(id)update date:(int32_t)date
{
self = [super init];
if (self != nil)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
_update = update;
_date = date;
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
return self;
}
@end
static inline void maybeProcessUser(TLUser *user, std::map<int, TLUser *> &processedUsers)
{
if (((TLUser$modernUser *)user).n_id != 0)
processedUsers[((TLUser$modernUser *)user).n_id] = user;
2014-07-10 16:11:09 +02:00
}
static inline void maybeProcessChat(TLChat *chat, std::map<int, TLChat *> &processedChats)
{
2015-10-01 18:19:52 +02:00
if (chat.n_id != 0)
processedChats[chat.n_id] = chat;
2014-07-10 16:11:09 +02:00
}
static NSMutableArray *delayedNotifications()
{
static NSMutableArray *array = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
array = [[NSMutableArray alloc] init];
});
return array;
}
@interface TGApplyUpdatesActor ()
2015-10-01 18:19:52 +02:00
@property (nonatomic, strong) NSMutableArray *updateList;
@property (nonatomic) bool waitingForApplyUpdates;
@property (nonatomic, strong) NSMutableArray *waitingForApplyUpdatesQueue;
@property (nonatomic, strong) TGTimer *timeoutTimer;
@property (nonatomic) NSTimeInterval overallTimeout;
2014-07-10 16:11:09 +02:00
@end
@implementation TGApplyUpdatesActor
+ (NSString *)genericPath
{
return @"/tg/service/tryupdates/@";
}
+ (void)clearState
{
[TGApplyUpdatesActor clearDelayedNotifications];
}
+ (void)clearDelayedNotifications
{
[ActionStageInstance() dispatchOnStageQueue:^
{
[delayedNotifications() removeAllObjects];
}];
}
- (id)initWithPath:(NSString *)path
{
self = [super initWithPath:path];
if (self != nil)
{
2015-10-01 18:19:52 +02:00
_updateList = [[NSMutableArray alloc] init];
_waitingForApplyUpdatesQueue = [[NSMutableArray alloc] init];
2014-07-10 16:11:09 +02:00
}
return self;
}
- (void)dealloc
{
2015-10-01 18:19:52 +02:00
[self cancelTimeoutTimer];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
- (void)prepare:(NSDictionary *)__unused options
2014-07-10 16:11:09 +02:00
{
bool messagesQueue = false;
2015-10-01 18:19:52 +02:00
if ([self.path isEqualToString:@"/tg/service/tryupdates/(withPts)"])
2014-07-10 16:11:09 +02:00
messagesQueue = true;
2015-10-01 18:19:52 +02:00
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withSeq)"])
messagesQueue = true;
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withQts)"])
messagesQueue = true;
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withDate)"])
messagesQueue = false;
else
NSAssert(false, ([NSString stringWithFormat:@"Invalid actor path %@", self.path]));
2014-07-10 16:11:09 +02:00
if (messagesQueue)
self.requestQueueName = @"messages";
}
- (void)execute:(NSDictionary *)options
{
2015-10-01 18:19:52 +02:00
[self dumpUpdates:[options objectForKey:@"updates"]];
[_updateList addObjectsFromArray:[options objectForKey:@"updates"]];
if ([self.path isEqualToString:@"/tg/service/tryupdates/(withPts)"])
[self checkPtsUpdates];
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withSeq)"])
[self checkSeqUpdates];
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withQts)"])
[self checkQtsUpdates];
else
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
NSArray *sortedDateUpdates = [_updateList sortedArrayUsingComparator:^NSComparisonResult(TGUpdatesWithDate *updates1, TGUpdatesWithDate *updates2)
{
return updates1.date < updates2.date ? NSOrderedAscending : NSOrderedDescending;
}];
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *chats = [[NSMutableArray alloc] init];
NSMutableArray *wrappedUpdates = [[NSMutableArray alloc] init];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
for (TGUpdatesWithDate *updates in sortedDateUpdates)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
for (id update in updates.updates)
{
[wrappedUpdates addObject:[[TGWrappedUpdate alloc] initWithUpdate:update date:updates.date]];
}
[users addObjectsFromArray:updates.users];
[chats addObjectsFromArray:updates.chats];
}
if (wrappedUpdates.count != 0)
{
[self _tryApplyingUpdates:wrappedUpdates users:users chats:chats optionalFinalSeq:0 optionalFinalDate:((TGWrappedUpdate *)wrappedUpdates.lastObject).date completion:^(bool)
{
}];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
[self completeAction];
}
}
- (void)completeAction
{
[ActionStageInstance() actionCompleted:self.path result:nil];
}
- (void)dumpUpdates:(NSArray *)updateList
{
for (id updates in updateList)
{
if ([updates isKindOfClass:[TGUpdatesWithPts class]])
{
for (id<TGSyntheticUpdateWithPts> update in ((TGUpdatesWithPts *)updates).updates)
TGLog(@"enqueued update with pts: %d [+%d]", [update pts], [update pts_count]);
}
else if ([updates isKindOfClass:[TGUpdatesWithSeq class]])
{
TGLog(@"enqueued updates with seq: %d..%d", ((TGUpdatesWithSeq *)updates).seqStart, ((TGUpdatesWithSeq *)updates).seqEnd);
}
else if ([updates isKindOfClass:[TGUpdatesWithQts class]])
{
for (id<TGSyntheticUpdateWithQts> update in ((TGUpdatesWithQts *)updates).updates)
TGLog(@"enqueued update with qts: %d", [update qts]);
}
2014-07-10 16:11:09 +02:00
}
}
- (void)watcherJoined:(ASHandle *)watcherHandle options:(NSDictionary *)options waitingInActorQueue:(bool)waitingInActorQueue
{
2015-10-01 18:19:52 +02:00
[self dumpUpdates:[options objectForKey:@"updates"]];
if (_waitingForApplyUpdates)
[_waitingForApplyUpdatesQueue addObjectsFromArray:[options objectForKey:@"updates"]];
else
[_updateList addObjectsFromArray:[options objectForKey:@"updates"]];
if ([self.path isEqualToString:@"/tg/service/tryupdates/(withPts)"])
{
if (!waitingInActorQueue && !_waitingForApplyUpdates)
[self checkPtsUpdates];
}
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withSeq)"])
{
if (!waitingInActorQueue && !_waitingForApplyUpdates)
[self checkSeqUpdates];
}
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withQts)"])
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (!waitingInActorQueue && !_waitingForApplyUpdates)
[self checkQtsUpdates];
}
else
{
[_updateList removeAllObjects];
NSArray *sortedDateUpdates = [_updateList sortedArrayUsingComparator:^NSComparisonResult(TGUpdatesWithDate *updates1, TGUpdatesWithDate *updates2)
{
return updates1.date < updates2.date ? NSOrderedAscending : NSOrderedDescending;
}];
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *chats = [[NSMutableArray alloc] init];
NSMutableArray *wrappedUpdates = [[NSMutableArray alloc] init];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
for (TGUpdatesWithDate *updates in sortedDateUpdates)
{
for (id update in updates.updates)
{
[wrappedUpdates addObject:[[TGWrappedUpdate alloc] initWithUpdate:update date:updates.date]];
}
[users addObjectsFromArray:updates.users];
[chats addObjectsFromArray:updates.chats];
}
if (wrappedUpdates.count != 0)
{
[self _tryApplyingUpdates:wrappedUpdates users:users chats:chats optionalFinalSeq:0 optionalFinalDate:((TGWrappedUpdate *)wrappedUpdates.lastObject).date completion:^(bool)
{
}];
}
2014-07-10 16:11:09 +02:00
}
[super watcherJoined:watcherHandle options:options waitingInActorQueue:waitingInActorQueue];
}
2015-10-01 18:19:52 +02:00
- (void)cancelTimeoutTimer
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (_timeoutTimer != nil)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
[_timeoutTimer invalidate];
_timeoutTimer = nil;
2014-07-10 16:11:09 +02:00
}
}
2015-10-01 18:19:52 +02:00
- (void)startTimeoutTimer
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
_overallTimeout += [_timeoutTimer remainingTime];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
[self cancelTimeoutTimer];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
__weak TGApplyUpdatesActor *weakSelf = self;
_timeoutTimer = [[TGTimer alloc] initWithTimeout:MAX(0.0, MIN(2.0, 5.0 - _overallTimeout)) repeat:false completion:^
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
__strong TGApplyUpdatesActor *strongSelf = weakSelf;
if (strongSelf != nil)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
strongSelf->_timeoutTimer = nil;
[strongSelf timeoutReached];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
} queue:[ActionStageInstance() globalStageDispatchQueue]];
[_timeoutTimer start];
}
- (void)timeoutReached
{
if ([self.path isEqualToString:@"/tg/service/tryupdates/(withPts)"])
[self _failPts];
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withSeq)"])
[self _failSeq];
else if ([self.path isEqualToString:@"/tg/service/tryupdates/(withQts)"])
[self _failQts];
}
- (void)checkPtsUpdates
{
if (_updateList.count == 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
[self completeAction];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
else
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
NSMutableArray *ptsUpdates = [[NSMutableArray alloc] init];
for (TGUpdatesWithPts *update in _updateList)
{
[ptsUpdates addObjectsFromArray:update.updates];
}
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
[ptsUpdates sortUsingComparator:^NSComparisonResult(id<TGSyntheticUpdateWithPts> update1, id<TGSyntheticUpdateWithPts> update2)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if ([update1 pts] == [update2 pts])
return [update1 pts_count] > [update2 pts_count] ? NSOrderedAscending : NSOrderedDescending;
return [update1 pts] < [update2 pts] ? NSOrderedAscending : NSOrderedDescending;
}];
int32_t databasePts = [[TGDatabase instance] databaseState].pts;
int32_t currentPts = databasePts;
NSMutableArray *inOrderUpdates = [[NSMutableArray alloc] init];
NSMutableArray *expiredUpdates = [[NSMutableArray alloc] init];
for (id<TGSyntheticUpdateWithPts> update in ptsUpdates)
{
if ([update pts] <= databasePts)
[expiredUpdates addObject:update];
else
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (currentPts + [update pts_count] == [update pts])
{
[inOrderUpdates addObject:update];
currentPts = [update pts];
}
else
{
TGLog(@"***** Missing updates: %d + %d != %d", (int)currentPts, (int)[update pts_count], (int)[update pts]);
[self startTimeoutTimer];
break;
}
}
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
if (expiredUpdates.count != 0)
{
NSMutableArray *affectedGroups = [[NSMutableArray alloc] init];
for (TGUpdatesWithPts *updates in _updateList)
{
for (id update in expiredUpdates)
{
if ([updates.updates containsObject:update])
{
if (![affectedGroups containsObject:updates])
[affectedGroups addObject:updates];
}
}
}
for (TGUpdatesWithPts *updates in affectedGroups)
{
NSMutableArray *filteredUpdates = [[NSMutableArray alloc] initWithArray:updates.updates];
for (id update in expiredUpdates)
{
[filteredUpdates removeObject:update];
}
if (filteredUpdates.count == 0)
[_updateList removeObject:updates];
}
}
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
if (inOrderUpdates.count != 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
NSMutableArray *affectedGroups = [[NSMutableArray alloc] init];
for (TGUpdatesWithPts *updates in _updateList)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
for (id update in inOrderUpdates)
{
if ([updates.updates containsObject:update])
{
if (![affectedGroups containsObject:updates])
[affectedGroups addObject:updates];
}
}
}
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *chats = [[NSMutableArray alloc] init];
for (TGUpdatesWithPts *updates in affectedGroups)
{
[users addObjectsFromArray:updates.users];
[chats addObjectsFromArray:updates.chats];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
NSMutableArray *filteredUpdates = [[NSMutableArray alloc] initWithArray:updates.updates];
for (id update in inOrderUpdates)
{
[filteredUpdates removeObject:update];
}
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
if (filteredUpdates.count == 0)
[_updateList removeObject:updates];
}
NSMutableArray *wrappedUpdates = [[NSMutableArray alloc] init];
for (id update in inOrderUpdates)
{
[wrappedUpdates addObject:[[TGWrappedUpdate alloc] initWithUpdate:update date:0]];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
[self _tryApplyingUpdates:wrappedUpdates users:users chats:chats optionalFinalSeq:0 optionalFinalDate:0 completion:^(bool success)
{
if (!success)
[self _failPts];
else
[self checkPtsUpdates];
}];
}
else
{
if (_updateList.count == 0)
[self completeAction];
}
}
}
- (void)checkQtsUpdates
{
if (_updateList.count == 0)
{
[self completeAction];
}
else
{
NSMutableArray *qtsUpdates = [[NSMutableArray alloc] init];
for (TGUpdatesWithQts *update in _updateList)
{
[qtsUpdates addObjectsFromArray:update.updates];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
[qtsUpdates sortUsingComparator:^NSComparisonResult(id<TGSyntheticUpdateWithQts> update1, id<TGSyntheticUpdateWithQts> update2)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
return [update1 qts] < [update2 qts] ? NSOrderedAscending : NSOrderedDescending;
}];
int32_t databaseQts = [[TGDatabase instance] databaseState].qts;
int32_t currentQts = databaseQts;
NSMutableArray *inOrderUpdates = [[NSMutableArray alloc] init];
NSMutableArray *expiredUpdates = [[NSMutableArray alloc] init];
for (id<TGSyntheticUpdateWithQts> update in qtsUpdates)
{
if ([update qts] <= databaseQts)
[expiredUpdates addObject:update];
else
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (currentQts + 1 == [update qts])
{
[inOrderUpdates addObject:update];
currentQts = [update qts];
}
else
{
TGLog(@"***** Missing updates: qts %d + 1 != %d", (int)currentQts, (int)[update qts]);
[self startTimeoutTimer];
break;
}
}
}
if (expiredUpdates.count != 0)
{
NSMutableArray *affectedGroups = [[NSMutableArray alloc] init];
for (TGUpdatesWithQts *updates in _updateList)
{
for (id update in expiredUpdates)
{
if ([updates.updates containsObject:update])
{
if (![affectedGroups containsObject:updates])
[affectedGroups addObject:updates];
}
}
}
for (TGUpdatesWithQts *updates in affectedGroups)
{
NSMutableArray *filteredUpdates = [[NSMutableArray alloc] initWithArray:updates.updates];
for (id update in expiredUpdates)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
[filteredUpdates removeObject:update];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
if (filteredUpdates.count == 0)
[_updateList removeObject:updates];
}
}
if (inOrderUpdates.count != 0)
{
NSMutableArray *affectedGroups = [[NSMutableArray alloc] init];
for (TGUpdatesWithQts *updates in _updateList)
{
for (id update in inOrderUpdates)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if ([updates.updates containsObject:update])
{
if (![affectedGroups containsObject:updates])
[affectedGroups addObject:updates];
}
2014-07-10 16:11:09 +02:00
}
}
2015-10-01 18:19:52 +02:00
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *chats = [[NSMutableArray alloc] init];
for (TGUpdatesWithQts *updates in affectedGroups)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
[users addObjectsFromArray:updates.users];
[chats addObjectsFromArray:updates.chats];
NSMutableArray *filteredUpdates = [[NSMutableArray alloc] initWithArray:updates.updates];
for (id update in inOrderUpdates)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
[filteredUpdates removeObject:update];
}
if (filteredUpdates.count == 0)
[_updateList removeObject:updates];
}
NSMutableArray *wrappedUpdates = [[NSMutableArray alloc] init];
for (id update in inOrderUpdates)
{
[wrappedUpdates addObject:[[TGWrappedUpdate alloc] initWithUpdate:update date:0]];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
[self _tryApplyingUpdates:wrappedUpdates users:users chats:chats optionalFinalSeq:0 optionalFinalDate:0 completion:^(bool success)
{
if (!success)
[self _failQts];
else
[self checkQtsUpdates];
}];
2014-07-10 16:11:09 +02:00
}
else
{
2015-10-01 18:19:52 +02:00
if (_updateList.count == 0)
[self completeAction];
2014-07-10 16:11:09 +02:00
}
}
}
2015-10-01 18:19:52 +02:00
- (void)checkSeqUpdates
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (_updateList.count == 0)
{
[self completeAction];
}
else
{
NSArray *seqUpdates = [_updateList sortedArrayUsingComparator:^NSComparisonResult(TGUpdatesWithSeq *updates1, TGUpdatesWithSeq *updates2)
{
return updates1.seqEnd < updates2.seqEnd ? NSOrderedAscending : NSOrderedDescending;
}];
int32_t currentSeq = [[TGDatabase instance] databaseState].seq;
NSMutableArray *inOrderUpdates = [[NSMutableArray alloc] init];
for (TGUpdatesWithSeq *updates in seqUpdates)
{
if (updates.seqStart == currentSeq + 1)
{
[inOrderUpdates addObject:updates];
currentSeq = updates.seqEnd;
}
else
{
TGLog(@"***** Missing updates: seq %d", (int)currentSeq + 1);
[self startTimeoutTimer];
}
}
if (inOrderUpdates.count != 0)
{
NSMutableArray *wrappedUpdates = [[NSMutableArray alloc] init];
NSMutableArray *users = [[NSMutableArray alloc] init];
NSMutableArray *chats = [[NSMutableArray alloc] init];
for (TGUpdatesWithSeq *updates in inOrderUpdates)
{
for (id update in updates.updates)
{
[wrappedUpdates addObject:[[TGWrappedUpdate alloc] initWithUpdate:update date:updates.date]];
}
[users addObjectsFromArray:updates.users];
[chats addObjectsFromArray:updates.chats];
[_updateList removeObject:updates];
}
[self _tryApplyingUpdates:wrappedUpdates users:users chats:chats optionalFinalSeq:((TGUpdatesWithSeq *)inOrderUpdates.lastObject).seqEnd optionalFinalDate:((TGWrappedUpdate *)wrappedUpdates.lastObject).date completion:^(bool success)
{
if (!success)
[self _failSeq];
else
[self checkSeqUpdates];
}];
}
else if (_updateList.count == 0)
{
[self completeAction];
}
}
}
- (void)_failPts
{
TGLog(@"***** Inconsistent state by (pts, pts_count)! Synchronization required.");
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
[self cancelTimeoutTimer];
2014-07-10 16:11:09 +02:00
[TGTelegraphInstance stateUpdateRequired];
2015-10-01 18:19:52 +02:00
[self completeAction];
}
- (void)_failSeq
{
TGLog(@"***** Inconsistent state by seq! Synchronization required.");
[self cancelTimeoutTimer];
[TGTelegraphInstance stateUpdateRequired];
[self completeAction];
}
- (void)_failQts
{
TGLog(@"***** Inconsistent state by qts! Synchronization required.");
[self cancelTimeoutTimer];
[TGTelegraphInstance stateUpdateRequired];
[self completeAction];
2014-07-10 16:11:09 +02:00
}
template<typename T>
static int64_t extractMessageConversationId(T concreteMessage, int &outFromUid)
{
int64_t fromUid = concreteMessage.from_id;
2015-10-01 18:19:52 +02:00
bool outgoing = concreteMessage.flags & 2;
2014-07-10 16:11:09 +02:00
if (!outgoing)
outFromUid = (int)fromUid;
if ([concreteMessage.to_id isKindOfClass:[TLPeer$peerUser class]])
{
TLPeer$peerUser *toUser = (TLPeer$peerUser *)concreteMessage.to_id;
int64_t toUid = toUser.user_id;
if (toUid == fromUid && !outgoing)
outgoing = true;
return outgoing ? toUid : fromUid;
}
else if ([concreteMessage.to_id isKindOfClass:[TLPeer$peerChat class]])
{
TLPeer$peerChat *toChat = (TLPeer$peerChat *)concreteMessage.to_id;
int64_t toUid = -toChat.chat_id;
return toUid;
}
2015-10-01 18:19:52 +02:00
else if ([concreteMessage.to_id isKindOfClass:[TLPeer$peerChannel class]])
{
TLPeer$peerChannel *toChannel = (TLPeer$peerChannel *)concreteMessage.to_id;
int64_t toUid = TGPeerIdFromChannelId(toChannel.channel_id);
return toUid;
}
2014-07-10 16:11:09 +02:00
return 0;
}
2015-10-01 18:19:52 +02:00
- (bool)_tryApplyingUpdates:(NSArray *)updates users:(NSArray *)users chats:(NSArray *)chats optionalFinalSeq:(int32_t)optionalFinalSeq optionalFinalDate:(int32_t)optionalFinalDate completion:(void (^)(bool))completion
2014-07-10 16:11:09 +02:00
{
static Class updateNewMessageClass = nil;
static Class updateNewEncryptedMessageClass = nil;
static Class updateDeleteMessagesClass = nil;
static Class updateRestoreMessagesClass = nil;
static Class updateChangePtsClass = nil;
static Class updateUserTypingClass = nil;
static Class updateChatUserTypingClass = nil;
static Class updateChatParticipantsClass = nil;
static Class updateChatParticipantAddClass = nil;
static Class updateChatParticipantDeleteClass = nil;
static Class updateContactLocatedClass = nil;
static Class messageClass = nil;
static Class messageServiceClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
updateNewMessageClass = [TLUpdate$updateNewMessage class];
updateNewEncryptedMessageClass = [TLUpdate$updateNewEncryptedMessage class];
updateDeleteMessagesClass = [TLUpdate$updateDeleteMessages class];
updateRestoreMessagesClass = [TLUpdate$updateRestoreMessages class];
updateChangePtsClass = [TLUpdate$updateChangePts class];
updateUserTypingClass = [TLUpdate$updateUserTyping class];
updateChatUserTypingClass = [TLUpdate$updateChatUserTyping class];
updateChatParticipantsClass = [TLUpdate$updateChatParticipants class];
updateChatParticipantAddClass = [TLUpdate$updateChatParticipantAdd class];
updateChatParticipantDeleteClass = [TLUpdate$updateChatParticipantDelete class];
updateContactLocatedClass = [TLUpdate$updateContactLocated class];
2015-10-01 18:19:52 +02:00
messageClass = [TLMessage$modernMessage class];
messageServiceClass = [TLMessage$modernMessageService class];
2014-07-10 16:11:09 +02:00
});
2015-10-01 18:19:52 +02:00
int32_t statePts = 0;
int32_t stateQts = 0;
2014-07-10 16:11:09 +02:00
TGDatabaseState databaseState = [[TGDatabase instance] databaseState];
2015-10-01 18:19:52 +02:00
for (TGWrappedUpdate *update in updates)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if ([update.update hasPts])
statePts = MAX(statePts, [(id<TGSyntheticUpdateWithPts>)update.update pts]);
if ([update.update respondsToSelector:@selector(qts)])
stateQts = MAX(stateQts, [(id<TGSyntheticUpdateWithQts>)update.update qts]);
2014-07-10 16:11:09 +02:00
}
std::map<int, TLUser *> processedUsers;
std::map<int, TLChat *> processedChats;
NSMutableArray *updatesWithDates = [[NSMutableArray alloc] init];
std::set<int> knownUsers;
std::set<int64_t> knownChats;
NSMutableArray *addedMessages = [[NSMutableArray alloc] init];
NSMutableArray *messagesForLocalNotification = [[NSMutableArray alloc] init];
NSMutableArray *allUpdates = [[NSMutableArray alloc] init];
int currentTime = (int)[[TGTelegramNetworking instance] globalTime];
bool failedProcessing = false;
bool updatesTooLong = false;
2015-10-01 18:19:52 +02:00
for (TLUser *userDesc in users)
{
maybeProcessUser(userDesc, processedUsers);
}
for (TLChat *chatDesc in chats)
{
maybeProcessChat(chatDesc, processedChats);
}
std::map<int64_t, int32_t> maxInboxReadMessageIdByPeerId;
std::map<int64_t, int32_t> maxOutboxReadMessageIdByPeerId;
for (TGWrappedUpdate *wrappedUpdate in updates)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if ([wrappedUpdate.update isKindOfClass:[TLUpdate$updateReadHistoryInbox class]])
{
TLUpdate$updateReadHistoryInbox *concreteUpdate = wrappedUpdate.update;
int64_t peerId = 0;
if ([concreteUpdate.peer isKindOfClass:[TLPeer$peerUser class]])
peerId = ((TLPeer$peerUser *)concreteUpdate.peer).user_id;
else if ([concreteUpdate.peer isKindOfClass:[TLPeer$peerChat class]])
peerId = -((TLPeer$peerChat *)concreteUpdate.peer).chat_id;
else if ([concreteUpdate.peer isKindOfClass:[TLPeer$peerChannel class]])
peerId = TGPeerIdFromChannelId(((TLPeer$peerChannel *)concreteUpdate.peer).channel_id);
auto it = maxInboxReadMessageIdByPeerId.find(peerId);
if (it == maxInboxReadMessageIdByPeerId.end())
maxInboxReadMessageIdByPeerId[peerId] = concreteUpdate.max_id;
else
maxInboxReadMessageIdByPeerId[peerId] = MAX(it->second, concreteUpdate.max_id);
}
else if ([wrappedUpdate.update isKindOfClass:[TLUpdate$updateReadHistoryOutbox class]])
{
TLUpdate$updateReadHistoryOutbox *concreteUpdate = wrappedUpdate.update;
int64_t peerId = 0;
if ([concreteUpdate.peer isKindOfClass:[TLPeer$peerUser class]])
peerId = ((TLPeer$peerUser *)concreteUpdate.peer).user_id;
else if ([concreteUpdate.peer isKindOfClass:[TLPeer$peerChat class]])
peerId = -((TLPeer$peerChat *)concreteUpdate.peer).chat_id;
else if ([concreteUpdate.peer isKindOfClass:[TLPeer$peerChannel class]])
peerId = TGPeerIdFromChannelId(((TLPeer$peerChannel *)concreteUpdate.peer).channel_id);
auto it = maxOutboxReadMessageIdByPeerId.find(peerId);
if (it == maxOutboxReadMessageIdByPeerId.end())
maxOutboxReadMessageIdByPeerId[peerId] = concreteUpdate.max_id;
else
maxOutboxReadMessageIdByPeerId[peerId] = MAX(it->second, concreteUpdate.max_id);
}
}
for (TGWrappedUpdate *wrappedUpdate in updates)
{
id update = wrappedUpdate.update;
int32_t date = wrappedUpdate.date;
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
if ([update isKindOfClass:updateNewMessageClass])
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
TLUpdate$updateNewMessage *newMessage = (TLUpdate$updateNewMessage *)update;
TLMessage *message = newMessage.message;
if (([message isKindOfClass:[TLMessage$modernMessage class]] || [message isKindOfClass:[TLMessage$modernMessageService class]]) && !(((TLMessage$modernMessage *)message).flags & 2))
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
TGMessage *parsedMessage = [[TGMessage alloc] initWithTelegraphMessageDesc:message];
2016-02-25 01:03:51 +01:00
if (parsedMessage.unread && !parsedMessage.outgoing && !parsedMessage.isSilent)
2015-10-01 18:19:52 +02:00
{
auto maxIt = maxInboxReadMessageIdByPeerId.find(parsedMessage.cid);
if (!(maxIt != maxInboxReadMessageIdByPeerId.end() && parsedMessage.mid <= maxIt->second))
[messagesForLocalNotification addObject:newMessage.message];
}
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
else
TGLog(@"Message %d does not match for local notification", (int)message.n_id);
int64_t conversationId = 0;
int fromUid = 0;
if ([message isKindOfClass:messageClass])
conversationId = extractMessageConversationId((TLMessage$message *)message, fromUid);
else if ([message isKindOfClass:messageServiceClass])
conversationId = extractMessageConversationId((TLMessage$modernMessageService *)message, fromUid);
if (conversationId != 0)
{
if (conversationId < 0)
{
if (knownChats.find(conversationId) == knownChats.end() && processedChats.find(-(int)conversationId) == processedChats.end())
{
bool contains = [TGDatabaseInstance() containsConversationWithId:conversationId];
if (contains)
knownChats.insert(conversationId);
else
{
TGLog(@"Unknown chat %" PRId64 "", conversationId);
failedProcessing = true;
}
}
}
else
{
if (knownUsers.find((int)conversationId) == knownUsers.end() && processedUsers.find((int)conversationId) == processedUsers.end())
{
bool contains = [TGDatabaseInstance() loadUser:(int)conversationId];
if (contains)
knownUsers.insert((int)conversationId);
else
{
TGLog(@"Unknown user %" PRId64 "", conversationId);
failedProcessing = true;
}
}
}
}
if (!failedProcessing && fromUid != 0 && fromUid != conversationId)
{
if (knownUsers.find(fromUid) == knownUsers.end() && processedUsers.find(fromUid) == processedUsers.end())
{
bool contains = [TGDatabaseInstance() loadUser:fromUid];
if (contains)
knownUsers.insert(fromUid);
else
{
TGLog(@"Unknown user %" PRId32 "", fromUid);
failedProcessing = true;
}
}
}
if (!failedProcessing)
{
if ([message isKindOfClass:[TLMessage$modernMessage class]])
{
2016-02-25 01:03:51 +01:00
TLMessageFwdHeader$messageFwdHeader *fwd_header = (TLMessageFwdHeader$messageFwdHeader *)((TLMessage$modernMessage *)message).fwd_header;
if (fwd_header != nil) {
if (fwd_header.from_id != 0) {
if (knownUsers.find(fwd_header.from_id) == knownUsers.end() && processedUsers.find(fwd_header.from_id) == processedUsers.end())
2015-10-01 18:19:52 +02:00
{
2016-02-25 01:03:51 +01:00
bool contains = [TGDatabaseInstance() loadUser:fwd_header.from_id];
2015-10-01 18:19:52 +02:00
if (contains)
2016-02-25 01:03:51 +01:00
knownUsers.insert(fwd_header.from_id);
2015-10-01 18:19:52 +02:00
else
{
2016-02-25 01:03:51 +01:00
TGLog(@"Unknown user %" PRId32 "", fwd_header.from_id);
2015-10-01 18:19:52 +02:00
failedProcessing = true;
}
}
}
2016-02-25 01:03:51 +01:00
if (fwd_header.channel_id != 0) {
int64_t peerId = TGPeerIdFromChannelId(fwd_header.channel_id);
if (peerId != 0) {
if (knownChats.find(peerId) == knownChats.end() && processedChats.find(TGChannelIdFromPeerId(peerId)) == processedChats.end()) {
bool contains = [TGDatabaseInstance() _channelExists:peerId];
if (contains)
knownChats.insert(TGChannelIdFromPeerId(peerId));
else
{
TGLog(@"Unknown channel %" PRId64 "", peerId);
failedProcessing = true;
}
2015-10-01 18:19:52 +02:00
}
}
}
}
}
}
if (failedProcessing)
break;
[addedMessages addObject:message];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
else if ([update isKindOfClass:updateUserTypingClass])
{
if (date > currentTime - 20)
[allUpdates addObject:update];
}
else if ([update isKindOfClass:updateChatUserTypingClass])
{
if (date > currentTime - 20)
[allUpdates addObject:update];
}
else if ([update isKindOfClass:[TLUpdate$updateEncryptedChatTyping class]])
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (date > currentTime - 20)
[allUpdates addObject:update];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
else if ([update isKindOfClass:updateContactLocatedClass])
{
if (date > currentTime - 5 * 60)
[allUpdates addObject:update];
}
else if ([update isKindOfClass:updateChatParticipantsClass])
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
TLUpdate$updateChatParticipants *updateChatParticipants = (TLUpdate$updateChatParticipants *)update;
int64_t conversationId = -updateChatParticipants.participants.chat_id;
if (conversationId < 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (knownChats.find(conversationId) == knownChats.end() && processedChats.find(-(int)conversationId) == processedChats.end())
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
bool contains = [TGDatabaseInstance() containsConversationWithId:conversationId];
if (contains)
knownChats.insert(conversationId);
2014-07-10 16:11:09 +02:00
else
2015-10-01 18:19:52 +02:00
failedProcessing = true;
2014-07-10 16:11:09 +02:00
}
}
2015-10-01 18:19:52 +02:00
[allUpdates addObject:update];
}
else if ([update isKindOfClass:updateChatParticipantAddClass] || [update isKindOfClass:updateChatParticipantDeleteClass])
{
int64_t conversationId = 0;
int32_t userId = 0;
if ([update isKindOfClass:updateChatParticipantAddClass])
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
conversationId = -((TLUpdate$updateChatParticipantAdd *)update).chat_id;
userId = ((TLUpdate$updateChatParticipantAdd *)update).user_id;
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
if ([update isKindOfClass:updateChatParticipantDeleteClass])
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
conversationId = -((TLUpdate$updateChatParticipantDelete *)update).chat_id;
userId = ((TLUpdate$updateChatParticipantDelete *)update).user_id;
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
if (conversationId < 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (knownChats.find(conversationId) == knownChats.end() && processedChats.find(-(int)conversationId) == processedChats.end())
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
bool contains = [TGDatabaseInstance() containsConversationWithId:conversationId];
if (contains)
knownChats.insert(conversationId);
else
failedProcessing = true;
2014-07-10 16:11:09 +02:00
}
}
2015-10-01 18:19:52 +02:00
if (userId != 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (knownUsers.find(userId) == knownUsers.end() && processedUsers.find(userId) == processedUsers.end())
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
bool contains = [TGDatabaseInstance() loadUser:userId];
if (contains)
knownUsers.insert(userId);
else
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
TGLog(@"Unknown user %" PRId32 "", userId);
failedProcessing = true;
2014-07-10 16:11:09 +02:00
}
}
}
2015-10-01 18:19:52 +02:00
[updatesWithDates addObject:@[update, @(date)]];
[allUpdates addObject:update];
}
else if ([update isKindOfClass:[TLUpdate$updateEncryption class]])
{
TLUpdate$updateEncryption *updateEncryption = (TLUpdate$updateEncryption *)update;
TGConversation *conversation = [[TGConversation alloc] initWithTelegraphEncryptedChatDesc:updateEncryption.chat];
if (conversation.conversationId != 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (conversation.chatParticipants.chatParticipantUids.count != 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
int userId = [conversation.chatParticipants.chatParticipantUids[0] intValue];
if ([TGDatabaseInstance() loadUser:userId] != nil)
[allUpdates addObject:update];
2014-07-10 16:11:09 +02:00
else
failedProcessing = true;
}
else
failedProcessing = true;
}
else
2015-10-01 18:19:52 +02:00
failedProcessing = true;
}
else if ([update isKindOfClass:updateNewEncryptedMessageClass])
{
TLUpdate$updateNewEncryptedMessage *updateNewEncryptedMessage = (TLUpdate$updateNewEncryptedMessage *)update;
if (![updateNewEncryptedMessage.message isKindOfClass:[TLEncryptedMessage$encryptedMessageService class]] && updateNewEncryptedMessage.message != nil && stateQts != 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
[messagesForLocalNotification addObject:@{@"message": updateNewEncryptedMessage.message, @"qts": @(stateQts)}];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
[allUpdates addObject:update];
}
else if ([update isKindOfClass:[TLUpdates$updatesTooLong class]])
{
failedProcessing = true;
updatesTooLong = true;
break;
}
else
{
[allUpdates addObject:update];
2014-07-10 16:11:09 +02:00
}
}
if (!failedProcessing)
{
NSMutableArray *usersToProcess = [[NSMutableArray alloc] initWithCapacity:processedUsers.size()];
for (std::map<int, TLUser *>::iterator it = processedUsers.begin(); it != processedUsers.end(); it++)
{
[usersToProcess addObject:it->second];
}
NSMutableArray *chatsToProcess = [[NSMutableArray alloc] initWithCapacity:processedChats.size()];
for (std::map<int, TLChat *>::iterator it = processedChats.begin(); it != processedChats.end(); it++)
{
[chatsToProcess addObject:it->second];
}
2015-10-01 18:19:52 +02:00
_waitingForApplyUpdates = true;
[TGUpdateStateRequestBuilder applyUpdates:addedMessages otherUpdates:allUpdates usersDesc:usersToProcess chatsDesc:chatsToProcess chatParticipantsDesc:nil updatesWithDates:updatesWithDates addedEncryptedActionsByPeerId:nil addedEncryptedUnparsedActionsByPeerId:nil completion:^(__unused bool applied)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
_waitingForApplyUpdates = false;
[_updateList addObjectsFromArray:_waitingForApplyUpdatesQueue];
[_waitingForApplyUpdatesQueue removeAllObjects];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
[delayedNotifications() addObjectsFromArray:messagesForLocalNotification];
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
if (stateQts != 0)
2014-07-10 16:11:09 +02:00
{
[TGDatabaseInstance() updateLatestQts:stateQts applied:false completion:^(int greaterQtsForSynchronization)
2015-10-01 18:19:52 +02:00
{
if (greaterQtsForSynchronization > 0)
{
[ActionStageInstance() requestActor:[[NSString alloc] initWithFormat:@"/tg/messages/reportDelivery/(qts)"] options:[[NSDictionary alloc] initWithObjectsAndKeys:[[NSNumber alloc] initWithInt:stateQts], @"qts", nil] watcher:TGTelegraphInstance];
}
}];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
if ([self.path isEqualToString:@"/tg/service/tryupdates/(withPts)"] || [self.path isEqualToString:@"/tg/service/tryupdates/(withSeq)"] || [self.path isEqualToString:@"/tg/service/tryupdates/(withQts)"])
{
if (statePts != 0)
TGLog(@"=== pts: %d", statePts);
if (optionalFinalSeq != 0)
TGLog(@"=== seq: %d", optionalFinalSeq);
if (stateQts != 0)
TGLog(@"=== qts: %d", stateQts);
[[TGDatabase instance] applyPts:statePts date:optionalFinalDate seq:optionalFinalSeq qts:stateQts unreadCount:-1];
}
else if (optionalFinalDate > databaseState.date)
{
[[TGDatabase instance] applyPts:0 date:optionalFinalDate seq:0 qts:0 unreadCount:-1];
}
if (completion)
completion(true);
}];
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
else
2014-07-10 16:11:09 +02:00
{
if (updatesTooLong)
TGLog(@"===== Updates too long, requesting complete difference");
else
TGLog(@"***** Unknown chat or user found, requesting complete difference");
2015-10-01 18:19:52 +02:00
if (completion)
completion(false);
2014-07-10 16:11:09 +02:00
}
2015-10-01 18:19:52 +02:00
return !failedProcessing;
2014-07-10 16:11:09 +02:00
}
- (void)cancel
{
2015-10-01 18:19:52 +02:00
[self cancelTimeoutTimer];
2014-07-10 16:11:09 +02:00
[super cancel];
}
2015-10-01 18:19:52 +02:00
+ (void)applyDelayedNotifications:(int)maxMid mids:(NSArray *)mids midsWithoutSound:(NSSet *)midsWithoutSound maxQts:(int)maxQts randomIds:(NSArray *)randomIds
2014-07-10 16:11:09 +02:00
{
dispatch_async(dispatch_get_main_queue(), ^
{
2015-10-01 18:19:52 +02:00
UIApplicationState applicationState = [UIApplication sharedApplication].applicationState;
if ([UIApplication sharedApplication] == nil)
applicationState = UIApplicationStateBackground;
2014-07-10 16:11:09 +02:00
[ActionStageInstance() dispatchOnStageQueue:^
{
if (applicationState != UIApplicationStateActive)
{
int globalMessageSoundId = 1;
bool globalMessagePreviewText = true;
int globalMessageMuteUntil = 0;
bool notFound = false;
2016-02-25 01:03:51 +01:00
[TGDatabaseInstance() loadPeerNotificationSettings:INT_MAX - 1 soundId:&globalMessageSoundId muteUntil:&globalMessageMuteUntil previewText:&globalMessagePreviewText messagesMuted:NULL notFound:&notFound];
2014-07-10 16:11:09 +02:00
if (notFound)
{
globalMessageSoundId = 1;
globalMessagePreviewText = true;
}
int globalGroupSoundId = 1;
bool globalGroupPreviewText = true;
int globalGroupMuteUntil = 0;
notFound = false;
2016-02-25 01:03:51 +01:00
[TGDatabaseInstance() loadPeerNotificationSettings:INT_MAX - 2 soundId:&globalGroupSoundId muteUntil:&globalGroupMuteUntil previewText:&globalGroupPreviewText messagesMuted:NULL notFound:&notFound];
2014-07-10 16:11:09 +02:00
if (notFound)
{
globalGroupSoundId = 1;
globalGroupPreviewText = true;
}
@try
{
std::set<int> midsSet;
for (NSNumber *nMid in mids)
{
midsSet.insert([nMid intValue]);
}
2015-10-01 18:19:52 +02:00
std::set<int> processedMidsSet;
2014-07-10 16:11:09 +02:00
std::set<int64_t> randomIdsSet;
for (NSNumber *nRandomId in randomIds)
{
randomIdsSet.insert([nRandomId longLongValue]);
}
2015-10-01 18:19:52 +02:00
int count = (int)delayedNotifications().count;
2014-07-10 16:11:09 +02:00
for (int i = 0; i < count; i++)
{
TGMessage *message = nil;
2015-10-01 18:19:52 +02:00
NSUInteger multiforwardCount = 0;
2014-07-10 16:11:09 +02:00
int messageQts = 0;
id abstractDesc = delayedNotifications()[i];
if ([abstractDesc respondsToSelector:@selector(allKeys)])
{
messageQts = [abstractDesc[@"qts"] intValue];
abstractDesc = abstractDesc[@"message"];
}
if ([abstractDesc isKindOfClass:[TLMessage class]])
{
if (mids == nil)
continue;
TLMessage *messageDesc = abstractDesc;
int mid = messageDesc.n_id;
if (mid == 0 || mid > maxMid)
continue;
[delayedNotifications() removeObjectAtIndex:i];
i--;
count--;
if (midsSet.find(mid) == midsSet.end())
continue;
2015-10-01 18:19:52 +02:00
if (processedMidsSet.find(mid) != processedMidsSet.end())
continue;
processedMidsSet.insert(mid);
2014-07-10 16:11:09 +02:00
message = [[TGMessage alloc] initWithTelegraphMessageDesc:messageDesc];
2015-10-01 18:19:52 +02:00
bool foundForward = false;
for (id media in message.mediaAttachments)
{
if ([media isKindOfClass:[TGForwardedMessageMediaAttachment class]])
{
foundForward = true;
break;
}
}
if (foundForward)
{
for (int j = i + 1; j >= 0 && j < count; j++)
{
if ([delayedNotifications()[j] isKindOfClass:[TLMessage class]])
{
TGMessage *nextMessage = [[TGMessage alloc] initWithTelegraphMessageDesc:delayedNotifications()[j]];
if (processedMidsSet.find(nextMessage.mid) != processedMidsSet.end())
continue;
processedMidsSet.insert(nextMessage.mid);
bool nextIsForward = false;
for (id media in nextMessage.mediaAttachments)
{
if ([media isKindOfClass:[TGForwardedMessageMediaAttachment class]])
{
nextIsForward = true;
break;
}
}
if (nextIsForward)
{
if (multiforwardCount == 0)
multiforwardCount = 1;
multiforwardCount++;
[delayedNotifications() removeObjectAtIndex:j];
j--;
count--;
}
}
}
}
2014-07-10 16:11:09 +02:00
}
else if ([abstractDesc isKindOfClass:[TLEncryptedMessage class]])
{
if (randomIds == nil)
continue;
TLEncryptedMessage *encryptedMessage = abstractDesc;
if (messageQts > maxQts)
continue;
[delayedNotifications() removeObjectAtIndex:i];
i--;
count--;
if (randomIdsSet.find(encryptedMessage.random_id) == randomIdsSet.end())
continue;
message = [[TGMessage alloc] init];
message.randomId = encryptedMessage.random_id;
message.cid = [TGDatabaseInstance() peerIdForEncryptedConversationId:encryptedMessage.chat_id];
}
else
{
TGLog(@"***** unknown notification message type %@", abstractDesc);
continue;
}
2015-10-01 18:19:52 +02:00
if (message.containsMention)
{
if ([TGDatabaseInstance() isPeerMuted:message.fromUid])
continue;
}
else
{
if ([TGDatabaseInstance() isPeerMuted:message.cid])
continue;
}
2014-07-10 16:11:09 +02:00
if (message.cid > 0 || message.cid <= INT_MIN)
{
if (globalMessageMuteUntil > 0)
continue;
}
else
{
if (globalGroupMuteUntil > 0)
continue;
}
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
if (localNotification == nil)
continue;
TGUser *user = nil;
NSString *chatName = nil;
int64_t notificationPeerId = 0;
if (message.cid <= INT_MIN)
{
notificationPeerId = [TGDatabaseInstance() encryptedParticipantIdForConversationId:message.cid];
}
else if (message.cid > 0)
{
user = [TGDatabaseInstance() loadUser:(int)message.cid];
notificationPeerId = message.cid;
}
else
{
2015-10-01 18:19:52 +02:00
if (message.containsMention)
notificationPeerId = message.fromUid;
else
notificationPeerId = message.cid;
2014-07-10 16:11:09 +02:00
user = [TGDatabaseInstance() loadUser:(int)message.fromUid];
2015-10-01 18:19:52 +02:00
TGConversation *conversation = [TGDatabaseInstance() loadConversationWithIdCached:message.cid];
if (conversation != nil)
chatName = conversation.chatTitle;
else
chatName = [TGDatabaseInstance() loadConversationWithId:message.cid].chatTitle;
2014-07-10 16:11:09 +02:00
}
if ([TGDatabaseInstance() isPeerMuted:notificationPeerId])
continue;
NSString *text = nil;
bool attachmentFound = false;
2016-02-25 01:03:51 +01:00
bool migrationFound = false;
2014-07-10 16:11:09 +02:00
for (TGMediaAttachment *attachment in message.mediaAttachments)
{
if (attachment.type == TGActionMediaAttachmentType)
{
TGActionMediaAttachment *actionAttachment = (TGActionMediaAttachment *)attachment;
switch (actionAttachment.actionType)
{
case TGMessageActionChatEditTitle:
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_TITLE_EDITED"), user.displayName, [((TGActionMediaAttachment *)attachment).actionData objectForKey:@"title"]];
attachmentFound = true;
break;
}
case TGMessageActionChatEditPhoto:
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_PHOTO_EDITED"), user.displayName, chatName];
attachmentFound = true;
break;
}
case TGMessageActionChatAddMember:
{
2016-02-25 01:03:51 +01:00
NSArray *uids = actionAttachment.actionData[@"uids"];
if (uids != nil) {
TGUser *authorUser = user;
NSMutableArray *subjectUsers = [[NSMutableArray alloc] init];
for (NSNumber *nUid in uids) {
TGUser *subjectUser = [TGDatabaseInstance() loadUser:[nUid intValue]];
if (user != nil) {
[subjectUsers addObject:subjectUser];
}
}
2014-07-10 16:11:09 +02:00
2016-02-25 01:03:51 +01:00
if (subjectUsers.count == 1 && authorUser.uid == ((TGUser *)subjectUsers[0]).uid) {
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_RETURNED"), authorUser.displayName, chatName];
} else {
NSMutableString *subjectNames = [[NSMutableString alloc] init];
for (TGUser *subjectUser in subjectUsers) {
if (subjectNames.length != 0) {
[subjectNames appendString:@", "];
}
[subjectNames appendString:subjectUser.displayName];
}
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_ADD_MEMBER"), authorUser.displayName, chatName, subjectNames];
}
2014-07-10 16:11:09 +02:00
attachmentFound = true;
2016-02-25 01:03:51 +01:00
} else {
NSNumber *nUid = [actionAttachment.actionData objectForKey:@"uid"];
if (nUid != nil)
{
TGUser *subjectUser = [TGDatabaseInstance() loadUser:[nUid intValue]];
if (subjectUser.uid == user.uid)
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_RETURNED"), user.displayName, chatName];
else if (subjectUser.uid == TGTelegraphInstance.clientUserId)
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_ADD_YOU"), user.displayName, chatName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_ADD_MEMBER"), user.displayName, chatName, subjectUser.displayName];
attachmentFound = true;
}
2014-07-10 16:11:09 +02:00
}
break;
}
case TGMessageActionChatDeleteMember:
{
NSNumber *nUid = [actionAttachment.actionData objectForKey:@"uid"];
if (nUid != nil)
{
TGUser *subjectUser = [TGDatabaseInstance() loadUser:[nUid intValue]];
if (subjectUser.uid == user.uid)
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_LEFT"), user.displayName, chatName];
else if (subjectUser.uid == TGTelegraphInstance.clientUserId)
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_DELETE_YOU"), user.displayName, chatName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_DELETE_MEMBER"), user.displayName, chatName, subjectUser.displayName];
attachmentFound = true;
}
break;
}
case TGMessageActionCreateChat:
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_CREATED"), user.displayName, chatName];
attachmentFound = true;
break;
}
2015-10-01 18:19:52 +02:00
case TGMessageActionChannelCreated:
{
text = @"";
attachmentFound = true;
break;
}
case TGMessageActionChannelCommentsStatusChanged:
{
text = [actionAttachment.actionData[@"enabled"] boolValue] ? TGLocalized(@"Channel.NotificationCommentsEnabled") : TGLocalized(@"Channel.NotificationCommentsDisabled");
attachmentFound = true;
break;
}
case TGMessageActionJoinedByLink:
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"Notification.JoinedGroupByLink"), user.displayName];
2016-02-25 01:03:51 +01:00
attachmentFound = true;
2015-10-01 18:19:52 +02:00
break;
}
2016-02-25 01:03:51 +01:00
case TGMessageActionGroupMigratedTo:
{
migrationFound = true;
break;
}
2014-07-10 16:11:09 +02:00
default:
break;
}
}
else if (attachment.type == TGImageMediaAttachmentType)
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_PHOTO"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_PHOTO"), user.displayName, chatName];
attachmentFound = true;
break;
}
else if (attachment.type == TGVideoMediaAttachmentType)
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_VIDEO"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_VIDEO"), user.displayName, chatName];
attachmentFound = true;
break;
}
else if (attachment.type == TGLocationMediaAttachmentType)
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_GEO"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_GEO"), user.displayName, chatName];
attachmentFound = true;
break;
}
else if (attachment.type == TGContactMediaAttachmentType)
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_CONTACT"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_CONTACT"), user.displayName, chatName];
attachmentFound = true;
break;
}
else if (attachment.type == TGDocumentMediaAttachmentType)
{
2015-10-01 18:19:52 +02:00
bool isAnimated = false;
2016-02-25 01:03:51 +01:00
bool isVoice = false;
2015-10-01 18:19:52 +02:00
CGSize imageSize = CGSizeZero;
bool isSticker = false;
for (id attribute in ((TGDocumentMediaAttachment *)attachment).attributes)
{
if ([attribute isKindOfClass:[TGDocumentAttributeAnimated class]])
{
isAnimated = true;
}
else if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]])
{
imageSize = ((TGDocumentAttributeImageSize *)attribute).size;
}
2016-02-25 01:03:51 +01:00
else if ([attribute isKindOfClass:[TGDocumentAttributeVideo class]]) {
imageSize = ((TGDocumentAttributeVideo *)attribute).size;
}
2015-10-01 18:19:52 +02:00
else if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
{
isSticker = true;
}
2016-02-25 01:03:51 +01:00
else if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]]) {
isVoice = ((TGDocumentAttributeAudio *)attribute).isVoice;
}
2015-10-01 18:19:52 +02:00
}
if (isSticker)
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_STICKER"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_STICKER"), user.displayName, chatName];
}
2016-02-25 01:03:51 +01:00
else if (isAnimated) {
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_GIF"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_GIF"), user.displayName, chatName];
}
else if (isVoice) {
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_AUDIO"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_AUDIO"), user.displayName, chatName];
}
2014-07-10 16:11:09 +02:00
else
2015-10-01 18:19:52 +02:00
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_DOC"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_DOC"), user.displayName, chatName];
}
2014-07-10 16:11:09 +02:00
attachmentFound = true;
break;
}
else if (attachment.type == TGAudioMediaAttachmentType)
{
if (message.cid > 0)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_AUDIO"), user.displayName];
else
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_AUDIO"), user.displayName, chatName];
attachmentFound = true;
break;
}
}
2016-02-25 01:03:51 +01:00
if (migrationFound) {
continue;
}
2014-07-10 16:11:09 +02:00
int soundId = 1;
bool notFound = false;
2016-02-25 01:03:51 +01:00
[TGDatabaseInstance() loadPeerNotificationSettings:notificationPeerId soundId:&soundId muteUntil:NULL previewText:NULL messagesMuted:NULL notFound:&notFound];
2014-07-10 16:11:09 +02:00
if (notFound)
{
soundId = 1;
}
if (soundId == 1)
soundId = (message.cid > 0 || message.cid <= INT_MIN) ? globalMessageSoundId : globalGroupSoundId;
2015-10-01 18:19:52 +02:00
if (soundId > 0 && ![midsWithoutSound containsObject:@(message.mid)])
2014-07-10 16:11:09 +02:00
localNotification.soundName = [[NSString alloc] initWithFormat:@"%d.m4a", soundId];
2015-10-01 18:19:52 +02:00
if (multiforwardCount != 0)
2014-07-10 16:11:09 +02:00
{
2015-10-01 18:19:52 +02:00
if (message.cid > 0)
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_FWDS"), user.displayName, [[NSString alloc] initWithFormat:@"%d", (int)multiforwardCount]];
}
2014-07-10 16:11:09 +02:00
else
2015-10-01 18:19:52 +02:00
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_FWDS"), user.displayName, chatName, [[NSString alloc] initWithFormat:@"%d", (int)multiforwardCount]];
}
2014-07-10 16:11:09 +02:00
}
else
{
2015-10-01 18:19:52 +02:00
if (message.cid <= INT_MIN)
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"ENCRYPTED_MESSAGE"), @""];
}
else if (message.cid > 0)
{
if (globalMessagePreviewText && !attachmentFound)
text = [[NSString alloc] initWithFormat:@"%@: %@", user.displayName, message.text];
else if (!attachmentFound)
text = [[NSString alloc] initWithFormat:TGLocalized(@"MESSAGE_NOTEXT"), user.displayName];
}
2014-07-10 16:11:09 +02:00
else
2015-10-01 18:19:52 +02:00
{
if (globalGroupPreviewText && !attachmentFound)
text = [[NSString alloc] initWithFormat:@"%@@%@: %@", user.displayName, chatName, message.text];
else if (!attachmentFound)
text = [[NSString alloc] initWithFormat:TGLocalized(@"CHAT_MESSAGE_NOTEXT"), user.displayName, chatName];
}
}
2016-02-25 01:03:51 +01:00
bool isLocked = [TGAppDelegateInstance isCurrentlyLocked];
if (isLocked)
2015-10-01 18:19:52 +02:00
{
text = [[NSString alloc] initWithFormat:TGLocalized(@"LOCKED_MESSAGE"), @""];
2014-07-10 16:11:09 +02:00
}
2016-02-25 01:03:51 +01:00
static dispatch_once_t onceToken;
static NSString *tokenString = nil;
dispatch_once(&onceToken, ^
{
unichar tokenChar = 0x2026;
tokenString = [[NSString alloc] initWithCharacters:&tokenChar length:1];
});
2014-07-10 16:11:09 +02:00
if (text.length > 256)
2016-02-25 01:03:51 +01:00
{
text = [NSString stringWithFormat:@"%@%@", [text substringToIndex:255], tokenString];
}
2014-07-10 16:11:09 +02:00
2015-10-01 18:19:52 +02:00
text = [text stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
#ifdef INTERNAL_RELEASE
text = [@"[L] " stringByAppendingString:text];
#endif
2014-07-10 16:11:09 +02:00
localNotification.alertBody = text;
2015-10-01 18:19:52 +02:00
localNotification.userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSNumber alloc] initWithLongLong:message.cid], @"cid", @(message.mid), @"mid", nil];
2016-02-25 01:03:51 +01:00
if (iosMajorVersion() >= 8 && !isLocked)
2015-10-01 18:19:52 +02:00
{
if (TGPeerIdIsGroup(message.cid))
localNotification.category = @"m";
else if (TGPeerIdIsChannel(message.cid))
localNotification.category = @"c";
else if (message.cid > INT_MIN)
localNotification.category = @"r";
}
2014-07-10 16:11:09 +02:00
if (text != nil)
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
}
@catch (NSException *e)
{
TGLog(@"%@", e);
}
}
else
{
2015-10-01 18:19:52 +02:00
TGLog(@"Not showing local notifications (applicationState = %d)", (int)applicationState);
2014-07-10 16:11:09 +02:00
[TGApplyUpdatesActor clearDelayedNotifications];
}
}];
});
}
@end