Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4303 lines
139 KiB

import ProgressivePreloader from "../../components/preloader";
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
import { tsNow } from "../../helpers/date";
import { copy, defineNotNumerableProperties, safeReplaceObject, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random";
import { splitStringByLength, limitSymbols } from "../../helpers/string";
import { Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputNotifyPeer, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PhotoSize, SendMessageAction, Update } from "../../layer";
import { InvokeApiOptions } from "../../types";
import { langPack } from "../langPack";
import { logger, LogLevels } from "../logger";
import type { ApiFileManager } from '../mtproto/apiFileManager';
//import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker';
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
import serverTimeManager from "../mtproto/serverTimeManager";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
import searchIndexManager from '../searchIndexManager';
import AppStorage from '../storage';
import DialogsStorage from "../storages/dialogs";
import FiltersStorage from "../storages/filters";
//import { telegramMeWebService } from "../mtproto/mtproto";
import apiUpdatesManager from "./apiUpdatesManager";
import appChatsManager from "./appChatsManager";
import appDocsManager, { MyDocument } from "./appDocsManager";
import appDownloadManager from "./appDownloadManager";
import appMessagesIdsManager from "./appMessagesIdsManager";
import appPeersManager from "./appPeersManager";
import appPhotosManager, { MyPhoto } from "./appPhotosManager";
import appPollsManager from "./appPollsManager";
import appStateManager from "./appStateManager";
import appUsersManager from "./appUsersManager";
import appWebPagesManager from "./appWebPagesManager";
//console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
5 years ago
const APITIMEOUT = 0;
5 years ago
export type HistoryStorage = {
5 years ago
count: number | null,
history: number[],
pending: number[],
readPromise?: Promise<boolean>,
readMaxId?: number,
maxOutId?: number,
reply_markup?: any
5 years ago
};
5 years ago
export type HistoryResult = {
count: number,
history: number[],
unreadOffset: number,
unreadSkip: boolean
};
export type Dialog = MTDialog.dialog;
export type MyMessage = Message.message | Message.messageService;
type MyInputMessagesFilter = 'inputMessagesFilterEmpty'
| 'inputMessagesFilterPhotos'
| 'inputMessagesFilterPhotoVideo'
| 'inputMessagesFilterVideo'
| 'inputMessagesFilterDocument'
| 'inputMessagesFilterVoice'
| 'inputMessagesFilterRoundVoice'
| 'inputMessagesFilterRoundVideo'
| 'inputMessagesFilterMusic'
| 'inputMessagesFilterUrl'
| 'inputMessagesFilterMyMentions'
| 'inputMessagesFilterChatPhotos'
| 'inputMessagesFilterPinned';
export type PinnedStorage = Partial<{
promise: Promise<PinnedStorage>,
count: number,
maxId: number
}>;
5 years ago
export class AppMessagesManager {
public messagesStorage: {[mid: string]: any} = {};
public messagesStorageByPeerId: {[peerId: string]: AppMessagesManager['messagesStorage']} = {};
public groupedMessagesStorage: {[groupId: string]: {[mid: string]: any}} = {}; // will be used for albums
5 years ago
public historiesStorage: {
[peerId: string]: HistoryStorage
5 years ago
} = {};
public searchesStorage: {
[peerId: string]: Partial<{
[inputFilter in MyInputMessagesFilter]: {
count?: number,
history: number[]
}
}>
} = {};
public pinnedMessages: {[peerId: string]: PinnedStorage} = {};
public pendingByRandomId: {[randomId: string]: [number, number]} = {};
public pendingByMessageId: any = {};
5 years ago
public pendingAfterMsgs: any = {};
public pendingTopMsgs: {[peerId: string]: number} = {};
5 years ago
public sendFilePromise: CancellablePromise<void> = Promise.resolve();
public tempId = -1;
public tempFinalizeCallbacks: {
[mid: string]: {
[callbackName: string]: Partial<{
deferred: CancellablePromise<void>,
callback: (mid: number) => Promise<any>
}>
}
} = {};
5 years ago
public needSingleMessages: number[] = [];
private fetchSingleMessagesPromise: Promise<void> = null;
5 years ago
public maxSeenId = 0;
5 years ago
public migratedFromTo: {[peerId: number]: number} = {};
public migratedToFrom: {[peerId: number]: number} = {};
5 years ago
public newMessagesHandlePromise = 0;
public newMessagesToHandle: {[peerId: string]: number[]} = {};
5 years ago
public newDialogsHandlePromise = 0;
public newDialogsToHandle: {[peerId: string]: {reload: true} | Dialog} = {};
5 years ago
public newUpdatesAfterReloadToHandle: any = {};
4 years ago
private reloadConversationsPromise: Promise<void>;
private reloadConversationsPeers: number[] = [];
5 years ago
private dialogsIndex = searchIndexManager.createIndex();
private cachedResults: {
query: string,
count: number,
dialogs: Dialog[],
folderId: number
} = {
query: '',
count: 0,
dialogs: [],
folderId: 0
};
private log = logger('MESSAGES'/* , LogLevels.error | LogLevels.debug | LogLevels.log | LogLevels.warn */);
public dialogsStorage: DialogsStorage;
public filtersStorage: FiltersStorage;
5 years ago
constructor() {
this.dialogsStorage = new DialogsStorage(this, appMessagesIdsManager, appChatsManager, appPeersManager, serverTimeManager);
this.filtersStorage = new FiltersStorage(appPeersManager, appUsersManager, /* apiManager, */ rootScope);
rootScope.on('apiUpdate', (e) => {
this.handleUpdate(e.detail);
5 years ago
});
rootScope.on('webpage_updated', (e) => {
const eventData = e.detail;
eventData.msgs.forEach((mid) => {
const message = this.getMessage(mid) as Message.message;
if(!message) return;
message.media = {
_: 'messageMediaWebPage',
webpage: appWebPagesManager.getWebPage(eventData.id)
};
rootScope.broadcast('message_edit', {
peerId: this.getMessagePeer(message),
mid: mid,
justMedia: true
});
5 years ago
});
});
/* rootScope.$on('draft_updated', (e) => {
5 years ago
let eventData = e.detail;;
var peerId = eventData.peerId;
5 years ago
var draft = eventData.draft;
var dialog = this.getDialogByPeerID(peerId)[0];
5 years ago
if(dialog) {
var topDate;
if(draft && draft.date) {
topDate = draft.date;
} else {
var channelId = appPeersManager.isChannel(peerId) ? -peerId : 0
5 years ago
var topDate = this.getMessage(dialog.top_message).date;
if(channelId) {
var channel = appChatsManager.getChat(channelId);
5 years ago
if(!topDate || channel.date && channel.date > topDate) {
topDate = channel.date;
}
}
}
if(!dialog.pFlags.pinned) {
dialog.index = this.dialogsStorage.generateDialogIndex(topDate);
5 years ago
}
this.dialogsStorage.pushDialog(dialog);
5 years ago
rootScope.$broadcast('dialog_draft', {
peerId,
draft,
5 years ago
index: dialog.index
});
}
}); */
appStateManager.addListener('save', () => {
const messages: any[] = [];
const dialogs: Dialog[] = [];
for(const folderId in this.dialogsStorage.byFolders) {
const folder = this.dialogsStorage.getFolder(+folderId);
for(let dialog of folder) {
const historyStorage = this.historiesStorage[dialog.peerId];
const history = [].concat(historyStorage?.pending ?? [], historyStorage?.history ?? []);
dialog = copy(dialog);
let removeUnread = 0;
for(const mid of history) {
const message = this.getMessage(mid);
if(/* message._ != 'messageEmpty' && */message.id > 0) {
messages.push(message);
if(message.fromId != dialog.peerId) {
appStateManager.setPeer(message.fromId, appPeersManager.getPeer(message.fromId));
}
dialog.top_message = message.mid;
break;
} else if(message.pFlags && message.pFlags.unread) {
++removeUnread;
}
}
if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread;
dialogs.push(dialog);
appStateManager.setPeer(dialog.peerId, appPeersManager.getPeer(dialog.peerId));
}
}
appStateManager.pushToState('dialogs', dialogs);
appStateManager.pushToState('messages', messages);
appStateManager.pushToState('filters', this.filtersStorage.filters);
appStateManager.pushToState('allDialogsLoaded', this.dialogsStorage.allDialogsLoaded);
appStateManager.pushToState('maxSeenMsgId', this.maxSeenId);
});
appStateManager.getState().then(state => {
if(state.maxSeenMsgId && !appMessagesIdsManager.getMessageIdInfo(state.maxSeenMsgId)[1]) {
this.maxSeenId = state.maxSeenMsgId;
}
const messages = state.messages;
if(messages) {
/* let tempId = this.tempId;
for(let message of messages) {
if(message.id < tempId) {
tempId = message.id;
}
}
if(tempId != this.tempId) {
this.log('Set tempId to:', tempId);
this.tempId = tempId;
} */
this.saveMessages(messages);
}
if(state.allDialogsLoaded) {
this.dialogsStorage.allDialogsLoaded = state.allDialogsLoaded;
}
if(state.filters) {
for(const filterId in state.filters) {
this.filtersStorage.saveDialogFilter(state.filters[filterId], false);
}
}
if(state.dialogs) {
state.dialogs.forEachReverse(dialog => {
this.saveConversation(dialog);
// ! WARNING, убрать это когда нужно будет делать чтобы pending сообщения сохранялись
const message = this.getMessage(dialog.top_message);
if(message.deleted) {
this.reloadConversation(dialog.peerId);
}
});
}
});
}
5 years ago
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 invokeAfterMessageIsSent(messageId: number, callbackName: string, callback: (mid: number) => Promise<any>) {
const finalize = this.tempFinalizeCallbacks[messageId] ?? (this.tempFinalizeCallbacks[messageId] = {});
const obj = finalize[callbackName] ?? (finalize[callbackName] = {deferred: deferredPromise<void>()});
obj.callback = callback;
return obj.deferred;
}
public editMessage(mid: number, text: string, options: Partial<{
noWebPage: true,
newMedia: any
}> = {}): Promise<void> {
/* if(!this.canEditMessage(messageId)) {
return Promise.reject({type: 'MESSAGE_EDIT_FORBIDDEN'});
} */
if(mid < 0) {
return this.invokeAfterMessageIsSent(mid, 'edit', (mid) => {
this.log('invoke editMessage callback', mid);
return this.editMessage(mid, text, options);
});
}
let entities: any[];
if(typeof(text) === 'string') {
entities = [];
text = RichTextProcessor.parseMarkdown(text, entities);
}
const message = this.getMessage(mid);
const peerId = this.getMessagePeer(message);
return apiManager.invokeApi('messages.editMessage', {
peer: appPeersManager.getInputPeerById(peerId),
id: appMessagesIdsManager.getMessageLocalId(mid),
message: text,
media: options.newMedia,
entities: entities ? this.getInputEntities(entities) : undefined,
no_webpage: options.noWebPage,
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, (error) => {
this.log.error('editMessage error:', error);
if(error && error.type == 'MESSAGE_NOT_MODIFIED') {
error.handled = true;
return;
}
if(error && error.type == 'MESSAGE_EMPTY') {
error.handled = true;
}
return Promise.reject(error);
});
}
public sendText(peerId: number, text: string, options: Partial<{
entities: any[],
replyToMsgId: number,
viaBotId: number,
queryId: string,
resultId: string,
noWebPage: true,
reply_markup: any,
clearDraft: true,
webPage: any
}> = {}) {
if(typeof(text) != 'string' || !text.length) {
5 years ago
return;
}
const MAX_LENGTH = 4096;
if(text.length > MAX_LENGTH) {
const splitted = splitStringByLength(text, MAX_LENGTH);
text = splitted[0];
if(splitted.length > 1) {
delete options.webPage;
}
for(let i = 1; i < splitted.length; ++i) {
setTimeout(() => {
this.sendText(peerId, splitted[i], options);
}, i);
}
}
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
5 years ago
var entities = options.entities || [];
if(!options.viaBotId) {
5 years ago
text = RichTextProcessor.parseMarkdown(text, entities);
}
var sendEntites = this.getInputEntities(entities);
if(!sendEntites.length) {
sendEntites = undefined;
}
var messageId = this.tempId--;
var randomIdS = randomLong();
var historyStorage = this.historiesStorage[peerId];
5 years ago
var pFlags: any = {};
var replyToMsgId = options.replyToMsgId;
var isChannel = appPeersManager.isChannel(peerId);
var isMegagroup = isChannel && appPeersManager.isMegagroup(peerId);
5 years ago
var asChannel = isChannel && !isMegagroup ? true : false;
var message: any;
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerId] = {count: null, history: [], pending: []};
5 years ago
}
var fromId = appUsersManager.getSelf().id;
if(peerId != fromId) {
5 years ago
pFlags.out = true;
if(!isChannel && !appUsersManager.isBot(peerId)) {
5 years ago
pFlags.unread = true;
}
}
if(asChannel) {
fromId = 0;
pFlags.post = true;
5 years ago
}
message = {
_: 'message',
id: messageId,
from_id: appPeersManager.getOutputPeer(fromId),
peer_id: appPeersManager.getOutputPeer(peerId),
5 years ago
pFlags: pFlags,
date: tsNow(true) + serverTimeManager.serverTimeOffset,
5 years ago
message: text,
random_id: randomIdS,
reply_to: {reply_to_msg_id: replyToMsgId},
via_bot_id: options.viaBotId,
5 years ago
reply_markup: options.reply_markup,
entities: entities,
views: asChannel && 1,
pending: true
};
if(options.webPage) {
message.media = {
_: 'messageMediaWebPage',
webpage: options.webPage
};
}
5 years ago
var toggleError = (on: any) => {
if(on) {
message.error = true;
} else {
delete message.error;
}
rootScope.broadcast('messages_pending');
5 years ago
}
message.send = () => {
toggleError(false);
var sentRequestOptions: any = {};
if(this.pendingAfterMsgs[peerId]) {
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
5 years ago
}
var apiPromise: any;
if(options.viaBotId) {
apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', {
peer: appPeersManager.getInputPeerById(peerId),
random_id: randomIdS,
reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined,
query_id: options.queryId,
id: options.resultId,
clear_draft: options.clearDraft
5 years ago
}, sentRequestOptions);
} else {
apiPromise = apiManager.invokeApiAfter('messages.sendMessage', {
no_webpage: options.noWebPage,
peer: appPeersManager.getInputPeerById(peerId),
5 years ago
message: text,
random_id: randomIdS,
reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined,
entities: sendEntites,
clear_draft: options.clearDraft
5 years ago
}, sentRequestOptions);
}
// this.log(flags, entities)
5 years ago
apiPromise.then((updates: any) => {
if(updates._ == 'updateShortSentMessage') {
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,
5 years ago
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];
5 years ago
}
});
5 years ago
this.pendingAfterMsgs[peerId] = sentRequestOptions;
5 years ago
}
this.saveMessages([message]);
historyStorage.pending.unshift(messageId);
rootScope.broadcast('history_append', {peerId, messageId, my: true});
5 years ago
setTimeout(() => message.send(), 0);
// setTimeout(function () {
// message.send()
// }, 5000)
/* if(options.clearDraft) { // WARNING
DraftsManager.clearDraft(peerId)
5 years ago
} */
this.pendingByRandomId[randomIdS] = [peerId, messageId];
5 years ago
}
public sendFile(peerId: number, file: File | Blob | MyDocument, options: Partial<{
isRoundMessage: true,
isVoiceMessage: true,
isGroupedItem: true,
isMedia: true,
replyToMsgId: number,
caption: string,
entities: MessageEntity[],
width: number,
height: number,
objectURL: string,
duration: number,
background: true,
waveform: Uint8Array
}> = {}) {
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
const messageId = this.tempId--;
const randomIdS = randomLong();
const historyStorage = this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {count: null, history: [], pending: []});
const pFlags: any = {};
const replyToMsgId = options.replyToMsgId;
const isChannel = appPeersManager.isChannel(peerId);
const isMegagroup = isChannel && appPeersManager.isMegagroup(peerId);
const asChannel = !!(isChannel && !isMegagroup);
let attachType: string, apiFileName: string;
5 years ago
const fileType = 'mime_type' in file ? file.mime_type : file.type;
const fileName = file instanceof File ? file.name : '';
const isDocument = !(file instanceof File) && !(file instanceof Blob);
5 years ago
let caption = options.caption || '';
const date = tsNow(true) + serverTimeManager.serverTimeOffset;
this.log('sendFile', file, fileType);
5 years ago
if(caption) {
let entities = options.entities || [];
caption = RichTextProcessor.parseMarkdown(caption, entities);
}
const attributes: DocumentAttribute[] = [];
const isPhoto = ['image/jpeg', 'image/png', 'image/bmp'].indexOf(fileType) >= 0;
let photo: MyPhoto, document: MyDocument;
let actionName = '';
if(isDocument) { // maybe it's a sticker or gif
attachType = 'document';
apiFileName = '';
} else if(fileType.indexOf('audio/') === 0 || ['video/ogg'].indexOf(fileType) >= 0) {
attachType = 'audio';
apiFileName = 'audio.' + (fileType.split('/')[1] == 'ogg' ? 'ogg' : 'mp3');
actionName = 'sendMessageUploadAudioAction';
if(options.isVoiceMessage) {
attachType = 'voice';
pFlags.media_unread = true;
}
let attribute: DocumentAttribute.documentAttributeAudio = {
_: 'documentAttributeAudio',
pFlags: {
voice: options.isVoiceMessage
},
waveform: options.waveform,
duration: options.duration || 0
};
attributes.push(attribute);
} else if(!options.isMedia) {
5 years ago
attachType = 'document';
apiFileName = 'document.' + fileType.split('/')[1];
actionName = 'sendMessageUploadDocumentAction';
} else if(isPhoto) {
5 years ago
attachType = 'photo';
apiFileName = 'photo.' + fileType.split('/')[1];
actionName = 'sendMessageUploadPhotoAction';
photo = {
_: 'photo',
id: '' + messageId,
sizes: [{
_: 'photoSize',
w: options.width,
h: options.height,
type: 'full',
location: null,
size: file.size
}],
w: options.width,
h: options.height
} as any;
defineNotNumerableProperties(photo, ['downloaded', 'url']);
photo.downloaded = file.size;
photo.url = options.objectURL || '';
appPhotosManager.savePhoto(photo);
} else if(fileType.indexOf('video/') === 0) {
attachType = 'video';
5 years ago
apiFileName = 'video.mp4';
actionName = 'sendMessageUploadVideoAction';
let videoAttribute: DocumentAttribute.documentAttributeVideo = {
_: 'documentAttributeVideo',
pFlags: {
round_message: options.isRoundMessage
},
duration: options.duration,
w: options.width,
h: options.height
};
attributes.push(videoAttribute);
} else {
attachType = 'document';
apiFileName = 'document.' + fileType.split('/')[1];
actionName = 'sendMessageUploadDocumentAction';
}
attributes.push({_: 'documentAttributeFilename', file_name: fileName || apiFileName});
if(['document', 'video', 'audio', 'voice'].indexOf(attachType) !== -1 && !isDocument) {
const thumbs: PhotoSize[] = [];
document = {
_: 'document',
id: '' + messageId,
duration: options.duration,
attributes,
w: options.width,
h: options.height,
thumbs,
mime_type: fileType,
size: file.size
} as any;
defineNotNumerableProperties(document, ['downloaded', 'url']);
// @ts-ignore
document.downloaded = file.size;
document.url = options.objectURL || '';
if(isPhoto) {
attributes.push({
_: 'documentAttributeImageSize',
w: options.width,
h: options.height
});
thumbs.push({
_: 'photoSize',
w: options.width,
h: options.height,
type: 'full',
location: null,
size: file.size,
url: options.objectURL
});
}
appDocsManager.saveDoc(document);
5 years ago
}
this.log('AMM: sendFile', attachType, apiFileName, file.type, options);
5 years ago
let fromId = appUsersManager.getSelf().id;
if(peerId != fromId) {
5 years ago
pFlags.out = true;
if(!isChannel && !appUsersManager.isBot(peerId)) {
5 years ago
pFlags.unread = true;
}
}
if(asChannel) {
fromId = 0;
5 years ago
pFlags.post = true;
}
const preloader = new ProgressivePreloader(null, true, false, 'prepend');
const media = {
5 years ago
_: 'messageMediaPending',
type: options.isGroupedItem && options.isMedia ? 'album' : attachType,
5 years ago
file_name: fileName || apiFileName,
size: file.size,
file,
preloader,
photo,
document,
w: options.width,
h: options.height,
url: options.objectURL
};
const message: any = {
5 years ago
_: 'message',
id: messageId,
from_id: appPeersManager.getOutputPeer(fromId),
peer_id: appPeersManager.getOutputPeer(peerId),
pFlags,
date,
5 years ago
message: caption,
media: isDocument ? {
5 years ago
_: 'messageMediaDocument',
pFlags: {},
document: file
} : media,
random_id: randomIdS,
reply_to: {reply_to_msg_id: replyToMsgId},
5 years ago
views: asChannel && 1,
pending: true
};
const toggleError = (on: boolean) => {
5 years ago
if(on) {
message.error = true;
} else {
delete message.error;
}
rootScope.broadcast('messages_pending');
5 years ago
};
let uploaded = false,
uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null;
5 years ago
const sentDeferred = deferredPromise<InputMedia>();
5 years ago
message.send = () => {
if(isDocument) {
const {id, access_hash, file_reference} = file as MyDocument;
5 years ago
const inputMedia: InputMedia = {
5 years ago
_: 'inputMediaDocument',
id: {
_: 'inputDocument',
id,
access_hash,
file_reference
5 years ago
}
};
sentDeferred.resolve(inputMedia);
5 years ago
} else if(file instanceof File || file instanceof Blob) {
const deferred = deferredPromise<void>();
5 years ago
this.sendFilePromise.then(() => {
if(!uploaded || message.error) {
uploaded = false;
uploadPromise = appDownloadManager.upload(file);
preloader.attachPromise(uploadPromise);
5 years ago
}
uploadPromise && uploadPromise.then((inputFile) => {
this.log('appMessagesManager: sendFile uploaded:', inputFile);
5 years ago
inputFile.name = apiFileName;
uploaded = true;
let inputMedia: InputMedia;
5 years ago
switch(attachType) {
case 'photo':
inputMedia = {
_: 'inputMediaUploadedPhoto',
file: inputFile
};
break;
5 years ago
default:
inputMedia = {
_: 'inputMediaUploadedDocument',
file: inputFile,
mime_type: fileType,
attributes
5 years ago
};
}
sentDeferred.resolve(inputMedia);
5 years ago
}, (/* error */) => {
toggleError(true);
});
uploadPromise.addNotifyListener((progress: {done: number, total: number}) => {
this.log('upload progress', progress);
const percents = Math.max(1, Math.floor(100 * progress.done / progress.total));
this.setTyping(peerId, {_: actionName, progress: percents | 0});
});
uploadPromise.catch(err => {
if(err.name === 'AbortError' && !uploaded) {
this.log('cancelling upload', media);
5 years ago
deferred.resolve();
sentDeferred.reject(err);
this.cancelPendingMessage(randomIdS);
this.setTyping(peerId, 'sendMessageCancelAction');
5 years ago
}
});
uploadPromise.finally(deferred.resolve);
5 years ago
});
this.sendFilePromise = deferred;
}
return sentDeferred;
5 years ago
};
historyStorage.pending.unshift(messageId);
this.pendingByRandomId[randomIdS] = [peerId, messageId];
5 years ago
if(!options.isGroupedItem) {
this.saveMessages([message]);
rootScope.broadcast('history_append', {peerId, messageId, my: true});
setTimeout(message.send, 0);
sentDeferred.then(inputMedia => {
this.setTyping(peerId, 'sendMessageCancelAction');
return apiManager.invokeApi('messages.sendMedia', {
background: options.background,
clear_draft: true,
peer: appPeersManager.getInputPeerById(peerId),
media: inputMedia,
message: caption,
random_id: randomIdS,
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;
}
5 years ago
toggleError(true);
});
});
}
return {message, promise: sentDeferred};
5 years ago
}
public async sendAlbum(peerId: number, files: File[], options: Partial<{
isMedia: true,
entities: MessageEntity[],
replyToMsgId: number,
caption: string,
sendFileDetails: Partial<{
duration: number,
width: number,
height: number,
objectURL: string,
}>[]
}> = {}) {
if(files.length === 1) {
return this.sendFile(peerId, files[0], {...options, ...options.sendFileDetails[0]});
}
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
const replyToMsgId = options.replyToMsgId;
let caption = options.caption || '';
let entities: MessageEntity[];
if(caption) {
entities = options.entities || [];
caption = RichTextProcessor.parseMarkdown(caption, entities);
}
this.log('AMM: sendAlbum', files, options);
const messages = files.map((file, idx) => {
const details = options.sendFileDetails[idx];
const o: any = {
isGroupedItem: true,
isMedia: options.isMedia,
...details
};
if(idx === 0) {
o.caption = caption;
o.entities = entities;
o.replyToMsgId = replyToMsgId;
}
return this.sendFile(peerId, file, o).message;
});
const groupId = messages[0].id;
messages.forEach(message => {
message.grouped_id = groupId;
});
this.saveMessages(messages);
rootScope.broadcast('history_append', {peerId, messageId: groupId, my: true});
//return;
const toggleError = (message: any, on: boolean) => {
if(on) {
message.error = true;
} else {
delete message.error;
}
rootScope.broadcast('messages_pending');
};
const inputPeer = appPeersManager.getInputPeerById(peerId);
const invoke = (multiMedia: any[]) => {
this.setTyping(peerId, 'sendMessageCancelAction');
return apiManager.invokeApi('messages.sendMultiMedia', {
peer: inputPeer,
multi_media: multiMedia,
reply_to_msg_id: appMessagesIdsManager.getMessageLocalId(replyToMsgId)
}).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, (error) => {
messages.forEach(message => toggleError(message, true));
});
};
const inputs: InputSingleMedia[] = [];
for(const message of messages) {
const inputMedia: InputMedia = await message.send();
this.log('sendAlbum uploaded item:', inputMedia);
await apiManager.invokeApi('messages.uploadMedia', {
peer: inputPeer,
media: inputMedia
}).then(messageMedia => {
let inputMedia: any;
if(messageMedia._ == 'messageMediaPhoto') {
const photo = appPhotosManager.savePhoto(messageMedia.photo);
inputMedia = appPhotosManager.getInput(photo);
} else if(messageMedia._ == 'messageMediaDocument') {
const doc = appDocsManager.saveDoc(messageMedia.document);
inputMedia = appDocsManager.getMediaInput(doc);
}
inputs.push({
_: 'inputSingleMedia',
media: inputMedia,
random_id: message.random_id,
message: caption,
entities
});
// * only 1 caption for all inputs
if(caption) {
caption = '';
entities = [];
}
}, () => {
toggleError(message, true);
});
}
invoke(inputs);
}
public sendOther(peerId: number, inputMedia: any, options: Partial<{
replyToMsgId: number,
viaBotId: number,
4 years ago
reply_markup: any,
clearDraft: true,
queryId: string
resultId: string
4 years ago
}> = {}) {
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
const messageId = this.tempId--;
const randomIdS = randomLong();
const historyStorage = this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {count: null, history: [], pending: []});
const replyToMsgId = options.replyToMsgId;
const isChannel = appPeersManager.isChannel(peerId);
const isMegagroup = isChannel && appPeersManager.isMegagroup(peerId);
4 years ago
const asChannel = isChannel && !isMegagroup ? true : false;
let fromId = appUsersManager.getSelf().id;
4 years ago
let media;
switch(inputMedia._) {
case 'inputMediaPoll': {
inputMedia.poll.id = messageId;
4 years ago
appPollsManager.savePoll(inputMedia.poll, {
_: 'pollResults',
flags: 4,
total_voters: 0,
pFlags: {},
});
const {poll, results} = appPollsManager.getPoll('' + messageId);
4 years ago
media = {
_: 'messageMediaPoll',
poll,
results
};
break;
}
/* case 'inputMediaPhoto':
media = {
_: 'messageMediaPhoto',
photo: appPhotosManager.getPhoto(inputMedia.id.id),
caption: inputMedia.caption || ''
};
break;
case 'inputMediaDocument':
var doc = appDocsManager.getDoc(inputMedia.id.id);
if(doc.sticker && doc.stickerSetInput) {
appStickersManager.pushPopularSticker(doc.id);
}
media = {
_: 'messageMediaDocument',
'document': doc,
caption: inputMedia.caption || ''
};
break;
case 'inputMediaContact':
media = {
_: 'messageMediaContact',
phone_number: inputMedia.phone_number,
first_name: inputMedia.first_name,
last_name: inputMedia.last_name,
user_id: 0
};
break;
case 'inputMediaGeoPoint':
media = {
_: 'messageMediaGeo',
geo: {
_: 'geoPoint',
'lat': inputMedia.geo_point['lat'],
'long': inputMedia.geo_point['long']
}
};
break;
case 'inputMediaVenue':
media = {
_: 'messageMediaVenue',
geo: {
_: 'geoPoint',
'lat': inputMedia.geo_point['lat'],
'long': inputMedia.geo_point['long']
},
title: inputMedia.title,
address: inputMedia.address,
provider: inputMedia.provider,
venue_id: inputMedia.venue_id
};
break;
case 'messageMediaPending':
media = inputMedia;
break; */
}
let pFlags: any = {};
if(peerId != fromId) {
4 years ago
pFlags.out = true;
if(!appUsersManager.isBot(peerId)) {
4 years ago
pFlags.unread = true;
}
}
4 years ago
if(asChannel) {
fromId = 0;
4 years ago
pFlags.post = true;
}
const message: any = {
_: 'message',
id: messageId,
from_id: appPeersManager.getOutputPeer(fromId),
peer_id: appPeersManager.getOutputPeer(peerId),
4 years ago
pFlags: pFlags,
date: tsNow(true) + serverTimeManager.serverTimeOffset,
4 years ago
message: '',
media: media,
random_id: randomIdS,
reply_to: {reply_to_msg_id: replyToMsgId},
via_bot_id: options.viaBotId,
4 years ago
reply_markup: options.reply_markup,
views: asChannel && 1,
pending: true,
};
let toggleError = (on: boolean) => {
/* const historyMessage = this.messagesForHistory[messageId];
4 years ago
if (on) {
message.error = true
if (historyMessage) {
historyMessage.error = true
}
} else {
delete message.error
if (historyMessage) {
delete historyMessage.error
}
} */
rootScope.broadcast('messages_pending');
4 years ago
};
message.send = () => {
const sentRequestOptions: any = {};
if(this.pendingAfterMsgs[peerId]) {
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
4 years ago
}
let apiPromise: Promise<any>;
if(options.viaBotId) {
apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', {
peer: appPeersManager.getInputPeerById(peerId),
random_id: randomIdS,
reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined,
query_id: options.queryId,
id: options.resultId,
clear_draft: options.clearDraft
4 years ago
}, sentRequestOptions);
} else {
apiPromise = apiManager.invokeApiAfter('messages.sendMedia', {
peer: appPeersManager.getInputPeerById(peerId),
4 years ago
media: inputMedia,
random_id: randomIdS,
reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined,
message: '',
clear_draft: options.clearDraft
4 years ago
}, sentRequestOptions);
}
4 years ago
apiPromise.then((updates) => {
if(updates.updates) {
updates.updates.forEach((update: any) => {
if(update._ == 'updateDraftMessage') {
update.local = true
}
});
}
apiUpdatesManager.processUpdateMessage(updates);
}, (error) => {
toggleError(true);
}).finally(() => {
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
delete this.pendingAfterMsgs[peerId];
4 years ago
}
});
this.pendingAfterMsgs[peerId] = sentRequestOptions;
4 years ago
}
this.saveMessages([message]);
historyStorage.pending.unshift(messageId);
rootScope.broadcast('history_append', {peerId, messageId, my: true});
4 years ago
setTimeout(message.send, 0);
/* if(options.clearDraft) {
DraftsManager.clearDraft(peerId)
4 years ago
} */
this.pendingByRandomId[randomIdS] = [peerId, messageId];
4 years ago
}
public cancelPendingMessage(randomId: string) {
const pendingData = this.pendingByRandomId[randomId];
5 years ago
this.log('cancelPendingMessage', randomId, pendingData);
5 years ago
if(pendingData) {
const peerId = pendingData[0];
const tempId = pendingData[1];
const historyStorage = this.historiesStorage[peerId];
const pos = historyStorage.pending.indexOf(tempId);
5 years ago
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: [tempId]
5 years ago
}
});
if(pos != -1) {
historyStorage.pending.splice(pos, 1);
}
delete this.messagesStorage[tempId];
5 years ago
return true;
}
return false;
}
public async getConversationsAll(query = '', folderId = 0) {
const limit = 100, outDialogs: Dialog[] = [];
for(; folderId < 2; ++folderId) {
let offsetIndex = 0;
for(;;) {
const {dialogs} = await appMessagesManager.getConversations(query, offsetIndex, limit, folderId);
if(dialogs.length) {
outDialogs.push(...dialogs);
offsetIndex = dialogs[dialogs.length - 1].index || 0;
} else {
break;
}
}
}
return outDialogs;
}
public getConversations(query = '', offsetIndex?: number, limit = 20, folderId = 0) {
const realFolderId = folderId > 1 ? 0 : folderId;
let curDialogStorage = this.dialogsStorage.getFolder(folderId);
5 years ago
if(query) {
if(!limit || this.cachedResults.query !== query || this.cachedResults.folderId != folderId) {
this.cachedResults.query = query;
this.cachedResults.folderId = folderId;
const results = searchIndexManager.search(query, this.dialogsIndex);
this.cachedResults.dialogs = [];
for(const peerId in this.dialogsStorage.dialogs) {
const dialog = this.dialogsStorage.dialogs[peerId];
if(results[dialog.peerId] && dialog.folder_id == folderId) {
this.cachedResults.dialogs.push(dialog);
}
}
this.cachedResults.dialogs.sort((d1, d2) => d2.index - d1.index);
this.cachedResults.count = this.cachedResults.dialogs.length;
}
curDialogStorage = this.cachedResults.dialogs;
} else {
this.cachedResults.query = '';
}
let offset = 0;
5 years ago
if(offsetIndex > 0) {
for(; offset < curDialogStorage.length; offset++) {
if(offsetIndex > curDialogStorage[offset].index) {
5 years ago
break;
}
}
}
if(query || this.dialogsStorage.allDialogsLoaded[realFolderId] || curDialogStorage.length >= offset + limit) {
5 years ago
return Promise.resolve({
dialogs: curDialogStorage.slice(offset, offset + limit),
count: this.dialogsStorage.allDialogsLoaded[realFolderId] ? curDialogStorage.length : null,
isEnd: this.dialogsStorage.allDialogsLoaded[realFolderId] && (offset + limit) >= curDialogStorage.length
5 years ago
});
}
return this.getTopMessages(limit, realFolderId).then(totalCount => {
//const curDialogStorage = this.dialogsStorage[folderId];
offset = 0;
5 years ago
if(offsetIndex > 0) {
for(; offset < curDialogStorage.length; offset++) {
if(offsetIndex > curDialogStorage[offset].index) {
5 years ago
break;
}
}
}
//this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length);
5 years ago
return {
dialogs: curDialogStorage.slice(offset, offset + limit),
count: totalCount,
isEnd: this.dialogsStorage.allDialogsLoaded[realFolderId] && (offset + limit) >= curDialogStorage.length
5 years ago
};
});
}
public getTopMessages(limit: number, folderId: number): Promise<number> {
const dialogs = this.dialogsStorage.getFolder(folderId);
let offsetId = 0;
let offsetDate = 0;
let offsetPeerId = 0;
let offsetIndex = 0;
let flags = 0;
5 years ago
if(this.dialogsStorage.dialogsOffsetDate[folderId]) {
offsetDate = this.dialogsStorage.dialogsOffsetDate[folderId] + serverTimeManager.serverTimeOffset;
offsetIndex = this.dialogsStorage.dialogsOffsetDate[folderId] * 0x10000;
//flags |= 1; // means pinned already loaded
5 years ago
}
/* if(this.dialogsStorage.dialogsOffsetDate[0]) {
flags |= 1; // means pinned already loaded
} */
//if(folderId > 0) {
//flags |= 1;
flags |= 2;
//}
5 years ago
// ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???
// ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0
return apiManager.invokeApi('messages.getDialogs', {
flags,
folder_id: folderId,
5 years ago
offset_date: offsetDate,
offset_id: appMessagesIdsManager.getMessageLocalId(offsetId),
offset_peer: appPeersManager.getInputPeerById(offsetPeerId),
limit,
hash: 0
5 years ago
}, {
//timeout: APITIMEOUT,
noErrorBox: true
}).then((dialogsResult) => {
if(dialogsResult._ == 'messages.dialogsNotModified') return null;
//this.log.error('messages.getDialogs result:', dialogsResult.dialogs, {...dialogsResult.dialogs[0]});
/* if(!offsetDate) {
5 years ago
telegramMeWebService.setAuthorized(true);
} */
5 years ago
appUsersManager.saveApiUsers(dialogsResult.users);
appChatsManager.saveApiChats(dialogsResult.chats);
this.saveMessages(dialogsResult.messages);
let maxSeenIdIncremented = offsetDate ? true : false;
let hasPrepend = false;
const noIdsDialogs: {[peerId: number]: Dialog} = {};
(dialogsResult.dialogs as Dialog[]).forEachReverse(dialog => {
//const d = Object.assign({}, dialog);
// ! нужно передавать folderId, так как по папке != 0 нет свойства folder_id
this.saveConversation(dialog, folderId);
/* if(dialog.peerId == -1213511294) {
this.log.error('lun bot', folderId, d);
} */
5 years ago
if(offsetIndex && dialog.index > offsetIndex) {
this.newDialogsToHandle[dialog.peerId] = dialog;
5 years ago
hasPrepend = true;
}
// ! это может случиться, если запрос идёт не по папке 0, а по 1. почему-то read'ов нет
// ! в итоге, чтобы получить 1 диалог, делается первый запрос по папке 0, потом запрос для архивных по папке 1, и потом ещё перезагрузка архивного диалога
5 years ago
if(!dialog.read_inbox_max_id && !dialog.read_outbox_max_id) {
noIdsDialogs[dialog.peerId] = dialog;
/* if(dialog.peerId == -1213511294) {
this.log.error('lun bot', folderId);
} */
5 years ago
}
if(!maxSeenIdIncremented &&
!appPeersManager.isChannel(appPeersManager.getPeerId(dialog.peer))) {
this.incrementMaxSeenId(dialog.top_message);
5 years ago
maxSeenIdIncremented = true;
}
});
5 years ago
if(Object.keys(noIdsDialogs).length) {
5 years ago
//setTimeout(() => { // test bad situation
this.reloadConversation(Object.keys(noIdsDialogs).map(id => +id)).then(() => {
rootScope.broadcast('dialogs_multiupdate', noIdsDialogs);
5 years ago
for(let peerId in noIdsDialogs) {
rootScope.broadcast('dialog_unread', {peerId: +peerId});
5 years ago
}
});
//}, 10e3);
}
const count = (dialogsResult as MessagesDialogs.messagesDialogsSlice).count;
5 years ago
if(!dialogsResult.dialogs.length ||
!count ||
dialogs.length >= count) {
this.dialogsStorage.allDialogsLoaded[folderId] = true;
5 years ago
}
if(hasPrepend) {
this.scheduleHandleNewDialogs();
5 years ago
} else {
rootScope.broadcast('dialogs_multiupdate', {});
5 years ago
}
return count;
5 years ago
});
}
public forwardMessages(peerId: number, mids: number[], options: Partial<{
withMyScore: true
}> = {}) {
peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;
mids = mids.slice().sort((a, b) => a - b);
const splitted = appMessagesIdsManager.splitMessageIdsByChannels(mids);
const promises: Promise<void>[] = [];
for(const channelId in splitted.msgIds) {
const msgIds = splitted.msgIds[channelId];
const randomIds: string[] = msgIds.map(() => randomLong());
const sentRequestOptions: InvokeApiOptions = {};
if(this.pendingAfterMsgs[peerId]) {
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
}
const promise = apiManager.invokeApiAfter('messages.forwardMessages', {
from_peer: appPeersManager.getInputPeerById(-channelId),
id: msgIds,
random_id: randomIds,
to_peer: appPeersManager.getInputPeerById(peerId),
with_my_score: options.withMyScore
}, sentRequestOptions).then((updates) => {
apiUpdatesManager.processUpdateMessage(updates);
}, () => {}).then(() => {
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
delete this.pendingAfterMsgs[peerId];
}
});
this.pendingAfterMsgs[peerId] = sentRequestOptions;
promises.push(promise);
}
return Promise.all(promises);
}
public getMessage(messageId: number)/* : Message */ {
return this.messagesStorage[messageId] || {
5 years ago
_: 'messageEmpty',
id: messageId,
deleted: true,
pFlags: {}
5 years ago
};
}
public getMessagePeer(message: any): number {
const toId = message.peer_id && appPeersManager.getPeerId(message.peer_id) || 0;
5 years ago
return toId;
5 years ago
}
public getDialogByPeerId(peerId: number): [Dialog, number] | [] {
return this.dialogsStorage.getDialog(peerId);
5 years ago
}
public reloadConversation(peerId: number | number[]) {
[].concat(peerId).forEach(peerId => {
if(!this.reloadConversationsPeers.includes(peerId)) {
this.reloadConversationsPeers.push(peerId);
this.log('will reloadConversation', peerId);
4 years ago
}
});
5 years ago
4 years ago
if(this.reloadConversationsPromise) return this.reloadConversationsPromise;
return this.reloadConversationsPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const peers = this.reloadConversationsPeers.map(peerId => appPeersManager.getInputDialogPeerById(peerId));
4 years ago
this.reloadConversationsPeers.length = 0;
apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => {
this.applyConversations(result);
resolve();
}, reject).finally(() => {
this.reloadConversationsPromise = null;
});
}, 0);
});
5 years ago
}
private doFlushHistory(inputPeer: any, justClear?: true): Promise<true> {
return apiManager.invokeApi('messages.deleteHistory', {
just_clear: justClear,
peer: inputPeer,
max_id: 0
}).then((affectedHistory) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedHistory.pts,
pts_count: affectedHistory.pts_count
}
});
if(!affectedHistory.offset) {
return true;
}
return this.doFlushHistory(inputPeer, justClear);
})
}
public async flushHistory(peerId: number, justClear?: true) {
if(appPeersManager.isChannel(peerId)) {
let promise = this.getHistory(peerId, 0, 1);
let historyResult = promise instanceof Promise ? await promise : promise;
let channelId = -peerId;
let maxId = appMessagesIdsManager.getMessageLocalId(historyResult.history[0] || 0);
return apiManager.invokeApi('channels.deleteHistory', {
channel: appChatsManager.getChannelInput(channelId),
max_id: maxId
}).then(() => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateChannelAvailableMessages',
channel_id: channelId,
available_min_id: maxId
}
});
return true;
});
}
return this.doFlushHistory(appPeersManager.getInputPeerById(peerId), justClear).then(() => {
delete this.historiesStorage[peerId];
for(let mid in this.messagesStorage) {
let message = this.messagesStorage[mid];
if(message.peerId == peerId) {
delete this.messagesStorage[mid];
}
}
if(justClear) {
rootScope.broadcast('dialog_flush', {peerId});
} else {
this.dialogsStorage.dropDialog(peerId);
rootScope.broadcast('dialog_drop', {peerId});
}
});
}
public hidePinnedMessages(peerId: number) {
return Promise.all([
appStateManager.getState(),
this.getPinnedMessage(peerId)
])
.then(([state, pinned]) => {
state.hiddenPinnedMessages[peerId] = pinned.maxId;
rootScope.broadcast('peer_pinned_hidden', {peerId, maxId: pinned.maxId});
});
}
public getPinnedMessage(peerId: number) {
const p = this.pinnedMessages[peerId] ?? (this.pinnedMessages[peerId] = {});
if(p.promise) return p.promise;
else if(p.maxId) return Promise.resolve(p);
return p.promise = this.getSearch(peerId, '', {_: 'inputMessagesFilterPinned'}, 0, 1).then(result => {
p.count = result.count;
p.maxId = result.history[0];
return p;
}).finally(() => {
delete p.promise;
});
}
public updatePinnedMessage(peerId: number, mid: number, unpin?: true, silent?: true, oneSide?: true) {
return apiManager.invokeApi('messages.updatePinnedMessage', {
peer: appPeersManager.getInputPeerById(peerId),
unpin,
silent,
pm_oneside: oneSide,
id: mid
}).then(updates => {
this.log('pinned updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
});
}
public unpinAllMessages(peerId: number): Promise<boolean> {
return apiManager.invokeApi('messages.unpinAllMessages', {
peer: appPeersManager.getInputPeerById(peerId)
}).then(affectedHistory => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedHistory.pts,
pts_count: affectedHistory.pts_count
}
});
if(!affectedHistory.offset) {
const storage = this.messagesStorageByPeerId[peerId];
for(const mid in storage) {
const message = storage[mid];
if(message.pFlags.pinned) {
delete message.pFlags.pinned;
}
}
rootScope.broadcast('peer_pinned_messages', {peerId, unpinAll: true});
delete this.pinnedMessages[peerId];
return true;
}
return this.unpinAllMessages(peerId);
});
}
public getAlbumText(grouped_id: string) {
const group = appMessagesManager.groupedMessagesStorage[grouped_id];
let foundMessages = 0, message: string, totalEntities: MessageEntity[];
for(const i in group) {
const m = group[i];
if(m.message) {
if(++foundMessages > 1) break;
message = m.message;
totalEntities = m.totalEntities;
}
}
if(foundMessages > 1) {
message = undefined;
totalEntities = undefined;
}
return {message, totalEntities};
}
public getMidsByAlbum(grouped_id: string) {
return getObjectKeysAndSort(this.groupedMessagesStorage[grouped_id], 'asc');
//return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b);
}
public getMidsByMid(mid: number) {
const message = this.messagesStorage[mid];
if(message?.grouped_id) return this.getMidsByAlbum(message.grouped_id);
else return [mid];
}
public saveMessages(messages: any[]) {
let albums: Set<string>;
messages.forEach((message) => {
if(message.pFlags === undefined) {
message.pFlags = {};
5 years ago
}
if(message._ == 'messageEmpty') {
5 years ago
return;
}
// * exclude from state
// defineNotNumerableProperties(message, ['rReply', 'mid', 'savedFrom', 'fwdFromId', 'fromId', 'peerId', 'reply_to_mid', 'viaBotId']);
const peerId = this.getMessagePeer(message);
const isChannel = message.peer_id._ == 'peerChannel';
const channelId = isChannel ? -peerId : 0;
const isBroadcast = isChannel && appChatsManager.isBroadcast(channelId);
5 years ago
const mid = appMessagesIdsManager.getFullMessageId(message.id, channelId);
message.mid = mid;
5 years ago
if(message.grouped_id) {
const storage = this.groupedMessagesStorage[message.grouped_id] ?? (this.groupedMessagesStorage[message.grouped_id] = {});
storage[mid] = message;
}
const dialog = this.getDialogByPeerId(peerId)[0];
5 years ago
if(dialog && mid > 0) {
if(mid > dialog[message.pFlags.out
5 years ago
? 'read_outbox_max_id'
: 'read_inbox_max_id']) {
message.pFlags.unread = true;
}
5 years ago
}
// this.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id'])
5 years ago
if(message.reply_to && message.reply_to.reply_to_msg_id) {
message.reply_to_mid = appMessagesIdsManager.getFullMessageId(message.reply_to.reply_to_msg_id, channelId);
5 years ago
}
message.date -= serverTimeManager.serverTimeOffset;
5 years ago
const myId = appUsersManager.getSelf().id;
message.peerId = peerId;
if(message.peerId == myId/* && !message.from_id && !message.fwd_from */) {
message.fromId = message.fwd_from ? (message.fwd_from.from_id ? appPeersManager.getPeerId(message.fwd_from.from_id) : 0) : myId;
} else {
message.fromId = message.pFlags.post || (!message.pFlags.out && !message.from_id) ? peerId : appPeersManager.getPeerId(message.from_id);
}
5 years ago
const fwdHeader = message.fwd_from;
5 years ago
if(fwdHeader) {
//if(peerId == myID) {
5 years ago
if(fwdHeader.saved_from_peer && fwdHeader.saved_from_msg_id) {
const savedFromPeerId = appPeersManager.getPeerId(fwdHeader.saved_from_peer);
const savedFromMid = appMessagesIdsManager.getFullMessageId(fwdHeader.saved_from_msg_id,
appPeersManager.isChannel(savedFromPeerId) ? -savedFromPeerId : 0);
message.savedFrom = savedFromPeerId + '_' + savedFromMid;
5 years ago
}
/* if(peerId < 0 || peerId == myID) {
message.fromId = appPeersManager.getPeerID(!message.from_id || deepEqual(message.from_id, fwdHeader.from_id) ? fwdHeader.from_id : message.from_id);
} */
/* } else {
5 years ago
apiMessage.fwdPostID = fwdHeader.channel_post;
} */
5 years ago
message.fwdFromId = appPeersManager.getPeerId(fwdHeader.from_id);
fwdHeader.date -= serverTimeManager.serverTimeOffset;
5 years ago
}
if(message.via_bot_id > 0) {
message.viaBotId = message.via_bot_id;
5 years ago
}
const mediaContext: ReferenceContext = {
type: 'message',
messageId: mid
5 years ago
};
if(message.media) {
switch(message.media._) {
5 years ago
case 'messageMediaEmpty':
delete message.media;
5 years ago
break;
case 'messageMediaPhoto':
if(message.media.ttl_seconds) {
message.media = {_: 'messageMediaUnsupportedWeb'};
5 years ago
} else {
message.media.photo = appPhotosManager.savePhoto(message.media.photo, mediaContext);
//appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
5 years ago
}
break;
case 'messageMediaPoll':
message.media.poll = appPollsManager.savePoll(message.media.poll, message.media.results);
break;
5 years ago
case 'messageMediaDocument':
if(message.media.ttl_seconds) {
message.media = {_: 'messageMediaUnsupportedWeb'};
5 years ago
} else {
message.media.document = appDocsManager.saveDoc(message.media.document, mediaContext); // 11.04.2020 warning
5 years ago
}
5 years ago
break;
case 'messageMediaWebPage':
/* if(apiMessage.media.webpage.document) {
5 years ago
appDocsManager.saveDoc(apiMessage.media.webpage.document, mediaContext);
} */
appWebPagesManager.saveWebPage(message.media.webpage, message.mid, mediaContext);
5 years ago
break;
/*case 'messageMediaGame':
5 years ago
AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext);
apiMessage.media.handleMessage = true;
break; */
case 'messageMediaInvoice':
message.media = {_: 'messageMediaUnsupportedWeb'};
5 years ago
break;
case 'messageMediaGeoLive':
message.media._ = 'messageMediaGeo';
5 years ago
break;
}
}
if(message.action) {
let migrateFrom: number;
let migrateTo: number;
switch(message.action._) {
//case 'messageActionChannelEditPhoto':
5 years ago
case 'messageActionChatEditPhoto':
message.action.photo = appPhotosManager.savePhoto(message.action.photo, mediaContext);
//appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода.
message.action._ = 'messageActionChannelEditPhoto';
5 years ago
}
break;
case 'messageActionChatEditTitle':
if(isBroadcast) {
message.action._ = 'messageActionChannelEditTitle';
5 years ago
}
break;
case 'messageActionChatDeletePhoto':
if(isBroadcast) {
message.action._ = 'messageActionChannelDeletePhoto';
5 years ago
}
break;
case 'messageActionChatAddUser':
if(message.action.users.length == 1) {
message.action.user_id = message.action.users[0];
if(message.fromId == message.action.user_id) {
5 years ago
if(isChannel) {
message.action._ = 'messageActionChatJoined';
5 years ago
} else {
message.action._ = 'messageActionChatReturn';
5 years ago
}
}
} else if(message.action.users.length > 1) {
message.action._ = 'messageActionChatAddUsers';
5 years ago
}
break;
case 'messageActionChatDeleteUser':
if(message.fromId == message.action.user_id) {
message.action._ = 'messageActionChatLeave';
5 years ago
}
break;
case 'messageActionChannelMigrateFrom':
migrateFrom = -message.action.chat_id;
migrateTo = -channelId;
5 years ago
break
case 'messageActionChatMigrateTo':
migrateFrom = -channelId;
migrateTo = -message.action.channel_id;
5 years ago
break;
case 'messageActionHistoryClear':
//apiMessage.deleted = true;
message.clear_history = true;
delete message.pFlags.out;
delete message.pFlags.unread;
5 years ago
break;
case 'messageActionPhoneCall':
delete message.fromId;
message.action.type =
(message.pFlags.out ? 'out_' : 'in_') +
5 years ago
(
message.action.reason._ == 'phoneCallDiscardReasonMissed' ||
message.action.reason._ == 'phoneCallDiscardReasonBusy'
5 years ago
? 'missed'
: 'ok'
);
break;
}
if(migrateFrom &&
migrateTo &&
!this.migratedFromTo[migrateFrom] &&
!this.migratedToFrom[migrateTo]) {
this.migrateChecks(migrateFrom, migrateTo);
}
}
if(message.grouped_id) {
if(!albums) {
albums = new Set();
}
albums.add(message.grouped_id);
} else {
message.rReply = this.getRichReplyText(message);
}
if(message.message && message.message.length && !message.totalEntities) {
const myEntities = RichTextProcessor.parseEntities(message.message);
const apiEntities = message.entities || [];
message.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !message.pending);
5 years ago
}
//if(!options.isEdited) {
this.messagesStorage[mid] = message;
(this.messagesStorageByPeerId[peerId] ?? (this.messagesStorageByPeerId[peerId] = {}))[mid] = message;
//}
});
if(albums) {
albums.forEach(groupId => {
const mids = this.groupedMessagesStorage[groupId];
for(const mid in mids) {
const message = this.messagesStorage[mid];
message.rReply = this.getRichReplyText(message);
}
});
}
}
public getRichReplyText(message: any, text: string = message.message) {
let messageText = '';
if(message.media) {
if(message.grouped_id) {
text = this.getAlbumText(message.grouped_id).message;
messageText += '<i>Album' + (text ? ', ' : '') + '</i>';
} else switch(message.media._) {
case 'messageMediaPhoto':
messageText += '<i>Photo' + (message.message ? ', ' : '') + '</i>';
break;
case 'messageMediaDice':
messageText += RichTextProcessor.wrapEmojiText(message.media.emoticon);
break;
case 'messageMediaGeo':
messageText += '<i>Geolocation</i>';
break;
case 'messageMediaPoll':
messageText += '<i>' + message.media.poll.rReply + '</i>';
break;
case 'messageMediaContact':
messageText += '<i>Contact</i>';
break;
case 'messageMediaDocument':
let document = message.media.document;
if(document.type == 'video') {
messageText = '<i>Video' + (message.message ? ', ' : '') + '</i>';
} else if(document.type == 'voice') {
messageText = '<i>Voice message</i>';
} else if(document.type == 'gif') {
messageText = '<i>GIF' + (message.message ? ', ' : '') + '</i>';
} else if(document.type == 'round') {
messageText = '<i>Video message' + (message.message ? ', ' : '') + '</i>';
} else if(document.type == 'sticker') {
messageText = (document.stickerEmoji || '') + '<i>Sticker</i>';
text = '';
} else {
messageText = '<i>' + document.file_name + (message.message ? ', ' : '') + '</i>';
}
break;
default:
//messageText += message.media._;
///////this.log.warn('Got unknown message.media type!', message);
break;
}
}
if(message.action) {
const str = this.wrapMessageActionText(message);
messageText = str ? '<i>' + str + '</i>' : '';
}
let messageWrapped = '';
if(text) {
// * 80 for chatlist in landscape orientation
text = limitSymbols(text, 75, 80);
const entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '));
messageWrapped = RichTextProcessor.wrapRichText(text, {
noLinebreaks: true,
entities,
noLinks: true,
noTextFormat: true
});
}
return messageText + messageWrapped;
5 years ago
}
public wrapMessageActionText(message: any) {
const action = message.action as MessageAction;
let str = '';
if((action as MessageAction.messageActionCustomAction).message) {
str = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true});
} else {
let _ = action._;
let suffix = '';
let l = '';
const getNameDivHTML = (peerId: number) => {
const title = appPeersManager.getPeerTitle(peerId);
return title ? `<div class="name inline" data-peer-id="${peerId}">${title}</div> ` : '';
};
switch(action._) {
case "messageActionPhoneCall": {
_ += '.' + (action as any).type;
const duration = action.duration;
if(duration) {
const d: string[] = [];
d.push(duration % 60 + ' s');
if(duration >= 60) d.push((duration / 60 | 0) + ' min');
//if(duration >= 3600) d.push((duration / 3600 | 0) + ' h');
suffix = ' (' + d.reverse().join(' ') + ')';
}
return langPack[_] + suffix;
}
case 'messageActionChatDeleteUser':
// @ts-ignore
case 'messageActionChatAddUsers':
case 'messageActionChatAddUser': {
const users: number[] = (action as MessageAction.messageActionChatAddUser).users || [(action as MessageAction.messageActionChatDeleteUser).user_id];
l = langPack[_].replace('{}', users.map((userId: number) => getNameDivHTML(userId)).join(', '));
break;
}
case 'messageActionBotAllowed': {
const anchorHTML = RichTextProcessor.wrapRichText(action.domain, {
entities: [{
_: 'messageEntityUrl',
length: action.domain.length,
offset: 0
}]
});
l = langPack[_].replace('{}', anchorHTML);
break;
}
default:
str = langPack[_] || `[${action._}]`;
break;
}
if(!l) {
l = langPack[_];
if(!l) {
l = '[' + _ + ']';
}
}
str = l[0].toUpperCase() == l[0] ? l : getNameDivHTML(message.fromId) + l + (suffix ? ' ' : '');
}
//this.log('message action:', action);
return str;
}
public editPeerFolders(peerIds: number[], folderId: number) {
apiManager.invokeApi('folders.editPeerFolders', {
folder_peers: peerIds.map(peerId => {
return {
_: 'inputFolderPeer',
peer: appPeersManager.getInputPeerById(peerId),
folder_id: folderId
};
})
}).then(updates => {
this.log('editPeerFolders updates:', updates);
apiUpdatesManager.processUpdateMessage(updates); // WARNING! возможно тут нужно добавлять channelId, и вызывать апдейт для каждого канала отдельно
});
}
public toggleDialogPin(peerId: number, filterId?: number) {
if(filterId > 1) {
this.filtersStorage.toggleDialogPin(peerId, filterId);
return;
}
const dialog = this.getDialogByPeerId(peerId)[0];
if(!dialog) return Promise.reject();
const pinned = dialog.pFlags?.pinned ? undefined : true;
return apiManager.invokeApi('messages.toggleDialogPin', {
peer: appPeersManager.getInputDialogPeerById(peerId),
pinned
}).then(bool => {
if(bool) {
const pFlags: Update.updateDialogPinned['pFlags'] = pinned ? {pinned} : {};
this.handleUpdate({
_: 'updateDialogPinned',
peer: appPeersManager.getDialogPeer(peerId),
folder_id: filterId,
pFlags
});
}
});
}
public markDialogUnread(peerId: number, read?: boolean) {
const dialog = this.getDialogByPeerId(peerId)[0];
if(!dialog) return Promise.reject();
const unread = read || dialog.pFlags?.unread_mark ? undefined : true;
return apiManager.invokeApi('messages.markDialogUnread', {
peer: appPeersManager.getInputDialogPeerById(peerId),
unread
}).then(bool => {
if(bool) {
const pFlags: Update.updateDialogUnreadMark['pFlags'] = unread ? {unread} : {};
this.handleUpdate({
_: 'updateDialogUnreadMark',
peer: appPeersManager.getDialogPeer(peerId),
pFlags
});
}
});
}
public migrateChecks(migrateFrom: number, migrateTo: number) {
5 years ago
if(!this.migratedFromTo[migrateFrom] &&
!this.migratedToFrom[migrateTo] &&
appChatsManager.hasChat(-migrateTo)) {
const fromChat = appChatsManager.getChat(-migrateFrom);
5 years ago
if(fromChat &&
fromChat.migrated_to &&
fromChat.migrated_to.channel_id == -migrateTo) {
this.migratedFromTo[migrateFrom] = migrateTo;
this.migratedToFrom[migrateTo] = migrateFrom;
setTimeout(() => {
const dropped = this.dialogsStorage.dropDialog(migrateFrom);
if(dropped.length) {
rootScope.broadcast('dialog_drop', {peerId: migrateFrom, dialog: dropped[0]});
5 years ago
}
rootScope.broadcast('dialog_migrate', {migrateFrom, migrateTo});
5 years ago
}, 100);
}
}
}
public canMessageBeEdited(message: any, kind: 'text' | 'poll') {
const goodMedias = [
5 years ago
'messageMediaPhoto',
'messageMediaDocument',
'messageMediaWebPage',
'messageMediaPending'
];
if(kind == 'poll') {
goodMedias.push('messageMediaPoll');
}
5 years ago
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)) {
5 years ago
return false;
}
5 years ago
if(message.media &&
message.media._ == 'messageMediaDocument' &&
message.media.document.sticker) {
return false;
}
return true;
}
public canEditMessage(messageId: number, kind: 'text' | 'poll' = 'text') {
if(!this.messagesStorage[messageId]) {
return false;
}
const message = this.messagesStorage[messageId];
if(!message || !this.canMessageBeEdited(message, kind)) {
return false;
}
// * second rule for saved messages, because there is no 'out' flag
if(message.pFlags.out || this.getMessagePeer(message) == appUsersManager.getSelf().id) {
return true;
}
if((message.date < tsNow(true) - (2 * 86400) && message.media?._ != 'messageMediaPoll') || !message.pFlags.out) {
return false;
}
return true;
}
public canDeleteMessage(messageId: number) {
const message = this.messagesStorage[messageId];
return message && (
message.peerId > 0
|| message.fromId == rootScope.myId
|| appChatsManager.getChat(message.peerId)._ == 'chat'
|| appChatsManager.hasRights(message.peerId, 'deleteRevoke')
);
}
public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) {
// * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages
// ! fix 'dialogFolder', maybe there is better way to do it, this only can happen by 'messages.getPinnedDialogs' by folder_id: 0
dialogsResult.dialogs.forEachReverse((dialog, idx) => {
if(dialog._ == 'dialogFolder') {
dialogsResult.dialogs.splice(idx, 1);
}
});
5 years ago
appUsersManager.saveApiUsers(dialogsResult.users);
appChatsManager.saveApiChats(dialogsResult.chats);
this.saveMessages(dialogsResult.messages);
//this.log('applyConversation', dialogsResult);
5 years ago
const updatedDialogs: {[peerId: number]: Dialog} = {};
(dialogsResult.dialogs as Dialog[]).forEach((dialog) => {
const peerId = appPeersManager.getPeerId(dialog.peer);
let topMessage = dialog.top_message;
const topPendingMessage = this.pendingTopMsgs[peerId];
if(topPendingMessage) {
if(!topMessage
|| (this.getMessage(topPendingMessage) as MyMessage).date > (this.getMessage(topMessage) as MyMessage).date) {
dialog.top_message = topMessage = topPendingMessage;
5 years ago
}
}
/* const d = Object.assign({}, dialog);
if(peerId == 239602833) {
this.log.error('applyConversation lun', dialog, d);
} */
5 years ago
if(topMessage) {
//const wasDialogBefore = this.getDialogByPeerID(peerId)[0];
// here need to just replace, not FULL replace dialog! WARNING
/* if(wasDialogBefore?.pFlags?.pinned && !dialog?.pFlags?.pinned) {
this.log.error('here need to just replace, not FULL replace dialog! WARNING', wasDialogBefore, dialog);
if(!dialog.pFlags) dialog.pFlags = {};
dialog.pFlags.pinned = true;
} */
5 years ago
this.saveConversation(dialog);
/* if(wasDialogBefore) {
rootScope.$broadcast('dialog_top', dialog);
} else { */
//if(wasDialogBefore?.top_message != topMessage) {
updatedDialogs[peerId] = dialog;
//}
//}
5 years ago
} else {
const dropped = this.dialogsStorage.dropDialog(peerId);
if(dropped.length) {
rootScope.broadcast('dialog_drop', {peerId: peerId, dialog: dropped[0]});
5 years ago
}
}
if(this.newUpdatesAfterReloadToHandle[peerId] !== undefined) {
for(const i in this.newUpdatesAfterReloadToHandle[peerId]) {
const update = this.newUpdatesAfterReloadToHandle[peerId][i];
5 years ago
this.handleUpdate(update);
}
delete this.newUpdatesAfterReloadToHandle[peerId];
5 years ago
}
});
if(Object.keys(updatedDialogs).length) {
rootScope.broadcast('dialogs_multiupdate', updatedDialogs);
5 years ago
}
}
public saveConversation(dialog: Dialog, folderId = 0) {
const peerId = appPeersManager.getPeerId(dialog.peer);
if(!peerId) {
5 years ago
return false;
}
if(dialog._ != 'dialog'/* || peerId == 239602833 */) {
console.error('saveConversation not regular dialog', dialog, Object.assign({}, dialog));
}
const channelId = appPeersManager.isChannel(peerId) ? -peerId : 0;
const peerText = appPeersManager.getPeerSearchText(peerId);
searchIndexManager.indexObject(peerId, peerText, this.dialogsIndex);
5 years ago
let mid: number, message;
5 years ago
if(dialog.top_message) {
mid = appMessagesIdsManager.getFullMessageId(dialog.top_message, channelId);
message = this.getMessage(mid);
5 years ago
} else {
mid = this.tempId--;
message = {
5 years ago
_: 'message',
id: mid,
mid: mid,
from_id: appPeersManager.getOutputPeer(appUsersManager.getSelf().id),
peer_id: appPeersManager.getOutputPeer(peerId),
5 years ago
deleted: true,
pFlags: {out: true},
5 years ago
date: 0,
message: ''
};
5 years ago
this.saveMessages([message]);
}
if(!message?.pFlags) {
this.log.error('saveConversation no message:', dialog, message);
}
if(!channelId && peerId < 0) {
const chat = appChatsManager.getChat(-peerId);
5 years ago
if(chat && chat.migrated_to && chat.pFlags.deactivated) {
const migratedToPeer = appPeersManager.getPeerId(chat.migrated_to);
this.migratedFromTo[peerId] = migratedToPeer;
this.migratedToFrom[migratedToPeer] = peerId;
5 years ago
return;
}
}
dialog.top_message = mid;
dialog.read_inbox_max_id = appMessagesIdsManager.getFullMessageId(dialog.read_inbox_max_id, channelId);
dialog.read_outbox_max_id = appMessagesIdsManager.getFullMessageId(dialog.read_outbox_max_id, channelId);
5 years ago
if(!dialog.hasOwnProperty('folder_id')) {
if(dialog._ == 'dialog') {
// ! СЛОЖНО ! СМОТРИ В getTopMessages
const wasDialogBefore = this.getDialogByPeerId(peerId)[0];
dialog.folder_id = wasDialogBefore ? wasDialogBefore.folder_id : folderId;
}/* else if(dialog._ == 'dialogFolder') {
dialog.folder_id = dialog.folder.id;
} */
}
dialog.peerId = peerId;
5 years ago
this.dialogsStorage.generateIndexForDialog(dialog);
this.dialogsStorage.pushDialog(dialog, message.date);
5 years ago
// Because we saved message without dialog present
if(message.mid > 0) {
if(message.mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true;
else delete message.pFlags.unread;
5 years ago
}
let historyStorage = this.historiesStorage[peerId];
if(historyStorage === undefined/* && !message.deleted */) { // warning
historyStorage = this.historiesStorage[peerId] = {count: null, history: [], pending: []};
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid);
/* if(mid < 0 && message.pFlags.unread) {
5 years ago
dialog.unread_count++;
} */
5 years ago
if(this.mergeReplyKeyboard(historyStorage, message)) {
rootScope.broadcast('history_reply_markup', {peerId});
5 years ago
}
} else if(!historyStorage.history.length && !historyStorage.pending.length) {
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid);
5 years ago
}
if(channelId && dialog.pts) {
apiUpdatesManager.addChannelState(channelId, dialog.pts);
5 years ago
}
//if(this.filtersStorage.inited) {
//this.filtersStorage.processDialog(dialog);
//}
5 years ago
}
public mergeReplyKeyboard(historyStorage: HistoryStorage, message: any) {
// this.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup)
5 years ago
if(!message.reply_markup &&
!message.pFlags?.out &&
5 years ago
!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) {
5 years ago
return false;
}
if(historyStorage.maxOutId &&
message.mid < historyStorage.maxOutId &&
5 years ago
messageReplyMarkup.pFlags.single_use) {
messageReplyMarkup.pFlags.hidden = true;
}
messageReplyMarkup = Object.assign({
mid: message.mid
}, messageReplyMarkup);
if(messageReplyMarkup._ != 'replyKeyboardHide') {
messageReplyMarkup.fromId = appPeersManager.getPeerId(message.from_id);
5 years ago
}
historyStorage.reply_markup = messageReplyMarkup;
// this.log('set', historyStorage.reply_markup)
5 years ago
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;
// this.log('set', historyStorage.reply_markup)
5 years ago
return true;
}
} else if(!historyStorage.maxOutId ||
message.mid > historyStorage.maxOutId) {
historyStorage.maxOutId = message.mid;
5 years ago
}
}
if(message.action &&
message.action._ == 'messageActionChatDeleteUser' &&
(lastReplyMarkup
? message.action.user_id == lastReplyMarkup.fromId
5 years ago
: appUsersManager.isBot(message.action.user_id)
)
) {
historyStorage.reply_markup = {
_: 'replyKeyboardHide',
mid: message.mid,
pFlags: {}
};
// this.log('set', historyStorage.reply_markup)
5 years ago
return true;
}
return false;
}
public getSearchStorage(peerId: number, inputFilter: MyInputMessagesFilter) {
if(!this.searchesStorage[peerId]) this.searchesStorage[peerId] = {};
if(!this.searchesStorage[peerId][inputFilter]) this.searchesStorage[peerId][inputFilter] = {history: []};
return this.searchesStorage[peerId][inputFilter];
}
public getSearchCounters(peerId: number, filters: MessagesFilter[]) {
return apiManager.invokeApi('messages.getSearchCounters', {
peer: appPeersManager.getInputPeerById(peerId),
filters
});
}
public getSearch(peerId = 0, query: string = '', inputFilter: {
_: MyInputMessagesFilter
} = {_: 'inputMessagesFilterEmpty'}, maxId: number, limit = 20, offsetRate = 0, backLimit = 0): Promise<{
count: number,
next_rate: number,
offset_id_offset: number,
history: number[]
}> {
const foundMsgs: number[] = [];
5 years ago
//this.log('search', maxId);
if(backLimit) {
limit += backLimit;
5 years ago
}
//const beta = inputFilter._ == 'inputMessagesFilterPinned' && !backLimit;
const beta = false;
5 years ago
let storage: {
count?: number;
history: number[];
};
// * костыль для limit 1, если нужно и получить сообщение, и узнать количество сообщений
if(peerId && !backLimit && !maxId && !query && limit !== 1/* && inputFilter._ !== 'inputMessagesFilterPinned' */) {
storage = beta ?
this.getSearchStorage(peerId, inputFilter._) :
this.historiesStorage[peerId];
let filtering = true;
5 years ago
const history = maxId ? storage.history.slice(storage.history.indexOf(maxId) + 1) : storage.history;
if(storage !== undefined && history.length) {
const neededContents: {
[messageMediaType: string]: boolean
5 years ago
} = {},
neededDocTypes: string[] = [],
excludeDocTypes: string[] = []/* ,
neededFlags: string[] = [] */;
5 years ago
switch(inputFilter._) {
case 'inputMessagesFilterPhotos':
neededContents['messageMediaPhoto'] = true;
break;
case 'inputMessagesFilterPhotoVideo':
neededContents['messageMediaPhoto'] = true;
neededContents['messageMediaDocument'] = true;
neededDocTypes.push('video');
5 years ago
break;
case 'inputMessagesFilterVideo':
neededContents['messageMediaDocument'] = true;
neededDocTypes.push('video');
5 years ago
break;
case 'inputMessagesFilterDocument':
neededContents['messageMediaDocument'] = true;
excludeDocTypes.push('video');
5 years ago
break;
case 'inputMessagesFilterVoice':
neededContents['messageMediaDocument'] = true;
neededDocTypes.push('voice');
break;
case 'inputMessagesFilterRoundVoice':
neededContents['messageMediaDocument'] = true;
neededDocTypes.push('round', 'voice');
5 years ago
break;
case 'inputMessagesFilterRoundVideo':
neededContents['messageMediaDocument'] = true;
neededDocTypes.push('round');
5 years ago
break;
case 'inputMessagesFilterMusic':
neededContents['messageMediaDocument'] = true;
neededDocTypes.push('audio');
5 years ago
break;
case 'inputMessagesFilterUrl':
neededContents['url'] = true;
break;
case 'inputMessagesFilterChatPhotos':
neededContents['avatar'] = true;
break;
/* case 'inputMessagesFilterPinned':
neededFlags.push('pinned');
break; */
/* case 'inputMessagesFilterMyMentions':
5 years ago
neededContents['mentioned'] = true;
break; */
5 years ago
default:
filtering = false;
break;
/* return Promise.resolve({
5 years ago
count: 0,
next_rate: 0,
5 years ago
history: [] as number[]
}); */
5 years ago
}
if(filtering) {
for(let i = 0, length = history.length; i < length; i++) {
const message = this.messagesStorage[history[i]];
//|| (neededContents['mentioned'] && message.totalEntities.find((e: any) => e._ == 'messageEntityMention'));
let found = false;
if(message.media && neededContents[message.media._] && !message.fwd_from) {
if(message.media._ == 'messageMediaDocument') {
if((neededDocTypes.length && !neededDocTypes.includes(message.media.document.type))
|| excludeDocTypes.includes(message.media.document.type)) {
continue;
}
}
found = true;
} else if(neededContents['url'] && message.message) {
const goodEntities = ['messageEntityTextUrl', 'messageEntityUrl'];
if((message.totalEntities as MessageEntity[]).find(e => goodEntities.includes(e._)) || RichTextProcessor.matchUrl(message.message)) {
found = true;
}
} else if(neededContents['avatar'] && message.action && ['messageActionChannelEditPhoto', 'messageActionChatEditPhoto'].includes(message.action._)) {
found = true;
}/* else if(neededFlags.find(flag => message.pFlags[flag])) {
found = true;
} */
if(found) {
foundMsgs.push(message.mid);
if(foundMsgs.length >= limit) {
break;
}
5 years ago
}
}
}
}
}
if(foundMsgs.length) {
if(foundMsgs.length < limit && (beta ? storage.count !== storage.history.length : true)) {
maxId = foundMsgs[foundMsgs.length - 1];
limit = limit - foundMsgs.length;
} else {
return Promise.resolve({
count: beta ? storage.count : 0,
next_rate: 0,
offset_id_offset: 0,
history: foundMsgs
});
5 years ago
}
} else if(beta && storage?.count) {
return Promise.resolve({
count: storage.count,
next_rate: 0,
offset_id_offset: 0,
history: []
});
5 years ago
}
let apiPromise: Promise<any>;
if(peerId || !query) {
apiPromise = apiManager.invokeApi('messages.search', {
peer: appPeersManager.getInputPeerById(peerId),
5 years ago
q: query || '',
filter: inputFilter as any as MessagesFilter,
5 years ago
min_date: 0,
max_date: 0,
limit,
offset_id: appMessagesIdsManager.getMessageLocalId(maxId) || 0,
add_offset: backLimit ? -backLimit : 0,
5 years ago
max_id: 0,
min_id: 0,
hash: 0
5 years ago
}, {
//timeout: APITIMEOUT,
5 years ago
noErrorBox: true
});
} else {
var offsetDate = 0;
var offsetPeerId = 0;
var offsetId = 0;
var offsetMessage = maxId && this.getMessage(maxId);
5 years ago
if(offsetMessage && offsetMessage.date) {
offsetDate = offsetMessage.date + serverTimeManager.serverTimeOffset;
offsetId = offsetMessage.id;
offsetPeerId = this.getMessagePeer(offsetMessage);
5 years ago
}
apiPromise = apiManager.invokeApi('messages.searchGlobal', {
5 years ago
q: query,
filter: inputFilter as any as MessagesFilter,
min_date: 0,
max_date: 0,
offset_rate: offsetRate,
offset_peer: appPeersManager.getInputPeerById(offsetPeerId),
offset_id: appMessagesIdsManager.getMessageLocalId(offsetId),
limit
5 years ago
}, {
//timeout: APITIMEOUT,
5 years ago
noErrorBox: true
});
}
return apiPromise.then((searchResult: any) => {
appUsersManager.saveApiUsers(searchResult.users);
appChatsManager.saveApiChats(searchResult.chats);
this.saveMessages(searchResult.messages);
if(beta && storage && (!maxId || storage.history[storage.history.length - 1] == maxId)) {
const storage = this.getSearchStorage(peerId, inputFilter._);
const add = (searchResult.messages.map((m: any) => m.mid) as number[]).filter(mid => storage.history.indexOf(mid) === -1);
storage.history.push(...add);
storage.history.sort((a, b) => b - a);
storage.count = searchResult.count;
}
this.log('messages.search result:', inputFilter, searchResult);
const foundCount: number = searchResult.count || (foundMsgs.length + searchResult.messages.length);
5 years ago
searchResult.messages.forEach((message: any) => {
const peerId = this.getMessagePeer(message);
if(peerId < 0) {
const chat = appChatsManager.getChat(-peerId);
5 years ago
if(chat.migrated_to) {
this.migrateChecks(peerId, -chat.migrated_to.channel_id);
5 years ago
}
}
5 years ago
foundMsgs.push(message.mid);
});
return {
count: foundCount,
offset_id_offset: searchResult.offset_id_offset,
next_rate: searchResult.next_rate,
5 years ago
history: foundMsgs
};
5 years ago
});
}
handleNewMessages = () => {
5 years ago
clearTimeout(this.newMessagesHandlePromise);
this.newMessagesHandlePromise = 0;
rootScope.broadcast('history_multiappend', this.newMessagesToHandle);
5 years ago
this.newMessagesToHandle = {};
};
5 years ago
handleNewDialogs = () => {
5 years ago
clearTimeout(this.newDialogsHandlePromise);
this.newDialogsHandlePromise = 0;
let newMaxSeenId = 0;
for(const peerId in this.newDialogsToHandle) {
const dialog = this.newDialogsToHandle[peerId];
if('reload' in dialog) {
this.reloadConversation(+peerId);
delete this.newDialogsToHandle[peerId];
5 years ago
} else {
this.dialogsStorage.pushDialog(dialog);
if(!appPeersManager.isChannel(+peerId)) {
newMaxSeenId = Math.max(newMaxSeenId, dialog.top_message || 0);
5 years ago
}
}
}
//this.log('after order:', this.dialogsStorage[0].map(d => d.peerId));
5 years ago
if(newMaxSeenId != 0) {
this.incrementMaxSeenId(newMaxSeenId);
5 years ago
}
rootScope.broadcast('dialogs_multiupdate', this.newDialogsToHandle as any);
5 years ago
this.newDialogsToHandle = {};
};
5 years ago
public scheduleHandleNewDialogs() {
if(!this.newDialogsHandlePromise) {
this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs, 0);
}
}
public deleteMessages(messageIds: number[], revoke: boolean) {
const splitted = appMessagesIdsManager.splitMessageIdsByChannels(messageIds);
const promises: Promise<any>[] = [];
for(const channelIdStr in splitted.msgIds) {
const channelId = +channelIdStr;
let msgIds = splitted.msgIds[channelId];
let promise: Promise<any>;
if(channelId > 0) {
const channel = appChatsManager.getChat(channelId);
if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) {
const goodMsgIds: number[] = [];
if (channel.pFlags.editor || channel.pFlags.megagroup) {
msgIds.forEach((msgId, i) => {
const message = this.getMessage(splitted.mids[channelId][i]);
if(message.pFlags.out) {
goodMsgIds.push(msgId);
}
});
}
if(!goodMsgIds.length) {
return;
}
msgIds = goodMsgIds;
}
promise = apiManager.invokeApi('channels.deleteMessages', {
channel: appChatsManager.getChannelInput(channelId),
id: msgIds
}).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteChannelMessages',
channel_id: channelId,
messages: msgIds,
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
} else {
promise = apiManager.invokeApi('messages.deleteMessages', {
revoke: revoke || undefined,
id: msgIds
}).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: msgIds,
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
}
promises.push(promise);
}
return Promise.all(promises);
}
public readHistory(peerId: number, maxId = 0) {
5 years ago
// console.trace('start read')
const isChannel = appPeersManager.isChannel(peerId);
const historyStorage = this.historiesStorage[peerId];
const foundDialog = this.getDialogByPeerId(peerId)[0];
5 years ago
if(!foundDialog || !foundDialog.unread_count) {
if(!historyStorage || !historyStorage.history.length) {
return Promise.resolve(false);
5 years ago
}
let foundUnread = !!historyStorage.history.find(messageId => {
const message = this.messagesStorage[messageId];
return message && !message.pFlags.out && message.pFlags.unread;
});
5 years ago
if(!foundUnread) {
return Promise.resolve(false);
5 years ago
}
}
if(isChannel) {
maxId = appMessagesIdsManager.getMessageLocalId(maxId);
}
if(!historyStorage.readMaxId || maxId > historyStorage.readMaxId) {
historyStorage.readMaxId = maxId;
}
5 years ago
if(historyStorage.readPromise) {
return historyStorage.readPromise;
5 years ago
}
let apiPromise: Promise<boolean>;
5 years ago
if(isChannel) {
apiPromise = apiManager.invokeApi('channels.readHistory', {
channel: appChatsManager.getChannelInput(-peerId),
max_id: maxId
}).then((res) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateReadChannelInbox',
max_id: maxId,
channel_id: -peerId
}
});
return res;
5 years ago
});
} else {
apiPromise = apiManager.invokeApi('messages.readHistory', {
peer: appPeersManager.getInputPeerById(peerId),
max_id: maxId
}).then((affectedMessages) => {
5 years ago
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateReadHistoryInbox',
max_id: maxId,
peer: appPeersManager.getOutputPeer(peerId)
}
});
return true;
});
}
apiPromise.finally(() => {
5 years ago
delete historyStorage.readPromise;
if(historyStorage.readMaxId > maxId) {
this.readHistory(peerId, historyStorage.readMaxId);
} else {
delete historyStorage.readMaxId;
}
});
5 years ago
return historyStorage.readPromise = apiPromise;
5 years ago
}
public readMessages(messageIds: number[]) {
const splitted = appMessagesIdsManager.splitMessageIdsByChannels(messageIds);
Object.keys(splitted.msgIds).forEach((channelId: number | string) => {
channelId = +channelId;
const msgIds = splitted.msgIds[channelId];
5 years ago
if(channelId > 0) {
apiManager.invokeApi('channels.readMessageContents', {
channel: appChatsManager.getChannelInput(channelId),
id: msgIds
5 years ago
}).then(() => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateChannelReadMessagesContents',
channel_id: channelId,
messages: msgIds
5 years ago
}
});
});
} else {
apiManager.invokeApi('messages.readMessageContents', {
id: msgIds
}).then((affectedMessages) => {
5 years ago
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateReadMessagesContents',
messages: msgIds,
5 years ago
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
});
}
});
}
public handleUpdate(update: Update) {
this.log.debug('handleUpdate', update._);
5 years ago
switch(update._) {
case 'updateMessageID': {
const randomId = update.random_id;
const pendingData = this.pendingByRandomId[randomId];
//this.log('AMM updateMessageID:', update, pendingData);
5 years ago
if(pendingData) {
const peerId: number = pendingData[0];
const tempId = pendingData[1];
const channelId = appPeersManager.isChannel(peerId) ? -peerId : 0;
const mid = appMessagesIdsManager.getFullMessageId(update.id, channelId);
const message = this.messagesStorage[mid];
5 years ago
if(message) {
const historyStorage = this.historiesStorage[peerId];
const pos = historyStorage.pending.indexOf(tempId);
5 years ago
if(pos != -1) {
historyStorage.pending.splice(pos, 1);
}
delete this.messagesStorage[tempId];
5 years ago
this.finalizePendingMessageCallbacks(tempId, mid);
5 years ago
} else {
this.pendingByMessageId[mid] = randomId;
5 years ago
}
}
break;
}
case 'updateNewMessage':
case 'updateNewChannelMessage': {
const message = update.message as MyMessage;
const peerId = this.getMessagePeer(message);
const foundDialog = this.getDialogByPeerId(peerId);
5 years ago
if(!foundDialog.length) {
this.newDialogsToHandle[peerId] = {reload: true};
this.scheduleHandleNewDialogs();
if(this.newUpdatesAfterReloadToHandle[peerId] === undefined) {
this.newUpdatesAfterReloadToHandle[peerId] = [];
5 years ago
}
this.newUpdatesAfterReloadToHandle[peerId].push(update);
5 years ago
break;
}
if(update._ == 'updateNewChannelMessage') {
const chat = appChatsManager.getChat(-peerId);
5 years ago
if(chat.pFlags && (chat.pFlags.left || chat.pFlags.kicked)) {
break;
}
}
this.saveMessages([message]);
// this.log.warn(dT(), 'message unread', message.mid, message.pFlags.unread)
5 years ago
let historyStorage = this.historiesStorage[peerId];
5 years ago
if(historyStorage === undefined) {
historyStorage = this.historiesStorage[peerId] = {
5 years ago
count: null,
history: [],
pending: []
};
}
const history = message.mid > 0 ? historyStorage.history : historyStorage.pending;
5 years ago
if(history.indexOf(message.mid) != -1) {
return false;
}
const topMsgId = history[0];
5 years ago
history.unshift(message.mid);
if(message.mid > 0 && message.mid < topMsgId) {
history.sort((a, b) => {
5 years ago
return b - a;
});
}
if(message.mid > 0 &&
historyStorage.count !== null) {
historyStorage.count++;
}
if(this.mergeReplyKeyboard(historyStorage, message)) {
rootScope.broadcast('history_reply_markup', {peerId});
5 years ago
}
if(!message.pFlags.out && message.from_id) {
appUsersManager.forceUserOnline(appPeersManager.getPeerId(message.from_id), message.date);
5 years ago
}
const randomId = this.pendingByMessageId[message.mid];
let pendingMessage: any;
5 years ago
if(randomId) {
if(pendingMessage = this.finalizePendingMessage(randomId, message)) {
rootScope.broadcast('history_update', {peerId, mid: message.mid});
5 years ago
}
delete this.pendingByMessageId[message.mid];
5 years ago
}
if(!pendingMessage) {
if(this.newMessagesToHandle[peerId] === undefined) {
this.newMessagesToHandle[peerId] = [];
5 years ago
}
this.newMessagesToHandle[peerId].push(message.mid);
5 years ago
if(!this.newMessagesHandlePromise) {
this.newMessagesHandlePromise = window.setTimeout(this.handleNewMessages, 0);
5 years ago
}
}
const inboxUnread = !message.pFlags.out && message.pFlags.unread;
const dialog = foundDialog[0];
5 years ago
dialog.top_message = message.mid;
if(inboxUnread) {
dialog.unread_count++;
}
if(!dialog.pFlags.pinned || !dialog.index) {
dialog.index = this.dialogsStorage.generateDialogIndex(message.date);
5 years ago
}
this.newDialogsToHandle[peerId] = dialog;
this.scheduleHandleNewDialogs();
break;
}
case 'updateDialogUnreadMark': {
this.log('updateDialogUnreadMark', update);
const peerId = appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer);
const foundDialog = this.getDialogByPeerId(peerId);
if(!foundDialog.length) {
this.newDialogsToHandle[peerId] = {reload: true};
this.scheduleHandleNewDialogs();
} else {
const dialog = foundDialog[0];
if(!update.pFlags.unread) {
delete dialog.pFlags.unread_mark;
} else {
dialog.pFlags.unread_mark = true;
}
rootScope.broadcast('dialogs_multiupdate', {peerId: dialog});
5 years ago
}
break;
}
case 'updateFolderPeers': { // only 0 and 1 folders
this.log('updateFolderPeers', update);
const peers = update.folder_peers;
this.scheduleHandleNewDialogs();
peers.forEach((folderPeer) => {
const {folder_id, peer} = folderPeer;
const peerId = appPeersManager.getPeerId(peer);
const dropped = this.dialogsStorage.dropDialog(peerId);
if(!dropped.length) {
this.newDialogsToHandle[peerId] = {reload: true};
} else {
const dialog = dropped[0];
this.newDialogsToHandle[peerId] = dialog;
if(dialog.pFlags?.pinned) {
delete dialog.pFlags.pinned;
this.dialogsStorage.pinnedOrders[folder_id].findAndSplice(p => p == dialog.peerId);
}
dialog.folder_id = folder_id;
this.dialogsStorage.generateIndexForDialog(dialog);
this.dialogsStorage.pushDialog(dialog); // need for simultaneously updatePinnedDialogs
}
});
break;
}
case 'updateDialogPinned': {
const folderId = update.folder_id ?? 0;
this.log('updateDialogPinned', update);
const peerId = appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer);
const foundDialog = this.getDialogByPeerId(peerId);
// этот код внизу никогда не сработает, в папках за пиннед отвечает updateDialogFilter
/* if(update.folder_id > 1) {
const filter = this.filtersStorage.filters[update.folder_id];
if(update.pFlags.pinned) {
filter.pinned_peers.unshift(peerId);
} else {
filter.pinned_peers.findAndSplice(p => p == peerId);
}
} */
this.scheduleHandleNewDialogs();
if(!foundDialog.length) {
this.newDialogsToHandle[peerId] = {reload: true};
} else {
const dialog = foundDialog[0];
this.newDialogsToHandle[peerId] = dialog;
if(!update.pFlags.pinned) {
delete dialog.pFlags.pinned;
this.dialogsStorage.pinnedOrders[folderId].findAndSplice(p => p == dialog.peerId);
} else { // means set
dialog.pFlags.pinned = true;
}
this.dialogsStorage.generateIndexForDialog(dialog);
}
5 years ago
break;
}
case 'updatePinnedDialogs': {
const folderId = update.folder_id ?? 0;
const handleOrder = (order: number[]) => {
this.dialogsStorage.pinnedOrders[folderId].length = 0;
let willHandle = false;
order.reverse(); // index must be higher
order.forEach((peerId) => {
newPinned[peerId] = true;
const foundDialog = this.getDialogByPeerId(peerId);
if(!foundDialog.length) {
this.newDialogsToHandle[peerId] = {reload: true};
willHandle = true;
return;
}
const dialog = foundDialog[0];
dialog.pFlags.pinned = true;
this.dialogsStorage.generateIndexForDialog(dialog);
this.newDialogsToHandle[peerId] = dialog;
willHandle = true;
});
this.dialogsStorage.getFolder(folderId).forEach(dialog => {
const peerId = dialog.peerId;
if(dialog.pFlags.pinned && !newPinned[peerId]) {
this.newDialogsToHandle[peerId] = {reload: true};
willHandle = true;
}
});
if(willHandle) {
this.scheduleHandleNewDialogs();
}
};
this.log('updatePinnedDialogs', update);
const newPinned: {[peerId: number]: true} = {};
5 years ago
if(!update.order) {
apiManager.invokeApi('messages.getPinnedDialogs', {
folder_id: folderId
}).then((dialogsResult) => {
// * for test reordering and rendering
// dialogsResult.dialogs.reverse();
5 years ago
this.applyConversations(dialogsResult);
handleOrder(dialogsResult.dialogs.map(d => d.peerId));
/* dialogsResult.dialogs.forEach((dialog) => {
newPinned[dialog.peerId] = true;
5 years ago
});
this.dialogsStorage.getFolder(folderId).forEach((dialog) => {
const peerId = dialog.peerId;
if(dialog.pFlags.pinned && !newPinned[peerId]) {
this.newDialogsToHandle[peerId] = {reload: true};
this.scheduleHandleNewDialogs();
5 years ago
}
}); */
5 years ago
});
5 years ago
break;
}
//this.log('before order:', this.dialogsStorage[0].map(d => d.peerId));
handleOrder(update.order.map(peer => appPeersManager.getPeerId((peer as DialogPeer.dialogPeer).peer)));
5 years ago
break;
}
5 years ago
case 'updateEditMessage':
case 'updateEditChannelMessage': {
const message = update.message as MyMessage;
const peerId = this.getMessagePeer(message);
const channelId = message.peer_id._ == 'peerChannel' ? -peerId : 0;
const mid = appMessagesIdsManager.getFullMessageId(message.id, channelId);
5 years ago
if(this.messagesStorage[mid] === undefined) {
break;
}
const oldMessage = this.messagesStorage[mid];
if(oldMessage.media?.webpage) {
appWebPagesManager.deleteWebPageFromPending(oldMessage.media.webpage, mid);
}
5 years ago
// console.trace(dT(), 'edit message', message)
this.saveMessages([message]);
5 years ago
const dialog = this.getDialogByPeerId(peerId)[0];
const isTopMessage = dialog && dialog.top_message == mid;
// @ts-ignore
if(message.clear_history) { // that's will never happen
5 years ago
if(isTopMessage) {
rootScope.broadcast('dialog_flush', {peerId: peerId});
5 years ago
}
} else {
rootScope.broadcast('message_edit', {
peerId,
mid,
justMedia: false
5 years ago
});
const groupId = (message as Message.message).grouped_id;
/* if(this.pinnedMessagesStorage[peerId]) {
let pinnedMid: number;
if(groupId) {
const mids = this.getMidsByAlbum(groupId);
pinnedMid = mids.find(mid => this.pinnedMessagesStorage[peerId] == mid);
} else if(this.pinnedMessagesStorage[peerId] == mid) {
pinnedMid = mid;
}
if(pinnedMid) {
rootScope.broadcast('peer_pinned_message', peerId);
}
} */
if(isTopMessage || groupId) {
const updatedDialogs: {[peerId: number]: Dialog} = {};
updatedDialogs[peerId] = dialog;
rootScope.broadcast('dialogs_multiupdate', updatedDialogs);
5 years ago
}
}
break;
}
case 'updateReadHistoryInbox':
case 'updateReadHistoryOutbox':
case 'updateReadChannelInbox':
case 'updateReadChannelOutbox': {
const channelId: number = (update as Update.updateReadChannelInbox).channel_id;
const maxId = appMessagesIdsManager.getFullMessageId(update.max_id, channelId);
const peerId = channelId ? -channelId : appPeersManager.getPeerId((update as Update.updateReadHistoryInbox).peer);
const isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox' ? true : undefined;
const foundDialog = this.getDialogByPeerId(peerId)[0];
const history = getObjectKeysAndSort(this.messagesStorageByPeerId[peerId] || {}, 'desc');
let newUnreadCount = 0;
let foundAffected = false;
5 years ago
//this.log.warn(dT(), 'read', peerId, isOut ? 'out' : 'in', maxId)
5 years ago
if(peerId > 0 && isOut) {
appUsersManager.forceUserOnline(peerId);
5 years ago
}
for(let i = 0, length = history.length; i < length; i++) {
const messageId = history[i];
if(messageId > maxId) {
5 years ago
continue;
}
const message = this.messagesStorage[messageId];
if(!message) {
continue;
}
5 years ago
if(message.pFlags.out != isOut) {
continue;
}
5 years ago
if(!message.pFlags.unread) {
break;
}
// this.log.warn('read', messageId, message.pFlags.unread, message)
5 years ago
if(message && message.pFlags.unread) {
delete message.pFlags.unread;
if(!foundAffected) {
foundAffected = true;
5 years ago
}
5 years ago
if(!message.pFlags.out) {
if(foundDialog) {
newUnreadCount = --foundDialog.unread_count;
5 years ago
}
//NotificationsManager.cancel('msg' + messageId); // warning
5 years ago
}
}
}
if(foundDialog) {
if(!isOut && newUnreadCount && foundDialog.top_message <= maxId) {
newUnreadCount = foundDialog.unread_count = 0;
5 years ago
}
5 years ago
foundDialog[isOut ? 'read_outbox_max_id' : 'read_inbox_max_id'] = maxId;
5 years ago
}
// need be commented for read out messages
//if(newUnreadCount != 0 || !isOut) { // fix 16.11.2019 (maybe not)
//////////this.log.warn(dT(), 'cnt', peerId, newUnreadCount, isOut, foundDialog, update, foundAffected);
rootScope.broadcast('dialog_unread', {peerId, count: newUnreadCount});
5 years ago
//}
if(foundAffected) {
rootScope.broadcast('messages_read');
5 years ago
}
break;
}
case 'updateChannelReadMessagesContents': {
const channelId: number = update.channel_id;
const newMessages: number[] = [];
update.messages.forEach((msgId: number) => {
newMessages.push(appMessagesIdsManager.getFullMessageId(msgId, channelId));
5 years ago
});
update.messages = newMessages;
}
case 'updateReadMessagesContents': {
const messages: number[] = update.messages;
for(const messageId of messages) {
const message = this.messagesStorage[messageId];
if(message) {
5 years ago
delete message.pFlags.media_unread;
}
}
rootScope.broadcast('messages_media_read', messages);
5 years ago
break;
}
case 'updateChannelAvailableMessages': {
const channelId: number = update.channel_id;
const messages: number[] = [];
const peerId: number = -channelId;
const history = (this.historiesStorage[peerId] || {}).history || [];
5 years ago
if(history.length) {
history.forEach((msgId: number) => {
5 years ago
if(!update.available_min_id ||
appMessagesIdsManager.getMessageLocalId(msgId) <= update.available_min_id) {
messages.push(msgId);
5 years ago
}
});
}
(update as any as Update.updateDeleteChannelMessages).messages = messages;
5 years ago
}
case 'updateDeleteMessages':
case 'updateDeleteChannelMessages': {
const historiesUpdated: {
[peerId: number]: {
count: number,
unread: number,
msgs: {[mid: number]: true},
albums?: {[groupId: string]: Set<number>},
}
} = {};
const channelId: number = (update as Update.updateDeleteChannelMessages).channel_id;
const messages = (update as any as Update.updateDeleteChannelMessages).messages;
for(const _messageId of messages) {
const mid = appMessagesIdsManager.getFullMessageId(_messageId, channelId);
const message: MyMessage = this.messagesStorage[mid];
5 years ago
if(message) {
const peerId = this.getMessagePeer(message);
const history = historiesUpdated[peerId] || (historiesUpdated[peerId] = {count: 0, unread: 0, msgs: {}});
5 years ago
if((message as Message.message).media) {
// @ts-ignore
const c = message.media.webpage || message.media;
const smth = c.photo || c.document;
if(smth?.file_reference) {
referenceDatabase.deleteContext(smth.file_reference, {type: 'message', messageId: mid});
}
// @ts-ignore
if(message.media.webpage) {
// @ts-ignore
appWebPagesManager.deleteWebPageFromPending(message.media.webpage, mid);
}
}
5 years ago
if(!message.pFlags.out && message.pFlags.unread) {
history.unread++;
}
history.count++;
history.msgs[mid] = true;
message.deleted = true;
delete this.messagesStorage[mid];
delete this.messagesStorageByPeerId[peerId][mid];
if(message._ != 'messageService' && message.grouped_id) {
const groupedStorage = this.groupedMessagesStorage[message.grouped_id];
if(groupedStorage) {
delete groupedStorage[mid];
if(!history.albums) history.albums = {};
(history.albums[message.grouped_id] || (history.albums[message.grouped_id] = new Set())).add(mid);
if(!Object.keys(groupedStorage).length) {
delete history.albums;
delete this.groupedMessagesStorage[message.grouped_id];
}
}
}
/* if(this.pinnedMessagesStorage[peerId] == mid) {
this.savePinnedMessage(peerId, 0);
} */
5 years ago
const peerMessagesToHandle = this.newMessagesToHandle[peerId];
5 years ago
if(peerMessagesToHandle && peerMessagesToHandle.length) {
const peerMessagesHandlePos = peerMessagesToHandle.indexOf(mid);
5 years ago
if(peerMessagesHandlePos != -1) {
peerMessagesToHandle.splice(peerMessagesHandlePos);
}
}
}
}
Object.keys(historiesUpdated).forEach(_peerId => {
const peerId = +_peerId;
const updatedData = historiesUpdated[peerId];
if(updatedData.albums) {
for(const groupId in updatedData.albums) {
rootScope.broadcast('album_edit', {peerId, groupId, deletedMids: [...updatedData.albums[groupId]]});
/* const mids = this.getMidsByAlbum(groupId);
if(mids.length) {
const mid = Math.max(...mids);
rootScope.$broadcast('message_edit', {peerId, mid, justMedia: false});
} */
}
}
const historyStorage = this.historiesStorage[peerId];
5 years ago
if(historyStorage !== undefined) {
const newHistory = historyStorage.history.filter(mid => !updatedData.msgs[mid]);
const newPending = historyStorage.pending.filter(mid => !updatedData.msgs[mid]);
5 years ago
historyStorage.history = newHistory;
if(updatedData.count &&
historyStorage.count !== null &&
historyStorage.count > 0) {
historyStorage.count -= updatedData.count;
5 years ago
if(historyStorage.count < 0) {
historyStorage.count = 0;
}
}
historyStorage.pending = newPending;
rootScope.broadcast('history_delete', {peerId, msgs: updatedData.msgs});
5 years ago
}
const foundDialog = this.getDialogByPeerId(peerId)[0];
5 years ago
if(foundDialog) {
if(updatedData.unread) {
foundDialog.unread_count -= updatedData.unread;
rootScope.broadcast('dialog_unread', {
peerId,
5 years ago
count: foundDialog.unread_count
});
}
if(updatedData.msgs[foundDialog.top_message]) {
this.reloadConversation(peerId);
5 years ago
}
}
});
5 years ago
break;
}
case 'updateChannel': {
const channelId: number = update.channel_id;
const peerId = -channelId;
const channel = appChatsManager.getChat(channelId);
5 years ago
const needDialog = channel._ == 'channel' && (!channel.pFlags.left && !channel.pFlags.kicked);
const foundDialog = this.getDialogByPeerId(peerId);
const hasDialog = foundDialog.length > 0;
5 years ago
const canViewHistory = channel._ == 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked);
const hasHistory = this.historiesStorage[peerId] !== undefined;
5 years ago
if(canViewHistory != hasHistory) {
delete this.historiesStorage[peerId];
rootScope.broadcast('history_forbidden', peerId);
5 years ago
}
5 years ago
if(hasDialog != needDialog) {
if(needDialog) {
this.reloadConversation(-channelId);
5 years ago
} else {
if(foundDialog[0]) {
this.dialogsStorage.dropDialog(peerId);
rootScope.broadcast('dialog_drop', {peerId: peerId, dialog: foundDialog[0]});
5 years ago
}
}
}
5 years ago
break;
}
// @ts-ignore
5 years ago
case 'updateChannelReload': {
// @ts-ignore
const channelId: number = update.channel_id;
const peerId = -channelId;
this.dialogsStorage.dropDialog(peerId);
delete this.historiesStorage[peerId];
this.reloadConversation(-channelId).then(() => {
rootScope.broadcast('history_reload', peerId);
5 years ago
});
5 years ago
break;
}
case 'updateChannelMessageViews': {
const views = update.views;
const mid = appMessagesIdsManager.getFullMessageId(update.id, update.channel_id);
const message = this.getMessage(mid);
5 years ago
if(message && message.views && message.views < views) {
message.views = views;
rootScope.broadcast('message_views', {mid, views});
5 years ago
}
break;
}
case 'updateServiceNotification': {
this.log('updateServiceNotification', update);
const fromId = 777000;
const peerId = fromId;
const messageId = this.tempId--;
const message: any = {
5 years ago
_: 'message',
id: messageId,
from_id: appPeersManager.getOutputPeer(fromId),
peer_id: appPeersManager.getOutputPeer(peerId),
5 years ago
pFlags: {unread: true},
date: (update.inbox_date || tsNow(true)) + serverTimeManager.serverTimeOffset,
5 years ago
message: update.message,
media: update.media,
entities: update.entities
};
if(!appUsersManager.hasUser(fromId)) {
5 years ago
appUsersManager.saveApiUsers([{
_: 'user',
id: fromId,
5 years ago
pFlags: {verified: true},
access_hash: 0,
first_name: 'Telegram',
phone: '42777'
}]);
}
this.saveMessages([message]);
if(update.inbox_date) {
this.pendingTopMsgs[peerId] = messageId;
5 years ago
this.handleUpdate({
_: 'updateNewMessage',
message: message
} as any);
5 years ago
}
5 years ago
break;
}
case 'updatePinnedMessages':
case 'updatePinnedChannelMessages': {
const channelId = update._ == 'updatePinnedChannelMessages' ? update.channel_id : undefined;
const peerId = channelId ? -channelId : appPeersManager.getPeerId((update as Update.updatePinnedMessages).peer);
/* const storage = this.getSearchStorage(peerId, 'inputMessagesFilterPinned');
if(storage.count !== storage.history.length) {
if(storage.count !== undefined) {
delete this.searchesStorage[peerId]['inputMessagesFilterPinned'];
}
rootScope.broadcast('peer_pinned_messages', peerId);
break;
} */
const messages = channelId ? update.messages.map(messageId => appMessagesIdsManager.getFullMessageId(messageId, channelId)) : update.messages;
const missingMessages = messages.filter(mid => !this.messagesStorage[mid]);
const getMissingPromise = missingMessages.length ? Promise.all(missingMessages.map(mid => this.wrapSingleMessage(mid))) : Promise.resolve();
getMissingPromise.finally(() => {
const werePinned = update.pFlags?.pinned;
if(werePinned) {
for(const mid of messages) {
//storage.history.push(mid);
const message = this.messagesStorage[mid];
message.pFlags.pinned = true;
}
/* if(this.pinnedMessages[peerId]?.maxId) {
const maxMid = Math.max(...messages);
this.pinnedMessages
} */
//storage.history.sort((a, b) => b - a);
} else {
for(const mid of messages) {
//storage.history.findAndSplice(_mid => _mid == mid);
const message = this.messagesStorage[mid];
delete message.pFlags.pinned;
}
}
delete this.pinnedMessages[peerId];
appStateManager.getState().then(state => {
delete state.hiddenPinnedMessages[peerId];
rootScope.broadcast('peer_pinned_messages', {peerId, mids: messages, pinned: werePinned});
});
});
break;
}
case 'updateNotifySettings': {
const {peer, notify_settings} = update;
const peerId = appPeersManager.getPeerId((peer as NotifyPeer.notifyPeer).peer);
const dialog = this.getDialogByPeerId(peerId)[0];
if(dialog) {
dialog.notify_settings = notify_settings;
rootScope.broadcast('dialog_notify_settings', peerId);
}
/////this.log('updateNotifySettings', peerId, notify_settings);
break;
}
}
}
public isPeerMuted(peerId: number) {
if(peerId == rootScope.myId) return false;
const dialog = this.getDialogByPeerId(peerId)[0];
let muted = false;
if(dialog && dialog.notify_settings && dialog.notify_settings.mute_until) {
muted = new Date(dialog.notify_settings.mute_until * 1000) > new Date();
}
return muted;
}
public mutePeer(peerId: number) {
let inputPeer = appPeersManager.getInputPeerById(peerId);
let inputNotifyPeer: InputNotifyPeer.inputNotifyPeer = {
_: 'inputNotifyPeer',
peer: inputPeer
};
let settings: InputPeerNotifySettings = {
_: 'inputPeerNotifySettings'
};
let dialog = appMessagesManager.getDialogByPeerId(peerId)[0];
let muted = true;
if(dialog && dialog.notify_settings) {
muted = dialog.notify_settings.mute_until > (Date.now() / 1000 | 0);
}
if(!muted) {
settings.mute_until = 2147483647;
5 years ago
}
apiManager.invokeApi('account.updateNotifySettings', {
peer: inputNotifyPeer,
settings: settings
}).then(bool => {
if(bool) {
this.handleUpdate({
_: 'updateNotifySettings',
peer: {
_: 'notifyPeer',
peer: appPeersManager.getOutputPeer(peerId)
},
notify_settings: { // ! WOW, IT WORKS !
...settings,
_: 'peerNotifySettings',
}
});
}
});
/* return apiManager.invokeApi('account.getNotifySettings', {
peer: inputNotifyPeer
}).then((settings: any) => {
settings.mute_until = 2000000000; // 2147483646
return apiManager.invokeApi('account.updateNotifySettings', {
peer: inputNotifyPeer,
settings: Object.assign(settings, {
_: 'inputPeerNotifySettings'
})
}).then(res => {
this.log('mute result:', res);
});
}); */
}
public canWriteToPeer(peerId: number) {
const isChannel = appPeersManager.isChannel(peerId);
const hasRights = isChannel && appChatsManager.hasRights(-peerId, 'send');
return (!isChannel || hasRights) && (peerId < 0 || appUsersManager.canSendToUser(peerId));
5 years ago
}
public finalizePendingMessage(randomId: number, finalMessage: any) {
const pendingData = this.pendingByRandomId[randomId];
// this.log('pdata', randomID, pendingData)
5 years ago
if(pendingData) {
const peerId = pendingData[0];
const tempId = pendingData[1];
const historyStorage = this.historiesStorage[peerId];
5 years ago
// this.log('pending', randomID, historyStorage.pending)
const pos = historyStorage.pending.indexOf(tempId);
5 years ago
if(pos != -1) {
historyStorage.pending.splice(pos, 1);
}
const message = this.messagesStorage[tempId];
if(message) {
5 years ago
delete message.pending;
delete message.error;
delete message.random_id;
delete message.send;
rootScope.broadcast('messages_pending');
5 years ago
}
delete this.messagesStorage[tempId];
5 years ago
this.finalizePendingMessageCallbacks(tempId, finalMessage.mid);
5 years ago
return message;
}
return false;
5 years ago
}
public finalizePendingMessageCallbacks(tempId: number, mid: number) {
const callbacks = this.tempFinalizeCallbacks[tempId];
this.log.warn(callbacks, tempId);
if(callbacks !== undefined) {
for(const name in callbacks) {
const {deferred, callback} = callbacks[name];
this.log(`finalizePendingMessageCallbacks: will invoke ${name} callback`);
callback(mid).then(deferred.resolve, deferred.reject);
}
delete this.tempFinalizeCallbacks[tempId];
}
// set cached url to media
const message = appMessagesManager.getMessage(mid);
if(message.media) {
if(message.media.photo) {
const photo = appPhotosManager.getPhoto('' + tempId);
if(/* photo._ != 'photoEmpty' */photo) {
const newPhoto = message.media.photo;
// костыль
defineNotNumerableProperties(newPhoto, ['downloaded', 'url']);
newPhoto.downloaded = photo.downloaded;
newPhoto.url = photo.url;
}
} else if(message.media.document) {
const doc = appDocsManager.getDoc('' + tempId);
if(/* doc._ != 'documentEmpty' && */doc?.type && doc.type != 'sticker') {
const newDoc = message.media.document;
newDoc.downloaded = doc.downloaded;
newDoc.url = doc.url;
}
} else if(message.media.poll) {
delete appPollsManager.polls[tempId];
delete appPollsManager.results[tempId];
}
}
rootScope.broadcast('message_sent', {tempId, mid});
5 years ago
}
public incrementMaxSeenId(maxId: number) {
if(!maxId || !(!this.maxSeenId || maxId > this.maxSeenId)) {
5 years ago
return false;
}
this.maxSeenId = maxId;
AppStorage.set({max_seen_msg: maxId});
5 years ago
apiManager.invokeApi('messages.receivedMessages', {
max_id: maxId
5 years ago
});
}
public getHistory(peerId: number, maxId = 0, limit: number, backLimit?: number) {
if(this.migratedFromTo[peerId]) {
peerId = this.migratedFromTo[peerId];
5 years ago
}
const historyStorage = this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {count: null, history: [], pending: []});
const unreadOffset = 0;
const unreadSkip = false;
let offset = 0;
let offsetNotFound = false;
let isMigrated = false;
let reqPeerId = peerId;
if(this.migratedToFrom[peerId]) {
5 years ago
isMigrated = true;
if(maxId && maxId < appMessagesIdsManager.fullMsgIdModulus) {
reqPeerId = this.migratedToFrom[peerId];
5 years ago
}
}
if(maxId > 0) {
5 years ago
offsetNotFound = true;
for(; offset < historyStorage.history.length; offset++) {
if(maxId > historyStorage.history[offset]) {
5 years ago
offsetNotFound = false;
break;
}
}
}
if(!offsetNotFound && (
historyStorage.count !== null && historyStorage.history.length == historyStorage.count ||
historyStorage.history.length >= offset + limit
5 years ago
)) {
if(backLimit) {
backLimit = Math.min(offset, backLimit);
offset = Math.max(0, offset - backLimit);
limit += backLimit;
} else {
limit = limit;
5 years ago
}
5 years ago
let history = historyStorage.history.slice(offset, offset + limit);
if(!maxId && historyStorage.pending.length) {
5 years ago
history = historyStorage.pending.slice().concat(history);
}
5 years ago
return this.wrapHistoryResult({
5 years ago
count: historyStorage.count,
history: history,
unreadOffset: unreadOffset,
unreadSkip: unreadSkip
});
}
if(offsetNotFound) {
offset = 0;
}
if((backLimit || unreadSkip || maxId) && historyStorage.history.indexOf(maxId) == -1) {
5 years ago
if(backLimit) {
offset = -backLimit;
limit += backLimit;
}
return this.requestHistory(reqPeerId, maxId, limit, offset).then((historyResult) => {
5 years ago
historyStorage.count = historyResult.count || historyResult.messages.length;
if(isMigrated) {
historyStorage.count++;
}
let history: number[] = [];
5 years ago
historyResult.messages.forEach((message: any) => {
history.push(message.mid);
});
5 years ago
if(!maxId && historyStorage.pending.length) {
5 years ago
history = historyStorage.pending.slice().concat(history);
}
return this.wrapHistoryResult({
5 years ago
count: historyStorage.count,
history: history,
unreadOffset: unreadOffset,
unreadSkip: unreadSkip
});
});
}
return this.fillHistoryStorage(peerId, maxId, limit, historyStorage).then(() => {
5 years ago
offset = 0;
if(maxId > 0) {
5 years ago
for(offset = 0; offset < historyStorage.history.length; offset++) {
if(maxId > historyStorage.history[offset]) {
5 years ago
break;
}
}
}
let history = historyStorage.history.slice(backLimit ? Math.max(offset - backLimit, 0) : offset, offset + limit);
if(!maxId && historyStorage.pending.length) {
5 years ago
history = historyStorage.pending.slice().concat(history);
}
return this.wrapHistoryResult({
5 years ago
count: historyStorage.count,
history: history,
unreadOffset: unreadOffset,
unreadSkip: unreadSkip
});
});
}
public fillHistoryStorage(peerId: number, maxId: number, fullLimit: number, historyStorage: HistoryStorage): Promise<boolean> {
// this.log('fill history storage', peerId, maxId, fullLimit, angular.copy(historyStorage))
const offset = (this.migratedFromTo[peerId] && !maxId) ? 1 : 0;
return this.requestHistory(peerId, maxId, fullLimit, offset).then((historyResult: any) => {
5 years ago
historyStorage.count = historyResult.count || historyResult.messages.length;
if(!maxId && historyResult.messages.length) {
maxId = historyResult.messages[0].mid + 1;
5 years ago
}
let offset = 0;
if(maxId > 0) {
for(; offset < historyStorage.history.length; offset++) {
if(maxId > historyStorage.history[offset]) {
5 years ago
break;
}
}
}
const wasTotalCount = historyStorage.history.length;
5 years ago
historyStorage.history.splice(offset, historyStorage.history.length - offset);
5 years ago
historyResult.messages.forEach((message: any) => {
if(this.mergeReplyKeyboard(historyStorage, message)) {
rootScope.broadcast('history_reply_markup', {peerId});
5 years ago
}
historyStorage.history.push(message.mid);
});
const totalCount = historyStorage.history.length;
5 years ago
fullLimit -= (totalCount - wasTotalCount);
const migratedNextPeer = this.migratedFromTo[peerId];
const migratedPrevPeer = this.migratedToFrom[peerId]
const isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined;
5 years ago
if(isMigrated) {
historyStorage.count = Math.max(historyStorage.count, totalCount) + 1;
}
if(fullLimit > 0) {
maxId = historyStorage.history[totalCount - 1];
5 years ago
if(isMigrated) {
if(!historyResult.messages.length) {
if(migratedPrevPeer) {
maxId = 0;
peerId = migratedPrevPeer;
5 years ago
} else {
historyStorage.count = totalCount;
return true;
}
}
return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage);
5 years ago
} else if(totalCount < historyStorage.count) {
return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage);
5 years ago
}
}
return true;
});
}
public wrapHistoryResult(result: HistoryResult) {
if(result.unreadOffset) {
for(let i = result.history.length - 1; i >= 0; i--) {
const message = this.messagesStorage[result.history[i]];
5 years ago
if(message && !message.pFlags.out && message.pFlags.unread) {
result.unreadOffset = i + 1;
break;
}
}
}
5 years ago
return result;
5 years ago
}
public requestHistory(peerId: number, maxId: number, limit = 0, offset = 0, offsetDate = 0): Promise<any> {
const isChannel = appPeersManager.isChannel(peerId);
5 years ago
//console.trace('requestHistory', peerId, maxId, limit, offset);
5 years ago
rootScope.broadcast('history_request');
5 years ago
return apiManager.invokeApi('messages.getHistory', {
peer: appPeersManager.getInputPeerById(peerId),
offset_id: maxId ? appMessagesIdsManager.getMessageLocalId(maxId) : 0,
offset_date: offsetDate,
add_offset: offset,
limit: limit,
5 years ago
max_id: 0,
min_id: 0,
hash: 0
}, {
//timeout: APITIMEOUT,
5 years ago
noErrorBox: true
}).then((historyResult) => {
this.log('requestHistory result:', peerId, historyResult, maxId, limit, offset);
5 years ago
if(historyResult._ == 'messages.messagesNotModified') {
return historyResult;
}
5 years ago
appUsersManager.saveApiUsers(historyResult.users);
appChatsManager.saveApiChats(historyResult.chats);
this.saveMessages(historyResult.messages);
if(isChannel) {
apiUpdatesManager.addChannelState(-peerId, (historyResult as MessagesMessages.messagesChannelMessages).pts);
5 years ago
}
let length = historyResult.messages.length;
if(length && historyResult.messages[length - 1].deleted) {
5 years ago
historyResult.messages.splice(length - 1, 1);
length--;
(historyResult as MessagesMessages.messagesMessagesSlice).count--;
5 years ago
}
// will load more history if last message is album grouped (because it can be not last item)
const historyStorage = this.historiesStorage[peerId];
// historyResult.messages: desc sorted
if(length && (historyResult.messages[length - 1] as Message.message).grouped_id
&& (historyStorage.history.length + historyResult.messages.length) < (historyResult as MessagesMessages.messagesMessagesSlice).count) {
return this.requestHistory(peerId, (historyResult.messages[length - 1] as Message.message).mid, 10, 0).then((_historyResult) => {
return historyResult;
});
}
// don't need the intro now
/* if(peerId < 0 || !appUsersManager.isBot(peerId) || (length == limit && limit < historyResult.count)) {
5 years ago
return historyResult;
} */
return historyResult;
5 years ago
/* return appProfileManager.getProfile(peerId).then((userFull: any) => {
5 years ago
var description = userFull.bot_info && userFull.bot_info.description;
if(description) {
var messageId = this.tempId--;
5 years ago
var message = {
_: 'messageService',
id: messageId,
from_id: peerId,
peer_id: appPeersManager.getOutputPeer(peerId),
5 years ago
pFlags: {},
date: tsNow(true) + serverTimeManager.serverTimeOffset,
5 years ago
action: {
_: 'messageActionBotIntro',
description: description
}
}
this.saveMessages([message]);
historyResult.messages.push(message);
if(historyResult.count) {
historyResult.count++;
}
}
return historyResult;
}); */
5 years ago
}, (error) => {
switch (error.type) {
case 'CHANNEL_PRIVATE':
let channel = appChatsManager.getChat(-peerId);
5 years ago
channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};
apiUpdatesManager.processUpdateMessage({
_: 'updates',
updates: [{
_: 'updateChannel',
channel_id: -peerId
5 years ago
}],
chats: [channel],
users: []
});
break;
}
throw error;
5 years ago
});
}
public fetchSingleMessages() {
if(this.fetchSingleMessagesPromise) {
return this.fetchSingleMessagesPromise;
}
return this.fetchSingleMessagesPromise = new Promise((resolve) => {
setTimeout(() => {
const mids = this.needSingleMessages.slice();
this.needSingleMessages.length = 0;
const splitted = appMessagesIdsManager.splitMessageIdsByChannels(mids);
let promises: Promise<void>[] = [];
Object.keys(splitted.msgIds).forEach((channelId: number | string) => {
channelId = +channelId;
const msgIds: InputMessage[] = splitted.msgIds[channelId].map((msgId: number) => {
return {
_: 'inputMessageID',
id: msgId
};
});
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
if(channelId > 0) {
promise = apiManager.invokeApi('channels.getMessages', {
channel: appChatsManager.getChannelInput(channelId),
id: msgIds
});
} else {
promise = apiManager.invokeApi('messages.getMessages', {
id: msgIds
});
}
promises.push(promise.then(getMessagesResult => {
if(getMessagesResult._ != 'messages.messagesNotModified') {
appUsersManager.saveApiUsers(getMessagesResult.users);
appChatsManager.saveApiChats(getMessagesResult.chats);
this.saveMessages(getMessagesResult.messages);
}
rootScope.broadcast('messages_downloaded', splitted.mids[+channelId]);
}));
5 years ago
});
Promise.all(promises).finally(() => {
this.fetchSingleMessagesPromise = null;
if(this.needSingleMessages.length) this.fetchSingleMessages();
resolve();
});
}, 0);
5 years ago
});
}
public wrapSingleMessage(msgId: number, overwrite = false): Promise<void> {
if(this.messagesStorage[msgId] && !overwrite) {
rootScope.broadcast('messages_downloaded', [msgId]);
return Promise.resolve();
} else if(this.needSingleMessages.indexOf(msgId) == -1) {
this.needSingleMessages.push(msgId);
return this.fetchSingleMessages();
} else if(this.fetchSingleMessagesPromise) {
return this.fetchSingleMessagesPromise;
}
5 years ago
}
public setTyping(peerId: number, _action: any): Promise<boolean> {
if(!rootScope.myId || !peerId || !this.canWriteToPeer(peerId)) return Promise.resolve(false);
const action: SendMessageAction = typeof(_action) == 'string' ? {_: _action} : _action;
return apiManager.invokeApi('messages.setTyping', {
peer: appPeersManager.getInputPeerById(peerId),
action
}) as Promise<boolean>;
}
5 years ago
}
const appMessagesManager = new AppMessagesManager();
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appMessagesManager = appMessagesManager);
export default appMessagesManager;