mirror of
https://github.com/danog/Telegram.git
synced 2024-12-12 09:29:55 +01:00
1302 lines
62 KiB
Plaintext
1302 lines
62 KiB
Plaintext
#import "TGModernSendBroadcastMessageActor.h"
|
|
|
|
#import "ActionStage.h"
|
|
#import "SGraphObjectNode.h"
|
|
|
|
#import "TGPreparedTextMessage.h"
|
|
#import "TGPreparedMapMessage.h"
|
|
#import "TGPreparedLocalImageMessage.h"
|
|
#import "TGPreparedRemoteImageMessage.h"
|
|
#import "TGPreparedLocalVideoMessage.h"
|
|
#import "TGPreparedRemoteVideoMessage.h"
|
|
#import "TGPreparedForwardedMessage.h"
|
|
#import "TGPreparedContactMessage.h"
|
|
#import "TGPreparedLocalDocumentMessage.h"
|
|
#import "TGPreparedLocalAudioMessage.h"
|
|
#import "TGPreparedDownloadImageMessage.h"
|
|
#import "TGPreparedDownloadDocumentMessage.h"
|
|
#import "TGPreparedCloudDocumentMessage.h"
|
|
#import "TGPreparedRemoteDocumentMessage.h"
|
|
|
|
#import "TGTelegraph.h"
|
|
#import "TGTelegramNetworking.h"
|
|
#import "TGDatabase.h"
|
|
|
|
#import "TGRemoteImageView.h"
|
|
#import "TGImageDownloadActor.h"
|
|
#import "TGVideoDownloadActor.h"
|
|
#import "TGMediaStoreContext.h"
|
|
|
|
#import "TGImageUtils.h"
|
|
#import "TGStringUtils.h"
|
|
|
|
#import "TGMessage+Telegraph.h"
|
|
#import "TGConversation+Telegraph.h"
|
|
|
|
#import "TGConversationAddMessagesActor.h"
|
|
#import "TGUserDataRequestBuilder.h"
|
|
|
|
#import "TLMessage$modernMessage.h"
|
|
#import "TLMessage$modernMessageService.h"
|
|
|
|
#import "TLUpdates+TG.h"
|
|
|
|
#import <WebP/decode.h>
|
|
|
|
#import "TGAppDelegate.h"
|
|
|
|
@interface TGModernSendBroadcastMessageActor ()
|
|
{
|
|
int64_t _conversationId;
|
|
NSArray *_userIds;
|
|
NSArray *_secretChatConversationIds;
|
|
NSArray *_chatConversationIds;
|
|
|
|
bool _shouldPostAlmostDeliveredMessage;
|
|
|
|
NSArray *_childSendMessageActionPaths;
|
|
NSMutableArray *_remainingChildSendMessageActionPaths;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGModernSendBroadcastMessageActor
|
|
|
|
+ (void)load
|
|
{
|
|
@autoreleasepool
|
|
{
|
|
[ASActor registerActorClass:self];
|
|
}
|
|
}
|
|
|
|
+ (NSString *)genericPath
|
|
{
|
|
return @"/tg/sendBroadcastMessage/@/@";
|
|
}
|
|
|
|
- (void)prepare:(NSDictionary *)options
|
|
{
|
|
[super prepare:options];
|
|
|
|
_conversationId = [options[@"conversationId"] longLongValue];
|
|
_userIds = options[@"userIds"];
|
|
_secretChatConversationIds = options[@"secretChatConversationIds"];
|
|
_chatConversationIds = options[@"chatConversationIds"];
|
|
}
|
|
|
|
- (int64_t)peerId
|
|
{
|
|
return _conversationId;
|
|
}
|
|
|
|
- (TGPreparedMessage *)copyPreparedMessage:(TGPreparedMessage *)preparedMessage
|
|
{
|
|
if ([preparedMessage isKindOfClass:[TGPreparedTextMessage class]])
|
|
{
|
|
TGPreparedTextMessage *preparedTextMessage = (TGPreparedTextMessage *)preparedMessage;
|
|
|
|
TGPreparedTextMessage *copyMessage = [[TGPreparedTextMessage alloc] initWithText:preparedTextMessage.text replyMessage:preparedTextMessage.replyMessage disableLinkPreviews:preparedTextMessage.disableLinkPreviews parsedWebpage:nil];
|
|
return copyMessage;
|
|
}
|
|
else if ([preparedMessage isKindOfClass:[TGPreparedLocalImageMessage class]])
|
|
{
|
|
TGPreparedLocalImageMessage *preparedLocalImageMessage = (TGPreparedLocalImageMessage *)preparedMessage;
|
|
|
|
return [TGPreparedLocalImageMessage messageByCopyingMessageData:preparedLocalImageMessage];
|
|
}
|
|
else if ([preparedMessage isKindOfClass:[TGPreparedLocalVideoMessage class]])
|
|
{
|
|
TGPreparedLocalVideoMessage *preparedLocalVideoMessage = (TGPreparedLocalVideoMessage *)preparedMessage;
|
|
|
|
return [TGPreparedLocalVideoMessage messageByCopyingDataFromMessage:preparedLocalVideoMessage];
|
|
}
|
|
else if ([preparedMessage isKindOfClass:[TGPreparedLocalAudioMessage class]])
|
|
{
|
|
TGPreparedLocalAudioMessage *preparedLocalAudioMessage = (TGPreparedLocalAudioMessage *)preparedMessage;
|
|
|
|
return [TGPreparedLocalAudioMessage messageByCopyingDataFromMessage:preparedLocalAudioMessage];
|
|
}
|
|
else if ([preparedMessage isKindOfClass:[TGPreparedLocalDocumentMessage class]])
|
|
{
|
|
TGPreparedLocalDocumentMessage *preparedLocalDocumentMessage = (TGPreparedLocalDocumentMessage *)preparedMessage;
|
|
|
|
return [TGPreparedLocalDocumentMessage messageByCopyingDataFromMessage:preparedLocalDocumentMessage];
|
|
}
|
|
else if ([preparedMessage isKindOfClass:[TGPreparedMapMessage class]])
|
|
{
|
|
TGPreparedMapMessage *preparedMapMessage = (TGPreparedMapMessage *)preparedMessage;
|
|
|
|
return [[TGPreparedMapMessage alloc] initWithLatitude:preparedMapMessage.latitude longitude:preparedMapMessage.longitude venue:preparedMapMessage.venue replyMessage:nil];
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)_commitSend
|
|
{
|
|
if (_conversationId == 0)
|
|
[self _fail];
|
|
else
|
|
{
|
|
if (_secretChatConversationIds.count != 0 || _chatConversationIds.count != 0)
|
|
{
|
|
[self beginUploadProgress];
|
|
|
|
NSMutableArray *actionsWithOptions = [[NSMutableArray alloc] init];
|
|
|
|
NSMutableArray *childSendMessageActionPaths = [[NSMutableArray alloc] init];
|
|
|
|
for (NSNumber *nPeerId in _secretChatConversationIds)
|
|
{
|
|
TGPreparedMessage *preparedMessage = [self copyPreparedMessage:self.preparedMessage];
|
|
|
|
int64_t encryptedConversationId = [TGDatabaseInstance() encryptedConversationIdForPeerId:[nPeerId longLongValue]];
|
|
int64_t accessHash = [TGDatabaseInstance() encryptedConversationAccessHash:[nPeerId longLongValue]];
|
|
|
|
if (preparedMessage.randomId == 0)
|
|
{
|
|
int64_t randomId = 0;
|
|
arc4random_buf(&randomId, sizeof(randomId));
|
|
preparedMessage.randomId = randomId;
|
|
}
|
|
|
|
int32_t messageId = [[TGDatabaseInstance() generateLocalMids:1][0] intValue];
|
|
preparedMessage.mid = messageId;
|
|
|
|
preparedMessage.date = (int)[[TGTelegramNetworking instance] approximateRemoteTime];
|
|
|
|
TGMessage *message = [preparedMessage message];
|
|
if (message == nil)
|
|
{
|
|
TGLog(@"***** Failed to generate message from prepared message");
|
|
continue;
|
|
}
|
|
|
|
message.outgoing = true;
|
|
message.unread = true;
|
|
message.fromUid = TGTelegraphInstance.clientUserId;
|
|
message.toUid = [nPeerId longLongValue];
|
|
message.deliveryState = TGMessageDeliveryStatePending;
|
|
message.randomId = preparedMessage.randomId;
|
|
message.isBroadcast = true;
|
|
|
|
NSString *path = [[NSString alloc] initWithFormat:@"/tg/sendSecretMessage/(%" PRId64 ")/(%" PRId32 ")", (int64_t)[nPeerId longLongValue], messageId];
|
|
NSDictionary *options = @{@"conversationId": nPeerId, @"encryptedConversationId": @(encryptedConversationId), @"accessHash": @(accessHash), @"preparedMessage": preparedMessage};
|
|
|
|
[TGDatabaseInstance() addMessagesToConversation:@[message] conversationId:[nPeerId longLongValue] updateConversation:nil dispatch:true countUnread:false updateDates:false];
|
|
|
|
[childSendMessageActionPaths addObject:path];
|
|
[actionsWithOptions addObject:@[path, options]];
|
|
}
|
|
|
|
for (NSNumber *nPeerId in _chatConversationIds)
|
|
{
|
|
TGPreparedMessage *preparedMessage = [self copyPreparedMessage:self.preparedMessage];
|
|
|
|
if (preparedMessage.randomId == 0)
|
|
{
|
|
int64_t randomId = 0;
|
|
arc4random_buf(&randomId, sizeof(randomId));
|
|
preparedMessage.randomId = randomId;
|
|
}
|
|
|
|
int32_t messageId = [[TGDatabaseInstance() generateLocalMids:1][0] intValue];
|
|
preparedMessage.mid = messageId;
|
|
|
|
preparedMessage.date = (int)[[TGTelegramNetworking instance] approximateRemoteTime];
|
|
|
|
TGMessage *message = [preparedMessage message];
|
|
if (message == nil)
|
|
{
|
|
TGLog(@"***** Failed to generate message from prepared message");
|
|
continue;
|
|
}
|
|
|
|
message.outgoing = true;
|
|
message.unread = true;
|
|
message.fromUid = TGTelegraphInstance.clientUserId;
|
|
message.toUid = [nPeerId longLongValue];
|
|
message.deliveryState = TGMessageDeliveryStatePending;
|
|
message.randomId = preparedMessage.randomId;
|
|
message.isBroadcast = true;
|
|
|
|
NSString *path = [[NSString alloc] initWithFormat:@"/tg/sendCommonMessage/(%" PRId64 ")/(%" PRId32 ")", (int64_t)[nPeerId longLongValue], messageId];
|
|
NSDictionary *options = @{@"conversationId": nPeerId, @"preparedMessage": preparedMessage};
|
|
|
|
[TGDatabaseInstance() addMessagesToConversation:@[message] conversationId:[nPeerId longLongValue] updateConversation:nil dispatch:true countUnread:false updateDates:false];
|
|
|
|
[childSendMessageActionPaths addObject:path];
|
|
[actionsWithOptions addObject:@[path, options]];
|
|
}
|
|
|
|
_childSendMessageActionPaths = childSendMessageActionPaths;
|
|
_remainingChildSendMessageActionPaths = [[NSMutableArray alloc] initWithArray:_childSendMessageActionPaths];
|
|
|
|
for (NSArray *actionAndOption in actionsWithOptions)
|
|
{
|
|
[ActionStageInstance() requestActor:actionAndOption[0] options:actionAndOption[1] watcher:self];
|
|
}
|
|
|
|
if (actionsWithOptions.count == 0)
|
|
[self _commitSendBroadcast];
|
|
}
|
|
else
|
|
[self _commitSendBroadcast];
|
|
}
|
|
}
|
|
|
|
- (void)_commitSendBroadcast
|
|
{
|
|
if ([self.preparedMessage isKindOfClass:[TGPreparedTextMessage class]])
|
|
{
|
|
TGPreparedTextMessage *textMessage = (TGPreparedTextMessage *)self.preparedMessage;
|
|
|
|
if (self.preparedMessage.randomId != 0 && self.preparedMessage.mid != 0)
|
|
[TGDatabaseInstance() setTempIdForMessageId:textMessage.mid peerId:0 tempId:textMessage.randomId];
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMessage:_userIds messageText:textMessage.text geo:nil tmpId:textMessage.randomId actor:self];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedMapMessage class]])
|
|
{
|
|
TGPreparedMapMessage *mapMessage = (TGPreparedMapMessage *)self.preparedMessage;
|
|
|
|
TLInputGeoPoint$inputGeoPoint *geoPoint = [[TLInputGeoPoint$inputGeoPoint alloc] init];
|
|
geoPoint.lat = mapMessage.latitude;
|
|
geoPoint.n_long = mapMessage.longitude;
|
|
|
|
TLInputMedia *media = nil;
|
|
if (mapMessage.venue != nil)
|
|
{
|
|
TGVenueAttachment *venue = mapMessage.venue;
|
|
|
|
TLInputMedia$inputMediaVenue *venueMedia = [[TLInputMedia$inputMediaVenue alloc] init];
|
|
venueMedia.geo_point = geoPoint;
|
|
venueMedia.title = venue.title;
|
|
venueMedia.address = venue.address;
|
|
venueMedia.provider = venue.provider;
|
|
venueMedia.venue_id = venue.venueId;
|
|
media = venueMedia;
|
|
}
|
|
else
|
|
{
|
|
TLInputMedia$inputMediaGeoPoint *geoMedia = [[TLInputMedia$inputMediaGeoPoint alloc] init];
|
|
geoMedia.geo_point = geoPoint;
|
|
media = geoMedia;
|
|
}
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:media tmpId:mapMessage.randomId actor:self];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalImageMessage class]])
|
|
{
|
|
TGPreparedLocalImageMessage *localImageMessage = (TGPreparedLocalImageMessage *)self.preparedMessage;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
[self uploadFilesWithExtensions:@[@[localImageMessage.localImageDataPath, @"jpg", @(true)]]];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedRemoteImageMessage class]])
|
|
{
|
|
TGPreparedRemoteImageMessage *remoteImageMessage = (TGPreparedRemoteImageMessage *)self.preparedMessage;
|
|
|
|
TLInputMedia$inputMediaPhoto *remotePhoto = [[TLInputMedia$inputMediaPhoto alloc] init];
|
|
TLInputPhoto$inputPhoto *inputId = [[TLInputPhoto$inputPhoto alloc] init];
|
|
inputId.n_id = remoteImageMessage.imageId;
|
|
inputId.access_hash = remoteImageMessage.accessHash;
|
|
remotePhoto.n_id = inputId;
|
|
remotePhoto.caption = remoteImageMessage.caption;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:remotePhoto tmpId:remoteImageMessage.randomId actor:self];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedRemoteDocumentMessage class]])
|
|
{
|
|
TGPreparedRemoteDocumentMessage *remoteDocumentMessage = (TGPreparedRemoteDocumentMessage *)self.preparedMessage;
|
|
|
|
TLInputMedia$inputMediaDocument *remoteDocument = [[TLInputMedia$inputMediaDocument alloc] init];
|
|
TLInputDocument$inputDocument *inputDocument = [[TLInputDocument$inputDocument alloc] init];
|
|
inputDocument.n_id = remoteDocumentMessage.documentId;
|
|
inputDocument.access_hash = remoteDocumentMessage.accessHash;
|
|
remoteDocument.n_id = inputDocument;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:remoteDocument tmpId:remoteDocumentMessage.randomId actor:self];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalVideoMessage class]])
|
|
{
|
|
TGPreparedLocalVideoMessage *localVideoMessage = (TGPreparedLocalVideoMessage *)self.preparedMessage;
|
|
|
|
UIImage *thumbnailImage = [[UIImage alloc] initWithContentsOfFile:[self pathForLocalImagePath:localVideoMessage.localThumbnailDataPath]];
|
|
CGSize thumbnailSize = TGFitSize(thumbnailImage.size, CGSizeMake(90, 90));
|
|
NSData *thumbnailData = UIImageJPEGRepresentation(TGScaleImageToPixelSize(thumbnailImage, thumbnailSize), 0.6f);
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
NSMutableArray *desc = [[NSMutableArray alloc] initWithArray:@[[localVideoMessage localVideoPath], @"mp4", @(true)]];
|
|
if (localVideoMessage.liveData != nil)
|
|
[desc addObject:localVideoMessage.liveData];
|
|
|
|
[self uploadFilesWithExtensions:@[desc, @[thumbnailData, @"jpg", @(false)]]];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedRemoteVideoMessage class]])
|
|
{
|
|
TGPreparedRemoteVideoMessage *remoteVideoMessage = (TGPreparedRemoteVideoMessage *)self.preparedMessage;
|
|
|
|
TLInputMedia$inputMediaVideo *remoteVideo = [[TLInputMedia$inputMediaVideo alloc] init];
|
|
TLInputVideo$inputVideo *inputVideo = [[TLInputVideo$inputVideo alloc] init];
|
|
inputVideo.n_id = remoteVideoMessage.videoId;
|
|
inputVideo.access_hash = remoteVideoMessage.accessHash;
|
|
remoteVideo.n_id = inputVideo;
|
|
remoteVideo.caption = remoteVideoMessage.caption;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:remoteVideo tmpId:remoteVideoMessage.randomId actor:self];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalDocumentMessage class]])
|
|
{
|
|
TGPreparedLocalDocumentMessage *localDocumentMessage = (TGPreparedLocalDocumentMessage *)self.preparedMessage;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
NSMutableArray *uploadFiles = [[NSMutableArray alloc] init];
|
|
|
|
[uploadFiles addObject:@[
|
|
[[localDocumentMessage localDocumentDirectory] stringByAppendingPathComponent:[localDocumentMessage localDocumentFileName]], @"bin", @(true)
|
|
]];
|
|
|
|
if (localDocumentMessage.localThumbnailDataPath != nil)
|
|
{
|
|
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self pathForLocalImagePath:localDocumentMessage.localThumbnailDataPath]];
|
|
if (image != nil)
|
|
{
|
|
NSData *thumbnailData = UIImageJPEGRepresentation(TGScaleImageToPixelSize(image, TGFitSize(image.size, CGSizeMake(90, 90))), 0.6f);
|
|
if (thumbnailData != nil)
|
|
[uploadFiles addObject:@[thumbnailData, @"jpg", @(false)]];
|
|
}
|
|
}
|
|
|
|
[self uploadFilesWithExtensions:uploadFiles];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedContactMessage class]])
|
|
{
|
|
TGPreparedContactMessage *contactMessage = (TGPreparedContactMessage *)self.preparedMessage;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
TLInputMedia$inputMediaContact *inputContact = [[TLInputMedia$inputMediaContact alloc] init];
|
|
inputContact.first_name = contactMessage.firstName;
|
|
inputContact.last_name = contactMessage.lastName;
|
|
inputContact.phone_number = contactMessage.phoneNumber;
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:inputContact tmpId:contactMessage.randomId actor:self];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalAudioMessage class]])
|
|
{
|
|
TGPreparedLocalAudioMessage *localAudioMessage = (TGPreparedLocalAudioMessage *)self.preparedMessage;
|
|
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
|
|
NSString *pathExtension = [[localAudioMessage localAudioFilePath1] pathExtension];
|
|
if (pathExtension.length == 0)
|
|
pathExtension = @"m4a";
|
|
|
|
NSMutableArray *desc = [[NSMutableArray alloc] initWithArray:@[[localAudioMessage localAudioFilePath1], pathExtension, @(true)]];
|
|
if (localAudioMessage.liveData != nil)
|
|
[desc addObject:localAudioMessage.liveData];
|
|
[self uploadFilesWithExtensions:@[desc]];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadImageMessage class]])
|
|
{
|
|
TGPreparedDownloadImageMessage *downloadImageMessage = (TGPreparedDownloadImageMessage *)self.preparedMessage;
|
|
|
|
[self setupFailTimeout:[TGModernSendBroadcastMessageActor defaultTimeoutInterval]];
|
|
|
|
bool dispatchThumbnail = false;
|
|
NSString *url = [downloadImageMessage.imageInfo imageUrlForLargestSize:NULL];
|
|
NSString *imagePath = [self filePathForLocalImageUrl:url];
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:[imagePath stringByDeletingLastPathComponent] withIntermediateDirectories:true attributes:nil error:nil];
|
|
NSData *imageData = [[NSData alloc] initWithContentsOfFile:imagePath];
|
|
if (imageData == nil)
|
|
{
|
|
imageData = [[[TGMediaStoreContext instance] temporaryFilesCache] getValueForKey:[url dataUsingEncoding:NSUTF8StringEncoding]];
|
|
if (imageData != nil)
|
|
{
|
|
[imageData writeToFile:imagePath atomically:false];
|
|
|
|
dispatchThumbnail = true;
|
|
}
|
|
}
|
|
|
|
if (imageData != nil)
|
|
{
|
|
[self _uploadDownloadedData:imageData dispatchThumbnail:dispatchThumbnail];
|
|
}
|
|
else
|
|
{
|
|
self.uploadProgressContainsPreDownloads = true;
|
|
|
|
NSString *path = [[NSString alloc] initWithFormat:@"/temporaryDownload/(%@)", url];
|
|
[ActionStageInstance() requestActor:path options:@{@"url": url, @"file": imagePath, @"queue": @"messagePreDownloads"} flags:0 watcher:self];
|
|
|
|
[self beginUploadProgress];
|
|
}
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadDocumentMessage class]])
|
|
{
|
|
TGPreparedDownloadDocumentMessage *downloadDocumentMessage = (TGPreparedDownloadDocumentMessage *)self.preparedMessage;
|
|
|
|
bool dispatchThumbnail = false;
|
|
|
|
NSString *documentPath = [self filePathForLocalDocumentId:downloadDocumentMessage.localDocumentId attributes:downloadDocumentMessage.attributes];
|
|
NSData *documentData = [[NSData alloc] initWithContentsOfFile:documentPath];
|
|
if (documentData == nil)
|
|
{
|
|
NSString *documentUrl = downloadDocumentMessage.documentUrl;
|
|
if ([documentUrl isKindOfClass:[NSURL class]])
|
|
documentUrl = [(NSURL *)documentUrl path];
|
|
|
|
documentData = [[[TGMediaStoreContext instance] temporaryFilesCache] getValueForKey:[documentUrl dataUsingEncoding:NSUTF8StringEncoding]];
|
|
if (documentData != nil)
|
|
{
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:[documentPath stringByDeletingLastPathComponent] withIntermediateDirectories:true attributes:nil error:nil];
|
|
[documentData writeToFile:documentPath atomically:false];
|
|
|
|
dispatchThumbnail = true;
|
|
}
|
|
}
|
|
|
|
if (documentData != nil)
|
|
{
|
|
[self _uploadDownloadedData:documentData dispatchThumbnail:dispatchThumbnail];
|
|
}
|
|
else
|
|
{
|
|
[self setupFailTimeout:[TGModernSendBroadcastMessageActor defaultTimeoutInterval]];
|
|
self.uploadProgressContainsPreDownloads = true;
|
|
|
|
NSString *path = [[NSString alloc] initWithFormat:@"/temporaryDownload/(%@)", [TGStringUtils stringByEscapingForActorURL:downloadDocumentMessage.documentUrl]];
|
|
[ActionStageInstance() requestActor:path options:@{@"url": downloadDocumentMessage.documentUrl, @"size": @(downloadDocumentMessage.size), @"path": documentPath, @"queue": @"messagePreDownloads"} flags:0 watcher:self];
|
|
|
|
[self beginUploadProgress];
|
|
}
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedCloudDocumentMessage class]])
|
|
{
|
|
TGPreparedCloudDocumentMessage *cloudDocumentMessage = (TGPreparedCloudDocumentMessage *)self.preparedMessage;
|
|
|
|
bool dispatchThumbnail = false;
|
|
|
|
NSString *documentPath = [self filePathForLocalDocumentId:cloudDocumentMessage.localDocumentId attributes:cloudDocumentMessage.attributes];
|
|
NSData *documentData = [[NSData alloc] initWithContentsOfFile:documentPath];
|
|
if (documentData == nil)
|
|
{
|
|
NSString *documentUrl = [cloudDocumentMessage.documentUrl path];
|
|
if ([documentUrl isKindOfClass:[NSURL class]])
|
|
documentUrl = [(NSURL *)documentUrl path];
|
|
|
|
documentData = [[[TGMediaStoreContext instance] temporaryFilesCache] getValueForKey:[documentUrl dataUsingEncoding:NSUTF8StringEncoding]];
|
|
if (documentData != nil)
|
|
{
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:[documentPath stringByDeletingLastPathComponent] withIntermediateDirectories:true attributes:nil error:nil];
|
|
[documentData writeToFile:documentPath atomically:false];
|
|
|
|
dispatchThumbnail = true;
|
|
}
|
|
}
|
|
|
|
if (documentData != nil)
|
|
{
|
|
[self _uploadDownloadedData:documentData dispatchThumbnail:dispatchThumbnail];
|
|
}
|
|
else
|
|
{
|
|
[self setupFailTimeout:[TGModernSendBroadcastMessageActor defaultTimeoutInterval]];
|
|
self.uploadProgressContainsPreDownloads = true;
|
|
|
|
NSString *path = [[NSString alloc] initWithFormat:@"/iCloudDownload/(%@)", [TGStringUtils stringByEscapingForActorURL:cloudDocumentMessage.documentUrl.absoluteString]];
|
|
[ActionStageInstance() requestActor:path options:@{@"url": cloudDocumentMessage.documentUrl, @"path": documentPath, @"queue": @"messagePreDownloads"} flags:0 watcher:self];
|
|
|
|
[self beginUploadProgress];
|
|
}
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
|
|
- (NSString *)filePathForLocalDocumentId:(int64_t)localDocumentId attributes:(NSArray *)attributes
|
|
{
|
|
NSString *directory = nil;
|
|
directory = [TGPreparedLocalDocumentMessage localDocumentDirectoryForLocalDocumentId:localDocumentId];
|
|
|
|
NSString *fileName = @"file";
|
|
for (id attribute in attributes)
|
|
{
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeFilename class]])
|
|
{
|
|
fileName = ((TGDocumentAttributeFilename *)attribute).filename;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NSString *filePath = [directory stringByAppendingPathComponent:[TGDocumentMediaAttachment safeFileNameForFileName:fileName]];
|
|
return filePath;
|
|
}
|
|
|
|
- (NSString *)filePathForLocalImageUrl:(NSString *)localImageUrl
|
|
{
|
|
static NSString *filesDirectory = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
filesDirectory = [[TGAppDelegate documentsPath] stringByAppendingPathComponent:@"files"];
|
|
});
|
|
|
|
int64_t localImageId = murMurHash32(localImageUrl);
|
|
|
|
NSString *photoDirectoryName = [[NSString alloc] initWithFormat:@"image-local-%" PRIx64 "", localImageId];
|
|
NSString *photoDirectory = [filesDirectory stringByAppendingPathComponent:photoDirectoryName];
|
|
|
|
NSString *imagePath = [photoDirectory stringByAppendingPathComponent:@"image.jpg"];
|
|
return imagePath;
|
|
}
|
|
|
|
- (NSString *)filePathForRemoteImageId:(int64_t)remoteImageId
|
|
{
|
|
static NSString *filesDirectory = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
filesDirectory = [[TGAppDelegate documentsPath] stringByAppendingPathComponent:@"files"];
|
|
});
|
|
|
|
NSString *photoDirectoryName = [[NSString alloc] initWithFormat:@"image-remote-%" PRIx64 "", remoteImageId];
|
|
NSString *photoDirectory = [filesDirectory stringByAppendingPathComponent:photoDirectoryName];
|
|
|
|
NSString *imagePath = [photoDirectory stringByAppendingPathComponent:@"image.jpg"];
|
|
return imagePath;
|
|
}
|
|
|
|
- (void)_fail
|
|
{
|
|
std::vector<TGDatabaseMessageFlagValue> flags;
|
|
flags.push_back((TGDatabaseMessageFlagValue){.flag = TGDatabaseMessageFlagDeliveryState, .value = TGMessageDeliveryStateFailed});
|
|
[TGDatabaseInstance() updateMessage:self.preparedMessage.mid peerId:0 flags:flags media:nil dispatch:true];
|
|
|
|
[ActionStageInstance() dispatchMessageToWatchers:self.path messageType:@"messageDeliveryFailed" message:@{
|
|
@"previousMid": @(self.preparedMessage.mid)
|
|
}];
|
|
|
|
[super _fail];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)_uploadDownloadedData:(NSData *)data dispatchThumbnail:(bool)dispatchThumbnail
|
|
{
|
|
if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadImageMessage class]])
|
|
{
|
|
TGPreparedDownloadImageMessage *downloadImageMessage = (TGPreparedDownloadImageMessage *)self.preparedMessage;
|
|
if (dispatchThumbnail)
|
|
{
|
|
NSString *thumbnailUrl = [downloadImageMessage.imageInfo closestImageUrlWithSize:CGSizeZero resultingSize:NULL];
|
|
if (thumbnailUrl != nil)
|
|
{
|
|
[ActionStageInstance() dispatchResource:[[NSString alloc] initWithFormat:@"/as/media/imageThumbnailUpdated"] resource:thumbnailUrl];
|
|
}
|
|
}
|
|
|
|
[self uploadFilesWithExtensions:@[@[data, @"jpg", @(true)]]];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadDocumentMessage class]])
|
|
{
|
|
TGPreparedDownloadDocumentMessage *downloadDocumentMessage = (TGPreparedDownloadDocumentMessage *)self.preparedMessage;
|
|
if (dispatchThumbnail)
|
|
{
|
|
NSString *thumbnailUrl = [downloadDocumentMessage.thumbnailInfo imageUrlForLargestSize:NULL];
|
|
if (thumbnailUrl != nil)
|
|
{
|
|
[ActionStageInstance() dispatchResource:[[NSString alloc] initWithFormat:@"/as/media/imageThumbnailUpdated"] resource:thumbnailUrl];
|
|
}
|
|
}
|
|
|
|
TGDocumentAttributeFilename *fileNameAttribute;
|
|
NSArray *attributes = downloadDocumentMessage.attributes;
|
|
bool hasImageSizeAttribute = false;
|
|
bool hasStickerAttribute = false;
|
|
for (id attribute in attributes)
|
|
{
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeFilename class]])
|
|
fileNameAttribute = (TGDocumentAttributeFilename *)attribute;
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]])
|
|
hasImageSizeAttribute = true;
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
|
|
hasStickerAttribute = true;
|
|
}
|
|
|
|
NSString *fileExtension = @"gif";
|
|
if (fileNameAttribute != nil)
|
|
fileExtension = [fileNameAttribute.filename pathExtension];
|
|
|
|
NSMutableArray *files = [[NSMutableArray alloc] init];
|
|
[files addObject:@[data, fileExtension, @(true)]];
|
|
|
|
if ([fileExtension isEqualToString:@"webp"])
|
|
{
|
|
CGSize imageSize = CGSizeZero;
|
|
int width = 0, height = 0;
|
|
if(WebPGetInfo((uint8_t const *)data.bytes, data.length, &width, &height))
|
|
imageSize = CGSizeMake(width, height);
|
|
|
|
NSMutableArray *documentAttributes = [downloadDocumentMessage.attributes mutableCopy];
|
|
if (!hasImageSizeAttribute)
|
|
{
|
|
if (imageSize.width > FLT_EPSILON && imageSize.height > FLT_EPSILON)
|
|
[documentAttributes addObject:[[TGDocumentAttributeImageSize alloc] initWithSize:imageSize]];
|
|
}
|
|
if (!hasStickerAttribute)
|
|
[documentAttributes addObject:[[TGDocumentAttributeSticker alloc] init]];
|
|
|
|
downloadDocumentMessage.attributes = documentAttributes;
|
|
}
|
|
|
|
UIImage *image = [[UIImage alloc] initWithData:data];
|
|
NSData *thumbnailData = nil;
|
|
if (image != nil)
|
|
{
|
|
image = TGScaleImageToPixelSize(image, TGFitSize(image.size, CGSizeMake(90, 90)));
|
|
if (image != nil)
|
|
thumbnailData = UIImageJPEGRepresentation(image, 0.6f);
|
|
if (thumbnailData != nil)
|
|
[files addObject:@[thumbnailData, @"jpg", @(false)]];
|
|
}
|
|
[self uploadFilesWithExtensions:files];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedCloudDocumentMessage class]])
|
|
{
|
|
TGPreparedCloudDocumentMessage *cloudDocumentMessage = (TGPreparedCloudDocumentMessage *)self.preparedMessage;
|
|
if (dispatchThumbnail)
|
|
{
|
|
NSString *thumbnailUrl = [cloudDocumentMessage.thumbnailInfo imageUrlForLargestSize:NULL];
|
|
if (thumbnailUrl != nil)
|
|
{
|
|
[ActionStageInstance() dispatchResource:[[NSString alloc] initWithFormat:@"/as/media/imageThumbnailUpdated"] resource:thumbnailUrl];
|
|
}
|
|
}
|
|
|
|
TGDocumentAttributeFilename *fileNameAttribute;
|
|
NSArray *attributes = cloudDocumentMessage.attributes;
|
|
bool hasImageSizeAttribute = false;
|
|
bool hasStickerAttribute = false;
|
|
for (id attribute in attributes)
|
|
{
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeFilename class]])
|
|
fileNameAttribute = (TGDocumentAttributeFilename *)attribute;
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]])
|
|
hasImageSizeAttribute = true;
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
|
|
hasStickerAttribute = true;
|
|
}
|
|
|
|
NSString *fileExtension = @"gif";
|
|
if (fileNameAttribute != nil)
|
|
fileExtension = [fileNameAttribute.filename pathExtension];
|
|
|
|
if ([fileExtension isEqualToString:@"webp"])
|
|
{
|
|
CGSize imageSize = CGSizeZero;
|
|
int width = 0, height = 0;
|
|
if(WebPGetInfo((uint8_t const *)data.bytes, data.length, &width, &height))
|
|
imageSize = CGSizeMake(width, height);
|
|
|
|
NSMutableArray *documentAttributes = [cloudDocumentMessage.attributes mutableCopy];
|
|
if (!hasImageSizeAttribute)
|
|
{
|
|
if (imageSize.width > FLT_EPSILON && imageSize.height > FLT_EPSILON)
|
|
[documentAttributes addObject:[[TGDocumentAttributeImageSize alloc] initWithSize:imageSize]];
|
|
}
|
|
if (!hasStickerAttribute)
|
|
[documentAttributes addObject:[[TGDocumentAttributeSticker alloc] init]];
|
|
|
|
cloudDocumentMessage.attributes = documentAttributes;
|
|
}
|
|
|
|
NSMutableArray *files = [[NSMutableArray alloc] init];
|
|
[files addObject:@[data, fileExtension, @(true)]];
|
|
|
|
UIImage *image = [[UIImage alloc] initWithData:data];
|
|
NSData *thumbnailData = nil;
|
|
if (image != nil)
|
|
{
|
|
image = TGScaleImageToPixelSize(image, TGFitSize(image.size, CGSizeMake(90, 90)));
|
|
if (image != nil)
|
|
thumbnailData = UIImageJPEGRepresentation(image, 0.6f);
|
|
if (thumbnailData != nil)
|
|
[files addObject:@[thumbnailData, @"jpg", @(false)]];
|
|
}
|
|
[self uploadFilesWithExtensions:files];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)uploadsStarted
|
|
{
|
|
[self setupFailTimeout:[TGModernSendMessageActor defaultTimeoutInterval]];
|
|
}
|
|
|
|
- (void)uploadProgressChanged
|
|
{
|
|
[self restartFailTimeoutIfRunning];
|
|
}
|
|
|
|
- (void)uploadsCompleted:(NSDictionary *)filePathToUploadedFile
|
|
{
|
|
[self restartFailTimeoutIfRunning];
|
|
|
|
if ([self.preparedMessage isKindOfClass:[TGPreparedLocalImageMessage class]])
|
|
{
|
|
TGPreparedLocalImageMessage *localImageMessage = (TGPreparedLocalImageMessage *)self.preparedMessage;
|
|
|
|
NSDictionary *fileInfo = filePathToUploadedFile[localImageMessage.localImageDataPath];
|
|
if (fileInfo != nil)
|
|
{
|
|
TLInputMedia$inputMediaUploadedPhoto *uploadedPhoto = [[TLInputMedia$inputMediaUploadedPhoto alloc] init];
|
|
uploadedPhoto.file = fileInfo[@"file"];
|
|
uploadedPhoto.caption = localImageMessage.caption;
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:uploadedPhoto tmpId:localImageMessage.randomId actor:self];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalVideoMessage class]])
|
|
{
|
|
TGPreparedLocalVideoMessage *localVideoMessage = (TGPreparedLocalVideoMessage *)self.preparedMessage;
|
|
|
|
NSDictionary *videoFileInfo = filePathToUploadedFile[[localVideoMessage localVideoPath]];
|
|
NSDictionary *thumbnailFileInfo = filePathToUploadedFile[@"embedded-data://0"];
|
|
if (videoFileInfo != nil && thumbnailFileInfo != nil)
|
|
{
|
|
TLInputMedia$inputMediaUploadedThumbVideo *uploadedVideo = [[TLInputMedia$inputMediaUploadedThumbVideo alloc] init];
|
|
uploadedVideo.file = videoFileInfo[@"file"];
|
|
uploadedVideo.thumb = thumbnailFileInfo[@"file"];
|
|
uploadedVideo.duration = (int32_t)localVideoMessage.duration;
|
|
uploadedVideo.w = (int32_t)localVideoMessage.videoSize.width;
|
|
uploadedVideo.h = (int32_t)localVideoMessage.videoSize.height;
|
|
uploadedVideo.caption = localVideoMessage.caption;
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:uploadedVideo tmpId:localVideoMessage.randomId actor:self];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalDocumentMessage class]])
|
|
{
|
|
TGPreparedLocalDocumentMessage *localDocumentMessage = (TGPreparedLocalDocumentMessage *)self.preparedMessage;
|
|
|
|
NSDictionary *documentFileInfo = filePathToUploadedFile[[[localDocumentMessage localDocumentDirectory] stringByAppendingPathComponent:[localDocumentMessage localDocumentFileName]]];
|
|
NSDictionary *thumbnailFileInfo = filePathToUploadedFile[@"embedded-data://0"];
|
|
|
|
if (documentFileInfo != nil)
|
|
{
|
|
id uploadedDocument = nil;
|
|
|
|
if (localDocumentMessage.localThumbnailDataPath != nil && thumbnailFileInfo != nil)
|
|
{
|
|
TLInputMedia$inputMediaUploadedThumbDocument *thumbUploadedDocument = [[TLInputMedia$inputMediaUploadedThumbDocument alloc] init];
|
|
thumbUploadedDocument.file = documentFileInfo[@"file"];
|
|
thumbUploadedDocument.attributes = [self attributesForNativeAttributes:localDocumentMessage.attributes];
|
|
thumbUploadedDocument.mime_type = localDocumentMessage.mimeType.length == 0 ? @"application/octet-stream" : localDocumentMessage.mimeType;
|
|
thumbUploadedDocument.thumb = thumbnailFileInfo[@"file"];
|
|
|
|
uploadedDocument = thumbUploadedDocument;
|
|
}
|
|
else
|
|
{
|
|
TLInputMedia$inputMediaUploadedDocument *plainUploadedDocument = [[TLInputMedia$inputMediaUploadedDocument alloc] init];
|
|
plainUploadedDocument.file = documentFileInfo[@"file"];
|
|
plainUploadedDocument.attributes = [self attributesForNativeAttributes:localDocumentMessage.attributes];
|
|
plainUploadedDocument.mime_type = localDocumentMessage.mimeType.length == 0 ? @"application/octet-stream" : localDocumentMessage.mimeType;
|
|
|
|
uploadedDocument = plainUploadedDocument;
|
|
}
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:uploadedDocument tmpId:localDocumentMessage.randomId actor:self];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalAudioMessage class]])
|
|
{
|
|
TGPreparedLocalAudioMessage *localAudioMessage = (TGPreparedLocalAudioMessage *)self.preparedMessage;
|
|
|
|
NSDictionary *audioFileInfo = filePathToUploadedFile[[localAudioMessage localAudioFilePath1]];
|
|
if (audioFileInfo != nil)
|
|
{
|
|
TLInputMedia$inputMediaUploadedAudio *uploadedAudio = [[TLInputMedia$inputMediaUploadedAudio alloc] init];
|
|
uploadedAudio.file = audioFileInfo[@"file"];
|
|
uploadedAudio.duration = localAudioMessage.duration;
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:uploadedAudio tmpId:localAudioMessage.randomId actor:self];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadImageMessage class]])
|
|
{
|
|
TGPreparedDownloadImageMessage *downloadImageMessage = (TGPreparedDownloadImageMessage *)self.preparedMessage;
|
|
|
|
NSDictionary *fileInfo = filePathToUploadedFile[@"embedded-data://0"];
|
|
if (fileInfo != nil)
|
|
{
|
|
TLInputMedia$inputMediaUploadedPhoto *uploadedPhoto = [[TLInputMedia$inputMediaUploadedPhoto alloc] init];
|
|
uploadedPhoto.file = fileInfo[@"file"];
|
|
uploadedPhoto.caption = downloadImageMessage.caption;
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:uploadedPhoto tmpId:downloadImageMessage.randomId actor:self];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadDocumentMessage class]] || [self.preparedMessage isKindOfClass:[TGPreparedCloudDocumentMessage class]])
|
|
{
|
|
TGPreparedDownloadDocumentMessage *downloadDocumentMessage = (TGPreparedDownloadDocumentMessage *)self.preparedMessage;
|
|
|
|
NSDictionary *documentFileInfo = filePathToUploadedFile[@"embedded-data://0"];
|
|
NSDictionary *thumbnailFileInfo = filePathToUploadedFile[@"embedded-data://1"];
|
|
if (documentFileInfo != nil)
|
|
{
|
|
id uploadedDocument = nil;
|
|
|
|
if (thumbnailFileInfo != nil)
|
|
{
|
|
TLInputMedia$inputMediaUploadedThumbDocument *thumbUploadedDocument = [[TLInputMedia$inputMediaUploadedThumbDocument alloc] init];
|
|
thumbUploadedDocument.file = documentFileInfo[@"file"];
|
|
thumbUploadedDocument.attributes = [self attributesForNativeAttributes:downloadDocumentMessage.attributes];
|
|
thumbUploadedDocument.mime_type = downloadDocumentMessage.mimeType.length == 0 ? @"application/octet-stream" : downloadDocumentMessage.mimeType;
|
|
thumbUploadedDocument.thumb = thumbnailFileInfo[@"file"];
|
|
|
|
uploadedDocument = thumbUploadedDocument;
|
|
}
|
|
else
|
|
{
|
|
TLInputMedia$inputMediaUploadedDocument *plainUploadedDocument = [[TLInputMedia$inputMediaUploadedDocument alloc] init];
|
|
plainUploadedDocument.file = documentFileInfo[@"file"];
|
|
plainUploadedDocument.attributes = [self attributesForNativeAttributes:downloadDocumentMessage.attributes];
|
|
plainUploadedDocument.mime_type = downloadDocumentMessage.mimeType.length == 0 ? @"application/octet-stream" : downloadDocumentMessage.mimeType;
|
|
|
|
uploadedDocument = plainUploadedDocument;
|
|
}
|
|
|
|
self.cancelToken = [TGTelegraphInstance doBroadcastSendMedia:_userIds media:uploadedDocument tmpId:downloadDocumentMessage.randomId actor:self];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else
|
|
[self _fail];
|
|
|
|
[super uploadsCompleted:filePathToUploadedFile];
|
|
}
|
|
|
|
- (NSArray *)attributesForNativeAttributes:(NSArray *)nativeAttributes
|
|
{
|
|
NSMutableArray *attributes = [[NSMutableArray alloc] init];
|
|
for (id attribute in nativeAttributes)
|
|
{
|
|
if ([attribute isKindOfClass:[TGDocumentAttributeFilename class]])
|
|
{
|
|
TLDocumentAttribute$documentAttributeFilename *concreteAttribute = [[TLDocumentAttribute$documentAttributeFilename alloc] init];
|
|
concreteAttribute.file_name = ((TGDocumentAttributeFilename *)attribute).filename;
|
|
[attributes addObject:concreteAttribute];
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeAnimated class]])
|
|
{
|
|
[attributes addObject:[[TLDocumentAttribute$documentAttributeAnimated alloc] init]];
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]])
|
|
{
|
|
TLDocumentAttribute$documentAttributeImageSize *concreteAttribute = [[TLDocumentAttribute$documentAttributeImageSize alloc] init];
|
|
concreteAttribute.w = (int32_t)((TGDocumentAttributeImageSize *)attribute).size.width;
|
|
concreteAttribute.h = (int32_t)((TGDocumentAttributeImageSize *)attribute).size.height;
|
|
[attributes addObject:concreteAttribute];
|
|
}
|
|
else if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]])
|
|
{
|
|
[attributes addObject:[[TLDocumentAttribute$documentAttributeSticker alloc] init]];
|
|
}
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)sendBroadcastSuccess:(TLUpdates *)updates
|
|
{
|
|
if (updates.messages.count != 0)
|
|
{
|
|
int date = 0;
|
|
TLMessage *message = updates.messages[0];
|
|
if ([message isKindOfClass:[TLMessage$modernMessage class]])
|
|
date = ((TLMessage$modernMessage *)message).date;
|
|
else if ([message isKindOfClass:[TLMessage$modernMessageService class]])
|
|
date = ((TLMessage$modernMessageService *)message).date;
|
|
|
|
if (date != 0)
|
|
{
|
|
NSMutableArray *parsedMessages = [[NSMutableArray alloc] init];
|
|
|
|
for (TLMessage *message in updates.messages)
|
|
{
|
|
TGMessage *parsedMessage = [[TGMessage alloc] initWithTelegraphMessageDesc:message];
|
|
if (parsedMessage.mid != 0 && parsedMessage.cid != 0)
|
|
{
|
|
parsedMessage.isBroadcast = true;
|
|
[parsedMessages addObject:parsedMessage];
|
|
}
|
|
}
|
|
|
|
NSMutableDictionary *chats = [[NSMutableDictionary alloc] init];
|
|
|
|
for (TLChat *chatDesc in updates.chats)
|
|
{
|
|
TGConversation *conversation = [[TGConversation alloc] initWithTelegraphChatDesc:chatDesc];
|
|
if (conversation != nil)
|
|
{
|
|
[chats setObject:conversation forKey:[[NSNumber alloc] initWithLongLong:conversation.conversationId]];
|
|
}
|
|
}
|
|
|
|
[TGUserDataRequestBuilder executeUserDataUpdate:updates.users];
|
|
|
|
static int actionId = 0;
|
|
[[[TGConversationAddMessagesActor alloc] initWithPath:[[NSString alloc] initWithFormat:@"/tg/addmessage/(sendBroadcast%d)", actionId++]] execute:[[NSDictionary alloc] initWithObjectsAndKeys:chats, @"chats", parsedMessages, @"messages", @true, @"doNotModifyDates", nil]];
|
|
|
|
if (self.preparedMessage.randomId != 0)
|
|
[TGDatabaseInstance() removeTempIds:@[@(self.preparedMessage.randomId)]];
|
|
|
|
TGMessage *message = [parsedMessages[0] copy];
|
|
message.mid = self.preparedMessage.mid;
|
|
message.cid = _conversationId;
|
|
|
|
if ([self.preparedMessage isKindOfClass:[TGPreparedLocalImageMessage class]])
|
|
{
|
|
TGPreparedLocalImageMessage *localImageMessage = (TGPreparedLocalImageMessage *)self.preparedMessage;
|
|
|
|
NSMutableArray *imageFilePaths = [[NSMutableArray alloc] init];
|
|
if (localImageMessage.localImageDataPath != nil)
|
|
[imageFilePaths addObject:localImageMessage.localImageDataPath];
|
|
if (localImageMessage.localThumbnailDataPath != nil)
|
|
[imageFilePaths addObject:localImageMessage.localThumbnailDataPath];
|
|
|
|
for (TGMediaAttachment *attachment in message.mediaAttachments)
|
|
{
|
|
if ([attachment isKindOfClass:[TGImageMediaAttachment class]])
|
|
{
|
|
TGImageMediaAttachment *imageAttachment = (TGImageMediaAttachment *)attachment;
|
|
|
|
NSString *imageUrl = [imageAttachment.imageInfo closestImageUrlWithSize:localImageMessage.imageSize resultingSize:NULL];
|
|
if (imageUrl != nil && localImageMessage.localImageDataPath != nil)
|
|
{
|
|
[[TGRemoteImageView sharedCache] moveToCache:[self pathForLocalImagePath:localImageMessage.localImageDataPath] cacheUrl:imageUrl];
|
|
[imageFilePaths removeObject:localImageMessage.localImageDataPath];
|
|
[TGImageDownloadActor addUrlRewrite:localImageMessage.localImageDataPath newUrl:imageUrl];
|
|
}
|
|
|
|
NSString *thumbnailUrl = [imageAttachment.imageInfo closestImageUrlWithSize:localImageMessage.thumbnailSize resultingSize:NULL];
|
|
if (thumbnailUrl != nil && localImageMessage.localThumbnailDataPath != nil)
|
|
{
|
|
[[TGRemoteImageView sharedCache] moveToCache:[self pathForLocalImagePath:localImageMessage.localThumbnailDataPath] cacheUrl:thumbnailUrl];
|
|
[imageFilePaths removeObject:localImageMessage.localThumbnailDataPath];
|
|
[TGImageDownloadActor addUrlRewrite:localImageMessage.localThumbnailDataPath newUrl:thumbnailUrl];
|
|
}
|
|
|
|
if (localImageMessage.assetUrl.length != 0)
|
|
[TGImageDownloadActor addServerMediaSataForAssetUrl:localImageMessage.assetUrl attachment:imageAttachment];
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (imageFilePaths.count != 0)
|
|
{
|
|
NSMutableArray *absolutePathsToRemove = [[NSMutableArray alloc] init];
|
|
for (NSString *path in imageFilePaths)
|
|
{
|
|
[absolutePathsToRemove addObject:[self pathForLocalImagePath:path]];
|
|
}
|
|
|
|
dispatch_async([TGCache diskCacheQueue], ^
|
|
{
|
|
for (NSString *path in absolutePathsToRemove)
|
|
{
|
|
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalVideoMessage class]])
|
|
{
|
|
TGPreparedLocalVideoMessage *localVideoMessage = (TGPreparedLocalVideoMessage *)self.preparedMessage;
|
|
|
|
NSMutableArray *dataFilePaths = [[NSMutableArray alloc] init];
|
|
if (localVideoMessage.localThumbnailDataPath != nil)
|
|
[dataFilePaths addObject:localVideoMessage.localThumbnailDataPath];
|
|
if ([localVideoMessage localVideoPath] != nil)
|
|
[dataFilePaths addObject:[localVideoMessage localVideoPath]];
|
|
|
|
for (TGMediaAttachment *attachment in message.mediaAttachments)
|
|
{
|
|
if ([attachment isKindOfClass:[TGVideoMediaAttachment class]])
|
|
{
|
|
TGVideoMediaAttachment *videoAttachment = (TGVideoMediaAttachment *)attachment;
|
|
|
|
NSString *documentsDirectory = [TGAppDelegate documentsPath];
|
|
NSString *videosDirectory = [documentsDirectory stringByAppendingPathComponent:@"video"];
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:videosDirectory])
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:videosDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
|
|
|
NSString *updatedVideoPath = [videosDirectory stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"remote%llx.mov", videoAttachment.videoId]];
|
|
[[NSFileManager defaultManager] moveItemAtPath:[localVideoMessage localVideoPath] toPath:updatedVideoPath error:nil];
|
|
[dataFilePaths removeObject:[localVideoMessage localVideoPath]];
|
|
|
|
NSString *remoteUrl = [videoAttachment.videoInfo urlWithQuality:1 actualQuality:NULL actualSize:NULL];
|
|
if (remoteUrl != nil)
|
|
{
|
|
[TGVideoDownloadActor rewriteLocalFilePath:[[NSString alloc] initWithFormat:@"local-video:local%llx.mov", localVideoMessage.localVideoId] remoteUrl:remoteUrl];
|
|
}
|
|
|
|
[[TGRemoteImageView sharedCache] changeCacheItemUrl:[[NSString alloc] initWithFormat:@"video-thumbnail-local%llx.jpg", localVideoMessage.localVideoId] newUrl:[[NSString alloc] initWithFormat:@"video-thumbnail-remote%llx.jpg", videoAttachment.videoId]];
|
|
|
|
NSString *thumbnailUrl = [videoAttachment.thumbnailInfo closestImageUrlWithSize:localVideoMessage.thumbnailSize resultingSize:NULL];
|
|
if (thumbnailUrl != nil && localVideoMessage.localThumbnailDataPath != nil)
|
|
{
|
|
[[TGRemoteImageView sharedCache] moveToCache:[self pathForLocalImagePath:localVideoMessage.localThumbnailDataPath] cacheUrl:thumbnailUrl];
|
|
[dataFilePaths removeObject:localVideoMessage.localThumbnailDataPath];
|
|
[TGImageDownloadActor addUrlRewrite:localVideoMessage.localThumbnailDataPath newUrl:thumbnailUrl];
|
|
}
|
|
|
|
if (localVideoMessage.assetUrl.length != 0)
|
|
[TGImageDownloadActor addServerMediaSataForAssetUrl:localVideoMessage.assetUrl attachment:videoAttachment];
|
|
}
|
|
}
|
|
|
|
if (dataFilePaths.count != 0)
|
|
{
|
|
NSMutableArray *absolutePathsToRemove = [[NSMutableArray alloc] init];
|
|
for (NSString *path in dataFilePaths)
|
|
{
|
|
[absolutePathsToRemove addObject:[self pathForLocalImagePath:path]];
|
|
}
|
|
|
|
dispatch_async([TGCache diskCacheQueue], ^
|
|
{
|
|
for (NSString *path in absolutePathsToRemove)
|
|
{
|
|
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalDocumentMessage class]])
|
|
{
|
|
TGPreparedLocalDocumentMessage *localDocumentMessage = (TGPreparedLocalDocumentMessage *)self.preparedMessage;
|
|
|
|
NSMutableArray *dataFilePaths = [[NSMutableArray alloc] init];
|
|
if (localDocumentMessage.localThumbnailDataPath != nil)
|
|
[dataFilePaths addObject:localDocumentMessage.localThumbnailDataPath];
|
|
if ([localDocumentMessage localDocumentDirectory] != nil)
|
|
[dataFilePaths addObject:[localDocumentMessage localDocumentDirectory]];
|
|
|
|
for (TGMediaAttachment *attachment in message.mediaAttachments)
|
|
{
|
|
if ([attachment isKindOfClass:[TGDocumentMediaAttachment class]])
|
|
{
|
|
TGDocumentMediaAttachment *documentAttachment = (TGDocumentMediaAttachment *)attachment;
|
|
|
|
if (documentAttachment.thumbnailInfo != nil && localDocumentMessage.localThumbnailDataPath != nil)
|
|
{
|
|
NSString *thumbnailUri = [[documentAttachment thumbnailInfo] imageUrlForLargestSize:NULL];
|
|
if (thumbnailUri != nil)
|
|
{
|
|
[[TGRemoteImageView sharedCache] moveToCache:[self pathForLocalImagePath:localDocumentMessage.localThumbnailDataPath] cacheUrl:thumbnailUri];
|
|
[dataFilePaths removeObject:localDocumentMessage.localThumbnailDataPath];
|
|
}
|
|
}
|
|
|
|
NSString *updatedDocumentDirectory = [TGPreparedLocalDocumentMessage localDocumentDirectoryForDocumentId:documentAttachment.documentId];
|
|
[[NSFileManager defaultManager] moveItemAtPath:[localDocumentMessage localDocumentDirectory] toPath:updatedDocumentDirectory error:nil];
|
|
}
|
|
}
|
|
|
|
if (dataFilePaths.count != 0)
|
|
{
|
|
NSMutableArray *absolutePathsToRemove = [[NSMutableArray alloc] init];
|
|
for (NSString *path in dataFilePaths)
|
|
{
|
|
[absolutePathsToRemove addObject:[self pathForLocalImagePath:path]];
|
|
}
|
|
|
|
dispatch_async([TGCache diskCacheQueue], ^
|
|
{
|
|
for (NSString *path in absolutePathsToRemove)
|
|
{
|
|
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedLocalAudioMessage class]])
|
|
{
|
|
TGPreparedLocalAudioMessage *localAudioMessage = (TGPreparedLocalAudioMessage *)self.preparedMessage;
|
|
|
|
NSMutableArray *dataFilePaths = [[NSMutableArray alloc] init];
|
|
if ([localAudioMessage localAudioFileDirectory] != nil)
|
|
[dataFilePaths addObject:[localAudioMessage localAudioFileDirectory]];
|
|
|
|
for (TGMediaAttachment *attachment in message.mediaAttachments)
|
|
{
|
|
if ([attachment isKindOfClass:[TGAudioMediaAttachment class]])
|
|
{
|
|
TGAudioMediaAttachment *audioAttachment = (TGAudioMediaAttachment *)attachment;
|
|
|
|
[[NSFileManager defaultManager] moveItemAtPath:[localAudioMessage localAudioFileDirectory] toPath:[TGPreparedLocalAudioMessage localAudioFileDirectoryForRemoteAudioId:audioAttachment.audioId] error:nil];
|
|
}
|
|
}
|
|
|
|
if (dataFilePaths.count != 0)
|
|
{
|
|
NSMutableArray *absolutePathsToRemove = [[NSMutableArray alloc] init];
|
|
for (NSString *path in dataFilePaths)
|
|
{
|
|
[absolutePathsToRemove addObject:[self pathForLocalImagePath:path]];
|
|
}
|
|
|
|
dispatch_async([TGCache diskCacheQueue], ^
|
|
{
|
|
for (NSString *path in absolutePathsToRemove)
|
|
{
|
|
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
std::vector<TGDatabaseMessageFlagValue> flags;
|
|
flags.push_back((TGDatabaseMessageFlagValue){.flag = TGDatabaseMessageFlagDeliveryState, .value = TGMessageDeliveryStateDelivered});
|
|
if (date != 0)
|
|
flags.push_back((TGDatabaseMessageFlagValue){.flag = TGDatabaseMessageFlagDate, .value = date});
|
|
[TGDatabaseInstance() updateMessage:self.preparedMessage.mid peerId:0 flags:flags media:message.mediaAttachments dispatch:true];
|
|
|
|
if (self.preparedMessage.randomId != 0)
|
|
[TGDatabaseInstance() removeTempIds:@[@(self.preparedMessage.randomId)]];
|
|
|
|
int64_t conversationId = _conversationId;
|
|
id resource = [[SGraphObjectNode alloc] initWithObject:[[NSArray alloc] initWithObjects:[[NSNumber alloc] initWithInt:self.preparedMessage.mid], message, nil]];
|
|
|
|
[self _success:@{
|
|
@"previousMid": @(self.preparedMessage.mid),
|
|
@"mid": @(self.preparedMessage.mid),
|
|
@"date": @(date),
|
|
@"message": message
|
|
}];
|
|
|
|
[ActionStageInstance() dispatchResource:[[NSString alloc] initWithFormat:@"/tg/conversation/(%lld)/messagesChanged", conversationId] resource:resource];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
|
|
[[TGTelegramNetworking instance] addUpdates:updates];
|
|
}
|
|
|
|
- (void)sendBroadcastFailed
|
|
{
|
|
[self _fail];
|
|
}
|
|
|
|
- (void)actorCompleted:(int)status path:(NSString *)path result:(id)result
|
|
{
|
|
if ([_childSendMessageActionPaths containsObject:path])
|
|
{
|
|
if (status == ASStatusSuccess)
|
|
{
|
|
[_remainingChildSendMessageActionPaths removeObject:path];
|
|
|
|
if (_remainingChildSendMessageActionPaths.count == 0)
|
|
[self _commitSendBroadcast];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([path hasPrefix:@"/temporaryDownload/"])
|
|
{
|
|
if (status == ASStatusSuccess)
|
|
{
|
|
if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadDocumentMessage class]])
|
|
{
|
|
NSData *documentData = result;
|
|
TGPreparedDownloadDocumentMessage *downloadDocumentMessage = (TGPreparedDownloadDocumentMessage *)self.preparedMessage;
|
|
NSString *documentPath = [self filePathForLocalDocumentId:downloadDocumentMessage.localDocumentId attributes:downloadDocumentMessage.attributes];
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:[documentPath stringByDeletingLastPathComponent] withIntermediateDirectories:true attributes:nil error:nil];
|
|
[documentData writeToFile:documentPath atomically:false];
|
|
}
|
|
else if ([self.preparedMessage isKindOfClass:[TGPreparedDownloadImageMessage class]])
|
|
{
|
|
NSData *imageData = result;
|
|
TGPreparedDownloadImageMessage *downloadImageMessage = (TGPreparedDownloadImageMessage *)self.preparedMessage;
|
|
NSString *imagePath = [self filePathForLocalImageUrl:[downloadImageMessage.imageInfo imageUrlForLargestSize:NULL]];
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:[imagePath stringByDeletingLastPathComponent] withIntermediateDirectories:true attributes:nil error:nil];
|
|
[imageData writeToFile:imagePath atomically:false];
|
|
}
|
|
[self _uploadDownloadedData:result dispatchThumbnail:true];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
else if ([path hasPrefix:@"/iCloudDownload/"])
|
|
{
|
|
if (status == ASStatusSuccess)
|
|
{
|
|
TGPreparedCloudDocumentMessage *cloudDocumentMessage = (TGPreparedCloudDocumentMessage *)self.preparedMessage;
|
|
NSString *documentPath = [self filePathForLocalDocumentId:cloudDocumentMessage.localDocumentId attributes:cloudDocumentMessage.attributes];
|
|
NSError *error;
|
|
NSData *documentData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:documentPath]
|
|
options:NSDataReadingMappedIfSafe
|
|
error:&error];
|
|
|
|
|
|
|
|
|
|
[self _uploadDownloadedData:documentData dispatchThumbnail:true];
|
|
}
|
|
else
|
|
[self _fail];
|
|
}
|
|
|
|
[super actorCompleted:status path:path result:result];
|
|
}
|
|
|
|
- (void)actorMessageReceived:(NSString *)path messageType:(NSString *)messageType message:(id)message
|
|
{
|
|
if ([path hasPrefix:@"/temporaryDownload/"] || [path hasPrefix:@"/iCloudDownload/"])
|
|
{
|
|
[self restartFailTimeoutIfRunning];
|
|
|
|
[self updatePreDownloadsProgress:[message floatValue]];
|
|
}
|
|
|
|
if ([self.superclass instancesRespondToSelector:@selector(actorMessageReceived:messageType:message:)])
|
|
[super actorMessageReceived:path messageType:messageType message:message];
|
|
}
|
|
|
|
@end
|