tweb/src/lib/appManagers/appMessagesManager.ts

3260 lines
105 KiB
TypeScript

import { SearchIndexManager, $rootScope, copy, tsNow, safeReplaceObject, dT, _, listMergeSorted, deepEqual } from "../utils";
import appMessagesIDsManager from "./appMessagesIDsManager";
import appChatsManager from "./appChatsManager";
import appUsersManager from "./appUsersManager";
import { RichTextProcessor } from "../richtextprocessor";
import { nextRandomInt, bigint } from "../bin_utils";
import { MTProto, telegramMeWebService } from "../mtproto/mtproto";
import apiUpdatesManager from "./apiUpdatesManager";
import appPhotosManager from "./appPhotosManager";
import appProfileManager from "./appProfileManager";
import AppStorage from '../storage';
import AppPeersManager from "./appPeersManager";
import ServerTimeManager from "../mtproto/serverTimeManager";
import apiFileManager, { CancellablePromise } from "../mtproto/apiFileManager";
import { MTDocument } from "../../components/misc";
import appDocsManager from "./appDocsManager";
type HistoryStorage = {
count: number | null,
history: number[],
pending: number[],
readPromise?: any
};
export class AppMessagesManager {
public messagesStorage: any = {};
public messagesForHistory: any = {};
public messagesForDialogs: any = {};
public historiesStorage: {
[peerID: string]: HistoryStorage
} = {};
public dialogsStorage: {
count: any,
dialogs: any[]
} = {count: null, dialogs: []};
public pendingByRandomID: any = {};
public pendingByMessageID: any = {};
public pendingAfterMsgs: any = {};
public pendingTopMsgs: any = {};
public sendFilePromise: CancellablePromise<void> = Promise.resolve();
public tempID = -1;
public dialogsIndex: any = SearchIndexManager.createIndex();
public cachedResults: any = {query: false};
public lastSearchFilter: any = {};
public lastSearchResults: any = [];
public needSingleMessages: any = [];
public fetchSingleMessagesTimeout = 0;
private fetchSingleMessagesPromise: Promise<void> = null;
public incrementedMessageViews: any = {};
public needIncrementMessageViews: any = [];
public incrementMessageViewsTimeout: any = false;
public maxSeenID = 0;
public allDialogsLoaded = false;
public dialogsOffsetDate = 0;
public pinnedIndex = 0;
public dialogsNum = 0;
public migratedFromTo: any = {};
public migratedToFrom: any = {};
public newMessagesHandlePromise = 0;
public newMessagesToHandle: any = {};
public newDialogsHandlePromise = 0;
public newDialogsToHandle: any = {};
public notificationsHandlePromise = 0;
public notificationsToHandle: any = {};
public newUpdatesAfterReloadToHandle: any = {};
public fwdMessagesPluralize = _('conversation_forwarded_X_messages');
public gameScorePluralize = _('conversation_scored_X');
constructor() {
AppStorage.get<number>('max_seen_msg').then((maxID) => {
if(maxID && !appMessagesIDsManager.getMessageIDInfo(maxID)[1]) {
this.maxSeenID = maxID;
}
})
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
let update: any = e.detail;
// if(update._ != 'updateUserStatus') {
// console.log('on apiUpdate', update)
// }
this.handleUpdate(update);
});
$rootScope.$on('webpage_updated', (e: CustomEvent) => {
let eventData = e.detail;
eventData.msgs((msgID: number) => {
var historyMessage = this.messagesForHistory[msgID];
if(historyMessage) {
/* historyMessage.media = {
_: 'messageMediaWebPage',
webpage: AppWebPagesManager.wrapForHistory(eventData.id)
}; */ // warning
$rootScope.$broadcast('message_edit', {
peerID: this.getMessagePeer(historyMessage),
id: historyMessage.id,
mid: msgID,
justMedia: true
});
}
});
});
$rootScope.$on('draft_updated', (e: CustomEvent) => {
let eventData = e.detail;;
var peerID = eventData.peerID;
var draft = eventData.draft;
var dialog = this.getDialogByPeerID(peerID)[0];
if(dialog) {
var topDate;
if(draft && draft.date) {
topDate = draft.date;
} else {
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0
var topDate = this.getMessage(dialog.top_message).date;
if(channelID) {
var channel = appChatsManager.getChat(channelID);
if(!topDate || channel.date && channel.date > topDate) {
topDate = channel.date;
}
}
}
if(!dialog.pFlags.pinned) {
dialog.index = this.generateDialogIndex(topDate);
}
this.pushDialogToStorage(dialog);
$rootScope.$broadcast('dialog_draft', {
peerID: peerID,
draft: draft,
index: dialog.index
});
}
})
}
public getInputEntities(entities: any) {
var sendEntites = copy(entities);
sendEntites.forEach((entity: any) => {
if(entity._ == 'messageEntityMentionName') {
entity._ = 'inputMessageEntityMentionName';
entity.user_id = appUsersManager.getUserInput(entity.user_id);
}
});
return sendEntites;
}
public sendText(peerID: number, text: string, options: any = {}) {
if(typeof(text) != 'string') {
return;
}
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
var entities = options.entities || [];
if(!options.viaBotID) {
text = RichTextProcessor.parseMarkdown(text, entities);
}
if(!text.length) {
return;
}
var sendEntites = this.getInputEntities(entities);
var messageID = this.tempID--;
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString();
var historyStorage = this.historiesStorage[peerID];
var flags = 0;
var pFlags: any = {};
var replyToMsgID = options.replyToMsgID;
var isChannel = AppPeersManager.isChannel(peerID);
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID);
var asChannel = isChannel && !isMegagroup ? true : false;
var message: any;
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerID] = {count: null, history: [], pending: []}
}
var fromID = appUsersManager.getSelf().id;
if(peerID != fromID) {
flags |= 2;
pFlags.out = true;
if(!isChannel && !appUsersManager.isBot(peerID)) {
flags |= 1;
pFlags.unread = true;
}
}
if(replyToMsgID) {
flags |= 8
}
if(asChannel) {
fromID = 0
pFlags.post = true
} else {
flags |= 256
}
message = {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
flags: flags,
pFlags: pFlags,
date: tsNow(true) + MTProto.serverTimeManager.serverTimeOffset,
message: text,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
reply_markup: options.reply_markup,
entities: entities,
views: asChannel && 1,
pending: true
};
var toggleError = (on: any) => {
var historyMessage = this.messagesForHistory[messageID];
if(on) {
message.error = true;
if(historyMessage) {
historyMessage.error = true;
}
} else {
delete message.error;
if(historyMessage) {
delete historyMessage.error;
}
}
$rootScope.$broadcast('messages_pending');
}
message.send = () => {
toggleError(false);
var sentRequestOptions: any = {};
if(this.pendingAfterMsgs[peerID]) {
sentRequestOptions.afterMessageID = this.pendingAfterMsgs[peerID].messageID;
}
var flags = 0;
if(replyToMsgID) {
flags |= 1;
}
if(asChannel) {
flags |= 16;
}
if(options.clearDraft) {
flags |= 128;
}
var apiPromise: any;
if(options.viaBotID) {
apiPromise = MTProto.apiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
random_id: randomID,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
}, sentRequestOptions);
} else {
if(sendEntites.length) {
flags |= 8;
}
apiPromise = MTProto.apiManager.invokeApi('messages.sendMessage', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID),
entities: sendEntites
}, sentRequestOptions);
}
// console.log(flags, entities)
apiPromise.then((updates: any) => {
if(updates._ == 'updateShortSentMessage') {
message.flags = updates.flags;
message.date = updates.date;
message.id = updates.id;
message.media = updates.media;
message.entities = updates.entities;
updates = {
_: 'updates',
users: [],
chats: [],
seq: 0,
updates: [{
_: 'updateMessageID',
random_id: randomIDS,
id: updates.id
}, {
_: isChannel
? 'updateNewChannelMessage'
: 'updateNewMessage',
message: message,
pts: updates.pts,
pts_count: updates.pts_count
}]
};
} else if(updates.updates) {
updates.updates.forEach((update: any) => {
if(update._ == 'updateDraftMessage') {
update.local = true;
}
});
}
// Testing bad situations
// var upd = angular.copy(updates)
// updates.updates.splice(0, 1)
apiUpdatesManager.processUpdateMessage(updates);
// $timeout(function () {
// ApiUpdatesManager.processUpdateMessage(upd)
// }, 5000)
}, (/* error: any */) => {
toggleError(true);
}).finally(() => {
if(this.pendingAfterMsgs[peerID] === sentRequestOptions) {
delete this.pendingAfterMsgs[peerID];
}
})
this.pendingAfterMsgs[peerID] = sentRequestOptions;
}
this.saveMessages([message]);
historyStorage.pending.unshift(messageID);
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
setTimeout(() => message.send(), 0);
// setTimeout(function () {
// message.send()
// }, 5000)
/* if(options.clearDraft) { // WARNING
DraftsManager.clearDraft(peerID)
} */
this.pendingByRandomID[randomIDS] = [peerID, messageID];
}
public sendFile(peerID: number, file: File | Blob | MTDocument, options: {
isMedia?: boolean,
replyToMsgID?: number,
caption?: string,
entities?: any[]
} = {}) {
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
var messageID = this.tempID--;
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString();
var historyStorage = this.historiesStorage[peerID];
var flags = 0;
var pFlags: any = {};
var replyToMsgID = options.replyToMsgID;
var isChannel = AppPeersManager.isChannel(peerID);
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID);
var asChannel = isChannel && !isMegagroup ? true : false;
var attachType: string, apiFileName: string;
let fileType = 'mime_type' in file ? file.mime_type : file.type;
let fileName = file instanceof File ? file.name : '';
let isDocument = !(file instanceof File) && !(file instanceof Blob);
let caption = options.caption || '';
if(caption) {
let entities = options.entities || [];
caption = RichTextProcessor.parseMarkdown(caption, entities);
}
if(!options.isMedia) {
attachType = 'document';
apiFileName = 'document.' + fileType.split('/')[1];
} else if(isDocument) { // maybe it's a sticker
attachType = 'document';
apiFileName = '';
} else if(['image/jpeg', 'image/png', 'image/bmp'].indexOf(fileType) >= 0) {
attachType = 'photo';
apiFileName = 'photo.' + fileType.split('/')[1];
} else if(fileType.substr(0, 6) == 'audio/' || ['video/ogg'].indexOf(fileType) >= 0) {
attachType = 'audio';
apiFileName = 'audio.' + (fileType.split('/')[1] == 'ogg' ? 'ogg' : 'mp3');
} else if(fileType.substr(0, 6) == 'video/') {
attachType = 'video';
apiFileName = 'video.mp4';
} else {
attachType = 'document';
apiFileName = 'document.' + fileType.split('/')[1];
}
// console.log(attachType, apiFileName, file.type)
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerID] = {count: null, history: [], pending: []};
}
var fromID = appUsersManager.getSelf().id;
if(peerID != fromID) {
flags |= 2;
pFlags.out = true;
if(!isChannel && !appUsersManager.isBot(peerID)) {
flags |= 1;
pFlags.unread = true;
}
}
if(replyToMsgID) {
flags |= 8;
}
if(asChannel) {
fromID = 0;
pFlags.post = true;
} else {
flags |= 256;
}
var media = {
_: 'messageMediaPending',
type: attachType,
file_name: fileName || apiFileName,
size: file.size,
progress: {
percent: 1,
total: file.size,
done: 0,
cancel: () => {}
}
};
var message: any = {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
flags: flags,
pFlags: pFlags,
date: tsNow(true) + ServerTimeManager.serverTimeOffset,
message: caption,
media: isDocument ? {
_: 'messageMediaDocument',
pFlags: {},
flags: 1,
document: file
} : media,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
views: asChannel && 1,
pending: true
};
var toggleError = (on: boolean) => {
var historyMessage = this.messagesForHistory[messageID];
if(on) {
message.error = true;
if(historyMessage) {
historyMessage.error = true;
}
} else {
delete message.error;
if(historyMessage) {
delete historyMessage.error;
}
}
$rootScope.$broadcast('messages_pending');
};
var uploaded = false,
uploadPromise: ReturnType<typeof apiFileManager.uploadFile> = null;
let invoke = (flags: number, inputMedia: any) => {
return MTProto.apiManager.invokeApi('messages.sendMedia', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
media: inputMedia,
message: caption,
random_id: randomID,
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID)
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, (error) => {
if(attachType == 'photo' &&
error.code == 400 &&
(error.type == 'PHOTO_INVALID_DIMENSIONS' ||
error.type == 'PHOTO_SAVE_FILE_INVALID')) {
error.handled = true
attachType = 'document'
message.send();
return;
}
toggleError(true);
});
};
message.send = () => {
let flags = 0;
if(replyToMsgID) {
flags |= 1;
}
if(asChannel) {
flags |= 16;
}
if(isDocument) {
let {id, access_hash, file_reference} = file as MTDocument;
let inputMedia = {
_: 'inputMediaDocument',
flags: 0,
id: {
_: 'inputDocument',
id: id,
access_hash: access_hash,
file_reference: file_reference
}
};
invoke(flags, inputMedia);
} else if(file instanceof File || file instanceof Blob) {
let deferredHelper: {
resolve?: () => void,
reject?: (error: any) => void
} = {};
let deferred: CancellablePromise<void> = new Promise((resolve, reject) => {
deferredHelper.resolve = resolve;
deferredHelper.reject = reject;
});
Object.assign(deferred, deferredHelper);
this.sendFilePromise.then(() => {
if(!uploaded || message.error) {
uploaded = false;
uploadPromise = apiFileManager.uploadFile(file);
}
uploadPromise && uploadPromise.then((inputFile) => {
inputFile.name = apiFileName;
uploaded = true;
var inputMedia;
switch(attachType) {
case 'photo':
inputMedia = {
_: 'inputMediaUploadedPhoto',
flags: 0,
file: inputFile
};
break;
case 'document':
default:
inputMedia = {
_: 'inputMediaUploadedDocument',
file: inputFile,
mime_type: fileType,
caption: '',
attributes: [
{_: 'documentAttributeFilename', file_name: fileName}
]
};
}
invoke(flags, inputMedia);
}, (/* error */) => {
toggleError(true);
});
uploadPromise.notify = (progress: {done: number, total: number}) => {
// console.log('upload progress', progress)
media.progress.done = progress.done;
media.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total));
$rootScope.$broadcast('history_update', {peerID: peerID});
};
media.progress.cancel = () => {
if(!uploaded) {
deferred.resolve();
uploadPromise.cancel();
this.cancelPendingMessage(randomIDS);
}
}
// @ts-ignore
uploadPromise['finally'](() => deferred.resolve());
});
this.sendFilePromise = deferred;
}
};
this.saveMessages([message]);
historyStorage.pending.unshift(messageID);
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
setTimeout(message.send.bind(this), 0);
this.pendingByRandomID[randomIDS] = [peerID, messageID];
}
public cancelPendingMessage(randomID: string) {
var pendingData = this.pendingByRandomID[randomID];
console.log('pending', randomID, pendingData);
if(pendingData) {
var peerID = pendingData[0];
var tempID = pendingData[1];
var historyStorage = this.historiesStorage[peerID];
var pos = historyStorage.pending.indexOf(tempID);
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: [tempID]
}
});
if(pos != -1) {
historyStorage.pending.splice(pos, 1);
}
delete this.messagesForHistory[tempID];
delete this.messagesStorage[tempID];
return true;
}
return false;
}
public async getConversation(peerID: number) {
var foundDialog = this.getDialogByPeerID(peerID)
if(foundDialog.length) {
return foundDialog[0];
}
return {
peerID: peerID,
top_message: 0,
index: this.generateDialogIndex(this.generateDialogPinnedDate()),
pFlags: {}
};
}
public getConversations(query?: string, offsetIndex?: number, limit = 20) {
var curDialogStorage = this.dialogsStorage;
var isSearch = typeof(query) == 'string' && query.length;
if(isSearch) {
if(!limit || this.cachedResults.query !== query) {
this.cachedResults.query = query
var results: any = SearchIndexManager.search(query, this.dialogsIndex);
this.cachedResults.dialogs = []
this.dialogsStorage.dialogs.forEach((dialog: any) => {
if(results[dialog.peerID]) {
this.cachedResults.dialogs.push(dialog);
}
})
this.cachedResults.count = this.cachedResults.dialogs.length;
}
curDialogStorage = this.cachedResults;
} else {
this.cachedResults.query = false;
}
var offset = 0
if(offsetIndex > 0) {
for(offset = 0; offset < curDialogStorage.dialogs.length; offset++) {
if(offsetIndex > curDialogStorage.dialogs[offset].index) {
break;
}
}
}
if(
isSearch ||
this.allDialogsLoaded ||
curDialogStorage.dialogs.length >= offset + limit
) {
return Promise.resolve({
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit)
});
}
return this.getTopMessages(limit).then(() => {
offset = 0
if(offsetIndex > 0) {
for(offset = 0; offset < curDialogStorage.dialogs.length; offset++) {
if(offsetIndex > curDialogStorage.dialogs[offset].index) {
break;
}
}
}
//console.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length);
return {
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit)
};
});
}
public getTopMessages(limit: number) {
var dialogs = this.dialogsStorage.dialogs;
var offsetDate = 0;
var offsetID = 0;
var offsetPeerID = 0;
var offsetIndex = 0;
var flags = 0;
if(this.dialogsOffsetDate) {
offsetDate = this.dialogsOffsetDate + MTProto.serverTimeManager.serverTimeOffset;
offsetIndex = this.dialogsOffsetDate * 0x10000;
flags |= 1;
}
let hash = 0;
/* let id = 296814355;
hash = (((hash * 0x4F25) & 0x7FFFFFFF) + id) & 0x7FFFFFFF; */
return MTProto.apiManager.invokeApi('messages.getDialogs', {
flags: flags,
offset_date: offsetDate,
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
limit: limit,
hash: hash
}, {
timeout: 300
}).then((dialogsResult: any) => {
if(!offsetDate) {
telegramMeWebService.setAuthorized(true);
}
appUsersManager.saveApiUsers(dialogsResult.users);
appChatsManager.saveApiChats(dialogsResult.chats);
this.saveMessages(dialogsResult.messages);
var maxSeenIdIncremented = offsetDate ? true : false;
var hasPrepend = false;
//dialogsResult.dialogs.reverse();
let length = dialogsResult.dialogs.length;
let noIDsDialogs: any = {};
for(let i = length - 1; i >= 0; --i) {
let dialog = dialogsResult.dialogs[i];
//}
//dialogsResult.dialogs.forEach((dialog: any) => {
this.saveConversation(dialog);
if(offsetIndex && dialog.index > offsetIndex) {
this.newDialogsToHandle[dialog.peerID] = dialog;
hasPrepend = true;
}
if(!dialog.read_inbox_max_id && !dialog.read_outbox_max_id) {
noIDsDialogs[dialog.peerID] = dialog;
}
if(!maxSeenIdIncremented &&
!AppPeersManager.isChannel(AppPeersManager.getPeerID(dialog.peer))) {
this.incrementMaxSeenID(dialog.top_message);
maxSeenIdIncremented = true;
}
}
//});
//dialogsResult.dialogs.reverse();
if(Object.keys(noIDsDialogs).length) {
//setTimeout(() => { // test bad situation
this.reloadConversation(Object.keys(noIDsDialogs).map(id => +id)).then(() => {
$rootScope.$broadcast('dialogs_multiupdate', noIDsDialogs);
for(let peerID in noIDsDialogs) {
$rootScope.$broadcast('dialog_unread', {peerID: +peerID});
}
});
//}, 10e3);
}
if(!dialogsResult.dialogs.length ||
!dialogsResult.count ||
dialogs.length >= dialogsResult.count) {
this.allDialogsLoaded = true;
}
if(hasPrepend && !this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
} else {
$rootScope.$broadcast('dialogs_multiupdate', {});
}
});
}
public generateDialogIndex(date?: any) {
if(date === undefined) {
date = tsNow(true) + MTProto.serverTimeManager.serverTimeOffset;
}
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
}
public pushDialogToStorage(dialog: any, offsetDate?: any) {
var dialogs = this.dialogsStorage.dialogs;
var pos = this.getDialogByPeerID(dialog.peerID)[1];
if(pos !== undefined) {
dialogs.splice(pos, 1);
}
if(offsetDate &&
!dialog.pFlags.pinned &&
(!this.dialogsOffsetDate || offsetDate < this.dialogsOffsetDate)) {
if(pos !== undefined) {
// So the dialog jumped to the last position
return false;
}
this.dialogsOffsetDate = offsetDate;
}
var index = dialog.index;
var i;
var len = dialogs.length;
if(!len || index < dialogs[len - 1].index) {
dialogs.push(dialog);
} else if(index >= dialogs[0].index) {
dialogs.unshift(dialog);
} else {
for(i = 0; i < len; i++) {
if(index > dialogs[i].index) {
dialogs.splice(i, 0, dialog);
break;
}
}
}
}
public getMessage(messageID: number) {
return this.messagesStorage[messageID] || {
_: 'messageEmpty',
deleted: true,
pFlags: {out: false, unread: false}
};
}
public getMessagePeer(message: any): number {
var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0;
if(toID < 0) {
return toID;
} else if(message.pFlags && message.pFlags.out || message.flags & 2) {
return toID;
}
return message.from_id;
}
public getDialogByPeerID(peerID: number) {
let length = this.dialogsStorage.dialogs.length;
for(var i = 0; i < length; i++) {
if(this.dialogsStorage.dialogs[i].peerID == peerID) {
return [this.dialogsStorage.dialogs[i], i];
}
}
return [];
}
public reloadConversation(peerID: number | number[]) {
let peers = [].concat(peerID).map(peerID => AppPeersManager.getInputPeerByID(peerID));
console.log('will reloadConversation', peerID);
return MTProto.apiManager.invokeApi('messages.getPeerDialogs', {
peers: peers
}).then(this.applyConversations.bind(this));
}
public saveMessages(apiMessages: any[], options: any = {}) {
apiMessages.forEach((apiMessage) => {
if(apiMessage.pFlags === undefined) {
apiMessage.pFlags = {};
}
if(!apiMessage.pFlags.out) {
apiMessage.pFlags.out = false;
}
if(!apiMessage.pFlags.unread) {
apiMessage.pFlags.unread = false;
}
if(apiMessage._ == 'messageEmpty') {
return;
}
var peerID = this.getMessagePeer(apiMessage);
var isChannel = apiMessage.to_id._ == 'peerChannel';
var channelID = isChannel ? -peerID : 0;
var isBroadcast = isChannel && appChatsManager.isBroadcast(channelID);
var mid = appMessagesIDsManager.getFullMessageID(apiMessage.id, channelID);
apiMessage.mid = mid;
var dialog = this.getDialogByPeerID(peerID)[0];
if(dialog && mid > 0) {
let dialogKey = apiMessage.pFlags.out
? 'read_outbox_max_id'
: 'read_inbox_max_id';
apiMessage.pFlags.unread = mid > dialog[dialogKey];
} else if(options.isNew) {
apiMessage.pFlags.unread = true;
}
// console.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id'])
if(apiMessage.reply_to_msg_id) {
apiMessage.reply_to_mid = appMessagesIDsManager.getFullMessageID(apiMessage.reply_to_msg_id, channelID);
}
apiMessage.date -= MTProto.serverTimeManager.serverTimeOffset;
apiMessage.peerID = peerID;
apiMessage.fromID = apiMessage.pFlags.post ? peerID : apiMessage.from_id;
var fwdHeader = apiMessage.fwd_from;
if(fwdHeader) {
if(peerID == appUsersManager.getSelf().id) {
if(fwdHeader.saved_from_peer && fwdHeader.saved_from_msg_id) {
var savedFromPeerID = AppPeersManager.getPeerID(fwdHeader.saved_from_peer);
var savedFromMid = appMessagesIDsManager.getFullMessageID(fwdHeader.saved_from_msg_id,
AppPeersManager.isChannel(savedFromPeerID) ? -savedFromPeerID : 0);
apiMessage.savedFrom = savedFromPeerID + '_' + savedFromMid;
}
apiMessage.fromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id;
} else {
apiMessage.fwdFromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id;
apiMessage.fwdPostID = fwdHeader.channel_post;
}
fwdHeader.date -= MTProto.serverTimeManager.serverTimeOffset;
}
if(apiMessage.via_bot_id > 0) {
apiMessage.viaBotID = apiMessage.via_bot_id;
}
var mediaContext = {
user_id: apiMessage.fromID,
date: apiMessage.date
};
if(apiMessage.media) {
switch(apiMessage.media._) {
case 'messageMediaEmpty':
delete apiMessage.media;
break;
case 'messageMediaPhoto':
if(apiMessage.media.ttl_seconds) {
apiMessage.media = {_: 'messageMediaUnsupportedWeb'};
} else {
appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
}
break
case 'messageMediaDocument':
if(apiMessage.media.ttl_seconds) {
apiMessage.media = {_: 'messageMediaUnsupportedWeb'};
} else {
appDocsManager.saveDoc(apiMessage.media.document, mediaContext);
}
break;
case 'messageMediaWebPage':
if(apiMessage.media.webpage.document) {
appDocsManager.saveDoc(apiMessage.media.webpage.document, mediaContext);
}
//AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.mid, mediaContext);
break;
/*case 'messageMediaWebPage':
AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.mid, mediaContext);
break;
case 'messageMediaGame':
AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext);
apiMessage.media.handleMessage = true;
break; */
case 'messageMediaInvoice':
apiMessage.media = {_: 'messageMediaUnsupportedWeb'};
break;
case 'messageMediaGeoLive':
apiMessage.media._ = 'messageMediaGeo';
break;
}
}
if(apiMessage.action) {
var migrateFrom;
var migrateTo;
switch (apiMessage.action._) {
case 'messageActionChatEditPhoto':
appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
if(isBroadcast) {
apiMessage.action._ = 'messageActionChannelEditPhoto';
}
break;
case 'messageActionChatEditTitle':
if(isBroadcast) {
apiMessage.action._ = 'messageActionChannelEditTitle';
}
break;
case 'messageActionChatDeletePhoto':
if(isBroadcast) {
apiMessage.action._ = 'messageActionChannelDeletePhoto';
}
break;
case 'messageActionChatAddUser':
if(apiMessage.action.users.length == 1) {
apiMessage.action.user_id = apiMessage.action.users[0];
if(apiMessage.fromID == apiMessage.action.user_id) {
if(isChannel) {
apiMessage.action._ = 'messageActionChatJoined';
} else {
apiMessage.action._ = 'messageActionChatReturn';
}
}
} else if(apiMessage.action.users.length > 1) {
apiMessage.action._ = 'messageActionChatAddUsers';
}
break;
case 'messageActionChatDeleteUser':
if(apiMessage.fromID == apiMessage.action.user_id) {
apiMessage.action._ = 'messageActionChatLeave';
}
break;
case 'messageActionChannelMigrateFrom':
migrateFrom = -apiMessage.action.chat_id;
migrateTo = -channelID;
break
case 'messageActionChatMigrateTo':
migrateFrom = -channelID;
migrateTo = -apiMessage.action.channel_id;
break;
case 'messageActionHistoryClear':
apiMessage.deleted = true;
apiMessage.clear_history = true;
apiMessage.pFlags.out = false;
apiMessage.pFlags.unread = false;
break;
case 'messageActionPhoneCall':
delete apiMessage.fromID;
apiMessage.action.type =
(apiMessage.pFlags.out ? 'out_' : 'in_') +
(
apiMessage.action.reason._ == 'phoneCallDiscardReasonMissed' ||
apiMessage.action.reason._ == 'phoneCallDiscardReasonBusy'
? 'missed'
: 'ok'
);
break;
}
if(migrateFrom &&
migrateTo &&
!this.migratedFromTo[migrateFrom] &&
!this.migratedToFrom[migrateTo]) {
this.migrateChecks(migrateFrom, migrateTo);
}
}
if(apiMessage.message && apiMessage.message.length) {
var myEntities = RichTextProcessor.parseEntities(apiMessage.message);
var apiEntities = apiMessage.entities || [];
apiMessage.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !apiMessage.pending);
}
apiMessage.canBeEdited = this.canMessageBeEdited(apiMessage);
if(!options.isEdited) {
this.messagesStorage[mid] = apiMessage;
}
})
}
public migrateChecks(migrateFrom: any, migrateTo: any) {
if(!this.migratedFromTo[migrateFrom] &&
!this.migratedToFrom[migrateTo] &&
appChatsManager.hasChat(-migrateTo)) {
var fromChat = appChatsManager.getChat(-migrateFrom);
if(fromChat &&
fromChat.migrated_to &&
fromChat.migrated_to.channel_id == -migrateTo) {
this.migratedFromTo[migrateFrom] = migrateTo;
this.migratedToFrom[migrateTo] = migrateFrom;
setTimeout(() => {
var foundDialog = this.getDialogByPeerID(migrateFrom);
if(foundDialog.length) {
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom});
}
$rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo});
}, 100);
}
}
}
public canMessageBeEdited(message: any) {
var goodMedias = [
'messageMediaPhoto',
'messageMediaDocument',
'messageMediaWebPage',
'messageMediaPending'
]
if(message._ != 'message' ||
message.deleted ||
message.fwd_from ||
message.via_bot_id ||
message.media && goodMedias.indexOf(message.media._) == -1 ||
message.fromID && appUsersManager.isBot(message.fromID)) {
return false;
}
if(message.media &&
message.media._ == 'messageMediaDocument' &&
message.media.document.sticker) {
return false;
}
return true;
}
public applyConversations(dialogsResult: any) {
appUsersManager.saveApiUsers(dialogsResult.users);
appChatsManager.saveApiChats(dialogsResult.chats);
this.saveMessages(dialogsResult.messages);
//console.log('applyConversation', dialogsResult);
var updatedDialogs: any = {};
var hasUpdated = false;
dialogsResult.dialogs.forEach((dialog: any) => {
var peerID = AppPeersManager.getPeerID(dialog.peer);
var topMessage = dialog.top_message;
var topPendingMesage = this.pendingTopMsgs[peerID];
if(topPendingMesage) {
if(!topMessage || this.getMessage(topPendingMesage).date > this.getMessage(topMessage).date) {
dialog.top_message = topMessage = topPendingMesage;
}
}
if(topMessage) {
var wasBefore = this.getDialogByPeerID(peerID).length > 0;
this.saveConversation(dialog);
if(wasBefore) {
this.clearDialogCache(topMessage);
$rootScope.$broadcast('dialog_top', dialog);
} else {
updatedDialogs[peerID] = dialog;
hasUpdated = true;
}
} else {
var foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog.length) {
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID});
}
}
if(this.newUpdatesAfterReloadToHandle[peerID] !== undefined) {
for(let i in this.newUpdatesAfterReloadToHandle[peerID]) {
let update = this.newUpdatesAfterReloadToHandle[peerID][i];
this.handleUpdate(update);
}
delete this.newUpdatesAfterReloadToHandle[peerID];
}
});
if(hasUpdated) {
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs);
}
}
public clearDialogCache(msgID: number) {
delete this.messagesForDialogs[msgID];
}
public saveConversation(dialog: any) {
var peerID = AppPeersManager.getPeerID(dialog.peer);
if(!peerID) {
return false;
}
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0;
var peerText = AppPeersManager.getPeerSearchText(peerID);
SearchIndexManager.indexObject(peerID, peerText, this.dialogsIndex);
//var isMegagroup = AppPeersManager.isMegagroup(channelID);
if(dialog.top_message) {
var mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
var message = this.getMessage(mid);
} else {
var mid = this.tempID--;
var message: any = {
_: 'message',
id: mid,
mid: mid,
from_id: appUsersManager.getSelf().id,
to_id: AppPeersManager.getOutputPeer(peerID),
deleted: true,
flags: 0,
pFlags: {unread: false, out: true},
date: 0,
message: ''
}
this.saveMessages([message]);
}
var offsetDate = message.date;
if(!channelID && peerID < 0) {
var chat = appChatsManager.getChat(-peerID)
if(chat && chat.migrated_to && chat.pFlags.deactivated) {
var migratedToPeer = AppPeersManager.getPeerID(chat.migrated_to)
this.migratedFromTo[peerID] = migratedToPeer;
this.migratedToFrom[migratedToPeer] = peerID;
return;
}
}
dialog.top_message = mid;
dialog.read_inbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_inbox_max_id, channelID);
//peerID == 228260936 && console.log('we get drunk', dialog, dialog.read_outbox_max_id);
dialog.read_outbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_outbox_max_id, channelID);
//peerID == 228260936 && console.log('we get high', dialog, dialog.read_outbox_max_id);
var topDate = message.date;
if(channelID) {
var channel = appChatsManager.getChat(channelID);
if(!topDate || channel.date && channel.date > topDate) {
topDate = channel.date;
}
}
var savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
if(savedDraft && savedDraft.date > topDate) {
topDate = savedDraft.date;
}
if(dialog.pFlags.pinned) {
topDate = this.generateDialogPinnedDate();
}
dialog.index = this.generateDialogIndex(topDate);
dialog.peerID = peerID;
this.pushDialogToStorage(dialog, offsetDate);
// Because we saved message without dialog present
var unreadKey = message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id';
if(message.mid > 0) {
if(message.mid > dialog[unreadKey]) message.pFlags.unread = true;
else message.pFlags.unread = false;
}
if(this.historiesStorage[peerID] === undefined && !message.deleted) {
var historyStorage: any = {count: null, history: [], pending: []};
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid)
if(mid < 0 && message.pFlags.unread) {
dialog.unread_count++;
}
this.historiesStorage[peerID] = historyStorage
if(this.mergeReplyKeyboard(historyStorage, message)) {
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
}
}
//NotificationsManager.savePeerSettings(peerID, dialog.notify_settings); // warning
/* if(dialog.pts || dialog.pFlags.pts) {
console.warn('dialog pts!', dialog, dialog.pts);
} */
if(channelID && dialog.pts) {
apiUpdatesManager.addChannelState(channelID, dialog.pts);
}
/*if(Config.Modes.packed && !channelID && dialog.unread_count > 0 &&
this.maxSeenID && dialog.top_message > this.maxSeenID &&
message.pFlags.unread && !message.pFlags.out) {
var notifyPeer = message.flags & 16 ? message.from_id : peerID
NotificationsManager.getPeerMuted(notifyPeer).then((muted: any) => {
if(!muted) {
this.notifyAboutMessage(message);
}
});
}*/ // WARNING
}
public handleNotifications() {
clearTimeout(this.notificationsHandlePromise);
this.notificationsHandlePromise = 0;
var timeout = $rootScope.idle.isIDLE /* && StatusManager.isOtherDeviceActive() */ ? 30000 : 1000;
Object.keys(this.notificationsToHandle).forEach((key: any) => {
let notifyPeerToHandle = this.notificationsToHandle[key];
notifyPeerToHandle.isMutedPromise.then((muted: boolean) => {
var topMessage = notifyPeerToHandle.top_message
if(muted ||
!topMessage.pFlags.unread) {
return;
}
setTimeout(() => {
if(topMessage.pFlags.unread) {
this.notifyAboutMessage(topMessage, {
fwd_count: notifyPeerToHandle.fwd_count
});
}
}, timeout);
});
});
this.notificationsToHandle = {};
}
public notifyAboutMessage(message: any, options: any = {}) {
var peerID = this.getMessagePeer(message);
var peerString: string;
var notification: any = {};
var notificationMessage = '',
notificationPhoto;
var notifySettings: any = {}; //NotificationsManager.getNotifySettings(); // warning
if(message.fwdFromID && options.fwd_count) {
notificationMessage = options.fwd_count;// this.fwdMessagesPluralize(options.fwd_count); // warning
} else if(message.message) {
if(notifySettings.nopreview) {
notificationMessage = 'conversation_message_sent';
} else {
notificationMessage = RichTextProcessor.wrapPlainText(message.message);
}
} else if(message.media) {
var captionEmoji = '';
switch (message.media._) {
case 'messageMediaPhoto':
notificationMessage = _('conversation_media_photo_raw');
captionEmoji = 'рџ–ј';
break
case 'messageMediaDocument':
switch (message.media.document.type) {
case 'gif':
notificationMessage = _('conversation_media_gif_raw');
captionEmoji = 'рџЋ¬'
break
case 'sticker':
notificationMessage = _('conversation_media_sticker');
var stickerEmoji = message.media.document.stickerEmojiRaw;
if(stickerEmoji !== undefined) {
notificationMessage = RichTextProcessor.wrapPlainText(stickerEmoji) + ' ' + notificationMessage;
}
break;
case 'video':
notificationMessage = _('conversation_media_video_raw');
captionEmoji = 'рџ“№';
break;
case 'round':
notificationMessage = _('conversation_media_round_raw');
captionEmoji = 'рџ“№';
break;
case 'voice':
case 'audio':
notificationMessage = _('conversation_media_audio_raw');
break;
default:
if(message.media.document.file_name) {
notificationMessage = RichTextProcessor.wrapPlainText('рџ“Ћ ' + message.media.document.file_name);
} else {
notificationMessage = _('conversation_media_document_raw');
captionEmoji = 'рџ“Ћ';
}
break;
}
break;
case 'messageMediaGeo':
case 'messageMediaVenue':
notificationMessage = _('conversation_media_location_raw');
captionEmoji = 'рџ“Ќ';
break;
case 'messageMediaContact':
notificationMessage = _('conversation_media_contact_raw');
break;
case 'messageMediaGame':
notificationMessage = RichTextProcessor.wrapPlainText('рџЋ® ' + message.media.game.title);
break;
case 'messageMediaUnsupported':
notificationMessage = _('conversation_media_unsupported_raw');
break;
default:
notificationMessage = _('conversation_media_attachment_raw');
break;
}
if(captionEmoji != '' &&
message.media.caption) {
notificationMessage = RichTextProcessor.wrapPlainText(captionEmoji + ' ' + message.media.caption);
}
} else if(message._ == 'messageService') {
switch(message.action._) {
case 'messageActionChatCreate':
notificationMessage = _('conversation_group_created_raw');
break
case 'messageActionChatEditTitle':
notificationMessage = _('conversation_group_renamed_raw');
break
case 'messageActionChatEditPhoto':
notificationMessage = _('conversation_group_photo_updated_raw');
break
case 'messageActionChatDeletePhoto':
notificationMessage = _('conversation_group_photo_removed_raw');
break
case 'messageActionChatAddUser':
case 'messageActionChatAddUsers':
notificationMessage = _('conversation_invited_user_message_raw');
break
case 'messageActionChatReturn':
notificationMessage = _('conversation_returned_to_group_raw');
break
case 'messageActionChatJoined':
notificationMessage = _('conversation_joined_group_raw');
break
case 'messageActionChatDeleteUser':
notificationMessage = _('conversation_kicked_user_message_raw');
break
case 'messageActionChatLeave':
notificationMessage = _('conversation_left_group_raw');
break
case 'messageActionChatJoinedByLink':
notificationMessage = _('conversation_joined_by_link_raw');
break
case 'messageActionChannelCreate':
notificationMessage = _('conversation_created_channel_raw');
break
case 'messageActionChannelEditTitle':
notificationMessage = _('conversation_changed_channel_name_raw');
break
case 'messageActionChannelEditPhoto':
notificationMessage = _('conversation_changed_channel_photo_raw')
break
case 'messageActionChannelDeletePhoto':
notificationMessage = _('conversation_removed_channel_photo_raw')
break
case 'messageActionPinMessage':
notificationMessage = _('conversation_pinned_message_raw')
break
case 'messageActionGameScore':
notificationMessage = message.action.score;//this.gameScorePluralize(message.action.score); // warning
break
case 'messageActionPhoneCall':
switch(message.action.type) {
case 'out_missed':
notificationMessage = _('message_service_phonecall_canceled_raw')
break
case 'in_missed':
notificationMessage = _('message_service_phonecall_missed_raw')
break
case 'out_ok':
notificationMessage = _('message_service_phonecall_outgoing_raw')
break
case 'in_ok':
notificationMessage = _('message_service_phonecall_incoming_raw')
break
}
break
}
}
if(peerID > 0) {
var fromUser = appUsersManager.getUser(message.from_id);
var fromPhoto = appUsersManager.getUserPhoto(message.from_id);
notification.title = (fromUser.first_name || '') +
(fromUser.first_name && fromUser.last_name ? ' ' : '') +
(fromUser.last_name || '')
if(!notification.title) {
notification.title = fromUser.phone || _('conversation_unknown_user_raw')
}
notificationPhoto = fromPhoto
peerString = appUsersManager.getUserString(peerID)
} else {
notification.title = appChatsManager.getChat(-peerID).title || _('conversation_unknown_chat_raw')
if(message.from_id > 0) {
var fromUser = appUsersManager.getUser(message.from_id)
notification.title = (fromUser.first_name || fromUser.last_name || _('conversation_unknown_user_raw')) +
' @ ' +
notification.title
}
notificationPhoto = appChatsManager.getChatPhoto(-peerID)
peerString = appChatsManager.getChatString(-peerID)
}
notification.title = RichTextProcessor.wrapPlainText(notification.title)
notification.onclick = function () {
$rootScope.$broadcast('history_focus', {
peerString: peerString,
messageID: message.flags & 16 ? message.mid : 0
})
}
notification.message = notificationMessage
notification.key = 'msg' + message.mid
notification.tag = peerString
notification.silent = message.pFlags.silent || false
if(notificationPhoto.location && !notificationPhoto.location.empty) {
MTProto.apiFileManager.downloadSmallFile(notificationPhoto.location/* , notificationPhoto.size */)
.then((blob) => {
if(message.pFlags.unread) {
notification.image = blob
// NotificationsManager.notify(notification) // warning
}
})
} else {
// NotificationsManager.notify(notification) // warning
}
}
public mergeReplyKeyboard(historyStorage: any, message: any) {
// console.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup)
if(!message.reply_markup &&
!message.pFlags.out &&
!message.action) {
return false;
}
if(message.reply_markup &&
message.reply_markup._ == 'replyInlineMarkup') {
return false;
}
var messageReplyMarkup = message.reply_markup;
var lastReplyMarkup = historyStorage.reply_markup;
if(messageReplyMarkup) {
if(lastReplyMarkup && lastReplyMarkup.mid >= message.mid) {
return false;
}
if(messageReplyMarkup.pFlags.selective &&
!(message.flags & 16)) {
return false;
}
if(historyStorage.maxOutID &&
message.mid < historyStorage.maxOutID &&
messageReplyMarkup.pFlags.single_use) {
messageReplyMarkup.pFlags.hidden = true;
}
messageReplyMarkup = Object.assign({
mid: message.mid
}, messageReplyMarkup);
if(messageReplyMarkup._ != 'replyKeyboardHide') {
messageReplyMarkup.fromID = message.from_id;
}
historyStorage.reply_markup = messageReplyMarkup;
// console.log('set', historyStorage.reply_markup)
return true;
}
if(message.pFlags.out) {
if(lastReplyMarkup) {
if(lastReplyMarkup.pFlags.single_use &&
!lastReplyMarkup.pFlags.hidden &&
(message.mid > lastReplyMarkup.mid || message.mid < 0) &&
message.message) {
lastReplyMarkup.pFlags.hidden = true;
// console.log('set', historyStorage.reply_markup)
return true;
}
} else if(!historyStorage.maxOutID ||
message.mid > historyStorage.maxOutID) {
historyStorage.maxOutID = message.mid;
}
}
if(message.action &&
message.action._ == 'messageActionChatDeleteUser' &&
(lastReplyMarkup
? message.action.user_id == lastReplyMarkup.fromID
: appUsersManager.isBot(message.action.user_id)
)
) {
historyStorage.reply_markup = {
_: 'replyKeyboardHide',
mid: message.mid,
flags: 0,
pFlags: {}
};
// console.log('set', historyStorage.reply_markup)
return true;
}
return false;
}
public getSearch(peerID = 0, query: string = '', inputFilter: {
_?: string
} = {_: 'inputMessagesFilterEmpty'}, maxID: number, limit: number) {
//peerID = peerID ? parseInt(peerID) : 0;
var foundMsgs: number[] = [];
var useSearchCache = !query;
var newSearchFilter = {peer: peerID, filter: inputFilter};
var sameSearchCache = useSearchCache
&& deepEqual(this.lastSearchFilter, newSearchFilter); //angular.equals(this.lastSearchFilter, newSearchFilter);
if(useSearchCache && !sameSearchCache) {
// console.warn(dT(), 'new search filter', lastSearchFilter, newSearchFilter)
this.lastSearchFilter = newSearchFilter;
this.lastSearchResults = [];
}
console.log(dT(), 'search', useSearchCache, sameSearchCache, this.lastSearchResults, maxID);
if(peerID && !maxID && !query) {
var historyStorage = this.historiesStorage[peerID];
if(historyStorage !== undefined && historyStorage.history.length) {
var neededContents: {
[type: string]: boolean
} = {},
neededDocType: string | boolean;
var neededLimit = limit || 20;
var message;
switch(inputFilter._) {
case 'inputMessagesFilterPhotos':
neededContents['messageMediaPhoto'] = true;
break;
case 'inputMessagesFilterPhotoVideo':
neededContents['messageMediaPhoto'] = true;
neededContents['messageMediaDocument'] = true;
neededDocType = 'video';
break;
case 'inputMessagesFilterVideo':
neededContents['messageMediaDocument'] = true;
neededDocType = 'video';
break;
case 'inputMessagesFilterDocument':
neededContents['messageMediaDocument'] = true;
neededDocType = false;
break;
case 'inputMessagesFilterVoice':
neededContents['messageMediaDocument'] = true;
neededDocType = 'voice';
break;
case 'inputMessagesFilterRoundVideo':
neededContents['messageMediaDocument'] = true;
neededDocType = 'round';
break;
case 'inputMessagesFilterMusic':
neededContents['messageMediaDocument'] = true;
neededDocType = 'audio';
break;
case 'inputMessagesFilterUrl':
neededContents['url'] = true;
break;
case 'inputMessagesFilterMyMentions':
neededContents['mentioned'] = true;
break;
default:
return Promise.resolve({
count: 0,
history: [] as number[]
});
}
for(let i = 0; i < historyStorage.history.length; i++) {
message = this.messagesStorage[historyStorage.history[i]];
if(message.media && neededContents[message.media._]) {
if(neededDocType !== undefined &&
message.media._ == 'messageMediaDocument' &&
message.media.document.type != neededDocType) {
continue;
}
foundMsgs.push(message.mid);
if(foundMsgs.length >= neededLimit) {
break;
}
}
}
}
// console.warn(dT(), 'before append', foundMsgs)
if(foundMsgs.length < neededLimit && this.lastSearchResults.length && sameSearchCache) {
var minID = foundMsgs.length ? foundMsgs[foundMsgs.length - 1] : false;
for(let i = 0; i < this.lastSearchResults.length; i++) {
if(minID === false || this.lastSearchResults[i] < minID) {
foundMsgs.push(this.lastSearchResults[i]);
if(foundMsgs.length >= neededLimit) {
break;
}
}
}
}
// console.warn(dT(), 'after append', foundMsgs)
}
if(foundMsgs.length || limit == 1000) {
if(useSearchCache) {
this.lastSearchResults = listMergeSorted(this.lastSearchResults, foundMsgs)
}
return Promise.resolve({
count: 0,
history: foundMsgs
});
}
var apiPromise
if(peerID || !query) {
apiPromise = MTProto.apiManager.invokeApi('messages.search', {
flags: 0,
peer: AppPeersManager.getInputPeerByID(peerID),
q: query || '',
filter: inputFilter || {_: 'inputMessagesFilterEmpty'},
min_date: 0,
max_date: 0,
limit: limit || 20,
offset_id: appMessagesIDsManager.getMessageLocalID(maxID) || 0,
add_offset: 0,
max_id: 0,
min_id: 0
}, {
timeout: 300,
noErrorBox: true
});
} else {
var offsetDate = 0;
var offsetPeerID = 0;
var offsetID = 0;
var offsetMessage = maxID && this.getMessage(maxID);
if(offsetMessage && offsetMessage.date) {
offsetDate = offsetMessage.date + ServerTimeManager.serverTimeOffset;
offsetID = offsetMessage.id;
offsetPeerID = this.getMessagePeer(offsetMessage);
}
apiPromise = MTProto.apiManager.invokeApi('messages.searchGlobal', {
q: query,
offset_date: offsetDate,
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID),
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
limit: limit || 20
}, {
timeout: 300,
noErrorBox: true
});
}
return apiPromise.then((searchResult: any) => {
appUsersManager.saveApiUsers(searchResult.users);
appChatsManager.saveApiChats(searchResult.chats);
this.saveMessages(searchResult.messages);
var foundCount: number = searchResult.count || searchResult.messages.length;
foundMsgs = [];
searchResult.messages.forEach((message: any) => {
var peerID = this.getMessagePeer(message);
if(peerID < 0) {
var chat = appChatsManager.getChat(-peerID);
if(chat.migrated_to) {
this.migrateChecks(peerID, -chat.migrated_to.channel_id);
}
}
foundMsgs.push(message.mid);
});
if(useSearchCache &&
(!maxID || sameSearchCache && this.lastSearchResults.indexOf(maxID) >= 0)) {
this.lastSearchResults = listMergeSorted(this.lastSearchResults, foundMsgs);
}
// console.log(dT(), 'after API', foundMsgs, lastSearchResults)
return {
count: foundCount,
history: foundMsgs
}
}, (error) => {
if(error.code == 400) {
error.handled = true;
}
return Promise.reject(error);
});
}
public generateDialogPinnedDate() {
return 0x7fffff00 + ((this.pinnedIndex++) & 0xff);
}
public handleNewMessages() {
clearTimeout(this.newMessagesHandlePromise);
this.newMessagesHandlePromise = 0;
$rootScope.$broadcast('history_multiappend', this.newMessagesToHandle);
this.newMessagesToHandle = {};
}
public handleNewDialogs() {
clearTimeout(this.newDialogsHandlePromise);
this.newDialogsHandlePromise = 0;
var newMaxSeenID = 0
Object.keys(this.newDialogsToHandle).forEach((peerID) => {
let dialog = this.newDialogsToHandle[peerID];
if(dialog.reload) {
this.reloadConversation(+peerID);
delete this.newDialogsToHandle[peerID];
} else {
this.pushDialogToStorage(dialog);
if(!AppPeersManager.isChannel(+peerID)) {
newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0);
}
}
})
if(newMaxSeenID != 0) {
this.incrementMaxSeenID(newMaxSeenID);
}
$rootScope.$broadcast('dialogs_multiupdate', this.newDialogsToHandle);
this.newDialogsToHandle = {};
}
public readHistory(peerID: number, maxID = 0) {
// console.trace('start read')
var isChannel = AppPeersManager.isChannel(peerID);
var historyStorage = this.historiesStorage[peerID];
var foundDialog = this.getDialogByPeerID(peerID)[0];
if(!foundDialog || !foundDialog.unread_count) {
if(!historyStorage || !historyStorage.history.length) {
return false;
}
let messageID, message;
let foundUnread = false;
for(let i = historyStorage.history.length; i >= 0; i--) {
messageID = historyStorage.history[i];
message = this.messagesStorage[messageID];
if(message && !message.pFlags.out && message.pFlags.unread) {
foundUnread = true;
break;
}
}
if(!foundUnread) {
return false;
}
}
if(historyStorage.readPromise) {
return historyStorage.readPromise;
}
var apiPromise: any;
if(isChannel) {
apiPromise = MTProto.apiManager.invokeApi('channels.readHistory', {
channel: appChatsManager.getChannelInput(-peerID),
max_id: maxID
});
} else {
apiPromise = MTProto.apiManager.invokeApi('messages.readHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
max_id: maxID
}).then((affectedMessages: any) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
}
historyStorage.readPromise = apiPromise.then(() => {
if(foundDialog) {
// console.log('done read history', peerID)
let index = -1;
if(maxID != 0 && historyStorage && historyStorage.history.length) {
index = historyStorage.history.findIndex((mid: number) => mid == maxID);
}
foundDialog.unread_count = index == -1 ? 0 : index;
console.log('readHistory set unread_count to:', foundDialog.unread_count, foundDialog);
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: foundDialog.unread_count});
$rootScope.$broadcast('messages_read');
if(historyStorage && historyStorage.history.length) {
console.warn('readPromise:', index, historyStorage.history[index != -1 ? index : 0]);
foundDialog.read_inbox_max_id = historyStorage.history[index != -1 ? index : 0];
}
}
/* if(foundDialog) {
// console.log('done read history', peerID)
foundDialog.unread_count = 0
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0})
$rootScope.$broadcast('messages_read')
if(historyStorage && historyStorage.history.length) {
foundDialog.read_inbox_max_id = historyStorage.history[0]
}
} */
}).finally(() => {
delete historyStorage.readPromise;
});
if(historyStorage && historyStorage.history.length) {
let messageID: number;
let message, i;
for(i = 0; i < historyStorage.history.length; i++) {
messageID = historyStorage.history[i];
message = this.messagesStorage[messageID];
if(message && !message.pFlags.out) {
message.pFlags.unread = false;
if(this.messagesForHistory[messageID]) {
this.messagesForHistory[messageID].pFlags.unread = false;
}
if(this.messagesForDialogs[messageID]) {
this.messagesForDialogs[messageID].pFlags.unread = false;
}
//NotificationsManager.cancel('msg' + messageID); // warning
}
if(messageID == maxID) break;
}
}
// NotificationsManager.soundReset(AppPeersManager.getPeerString(peerID)) // warning
return historyStorage.readPromise;
}
public readMessages(messageIDs: number[]) {
var splitted = appMessagesIDsManager.splitMessageIDsByChannels(messageIDs);
Object.keys(splitted.msgIDs).forEach((channelID: number | string) => {
channelID = +channelID;
let msgIDs = splitted.msgIDs[channelID];
if(channelID > 0) {
MTProto.apiManager.invokeApi('channels.readMessageContents', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
}).then(() => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateChannelReadMessagesContents',
channel_id: channelID,
messages: msgIDs
}
});
});
} else {
MTProto.apiManager.invokeApi('messages.readMessageContents', {
id: msgIDs
}).then((affectedMessages: any) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateReadMessagesContents',
messages: msgIDs,
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
}
});
}
public handleUpdate(update: any) {
console.log('AMM: handleUpdate:', update._);
switch(update._) {
case 'updateMessageID': {
var randomID = update.random_id;
var pendingData = this.pendingByRandomID[randomID];
if(pendingData) {
var peerID: number = pendingData[0];
var tempID = pendingData[1];
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0;
var mid = appMessagesIDsManager.getFullMessageID(update.id, channelID);
var message = this.messagesStorage[mid];
if(message) {
var historyStorage = this.historiesStorage[peerID];
var pos = historyStorage.pending.indexOf(tempID);
if(pos != -1) {
historyStorage.pending.splice(pos, 1);
}
delete this.messagesForHistory[tempID];
delete this.messagesStorage[tempID];
var msgs: any = {}
msgs[tempID] = true;
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs});
this.finalizePendingMessageCallbacks(tempID, mid);
} else {
this.pendingByMessageID[mid] = randomID;
}
}
break;
}
case 'updateNewMessage':
case 'updateNewChannelMessage': {
var message = update.message;
var peerID = this.getMessagePeer(message);
var historyStorage = this.historiesStorage[peerID];
var foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length) {
this.newDialogsToHandle[peerID] = {reload: true}
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
if(this.newUpdatesAfterReloadToHandle[peerID] === undefined) {
this.newUpdatesAfterReloadToHandle[peerID] = [];
}
this.newUpdatesAfterReloadToHandle[peerID].push(update);
break;
}
if(update._ == 'updateNewChannelMessage') {
var chat = appChatsManager.getChat(-peerID);
if(chat.pFlags && (chat.pFlags.left || chat.pFlags.kicked)) {
break;
}
}
this.saveMessages([message], {isNew: true});
// console.warn(dT(), 'message unread', message.mid, message.pFlags.unread)
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerID] = {
count: null,
history: [],
pending: []
};
}
var history = message.mid > 0 ? historyStorage.history : historyStorage.pending;
if(history.indexOf(message.mid) != -1) {
return false;
}
var topMsgID = history[0];
history.unshift(message.mid);
if(message.mid > 0 && message.mid < topMsgID) {
history.sort((a: any, b: any) => {
return b - a;
});
}
if(message.mid > 0 &&
historyStorage.count !== null) {
historyStorage.count++;
}
if(this.mergeReplyKeyboard(historyStorage, message)) {
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
}
if(!message.pFlags.out && message.from_id) {
appUsersManager.forceUserOnline(message.from_id);
}
var randomID = this.pendingByMessageID[message.mid],
pendingMessage;
if(randomID) {
if(pendingMessage = this.finalizePendingMessage(randomID, message)) {
$rootScope.$broadcast('history_update', {peerID: peerID});
}
delete this.pendingByMessageID[message.mid];
}
if(!pendingMessage) {
if(this.newMessagesToHandle[peerID] === undefined) {
this.newMessagesToHandle[peerID] = [];
}
this.newMessagesToHandle[peerID].push(message.mid);
if(!this.newMessagesHandlePromise) {
this.newMessagesHandlePromise = window.setTimeout(this.handleNewMessages.bind(this), 0);
}
}
var inboxUnread = !message.pFlags.out && message.pFlags.unread;
var dialog = foundDialog[0];
dialog.top_message = message.mid;
if(inboxUnread) {
dialog.unread_count++;
}
if(!dialog.pFlags.pinned || !dialog.index) {
dialog.index = this.generateDialogIndex(message.date);
}
this.newDialogsToHandle[peerID] = dialog;
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
if(inboxUnread &&
($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) {
var notifyPeer = message.flags & 16 ? message.from_id : peerID;
var notifyPeerToHandle = this.notificationsToHandle[notifyPeer];
if(notifyPeerToHandle === undefined) {
notifyPeerToHandle = this.notificationsToHandle[notifyPeer] = {
isMutedPromise: Promise.resolve()/* NotificationsManager.getPeerMuted(notifyPeer) */, // WARNING
fwd_count: 0,
from_id: 0
};
}
if(notifyPeerToHandle.from_id != message.from_id) {
notifyPeerToHandle.from_id = message.from_id;
notifyPeerToHandle.fwd_count = 0;
}
if(message.fwdFromID) {
notifyPeerToHandle.fwd_count++;
}
notifyPeerToHandle.top_message = message;
if(!this.notificationsHandlePromise) {
this.notificationsHandlePromise = window.setTimeout(this.handleNotifications.bind(this), 1000);
}
}
break;
}
case 'updateDialogPinned': {
var peerID = AppPeersManager.getPeerID(update.peer);
var foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length || !update.pFlags.pinned) {
this.newDialogsToHandle[peerID] = {reload: true};
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
break;
}
var dialog = foundDialog[0];
dialog.index = this.generateDialogIndex(this.generateDialogPinnedDate());
dialog.pFlags.pinned = true;
break;
}
case 'updatePinnedDialogs': {
var newPinned: any = {};
if(!update.order) {
MTProto.apiManager.invokeApi('messages.getPinnedDialogs', {}).then((dialogsResult: any) => {
dialogsResult.dialogs.reverse();
this.applyConversations(dialogsResult);
dialogsResult.dialogs.forEach((dialog: any) => {
newPinned[dialog.peerID] = true;
});
this.dialogsStorage.dialogs.forEach((dialog: any) => {
var peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true};
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
}
});
});
break;
}
update.order.reverse();
update.order.forEach((peer: any) => {
var peerID = AppPeersManager.getPeerID(peer);
newPinned[peerID] = true;
var foundDialog = this.getDialogByPeerID(peerID);
if(!foundDialog.length) {
this.newDialogsToHandle[peerID] = {reload: true}
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
return;
}
var dialog = foundDialog[0]
dialog.index = this.generateDialogIndex(this.generateDialogPinnedDate());
dialog.pFlags.pinned = true;
this.newDialogsToHandle[peerID] = dialog
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
})
this.dialogsStorage.dialogs.forEach((dialog: any) => {
var peerID = dialog.peerID;
if(dialog.pFlags.pinned && !newPinned[peerID]) {
this.newDialogsToHandle[peerID] = {reload: true}
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs.bind(this), 0);
}
}
})
break;
}
case 'updateEditMessage':
case 'updateEditChannelMessage': {
var message = update.message;
var peerID = this.getMessagePeer(message);
var channelID = message.to_id._ == 'peerChannel' ? -peerID : 0;
var mid = appMessagesIDsManager.getFullMessageID(message.id, channelID);
if(this.messagesStorage[mid] === undefined) {
break;
}
// console.trace(dT(), 'edit message', message)
this.saveMessages([message], {isEdited: true});
safeReplaceObject(this.messagesStorage[mid], message);
var wasForHistory = this.messagesForHistory[mid];
if(wasForHistory !== undefined) {
delete this.messagesForHistory[mid];
var newForHistory = this.wrapForHistory(mid);
safeReplaceObject(wasForHistory, newForHistory);
this.messagesForHistory[mid] = wasForHistory;
}
var dialog = this.getDialogByPeerID(peerID)[0];
var isTopMessage = dialog && dialog.top_message == mid;
if(message.clear_history) {
if(isTopMessage) {
$rootScope.$broadcast('dialog_flush', {peerID: peerID});
} else {
var msgs: any = {};
msgs[mid] = true;
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs});
}
} else {
$rootScope.$broadcast('message_edit', {
peerID: peerID,
id: message.id,
mid: mid
});
if(isTopMessage) {
var updatedDialogs: any = {};
updatedDialogs[peerID] = dialog;
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs);
}
}
break;
}
case 'updateReadHistoryInbox':
case 'updateReadHistoryOutbox':
case 'updateReadChannelInbox':
case 'updateReadChannelOutbox': {
var isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox';
var channelID: number = update.channel_id;
var maxID = appMessagesIDsManager.getFullMessageID(update.max_id, channelID);
var peerID = channelID ? -channelID : AppPeersManager.getPeerID(update.peer);
var foundDialog = this.getDialogByPeerID(peerID);
var history = (this.historiesStorage[peerID] || {}).history || [];
var newUnreadCount = 0;
var length = history.length;
var foundAffected = false;
var messageID: number, message;
var i;
//console.warn(dT(), 'read', peerID, isOut ? 'out' : 'in', maxID)
if(peerID > 0 && isOut) {
appUsersManager.forceUserOnline(peerID);
}
for(i = 0; i < length; i++) {
messageID = history[i];
if(messageID > maxID) {
continue;
}
message = this.messagesStorage[messageID];
if(message.pFlags.out != isOut) {
continue;
}
if(!message.pFlags.unread) {
break;
}
// console.warn('read', messageID, message.pFlags.unread, message)
if(message && message.pFlags.unread) {
message.pFlags.unread = false
if(this.messagesForHistory[messageID]) {
this.messagesForHistory[messageID].pFlags.unread = false;
if(!foundAffected) {
foundAffected = true;
}
}
if(this.messagesForDialogs[messageID]) {
this.messagesForDialogs[messageID].pFlags.unread = false;
}
if(!message.pFlags.out) {
if(foundDialog[0]) {
newUnreadCount = --foundDialog[0].unread_count;
}
//NotificationsManager.cancel('msg' + messageID); // warning
}
}
}
if(foundDialog[0]) {
if(!isOut &&
newUnreadCount &&
foundDialog[0].top_message <= maxID) {
newUnreadCount = foundDialog[0].unread_count = 0;
}
let dialogKey = isOut ? 'read_outbox_max_id' : 'read_inbox_max_id';
foundDialog[0][dialogKey] = maxID;
}
// need be commented for read out messages
//if(newUnreadCount != 0 || !isOut) { // fix 16.11.2019 (maybe not)
console.warn(dT(), 'cnt', peerID, newUnreadCount);
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: newUnreadCount});
//}
if(foundAffected) {
$rootScope.$broadcast('messages_read');
}
break;
}
case 'updateChannelReadMessagesContents': {
var channelID: number = update.channel_id;
var newMessages: any[] = [];
update.messages.forEach((msgID: number) => {
newMessages.push(appMessagesIDsManager.getFullMessageID(msgID, channelID));
});
update.messages = newMessages;
}
case 'updateReadMessagesContents': {
var messages: any[] = update.messages;
var len = messages.length;
var i;
var messageID: number, message;
var historyMessage;
for(i = 0; i < len; i++) {
messageID = messages[i];
if(message = this.messagesStorage[messageID]) {
delete message.pFlags.media_unread;
}
if(historyMessage = this.messagesForHistory[messageID]) {
delete historyMessage.pFlags.media_unread;
}
}
break;
}
case 'updateChannelAvailableMessages': {
var channelID: number = update.channel_id;
var messages: any[] = [];
var peerID: number = -channelID;
var history = (this.historiesStorage[peerID] || {}).history || [];
if(history.length) {
history.forEach((msgID: number) => {
if(!update.available_min_id ||
appMessagesIDsManager.getMessageLocalID(msgID) <= update.available_min_id) {
messages.push(msgID);
}
});
}
update.messages = messages;
}
case 'updateDeleteMessages':
case 'updateDeleteChannelMessages': {
var historiesUpdated: any = {};
var channelID: number = update.channel_id;
var messageID: number;
var message, i;
var peerID: number, foundDialog: any[];
let history: any;
var peerMessagesToHandle;
var peerMessagesHandlePos;
for (i = 0; i < update.messages.length; i++) {
messageID = appMessagesIDsManager.getFullMessageID(update.messages[i], channelID);
message = this.messagesStorage[messageID];
if(message) {
peerID = this.getMessagePeer(message);
history = historiesUpdated[peerID] || (historiesUpdated[peerID] = {count: 0, unread: 0, msgs: {}});
if(!message.pFlags.out && message.pFlags.unread) {
history.unread++;
// NotificationsManager.cancel('msg' + messageID); // warning
}
history.count++;
history.msgs[messageID] = true;
if(this.messagesForHistory[messageID]) {
this.messagesForHistory[messageID].deleted = true;
delete this.messagesForHistory[messageID];
}
if(this.messagesForDialogs[messageID]) {
this.messagesForDialogs[messageID].deleted = true;
delete this.messagesForDialogs[messageID];
}
message.deleted = true
this.messagesStorage[messageID] = {
deleted: true,
id: messageID,
from_id: message.from_id,
to_id: message.to_id,
flags: message.flags,
pFlags: message.pFlags,
date: message.date
};
peerMessagesToHandle = this.newMessagesToHandle[peerID];
if(peerMessagesToHandle && peerMessagesToHandle.length) {
peerMessagesHandlePos = peerMessagesToHandle.indexOf(messageID);
if(peerMessagesHandlePos != -1) {
peerMessagesToHandle.splice(peerMessagesHandlePos);
}
}
}
}
Object.keys(historiesUpdated).forEach((peerID: string) => {
let updatedData = historiesUpdated[peerID];
var historyStorage = this.historiesStorage[peerID];
if(historyStorage !== undefined) {
var newHistory = []
var newPending = []
for(var i = 0; i < historyStorage.history.length; i++) {
if(!updatedData.msgs[historyStorage.history[i]]) {
newHistory.push(historyStorage.history[i]);
}
}
historyStorage.history = newHistory;
if(updatedData.count &&
historyStorage.count !== null &&
historyStorage.count > 0) {
historyStorage.count -= updatedData.count
if(historyStorage.count < 0) {
historyStorage.count = 0;
}
}
for(var i = 0; i < historyStorage.pending.length; i++) {
if(!updatedData.msgs[historyStorage.pending[i]]) {
newPending.push(historyStorage.pending[i]);
}
}
historyStorage.pending = newPending;
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: updatedData.msgs});
}
var foundDialog = this.getDialogByPeerID(+peerID)[0];
if(foundDialog) {
if(updatedData.unread) {
foundDialog.unread_count -= updatedData.unread;
$rootScope.$broadcast('dialog_unread', {
peerID: peerID,
count: foundDialog.unread_count
});
}
if(updatedData.msgs[foundDialog.top_message]) {
this.reloadConversation(+peerID);
}
}
})
break;
}
case 'updateChannel': {
var channelID: number = update.channel_id;
var peerID = -channelID;
var channel = appChatsManager.getChat(channelID);
var needDialog = channel._ == 'channel' && (!channel.pFlags.left && !channel.pFlags.kicked);
var foundDialog = this.getDialogByPeerID(peerID);
var hasDialog = foundDialog.length > 0;
var canViewHistory = channel._ == 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked) && true || false;
var hasHistory = this.historiesStorage[peerID] !== undefined;
if(canViewHistory != hasHistory) {
delete this.historiesStorage[peerID];
$rootScope.$broadcast('history_forbidden', peerID);
}
if(hasDialog != needDialog) {
if(needDialog) {
this.reloadConversation(-channelID);
} else {
if(foundDialog[0]) {
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
$rootScope.$broadcast('dialog_drop', {peerID: peerID});
}
}
}
break;
}
case 'updateChannelReload': {
var channelID: number = update.channel_id;
var peerID = -channelID;
var foundDialog = this.getDialogByPeerID(peerID);
if(foundDialog[0]) {
this.dialogsStorage.dialogs.splice(foundDialog[1], 1);
}
delete this.historiesStorage[peerID];
this.reloadConversation(-channelID).then(() => {
$rootScope.$broadcast('history_reload', peerID);
});
break;
}
case 'updateChannelMessageViews': {
var views = update.views;
var mid = appMessagesIDsManager.getFullMessageID(update.id, update.channel_id);
var message = this.getMessage(mid);
if(message && message.views && message.views < views) {
message.views = views;
$rootScope.$broadcast('message_views', {
mid: mid,
views: views
});
}
break;
}
case 'updateServiceNotification': {
// update.inbox_date = tsNow(true)
// update.pFlags = {popup: true}
var fromID = 777000;
var peerID = fromID;
var messageID = this.tempID--;
var message: any = {
_: 'message',
id: messageID,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(peerID),
flags: 0,
pFlags: {unread: true},
date: (update.inbox_date || tsNow(true)) + MTProto.serverTimeManager.serverTimeOffset,
message: update.message,
media: update.media,
entities: update.entities
};
if(!appUsersManager.hasUser(fromID)) {
appUsersManager.saveApiUsers([{
_: 'user',
id: fromID,
pFlags: {verified: true},
access_hash: 0,
first_name: 'Telegram',
phone: '42777'
}]);
}
this.saveMessages([message]);
if(update.inbox_date) {
this.pendingTopMsgs[peerID] = messageID;
this.handleUpdate({
_: 'updateNewMessage',
message: message
});
}
if(update.pFlags.popup && update.message) {
var historyMessage = this.wrapForHistory(messageID);
//ErrorService.show({error: {code: 400, type: 'UPDATE_SERVICE_NOTIFICATION'}, historyMessage: historyMessage}); // warning
}
break;
}
}
}
public wrapForHistory(msgID: number) {
if(this.messagesForHistory[msgID] !== undefined) {
return this.messagesForHistory[msgID];
}
var message = copy(this.messagesStorage[msgID]) || {id: msgID};
if(message.media && message.media.progress !== undefined) {
message.media.progress = this.messagesStorage[msgID].media.progress;
}
var fromUser = message.from_id && appUsersManager.getUser(message.from_id);
var fromBot = fromUser && fromUser.pFlags.bot && fromUser.username || false;
var withBot = (fromBot ||
message.to_id && (
message.to_id.chat_id ||
message.to_id.user_id && appUsersManager.isBot(message.to_id.user_id)
)
)
if(message.media) {
if(message.media.caption &&
message.media.caption.length) {
message.media.rCaption = RichTextProcessor.wrapRichText(message.media.caption, {
noCommands: !withBot,
fromBot: fromBot
})
}
switch (message.media._) {
case 'messageMediaPhoto':
message.media.photo = appPhotosManager.wrapForHistory(message.media.photo.id)
break
/* case 'messageMediaDocument':
message.media.document = appDocsManager.wrapForHistory(message.media.document.id)
break */
case 'messageMediaGeo':
var mapUrl = 'https://maps.google.com/?q=' + message.media.geo['lat'] + ',' + message.media.geo['long']
message.media.mapUrl = mapUrl;//$sce.trustAsResourceUrl(mapUrl) // warning
break
case 'messageMediaVenue':
var mapUrl: string;
if(message.media.provider == 'foursquare' &&
message.media.venue_id) {
mapUrl = 'https://foursquare.com/v/' + encodeURIComponent(message.media.venue_id)
} else {
mapUrl = 'https://maps.google.com/?q=' + message.media.geo['lat'] + ',' + message.media.geo['long']
}
message.media.mapUrl = mapUrl;//$sce.trustAsResourceUrl(mapUrl) // warning
break
case 'messageMediaContact':
message.media.rFullName = RichTextProcessor.wrapRichText(
message.media.first_name + ' ' + (message.media.last_name || ''),
{noLinks: true, noLinebreaks: true}
)
break
/* case 'messageMediaWebPage':
if(!message.media.webpage ||
message.media.webpage._ == 'webPageEmpty') {
delete message.media
break
}
message.media.webpage = AppWebPagesManager.wrapForHistory(message.media.webpage.id)
break
case 'messageMediaGame':
message.media.game = AppGamesManager.wrapForHistory(message.media.game.id)
break */
}
} else if(message.action) {
switch (message.action._) {
case 'messageActionChatEditPhoto':
case 'messageActionChannelEditPhoto':
message.action.photo = appPhotosManager.wrapForHistory(message.action.photo.id);
break
case 'messageActionChatCreate':
case 'messageActionChatEditTitle':
case 'messageActionChannelCreate':
case 'messageActionChannelEditTitle':
message.action.rTitle = RichTextProcessor.wrapRichText(message.action.title, {noLinebreaks: true}) || 'chat_title_deleted';
break
case 'messageActionBotIntro':
message.action.rDescription = RichTextProcessor.wrapRichText(message.action.description, {
noCommands: !withBot,
fromBot: fromBot
});
break;
}
}
return this.messagesForHistory[msgID] = message;
}
public finalizePendingMessage(randomID: number, finalMessage: any) {
var pendingData = this.pendingByRandomID[randomID];
// console.log('pdata', randomID, pendingData)
if(pendingData) {
var peerID = pendingData[0];
var tempID = pendingData[1];
var historyStorage = this.historiesStorage[peerID],
message,
historyMessage;
// console.log('pending', randomID, historyStorage.pending)
var pos = historyStorage.pending.indexOf(tempID);
if(pos != -1) {
historyStorage.pending.splice(pos, 1);
}
if(message = this.messagesStorage[tempID]) {
delete message.pending;
delete message.error;
delete message.random_id;
delete message.send;
}
if(historyMessage = this.messagesForHistory[tempID]) {
this.messagesForHistory[finalMessage.mid] = Object.assign(historyMessage, this.wrapForHistory(finalMessage.mid));
delete historyMessage.pending;
delete historyMessage.error;
delete historyMessage.random_id;
delete historyMessage.send;
$rootScope.$broadcast('messages_pending');
}
delete this.messagesForHistory[tempID];
delete this.messagesStorage[tempID];
this.finalizePendingMessageCallbacks(tempID, finalMessage.mid);
return message;
}
return false
}
public finalizePendingMessageCallbacks(tempID: number, mid: number) {
$rootScope.$broadcast('message_sent', {tempID, mid});
}
public incrementMaxSeenID(maxID: number) {
if(!maxID || !(!this.maxSeenID || maxID > this.maxSeenID)) {
return false;
}
AppStorage.set({
max_seen_msg: maxID
});
MTProto.apiManager.invokeApi('messages.receivedMessages', {
max_id: maxID
});
}
public getHistory(peerID: number, maxID = 0, limit = 0, backLimit?: number, prerendered?: number) {
if(this.migratedFromTo[peerID]) {
peerID = this.migratedFromTo[peerID];
}
var historyStorage = this.historiesStorage[peerID];
var offset = 0;
var offsetNotFound = false;
var unreadOffset = 0;
var unreadSkip = false;
prerendered = prerendered ? Math.min(50, prerendered) : 0;
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerID] = {count: null, history: [], pending: []};
}
if(maxID < 0) {
maxID = 0;
}
var isMigrated = false;
var reqPeerID = peerID;
if(this.migratedToFrom[peerID]) {
isMigrated = true;
if(maxID && maxID < appMessagesIDsManager.fullMsgIDModulus) {
reqPeerID = this.migratedToFrom[peerID];
}
}
if(!limit && !maxID) {
var foundDialog = this.getDialogByPeerID(peerID)[0];
if(foundDialog && foundDialog.unread_count > 1) {
var unreadCount = foundDialog.unread_count;
if(unreadSkip = (unreadCount > 50)) {
if(foundDialog.read_inbox_max_id) {
maxID = foundDialog.read_inbox_max_id;
backLimit = 16;
unreadOffset = 16;
limit = 4;
} else {
limit = 20;
unreadOffset = 16;
offset = unreadCount - unreadOffset;
}
} else {
limit = Math.max(10, prerendered, unreadCount + 2);
unreadOffset = unreadCount;
}
} else if('Mobile' in Config) {
limit = 20;
}
}
if(maxID > 0) {
offsetNotFound = true;
for(offset = 0; offset < historyStorage.history.length; offset++) {
if(maxID > historyStorage.history[offset]) {
offsetNotFound = false;
break;
}
}
}
if(!offsetNotFound && (
historyStorage.count !== null && historyStorage.history.length == historyStorage.count ||
historyStorage.history.length >= offset + (limit || 1)
)) {
if(backLimit) {
backLimit = Math.min(offset, backLimit);
offset = Math.max(0, offset - backLimit);
limit += backLimit;
} else {
limit = limit || (offset ? 20 : (prerendered || 5));
}
var history = historyStorage.history.slice(offset, offset + limit);
if(!maxID && historyStorage.pending.length) {
history = historyStorage.pending.slice().concat(history);
}
return this.wrapHistoryResult(peerID, {
count: historyStorage.count,
history: history,
unreadOffset: unreadOffset,
unreadSkip: unreadSkip
});
}
if(!backLimit && !limit) {
limit = prerendered || 20;
}
if(offsetNotFound) {
offset = 0;
}
if((backLimit || unreadSkip || maxID) && historyStorage.history.indexOf(maxID) == -1) {
if(backLimit) {
offset = -backLimit;
limit += backLimit;
}
return this.requestHistory(reqPeerID, maxID, limit, offset).then((historyResult: any) => {
historyStorage.count = historyResult.count || historyResult.messages.length;
if(isMigrated) {
historyStorage.count++;
}
var history: number[] = [];
historyResult.messages.forEach((message: any) => {
history.push(message.mid);
})
if(!maxID && historyStorage.pending.length) {
history = historyStorage.pending.slice().concat(history);
}
return this.wrapHistoryResult(peerID, {
count: historyStorage.count,
history: history,
unreadOffset: unreadOffset,
unreadSkip: unreadSkip
});
});
}
return this.fillHistoryStorage(peerID, maxID, limit, historyStorage).then(() => {
offset = 0;
if(maxID > 0) {
for(offset = 0; offset < historyStorage.history.length; offset++) {
if(maxID > historyStorage.history[offset]) {
break;
}
}
}
var history = historyStorage.history.slice(offset, offset + limit);
if(!maxID && historyStorage.pending.length) {
history = historyStorage.pending.slice().concat(history);
}
return this.wrapHistoryResult(peerID, {
count: historyStorage.count,
history: history,
unreadOffset: unreadOffset,
unreadSkip: unreadSkip
});
});
}
public fillHistoryStorage(peerID: number, maxID: number, fullLimit: number, historyStorage: HistoryStorage): Promise<boolean> {
// console.log('fill history storage', peerID, maxID, fullLimit, angular.copy(historyStorage))
var offset = (this.migratedFromTo[peerID] && !maxID) ? 1 : 0;
return this.requestHistory(peerID, maxID, fullLimit, offset).then((historyResult: any) => {
historyStorage.count = historyResult.count || historyResult.messages.length;
var offset = 0;
if(!maxID && historyResult.messages.length) {
maxID = historyResult.messages[0].mid + 1;
}
if(maxID > 0) {
for(offset = 0; offset < historyStorage.history.length; offset++) {
if(maxID > historyStorage.history[offset]) {
break;
}
}
}
var wasTotalCount = historyStorage.history.length;
historyStorage.history.splice(offset, historyStorage.history.length - offset)
historyResult.messages.forEach((message: any) => {
if(this.mergeReplyKeyboard(historyStorage, message)) {
$rootScope.$broadcast('history_reply_markup', {peerID: peerID});
}
historyStorage.history.push(message.mid);
});
var totalCount = historyStorage.history.length;
fullLimit -= (totalCount - wasTotalCount);
var migratedNextPeer = this.migratedFromTo[peerID];
var migratedPrevPeer = this.migratedToFrom[peerID]
var isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined;
if(isMigrated) {
historyStorage.count = Math.max(historyStorage.count, totalCount) + 1;
}
if(fullLimit > 0) {
maxID = historyStorage.history[totalCount - 1];
if(isMigrated) {
if(!historyResult.messages.length) {
if(migratedPrevPeer) {
maxID = 0;
peerID = migratedPrevPeer;
} else {
historyStorage.count = totalCount;
return true;
}
}
return this.fillHistoryStorage(peerID, maxID, fullLimit, historyStorage);
} else if(totalCount < historyStorage.count) {
return this.fillHistoryStorage(peerID, maxID, fullLimit, historyStorage);
}
}
return true;
});
}
public wrapHistoryResult(peerID: number, result: any) {
var unreadOffset = result.unreadOffset;
if(unreadOffset) {
var i;
var message;
for(i = result.history.length - 1; i >= 0; i--) {
message = this.messagesStorage[result.history[i]];
if(message && !message.pFlags.out && message.pFlags.unread) {
result.unreadOffset = i + 1;
break;
}
}
}
return Promise.resolve(result);
}
public requestHistory(peerID: number, maxID: number, limit: number, offset = 0) {
var isChannel = AppPeersManager.isChannel(peerID);
console.trace('requestHistory', peerID, maxID, limit, offset);
return MTProto.apiManager.invokeApi('messages.getHistory', {
peer: AppPeersManager.getInputPeerByID(peerID),
offset_id: maxID ? appMessagesIDsManager.getMessageLocalID(maxID) : 0,
offset_date: 0,
add_offset: offset || 0,
limit: limit || 0,
max_id: 0,
min_id: 0,
hash: 0
}/* {
peer: AppPeersManager.getInputPeerByID(peerID),
offset_id: offset,
offset_date: 0,
add_offset: offset || 0,
limit: limit || 0,
max_id: maxID,
min_id: 0,
hash: 0
} */, {
timeout: 300,
noErrorBox: true
}).then((historyResult: any) => {
appUsersManager.saveApiUsers(historyResult.users);
appChatsManager.saveApiChats(historyResult.chats);
this.saveMessages(historyResult.messages);
if(isChannel) {
apiUpdatesManager.addChannelState(-peerID, historyResult.pts);
}
var length = historyResult.messages.length;
if(length &&
historyResult.messages[length - 1].deleted) {
historyResult.messages.splice(length - 1, 1);
length--;
historyResult.count--;
}
if(
peerID < 0 ||
!appUsersManager.isBot(peerID) ||
(length == limit && limit < historyResult.count)
) {
return historyResult;
}
return appProfileManager.getProfile(peerID).then((userFull: any) => {
var description = userFull.bot_info && userFull.bot_info.description;
if(description) {
var messageID = this.tempID--;
var message = {
_: 'messageService',
id: messageID,
from_id: peerID,
to_id: AppPeersManager.getOutputPeer(peerID),
flags: 0,
pFlags: {},
date: tsNow(true) + MTProto.serverTimeManager.serverTimeOffset,
action: {
_: 'messageActionBotIntro',
description: description
}
}
this.saveMessages([message]);
historyResult.messages.push(message);
if(historyResult.count) {
historyResult.count++;
}
}
return historyResult;
});
}, (error) => {
switch (error.type) {
case 'CHANNEL_PRIVATE':
var channel = appChatsManager.getChat(-peerID);
channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};
apiUpdatesManager.processUpdateMessage({
_: 'updates',
updates: [{
_: 'updateChannel',
channel_id: -peerID
}],
chats: [channel],
users: []
});
break;
}
return Promise.reject(error);
});
}
public wrapForDialog(msgID: number, dialog?: any) {
var useCache = msgID && dialog !== undefined;
var unreadCount = dialog && dialog.unread_count;
if(useCache && this.messagesForDialogs[msgID] !== undefined) {
delete this.messagesForDialogs[msgID].typing;
this.messagesForDialogs[msgID].unreadCount = unreadCount;
return this.messagesForDialogs[msgID];
}
var message = copy(this.messagesStorage[msgID]);
if(!message || !message.to_id) {
if(dialog && dialog.peerID) {
message = {
_: 'message',
to_id: AppPeersManager.getOutputPeer(dialog.peerID),
deleted: true,
date: tsNow(true),
pFlags: {out: true}
}
} else {
return message;
}
}
message.peerID = this.getMessagePeer(message);
message.peerData = AppPeersManager.getPeer(message.peerID);
message.peerString = AppPeersManager.getPeerString(message.peerID);
message.unreadCount = unreadCount;
message.index = dialog && dialog.index || (message.date * 0x10000);
message.pinned = dialog && dialog.pFlags.pinned || false;
if(message._ == 'messageService' && message.action.user_id) {
message.action.user = appUsersManager.getUser(message.action.user_id);
}
if(message.message && message.message.length) {
message.richMessage = RichTextProcessor.wrapRichText(message.message.substr(0, 128), {noLinks: true, noLinebreaks: true});
}
message.dateText = message.date; //dateOrTimeFilter(message.date); // warning
if(useCache) {
message.draft = '';//DraftsManager.getServerDraft(message.peerID); // warning
this.messagesForDialogs[msgID] = message;
}
return message;
}
public fetchSingleMessages() {
if(this.fetchSingleMessagesPromise) {
return this.fetchSingleMessagesPromise;
}
var mids = this.needSingleMessages.slice();
this.needSingleMessages = [];
var splitted = appMessagesIDsManager.splitMessageIDsByChannels(mids);
let promises: Promise<void>[] = [];
Object.keys(splitted.msgIDs).forEach((channelID: number | string) => {
let msgIDs = splitted.msgIDs[channelID].map((mid: number) => {
return {
_: 'inputMessageID',
id: mid
};
});
var promise;
channelID = +channelID;
if(channelID > 0) {
promise = MTProto.apiManager.invokeApi('channels.getMessages', {
channel: appChatsManager.getChannelInput(channelID),
id: msgIDs
});
} else {
promise = MTProto.apiManager.invokeApi('messages.getMessages', {
id: msgIDs
});
}
promises.push(promise.then((getMessagesResult: any) => {
appUsersManager.saveApiUsers(getMessagesResult.users);
appChatsManager.saveApiChats(getMessagesResult.chats);
this.saveMessages(getMessagesResult.messages);
$rootScope.$broadcast('messages_downloaded', splitted.mids[channelID])
}));
});
return this.fetchSingleMessagesPromise = Promise.all(promises).then(() => {
this.fetchSingleMessagesPromise = null;
if(this.needSingleMessages.length) this.fetchSingleMessages();
}).catch(() => {
this.fetchSingleMessagesPromise = null;
if(this.needSingleMessages.length) this.fetchSingleMessages();
});
}
/* public wrapSingleMessage(msgID: number) {
if(this.messagesStorage[msgID]) {
return this.wrapForDialog(msgID);
}
if(this.needSingleMessages.indexOf(msgID) == -1) {
this.needSingleMessages.push(msgID);
if(this.fetchSingleMessagesTimeout == 0) {
this.fetchSingleMessagesTimeout = window.setTimeout(this.fetchSingleMessages.bind(this), 100);
}
}
return {mid: msgID, loading: true};
} */
public wrapSingleMessage(msgID: number) {
if(this.messagesStorage[msgID]) {
$rootScope.$broadcast('messages_downloaded', msgID);
return;
}
if(this.needSingleMessages.indexOf(msgID) == -1) {
this.needSingleMessages.push(msgID);
this.fetchSingleMessages();
}
}
}
export default new AppMessagesManager();