4604 lines
146 KiB
TypeScript
4604 lines
146 KiB
TypeScript
import { MessageRender } from "../../components/chat/messageRender";
|
||
import ProgressivePreloader from "../../components/preloader";
|
||
import { listMergeSorted } from "../../helpers/array";
|
||
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
||
import { tsNow } from "../../helpers/date";
|
||
import { copy, defineNotNumerableProperties, deepEqual, safeReplaceObject, getObjectKeysAndSort } from "../../helpers/object";
|
||
import { randomLong } from "../../helpers/random";
|
||
import { splitStringByLength, limitSymbols } from "../../helpers/string";
|
||
import { Dialog as MTDialog, DialogFilter, DialogPeer, DocumentAttribute, InputMessage, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, PhotoSize, SendMessageAction, Update } from "../../layer";
|
||
import { InvokeApiOptions, Modify } 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 referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
|
||
import { default as ServerTimeManager, default as serverTimeManager } from "../mtproto/serverTimeManager";
|
||
import { RichTextProcessor } from "../richtextprocessor";
|
||
import $rootScope from "../rootScope";
|
||
import searchIndexManager from '../searchIndexManager';
|
||
import AppStorage from '../storage';
|
||
//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: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
|
||
|
||
const APITIMEOUT = 0;
|
||
|
||
export type HistoryStorage = {
|
||
count: number | null,
|
||
history: number[],
|
||
pending: number[],
|
||
|
||
readPromise?: Promise<boolean>,
|
||
readMaxID?: number,
|
||
maxOutID?: number,
|
||
reply_markup?: any
|
||
};
|
||
|
||
export type HistoryResult = {
|
||
count: number,
|
||
history: number[],
|
||
unreadOffset: number,
|
||
unreadSkip: boolean
|
||
};
|
||
|
||
export type Dialog = MTDialog.dialog;
|
||
|
||
export class DialogsStorage {
|
||
public dialogs: {[peerID: string]: Dialog} = {};
|
||
public byFolders: {[folderID: number]: Dialog[]} = {};
|
||
|
||
public allDialogsLoaded: {[folder_id: number]: boolean} = {};
|
||
public dialogsOffsetDate: {[folder_id: number]: number} = {};
|
||
public pinnedOrders: {[folder_id: number]: number[]} = {
|
||
0: [],
|
||
1: []
|
||
};
|
||
public dialogsNum = 0;
|
||
|
||
public getFolder(id: number) {
|
||
if(id <= 1) {
|
||
return this.byFolders[id] ?? (this.byFolders[id] = []);
|
||
}
|
||
|
||
const dialogs: {dialog: Dialog, index: number}[] = [];
|
||
const filter = appMessagesManager.filtersStorage.filters[id];
|
||
|
||
for(const peerID in this.dialogs) {
|
||
const dialog = this.dialogs[peerID];
|
||
if(appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) {
|
||
let index: number;
|
||
|
||
const pinnedIndex = filter.pinned_peers.indexOf(dialog.peerID);
|
||
if(pinnedIndex !== -1) {
|
||
index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinned_peers.length - 1 - pinnedIndex));
|
||
} else if(dialog.pFlags?.pinned) {
|
||
index = this.generateIndexForDialog(dialog, true);
|
||
} else {
|
||
index = dialog.index;
|
||
}
|
||
|
||
dialogs.push({dialog, index});
|
||
}
|
||
}
|
||
|
||
dialogs.sort((a, b) => b.index - a.index);
|
||
return dialogs.map(d => d.dialog);
|
||
}
|
||
|
||
public getDialog(peerID: number, folderID?: number): [Dialog, number] | [] {
|
||
const folders: Dialog[][] = [];
|
||
|
||
if(folderID === undefined) {
|
||
const dialogs = this.byFolders;
|
||
for(const folderID in dialogs) {
|
||
folders.push(dialogs[folderID]);
|
||
}
|
||
} else {
|
||
folders.push(this.getFolder(folderID));
|
||
}
|
||
|
||
for(let folder of folders) {
|
||
const index = folder.findIndex(dialog => dialog.peerID == peerID);
|
||
if(index !== -1) {
|
||
return [folder[index], index];
|
||
}
|
||
}
|
||
|
||
return [];
|
||
}
|
||
|
||
/*
|
||
var date = Date.now() / 1000 | 0;
|
||
var m = date * 0x10000;
|
||
|
||
var k = (date + 1) * 0x10000;
|
||
k - m;
|
||
65536
|
||
*/
|
||
public generateDialogIndex(date?: number) {
|
||
if(date === undefined) {
|
||
date = tsNow(true) + serverTimeManager.serverTimeOffset;
|
||
}
|
||
|
||
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
|
||
}
|
||
|
||
public generateIndexForDialog(dialog: Dialog, justReturn = false) {
|
||
const channelID = appPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
|
||
const mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
|
||
const message = appMessagesManager.getMessage(mid);
|
||
|
||
let topDate = (message as Message.message).date || Date.now() / 1000;
|
||
if(channelID) {
|
||
const channel = appChatsManager.getChat(channelID);
|
||
if(!topDate || channel.date && channel.date > topDate) {
|
||
topDate = channel.date;
|
||
}
|
||
}
|
||
|
||
const savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
|
||
if(savedDraft && savedDraft.date > topDate) {
|
||
topDate = savedDraft.date;
|
||
}
|
||
|
||
if(dialog.pFlags.pinned && !justReturn) {
|
||
topDate = this.generateDialogPinnedDate(dialog);
|
||
//this.log('topDate', peerID, topDate);
|
||
}
|
||
|
||
const index = this.generateDialogIndex(topDate);
|
||
if(justReturn) return index;
|
||
dialog.index = index;
|
||
}
|
||
|
||
public generateDialogPinnedDateByIndex(pinnedIndex: number) {
|
||
return 0x7fff0000 + (pinnedIndex & 0xFFFF); // 0xFFFF - потому что в папках может быть бесконечное число пиннедов
|
||
}
|
||
|
||
public generateDialogPinnedDate(dialog: Dialog) {
|
||
const order = this.pinnedOrders[dialog.folder_id];
|
||
|
||
const foundIndex = order.indexOf(dialog.peerID);
|
||
const pinnedIndex = foundIndex === -1 ? order.push(dialog.peerID) - 1 : foundIndex;
|
||
|
||
return this.generateDialogPinnedDateByIndex(pinnedIndex);
|
||
}
|
||
|
||
public pushDialog(dialog: Dialog, offsetDate?: number) {
|
||
const dialogs = this.getFolder(dialog.folder_id);
|
||
const pos = dialogs.findIndex(d => d.peerID == dialog.peerID);
|
||
if(pos !== -1) {
|
||
dialogs.splice(pos, 1);
|
||
}
|
||
|
||
//if(!this.dialogs[dialog.peerID]) {
|
||
this.dialogs[dialog.peerID] = dialog;
|
||
//}
|
||
|
||
if(offsetDate &&
|
||
!dialog.pFlags.pinned &&
|
||
(!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {
|
||
if(pos !== -1) {
|
||
// So the dialog jumped to the last position
|
||
return false;
|
||
}
|
||
this.dialogsOffsetDate[dialog.folder_id] = offsetDate;
|
||
}
|
||
|
||
const index = dialog.index;
|
||
const len = dialogs.length;
|
||
if(!len || index < dialogs[len - 1].index) {
|
||
dialogs.push(dialog);
|
||
} else if(index >= dialogs[0].index) {
|
||
dialogs.unshift(dialog);
|
||
} else {
|
||
for(let i = 0; i < len; i++) {
|
||
if(index > dialogs[i].index) {
|
||
dialogs.splice(i, 0, dialog);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public dropDialog(peerID: number): [Dialog, number] | [] {
|
||
const foundDialog = this.getDialog(peerID);
|
||
if(foundDialog[0]) {
|
||
this.byFolders[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||
delete this.dialogs[peerID];
|
||
}
|
||
|
||
return foundDialog;
|
||
}
|
||
}
|
||
|
||
export type MyDialogFilter = Modify<DialogFilter, {
|
||
pinned_peers: number[],
|
||
include_peers: number[],
|
||
exclude_peers: number[],
|
||
orderIndex?: number
|
||
}>;
|
||
|
||
export class FiltersStorage {
|
||
public filters: {[filterID: string]: MyDialogFilter} = {};
|
||
public orderIndex = 0;
|
||
|
||
constructor() {
|
||
$rootScope.$on('apiUpdate', (e) => {
|
||
this.handleUpdate(e.detail);
|
||
});
|
||
}
|
||
|
||
public handleUpdate(update: Update) {
|
||
switch(update._) {
|
||
case 'updateDialogFilter': {
|
||
//console.log('updateDialogFilter', update);
|
||
if(update.filter) {
|
||
this.saveDialogFilter(update.filter as any);
|
||
} else if(this.filters[update.id]) { // Папка удалена
|
||
//this.getDialogFilters(true);
|
||
$rootScope.$broadcast('filter_delete', this.filters[update.id]);
|
||
delete this.filters[update.id];
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
public testDialogForFilter(dialog: Dialog, filter: MyDialogFilter) {
|
||
// exclude_peers
|
||
for(const peerID of filter.exclude_peers) {
|
||
if(peerID == dialog.peerID) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// include_peers
|
||
for(const peerID of filter.include_peers) {
|
||
if(peerID == dialog.peerID) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
const pFlags = filter.pFlags;
|
||
|
||
// exclude_archived
|
||
if(pFlags.exclude_archived && dialog.folder_id == 1) {
|
||
return false;
|
||
}
|
||
|
||
// exclude_read
|
||
if(pFlags.exclude_read && !dialog.unread_count) {
|
||
return false;
|
||
}
|
||
|
||
// exclude_muted
|
||
if(pFlags.exclude_muted) {
|
||
const isMuted = (dialog.notify_settings?.mute_until * 1000) > Date.now();
|
||
if(isMuted) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
const peerID = dialog.peerID;
|
||
if(peerID < 0) {
|
||
// broadcasts
|
||
if(pFlags.broadcasts && appPeersManager.isBroadcast(peerID)) {
|
||
return true;
|
||
}
|
||
|
||
// groups
|
||
if(pFlags.groups && appPeersManager.isAnyGroup(peerID)) {
|
||
return true;
|
||
}
|
||
} else {
|
||
// bots
|
||
if(appPeersManager.isBot(peerID)) {
|
||
return !!pFlags.bots;
|
||
}
|
||
|
||
// non_contacts
|
||
if(pFlags.non_contacts && !appUsersManager.contactsList.has(peerID)) {
|
||
return true;
|
||
}
|
||
|
||
// contacts
|
||
if(pFlags.contacts && appUsersManager.contactsList.has(peerID)) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public toggleDialogPin(peerID: number, filterID: number) {
|
||
const filter = this.filters[filterID];
|
||
|
||
const wasPinned = filter.pinned_peers.findAndSplice(p => p == peerID);
|
||
if(!wasPinned) {
|
||
filter.pinned_peers.unshift(peerID);
|
||
}
|
||
|
||
return this.updateDialogFilter(filter);
|
||
}
|
||
|
||
public createDialogFilter(filter: MyDialogFilter) {
|
||
let maxID = Math.max(1, ...Object.keys(this.filters).map(i => +i));
|
||
filter = copy(filter);
|
||
filter.id = maxID + 1;
|
||
return this.updateDialogFilter(filter);
|
||
}
|
||
|
||
public updateDialogFilter(filter: MyDialogFilter, remove = false) {
|
||
const flags = remove ? 0 : 1;
|
||
|
||
return apiManager.invokeApi('messages.updateDialogFilter', {
|
||
flags,
|
||
id: filter.id,
|
||
filter: remove ? undefined : this.getOutputDialogFilter(filter)
|
||
}).then((bool: boolean) => { // возможно нужна проверка и откат, если результат не ТРУ
|
||
//console.log('updateDialogFilter bool:', bool);
|
||
|
||
if(bool) {
|
||
/* if(!this.filters[filter.id]) {
|
||
this.saveDialogFilter(filter);
|
||
}
|
||
|
||
$rootScope.$broadcast('filter_update', filter); */
|
||
|
||
this.handleUpdate({
|
||
_: 'updateDialogFilter',
|
||
id: filter.id,
|
||
filter: remove ? undefined : filter as any
|
||
});
|
||
}
|
||
|
||
return bool;
|
||
});
|
||
}
|
||
|
||
public getOutputDialogFilter(filter: MyDialogFilter) {
|
||
const c: MyDialogFilter = copy(filter);
|
||
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
|
||
// @ts-ignore
|
||
c[key] = c[key].map((peerID: number) => appPeersManager.getInputPeerByID(peerID));
|
||
});
|
||
|
||
c.include_peers.forEachReverse((peerID, idx) => {
|
||
if(c.pinned_peers.includes(peerID)) {
|
||
c.include_peers.splice(idx, 1);
|
||
}
|
||
});
|
||
|
||
return c as any as DialogFilter;
|
||
}
|
||
|
||
public async getDialogFilters(overwrite = false) {
|
||
if(Object.keys(this.filters).length && !overwrite) {
|
||
return this.filters;
|
||
}
|
||
|
||
const filters = await apiManager.invokeApi('messages.getDialogFilters');
|
||
for(const filter of filters) {
|
||
this.saveDialogFilter(filter as any as MyDialogFilter, false);
|
||
}
|
||
|
||
//console.log(this.filters);
|
||
return this.filters;
|
||
}
|
||
|
||
public saveDialogFilter(filter: MyDialogFilter, update = true) {
|
||
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
|
||
// @ts-ignore
|
||
filter[key] = filter[key].map((peer: any) => appPeersManager.getPeerID(peer));
|
||
});
|
||
|
||
filter.include_peers.forEachReverse((peerID, idx) => {
|
||
if(filter.pinned_peers.includes(peerID)) {
|
||
filter.include_peers.splice(idx, 1);
|
||
}
|
||
});
|
||
|
||
filter.include_peers = filter.pinned_peers.concat(filter.include_peers);
|
||
|
||
if(this.filters[filter.id]) {
|
||
Object.assign(this.filters[filter.id], filter);
|
||
} else {
|
||
this.filters[filter.id] = filter;
|
||
}
|
||
|
||
this.setOrderIndex(filter);
|
||
|
||
if(update) {
|
||
$rootScope.$broadcast('filter_update', filter);
|
||
}
|
||
}
|
||
|
||
public setOrderIndex(filter: MyDialogFilter) {
|
||
if(filter.hasOwnProperty('orderIndex')) {
|
||
if(filter.orderIndex > this.orderIndex) {
|
||
this.orderIndex = filter.orderIndex;
|
||
}
|
||
} else {
|
||
filter.orderIndex = this.orderIndex++;
|
||
}
|
||
}
|
||
}
|
||
|
||
type MyMessage = Message.message | Message.messageService;
|
||
type MyInputMessagesFilter = 'inputMessagesFilterEmpty'
|
||
| 'inputMessagesFilterPhotos'
|
||
| 'inputMessagesFilterPhotoVideo'
|
||
| 'inputMessagesFilterVideo'
|
||
| 'inputMessagesFilterDocument'
|
||
| 'inputMessagesFilterVoice'
|
||
| 'inputMessagesFilterRoundVoice'
|
||
| 'inputMessagesFilterRoundVideo'
|
||
| 'inputMessagesFilterMusic'
|
||
| 'inputMessagesFilterUrl'
|
||
| 'inputMessagesFilterMyMentions'
|
||
| 'inputMessagesFilterChatPhotos';
|
||
|
||
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
|
||
public historiesStorage: {
|
||
[peerID: string]: HistoryStorage
|
||
} = {};
|
||
public pinnedMessages: {[peerID: string]: number} = {};
|
||
public pendingByRandomID: {[randomID: string]: [number, number]} = {};
|
||
public pendingByMessageID: any = {};
|
||
public pendingAfterMsgs: any = {};
|
||
public pendingTopMsgs: {[peerID: string]: number} = {};
|
||
public sendFilePromise: CancellablePromise<void> = Promise.resolve();
|
||
public tempID = -1;
|
||
public tempFinalizeCallbacks: {
|
||
[mid: string]: {
|
||
[callbackName: string]: Partial<{
|
||
deferred: CancellablePromise<void>,
|
||
callback: (mid: number) => Promise<any>
|
||
}>
|
||
}
|
||
} = {};
|
||
|
||
public lastSearchFilter: any = {};
|
||
public lastSearchResults: any = [];
|
||
|
||
public needSingleMessages: number[] = [];
|
||
private fetchSingleMessagesPromise: Promise<void> = null;
|
||
|
||
public maxSeenID = 0;
|
||
|
||
public migratedFromTo: {[peerID: number]: number} = {};
|
||
public migratedToFrom: {[peerID: number]: number} = {};
|
||
|
||
public newMessagesHandlePromise = 0;
|
||
public newMessagesToHandle: {[peerID: string]: number[]} = {};
|
||
public newDialogsHandlePromise = 0;
|
||
public newDialogsToHandle: {[peerID: string]: {reload: true} | Dialog} = {};
|
||
public newUpdatesAfterReloadToHandle: any = {};
|
||
|
||
private reloadConversationsPromise: Promise<void>;
|
||
private reloadConversationsPeers: number[] = [];
|
||
|
||
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 = new DialogsStorage();
|
||
public filtersStorage = new FiltersStorage();
|
||
|
||
constructor() {
|
||
$rootScope.$on('apiUpdate', (e) => {
|
||
this.handleUpdate(e.detail);
|
||
});
|
||
|
||
$rootScope.$on('webpage_updated', (e) => {
|
||
const eventData = e.detail;
|
||
eventData.msgs.forEach((msgID) => {
|
||
const message = this.getMessage(msgID) as Message.message;
|
||
if(!message) return;
|
||
message.media = {
|
||
_: 'messageMediaWebPage',
|
||
webpage: appWebPagesManager.getWebPage(eventData.id)
|
||
};
|
||
|
||
$rootScope.$broadcast('message_edit', {
|
||
peerID: this.getMessagePeer(message),
|
||
mid: msgID,
|
||
justMedia: true
|
||
});
|
||
});
|
||
});
|
||
|
||
/* $rootScope.$on('draft_updated', (e) => {
|
||
let eventData = e.detail;;
|
||
var peerID = eventData.peerID;
|
||
var draft = eventData.draft;
|
||
|
||
var dialog = this.getDialogByPeerID(peerID)[0];
|
||
if(dialog) {
|
||
var topDate;
|
||
if(draft && draft.date) {
|
||
topDate = draft.date;
|
||
} else {
|
||
var channelID = appPeersManager.isChannel(peerID) ? -peerID : 0
|
||
var topDate = this.getMessage(dialog.top_message).date;
|
||
|
||
if(channelID) {
|
||
var channel = appChatsManager.getChat(channelID);
|
||
if(!topDate || channel.date && channel.date > topDate) {
|
||
topDate = channel.date;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(!dialog.pFlags.pinned) {
|
||
dialog.index = this.dialogsStorage.generateDialogIndex(topDate);
|
||
}
|
||
|
||
this.dialogsStorage.pushDialog(dialog);
|
||
|
||
$rootScope.$broadcast('dialog_draft', {
|
||
peerID,
|
||
draft,
|
||
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);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
|
||
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(messageID: number, text: string, options: Partial<{
|
||
noWebPage: true,
|
||
newMedia: any
|
||
}> = {}): Promise<void> {
|
||
/* if(!this.canEditMessage(messageID)) {
|
||
return Promise.reject({type: 'MESSAGE_EDIT_FORBIDDEN'});
|
||
} */
|
||
|
||
if(messageID < 0) {
|
||
return this.invokeAfterMessageIsSent(messageID, '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(messageID);
|
||
const peerID = this.getMessagePeer(message);
|
||
|
||
return apiManager.invokeApi('messages.editMessage', {
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
id: appMessagesIDsManager.getMessageLocalID(messageID),
|
||
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) {
|
||
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;
|
||
|
||
var entities = options.entities || [];
|
||
if(!options.viaBotID) {
|
||
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];
|
||
var pFlags: any = {};
|
||
var replyToMsgID = options.replyToMsgID;
|
||
var isChannel = appPeersManager.isChannel(peerID);
|
||
var isMegagroup = isChannel && appPeersManager.isMegagroup(peerID);
|
||
var asChannel = isChannel && !isMegagroup ? true : false;
|
||
var message: any;
|
||
|
||
if(historyStorage === undefined) {
|
||
historyStorage = this.historiesStorage[peerID] = {count: null, history: [], pending: []};
|
||
}
|
||
|
||
var fromID = appUsersManager.getSelf().id;
|
||
if(peerID != fromID) {
|
||
pFlags.out = true;
|
||
|
||
if(!isChannel && !appUsersManager.isBot(peerID)) {
|
||
pFlags.unread = true;
|
||
}
|
||
}
|
||
|
||
if(asChannel) {
|
||
fromID = 0;
|
||
pFlags.post = true;
|
||
}
|
||
|
||
message = {
|
||
_: 'message',
|
||
id: messageID,
|
||
from_id: appPeersManager.getOutputPeer(fromID),
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
pFlags: pFlags,
|
||
date: tsNow(true) + serverTimeManager.serverTimeOffset,
|
||
message: text,
|
||
random_id: randomIDS,
|
||
reply_to: {reply_to_msg_id: replyToMsgID},
|
||
via_bot_id: options.viaBotID,
|
||
reply_markup: options.reply_markup,
|
||
entities: entities,
|
||
views: asChannel && 1,
|
||
pending: true
|
||
};
|
||
|
||
if(options.webPage) {
|
||
message.media = {
|
||
_: 'messageMediaWebPage',
|
||
webpage: options.webPage
|
||
};
|
||
}
|
||
|
||
var toggleError = (on: any) => {
|
||
if(on) {
|
||
message.error = true;
|
||
} else {
|
||
delete message.error;
|
||
}
|
||
$rootScope.$broadcast('messages_pending');
|
||
}
|
||
|
||
message.send = () => {
|
||
toggleError(false);
|
||
var sentRequestOptions: any = {};
|
||
if(this.pendingAfterMsgs[peerID]) {
|
||
sentRequestOptions.afterMessageID = this.pendingAfterMsgs[peerID].messageID;
|
||
}
|
||
|
||
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
|
||
}, sentRequestOptions);
|
||
} else {
|
||
apiPromise = apiManager.invokeApiAfter('messages.sendMessage', {
|
||
no_webpage: options.noWebPage,
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
message: text,
|
||
random_id: randomIDS,
|
||
reply_to_msg_id: replyToMsgID ? appMessagesIDsManager.getMessageLocalID(replyToMsgID) : undefined,
|
||
entities: sendEntites,
|
||
clear_draft: options.clearDraft
|
||
}, sentRequestOptions);
|
||
}
|
||
|
||
// this.log(flags, entities)
|
||
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,
|
||
id: updates.id
|
||
}, {
|
||
_: isChannel
|
||
? 'updateNewChannelMessage'
|
||
: 'updateNewMessage',
|
||
message: message,
|
||
pts: updates.pts,
|
||
pts_count: updates.pts_count
|
||
}]
|
||
};
|
||
} else if(updates.updates) {
|
||
updates.updates.forEach((update: any) => {
|
||
if(update._ == 'updateDraftMessage') {
|
||
update.local = true;
|
||
}
|
||
});
|
||
}
|
||
// Testing bad situations
|
||
// var upd = angular.copy(updates)
|
||
// updates.updates.splice(0, 1)
|
||
|
||
apiUpdatesManager.processUpdateMessage(updates);
|
||
|
||
// $timeout(function () {
|
||
// ApiUpdatesManager.processUpdateMessage(upd)
|
||
// }, 5000)
|
||
}, (/* error: any */) => {
|
||
toggleError(true);
|
||
}).finally(() => {
|
||
if(this.pendingAfterMsgs[peerID] === sentRequestOptions) {
|
||
delete this.pendingAfterMsgs[peerID];
|
||
}
|
||
});
|
||
|
||
this.pendingAfterMsgs[peerID] = sentRequestOptions;
|
||
}
|
||
|
||
this.saveMessages([message]);
|
||
historyStorage.pending.unshift(messageID);
|
||
$rootScope.$broadcast('history_append', {peerID, messageID, my: true});
|
||
|
||
setTimeout(() => message.send(), 0);
|
||
// setTimeout(function () {
|
||
// message.send()
|
||
// }, 5000)
|
||
|
||
/* if(options.clearDraft) { // WARNING
|
||
DraftsManager.clearDraft(peerID)
|
||
} */
|
||
|
||
this.pendingByRandomID[randomIDS] = [peerID, messageID];
|
||
}
|
||
|
||
public sendFile(peerID: number, file: File | Blob | MyDocument, options: Partial<{
|
||
isMedia: boolean,
|
||
replyToMsgID: number,
|
||
caption: string,
|
||
entities: any[],
|
||
width: number,
|
||
height: number,
|
||
objectURL: string,
|
||
isRoundMessage: boolean,
|
||
duration: number,
|
||
background: boolean,
|
||
|
||
isVoiceMessage: boolean,
|
||
waveform: Uint8Array
|
||
}> = {}) {
|
||
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
|
||
var messageID = this.tempID--;
|
||
var randomIDS = randomLong();
|
||
var historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {count: null, history: [], pending: []});
|
||
var flags = 0;
|
||
var pFlags: any = {};
|
||
var replyToMsgID = options.replyToMsgID;
|
||
var isChannel = appPeersManager.isChannel(peerID);
|
||
var isMegagroup = isChannel && appPeersManager.isMegagroup(peerID);
|
||
var asChannel = isChannel && !isMegagroup ? true : false;
|
||
var attachType: string, apiFileName: string;
|
||
|
||
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);
|
||
let caption = options.caption || '';
|
||
|
||
const date = tsNow(true) + ServerTimeManager.serverTimeOffset;
|
||
|
||
this.log('sendFile', file, fileType);
|
||
|
||
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 actionName = '';
|
||
if(!options.isMedia) {
|
||
attachType = 'document';
|
||
apiFileName = 'document.' + fileType.split('/')[1];
|
||
actionName = 'sendMessageUploadDocumentAction';
|
||
} else if(isDocument) { // maybe it's a sticker or gif
|
||
attachType = 'document';
|
||
apiFileName = '';
|
||
} else if(isPhoto) {
|
||
attachType = 'photo';
|
||
apiFileName = 'photo.' + fileType.split('/')[1];
|
||
actionName = 'sendMessageUploadPhotoAction';
|
||
|
||
let photo: MyPhoto = {
|
||
_: '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('audio/') === 0 || ['video/ogg'].indexOf(fileType) >= 0) {
|
||
attachType = 'audio';
|
||
apiFileName = 'audio.' + (fileType.split('/')[1] == 'ogg' ? 'ogg' : 'mp3');
|
||
actionName = 'sendMessageUploadAudioAction';
|
||
|
||
let flags = 0;
|
||
if(options.isVoiceMessage) {
|
||
flags |= 1 << 10;
|
||
flags |= 1 << 2;
|
||
attachType = 'voice';
|
||
pFlags.media_unread = true;
|
||
}
|
||
|
||
let attribute: DocumentAttribute.documentAttributeAudio = {
|
||
_: 'documentAttributeAudio',
|
||
flags: flags,
|
||
pFlags: { // that's only for client, not going to telegram
|
||
voice: options.isVoiceMessage || undefined
|
||
},
|
||
waveform: options.waveform,
|
||
duration: options.duration || 0
|
||
};
|
||
|
||
attributes.push(attribute);
|
||
} else if(fileType.indexOf('video/') === 0) {
|
||
attachType = 'video';
|
||
apiFileName = 'video.mp4';
|
||
actionName = 'sendMessageUploadVideoAction';
|
||
|
||
let videoAttribute: DocumentAttribute.documentAttributeVideo = {
|
||
_: 'documentAttributeVideo',
|
||
pFlags: { // that's only for client, not going to telegram
|
||
supports_streaming: true,
|
||
round_message: options.isRoundMessage || undefined
|
||
},
|
||
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[] = [];
|
||
const doc: MyDocument = {
|
||
_: 'document',
|
||
id: '' + messageID,
|
||
duration: options.duration,
|
||
attributes,
|
||
w: options.width,
|
||
h: options.height,
|
||
thumbs,
|
||
mime_type: fileType,
|
||
size: file.size
|
||
} as any;
|
||
|
||
defineNotNumerableProperties(doc, ['downloaded', 'url']);
|
||
// @ts-ignore
|
||
doc.downloaded = file.size;
|
||
doc.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(doc);
|
||
}
|
||
|
||
this.log('AMM: sendFile', attachType, apiFileName, file.type, options);
|
||
|
||
var fromID = appUsersManager.getSelf().id;
|
||
if(peerID != fromID) {
|
||
flags |= 2;
|
||
pFlags.out = true;
|
||
|
||
if(!isChannel && !appUsersManager.isBot(peerID)) {
|
||
flags |= 1;
|
||
pFlags.unread = true;
|
||
}
|
||
}
|
||
|
||
if(replyToMsgID) {
|
||
flags |= 8;
|
||
}
|
||
|
||
if(asChannel) {
|
||
fromID = 0;
|
||
pFlags.post = true;
|
||
} else {
|
||
flags |= 256;
|
||
}
|
||
|
||
const preloader = new ProgressivePreloader(null, true, false, 'prepend');
|
||
|
||
const media = {
|
||
_: 'messageMediaPending',
|
||
type: attachType,
|
||
file_name: fileName || apiFileName,
|
||
size: file.size,
|
||
file: file,
|
||
preloader: preloader,
|
||
w: options.width,
|
||
h: options.height,
|
||
url: options.objectURL
|
||
};
|
||
|
||
const message: any = {
|
||
_: 'message',
|
||
id: messageID,
|
||
from_id: appPeersManager.getOutputPeer(fromID),
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
pFlags: pFlags,
|
||
date: date,
|
||
message: caption,
|
||
media: isDocument ? {
|
||
_: 'messageMediaDocument',
|
||
pFlags: {},
|
||
document: file
|
||
} : media,
|
||
random_id: randomIDS,
|
||
reply_to: {reply_to_msg_id: replyToMsgID},
|
||
views: asChannel && 1,
|
||
pending: true
|
||
};
|
||
|
||
const toggleError = (on: boolean) => {
|
||
if(on) {
|
||
message.error = true;
|
||
} else {
|
||
delete message.error;
|
||
}
|
||
|
||
$rootScope.$broadcast('messages_pending');
|
||
};
|
||
|
||
let uploaded = false,
|
||
uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null;
|
||
|
||
const invoke = (flags: number, inputMedia: any) => {
|
||
this.setTyping(peerID, 'sendMessageCancelAction');
|
||
|
||
return apiManager.invokeApi('messages.sendMedia', {
|
||
flags: flags,
|
||
background: options.background || undefined,
|
||
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;
|
||
}
|
||
|
||
toggleError(true);
|
||
});
|
||
};
|
||
|
||
message.send = () => {
|
||
let flags = 0;
|
||
if(replyToMsgID) {
|
||
flags |= 1;
|
||
}
|
||
if(options.background) {
|
||
flags |= 64;
|
||
}
|
||
flags |= 128; // clear_draft
|
||
|
||
if(isDocument) {
|
||
const {id, access_hash, file_reference} = file as MyDocument;
|
||
|
||
const inputMedia = {
|
||
_: 'inputMediaDocument',
|
||
id: {
|
||
_: 'inputDocument',
|
||
id: id,
|
||
access_hash: access_hash,
|
||
file_reference: file_reference
|
||
}
|
||
};
|
||
|
||
invoke(flags, inputMedia);
|
||
} else if(file instanceof File || file instanceof Blob) {
|
||
const deferred = deferredPromise<void>();
|
||
|
||
this.sendFilePromise.then(() => {
|
||
if(!uploaded || message.error) {
|
||
uploaded = false;
|
||
uploadPromise = appDownloadManager.upload(file);
|
||
preloader.attachPromise(uploadPromise);
|
||
}
|
||
|
||
uploadPromise && uploadPromise.then((inputFile) => {
|
||
this.log('appMessagesManager: sendFile uploaded:', inputFile);
|
||
|
||
inputFile.name = apiFileName;
|
||
uploaded = true;
|
||
var inputMedia;
|
||
switch(attachType) {
|
||
case 'photo':
|
||
inputMedia = {
|
||
_: 'inputMediaUploadedPhoto',
|
||
file: inputFile
|
||
};
|
||
break;
|
||
|
||
default:
|
||
inputMedia = {
|
||
_: 'inputMediaUploadedDocument',
|
||
file: inputFile,
|
||
mime_type: fileType,
|
||
attributes: attributes
|
||
};
|
||
}
|
||
|
||
invoke(flags, inputMedia);
|
||
}, (/* 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);
|
||
|
||
deferred.resolve();
|
||
this.cancelPendingMessage(randomIDS);
|
||
this.setTyping(peerID, 'sendMessageCancelAction');
|
||
}
|
||
});
|
||
|
||
uploadPromise.finally(deferred.resolve);
|
||
});
|
||
|
||
this.sendFilePromise = deferred;
|
||
}
|
||
};
|
||
|
||
this.saveMessages([message]);
|
||
historyStorage.pending.unshift(messageID);
|
||
$rootScope.$broadcast('history_append', {peerID, messageID, my: true});
|
||
|
||
setTimeout(message.send.bind(this), 0);
|
||
|
||
this.pendingByRandomID[randomIDS] = [peerID, messageID];
|
||
}
|
||
|
||
public async sendAlbum(peerID: number, files: File[], options: Partial<{
|
||
entities: any[],
|
||
replyToMsgID: number,
|
||
caption: string,
|
||
sendFileDetails: Partial<{
|
||
duration: number,
|
||
width: number,
|
||
height: number,
|
||
objectURL: string,
|
||
}>[]
|
||
}> = {}) {
|
||
peerID = appPeersManager.getPeerMigratedTo(peerID) || peerID;
|
||
let groupID: number;
|
||
let historyStorage = this.historiesStorage[peerID] ?? (this.historiesStorage[peerID] = {count: null, history: [], pending: []});
|
||
let flags = 0;
|
||
let pFlags: any = {};
|
||
let replyToMsgID = options.replyToMsgID;
|
||
let isChannel = appPeersManager.isChannel(peerID);
|
||
let isMegagroup = isChannel && appPeersManager.isMegagroup(peerID);
|
||
let asChannel = isChannel && !isMegagroup ? true : false;
|
||
|
||
let caption = options.caption || '';
|
||
|
||
let date = tsNow(true) + ServerTimeManager.serverTimeOffset;
|
||
|
||
if(caption) {
|
||
let entities = options.entities || [];
|
||
caption = RichTextProcessor.parseMarkdown(caption, entities);
|
||
}
|
||
|
||
this.log('AMM: sendAlbum', files, options);
|
||
|
||
let fromID = appUsersManager.getSelf().id;
|
||
if(peerID != fromID) {
|
||
pFlags.out = true;
|
||
|
||
if(!isChannel && !appUsersManager.isBot(peerID)) {
|
||
pFlags.unread = true;
|
||
}
|
||
}
|
||
|
||
if(replyToMsgID) {
|
||
flags |= 1;
|
||
}
|
||
|
||
if(asChannel) {
|
||
fromID = 0;
|
||
pFlags.post = true;
|
||
} else {
|
||
flags |= 128; // clear_draft
|
||
}
|
||
|
||
let ids = files.map(() => this.tempID--).reverse();
|
||
groupID = ids[ids.length - 1];
|
||
let messages = files.map((file, idx) => {
|
||
//let messageID = this.tempID--;
|
||
//if(!groupID) groupID = messageID;
|
||
let messageID = ids[idx];
|
||
let randomIDS = randomLong();
|
||
let preloader = new ProgressivePreloader(null, true, false, 'prepend');
|
||
|
||
let details = options.sendFileDetails[idx];
|
||
|
||
let media = {
|
||
_: 'messageMediaPending',
|
||
type: 'album',
|
||
preloader: preloader,
|
||
document: undefined as any,
|
||
photo: undefined as any
|
||
};
|
||
|
||
if(file.type.indexOf('video/') === 0) {
|
||
let videoAttribute: DocumentAttribute.documentAttributeVideo = {
|
||
_: 'documentAttributeVideo',
|
||
pFlags: { // that's only for client, not going to telegram
|
||
supports_streaming: true
|
||
},
|
||
duration: details.duration,
|
||
w: details.width,
|
||
h: details.height
|
||
};
|
||
|
||
let doc: MyDocument = {
|
||
_: 'document',
|
||
id: '' + messageID,
|
||
attributes: [videoAttribute],
|
||
thumbs: [],
|
||
mime_type: file.type,
|
||
size: file.size
|
||
} as any;
|
||
|
||
defineNotNumerableProperties(doc, ['downloaded', 'url']);
|
||
// @ts-ignore
|
||
doc.downloaded = file.size;
|
||
doc.url = details.objectURL || '';
|
||
|
||
appDocsManager.saveDoc(doc);
|
||
media.document = doc;
|
||
} else {
|
||
let photo: any = {
|
||
_: 'photo',
|
||
id: '' + messageID,
|
||
sizes: [{
|
||
_: 'photoSize',
|
||
w: details.width,
|
||
h: details.height,
|
||
type: 'm',
|
||
size: file.size
|
||
} as PhotoSize],
|
||
w: details.width,
|
||
h: details.height
|
||
};
|
||
|
||
defineNotNumerableProperties(photo, ['downloaded', 'url']);
|
||
// @ts-ignore
|
||
photo.downloaded = file.size;
|
||
photo.url = details.objectURL || '';
|
||
|
||
appPhotosManager.savePhoto(photo);
|
||
media.photo = photo;
|
||
}
|
||
|
||
let message = {
|
||
_: 'message',
|
||
id: messageID,
|
||
from_id: appPeersManager.getOutputPeer(fromID),
|
||
grouped_id: groupID,
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
pFlags: pFlags,
|
||
date: date,
|
||
message: caption,
|
||
media: media,
|
||
random_id: randomIDS,
|
||
reply_to: {reply_to_msg_id: replyToMsgID},
|
||
views: asChannel && 1,
|
||
pending: true,
|
||
error: false
|
||
};
|
||
|
||
this.saveMessages([message]);
|
||
historyStorage.pending.unshift(messageID);
|
||
//$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true});
|
||
|
||
this.pendingByRandomID[randomIDS] = [peerID, messageID];
|
||
|
||
return message;
|
||
});
|
||
|
||
$rootScope.$broadcast('history_append', {peerID, messageID: messages[messages.length - 1].id, my: true});
|
||
|
||
let toggleError = (message: any, on: boolean) => {
|
||
if(on) {
|
||
message.error = true;
|
||
} else {
|
||
delete message.error;
|
||
}
|
||
|
||
$rootScope.$broadcast('messages_pending');
|
||
};
|
||
|
||
let uploaded = false,
|
||
uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null;
|
||
|
||
let inputPeer = appPeersManager.getInputPeerByID(peerID);
|
||
let invoke = (multiMedia: any[]) => {
|
||
this.setTyping(peerID, 'sendMessageCancelAction');
|
||
|
||
return apiManager.invokeApi('messages.sendMultiMedia', {
|
||
flags: flags,
|
||
peer: inputPeer,
|
||
multi_media: multiMedia,
|
||
reply_to_msg_id: appMessagesIDsManager.getMessageLocalID(replyToMsgID)
|
||
}).then((updates) => {
|
||
apiUpdatesManager.processUpdateMessage(updates);
|
||
}, (error) => {
|
||
messages.forEach(message => toggleError(message, true));
|
||
});
|
||
};
|
||
|
||
let inputs: any[] = [];
|
||
for(let i = 0, length = files.length; i < length; ++i) {
|
||
const file = files[i];
|
||
const message = messages[i];
|
||
const media = message.media;
|
||
const preloader = media.preloader;
|
||
const actionName = file.type.indexOf('video/') === 0 ? 'sendMessageUploadVideoAction' : 'sendMessageUploadPhotoAction';
|
||
const deferred = deferredPromise<void>();
|
||
let canceled = false;
|
||
|
||
let apiFileName: string;
|
||
if(file.type.indexOf('video/') === 0) {
|
||
apiFileName = 'video.mp4';
|
||
} else {
|
||
apiFileName = 'photo.' + file.type.split('/')[1];
|
||
}
|
||
|
||
await this.sendFilePromise;
|
||
this.sendFilePromise = deferred;
|
||
|
||
if(!uploaded || message.error) {
|
||
uploaded = false;
|
||
uploadPromise = appDownloadManager.upload(file);
|
||
preloader.attachPromise(uploadPromise);
|
||
}
|
||
|
||
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 item', media);
|
||
canceled = true;
|
||
}
|
||
});
|
||
|
||
await uploadPromise.then((inputFile) => {
|
||
this.log('appMessagesManager: sendAlbum file uploaded:', inputFile);
|
||
|
||
if(canceled) {
|
||
return;
|
||
}
|
||
|
||
inputFile.name = apiFileName;
|
||
|
||
let inputMedia: any;
|
||
let details = options.sendFileDetails[i];
|
||
if(details.duration) {
|
||
inputMedia = {
|
||
_: 'inputMediaUploadedDocument',
|
||
file: inputFile,
|
||
mime_type: file.type,
|
||
attributes: [{
|
||
_: 'documentAttributeVideo',
|
||
supports_streaming: true,
|
||
duration: details.duration,
|
||
w: details.width,
|
||
h: details.height
|
||
}]
|
||
};
|
||
} else {
|
||
inputMedia = {
|
||
_: 'inputMediaUploadedPhoto',
|
||
file: inputFile
|
||
};
|
||
}
|
||
|
||
return apiManager.invokeApi('messages.uploadMedia', {
|
||
peer: inputPeer,
|
||
media: inputMedia
|
||
}).then(messageMedia => {
|
||
if(canceled) {
|
||
return;
|
||
}
|
||
|
||
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: []
|
||
});
|
||
|
||
caption = ''; // only 1 caption for all inputs
|
||
}, () => {
|
||
toggleError(message, true);
|
||
});
|
||
}, () => {
|
||
toggleError(message, true);
|
||
});
|
||
|
||
this.log('appMessagesManager: sendAlbum uploadPromise.finally!');
|
||
deferred.resolve();
|
||
}
|
||
|
||
uploaded = true;
|
||
invoke(inputs);
|
||
}
|
||
|
||
public sendOther(peerID: number, inputMedia: any, options: Partial<{
|
||
replyToMsgID: number,
|
||
viaBotID: number,
|
||
reply_markup: any,
|
||
clearDraft: true,
|
||
queryID: string
|
||
resultID: string
|
||
}> = {}) {
|
||
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);
|
||
const asChannel = isChannel && !isMegagroup ? true : false;
|
||
|
||
let fromID = appUsersManager.getSelf().id;
|
||
let media;
|
||
switch(inputMedia._) {
|
||
case 'inputMediaPoll': {
|
||
inputMedia.poll.id = messageID;
|
||
appPollsManager.savePoll(inputMedia.poll, {
|
||
_: 'pollResults',
|
||
flags: 4,
|
||
total_voters: 0,
|
||
pFlags: {},
|
||
});
|
||
|
||
const {poll, results} = appPollsManager.getPoll('' + messageID);
|
||
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) {
|
||
pFlags.out = true;
|
||
if(!appUsersManager.isBot(peerID)) {
|
||
pFlags.unread = true;
|
||
}
|
||
}
|
||
|
||
if(asChannel) {
|
||
fromID = 0;
|
||
pFlags.post = true;
|
||
}
|
||
|
||
const message: any = {
|
||
_: 'message',
|
||
id: messageID,
|
||
from_id: appPeersManager.getOutputPeer(fromID),
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
pFlags: pFlags,
|
||
date: tsNow(true) + ServerTimeManager.serverTimeOffset,
|
||
message: '',
|
||
media: media,
|
||
random_id: randomIDS,
|
||
reply_to: {reply_to_msg_id: replyToMsgID},
|
||
via_bot_id: options.viaBotID,
|
||
reply_markup: options.reply_markup,
|
||
views: asChannel && 1,
|
||
pending: true,
|
||
};
|
||
|
||
let toggleError = (on: boolean) => {
|
||
/* const historyMessage = this.messagesForHistory[messageID];
|
||
if (on) {
|
||
message.error = true
|
||
if (historyMessage) {
|
||
historyMessage.error = true
|
||
}
|
||
} else {
|
||
delete message.error
|
||
if (historyMessage) {
|
||
delete historyMessage.error
|
||
}
|
||
} */
|
||
$rootScope.$broadcast('messages_pending');
|
||
};
|
||
|
||
message.send = () => {
|
||
const sentRequestOptions: any = {};
|
||
if(this.pendingAfterMsgs[peerID]) {
|
||
sentRequestOptions.afterMessageID = this.pendingAfterMsgs[peerID].messageID;
|
||
}
|
||
|
||
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
|
||
}, sentRequestOptions);
|
||
} else {
|
||
apiPromise = apiManager.invokeApiAfter('messages.sendMedia', {
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
media: inputMedia,
|
||
random_id: randomIDS,
|
||
reply_to_msg_id: replyToMsgID ? appMessagesIDsManager.getMessageLocalID(replyToMsgID) : undefined,
|
||
message: '',
|
||
clear_draft: options.clearDraft
|
||
}, sentRequestOptions);
|
||
}
|
||
|
||
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];
|
||
}
|
||
});
|
||
this.pendingAfterMsgs[peerID] = sentRequestOptions;
|
||
}
|
||
|
||
this.saveMessages([message]);
|
||
historyStorage.pending.unshift(messageID);
|
||
$rootScope.$broadcast('history_append', {peerID, messageID, my: true});
|
||
|
||
setTimeout(message.send, 0);
|
||
|
||
/* if(options.clearDraft) {
|
||
DraftsManager.clearDraft(peerID)
|
||
} */
|
||
|
||
this.pendingByRandomID[randomIDS] = [peerID, messageID];
|
||
}
|
||
|
||
public cancelPendingMessage(randomID: string) {
|
||
const pendingData = this.pendingByRandomID[randomID];
|
||
|
||
this.log('cancelPendingMessage', randomID, pendingData);
|
||
|
||
if(pendingData) {
|
||
const peerID = pendingData[0];
|
||
const tempID = pendingData[1];
|
||
const historyStorage = this.historiesStorage[peerID];
|
||
const pos = historyStorage.pending.indexOf(tempID);
|
||
|
||
apiUpdatesManager.processUpdateMessage({
|
||
_: 'updateShort',
|
||
update: {
|
||
_: 'updateDeleteMessages',
|
||
messages: [tempID]
|
||
}
|
||
});
|
||
|
||
if(pos != -1) {
|
||
historyStorage.pending.splice(pos, 1);
|
||
}
|
||
|
||
delete this.messagesStorage[tempID];
|
||
|
||
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);
|
||
|
||
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;
|
||
if(offsetIndex > 0) {
|
||
for(; offset < curDialogStorage.length; offset++) {
|
||
if(offsetIndex > curDialogStorage[offset].index) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(query || this.dialogsStorage.allDialogsLoaded[realFolderID] || curDialogStorage.length >= offset + limit) {
|
||
return Promise.resolve({
|
||
dialogs: curDialogStorage.slice(offset, offset + limit),
|
||
count: this.dialogsStorage.allDialogsLoaded[realFolderID] ? curDialogStorage.length : null
|
||
});
|
||
}
|
||
|
||
return this.getTopMessages(limit, realFolderID).then(totalCount => {
|
||
//const curDialogStorage = this.dialogsStorage[folderID];
|
||
|
||
offset = 0;
|
||
if(offsetIndex > 0) {
|
||
for(; offset < curDialogStorage.length; offset++) {
|
||
if(offsetIndex > curDialogStorage[offset].index) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length);
|
||
|
||
return {
|
||
dialogs: curDialogStorage.slice(offset, offset + limit),
|
||
count: totalCount
|
||
};
|
||
});
|
||
}
|
||
|
||
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;
|
||
|
||
if(this.dialogsStorage.dialogsOffsetDate[folderID]) {
|
||
offsetDate = this.dialogsStorage.dialogsOffsetDate[folderID] + serverTimeManager.serverTimeOffset;
|
||
offsetIndex = this.dialogsStorage.dialogsOffsetDate[folderID] * 0x10000;
|
||
//flags |= 1; // means pinned already loaded
|
||
}
|
||
|
||
/* if(this.dialogsStorage.dialogsOffsetDate[0]) {
|
||
flags |= 1; // means pinned already loaded
|
||
} */
|
||
|
||
//if(folderID > 0) {
|
||
//flags |= 1;
|
||
flags |= 2;
|
||
//}
|
||
|
||
// ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:
|
||
// ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???
|
||
// ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0
|
||
return apiManager.invokeApi('messages.getDialogs', {
|
||
flags,
|
||
folder_id: folderID,
|
||
offset_date: offsetDate,
|
||
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
|
||
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
|
||
limit,
|
||
hash: 0
|
||
}, {
|
||
//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) {
|
||
telegramMeWebService.setAuthorized(true);
|
||
} */
|
||
|
||
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);
|
||
} */
|
||
|
||
if(offsetIndex && dialog.index > offsetIndex) {
|
||
this.newDialogsToHandle[dialog.peerID] = dialog;
|
||
hasPrepend = true;
|
||
}
|
||
|
||
// ! это может случиться, если запрос идёт не по папке 0, а по 1. почему-то read'ов нет
|
||
// ! в итоге, чтобы получить 1 диалог, делается первый запрос по папке 0, потом запрос для архивных по папке 1, и потом ещё перезагрузка архивного диалога
|
||
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);
|
||
} */
|
||
}
|
||
|
||
if(!maxSeenIdIncremented &&
|
||
!appPeersManager.isChannel(appPeersManager.getPeerID(dialog.peer))) {
|
||
this.incrementMaxSeenID(dialog.top_message);
|
||
maxSeenIdIncremented = true;
|
||
}
|
||
});
|
||
|
||
if(Object.keys(noIDsDialogs).length) {
|
||
//setTimeout(() => { // test bad situation
|
||
this.reloadConversation(Object.keys(noIDsDialogs).map(id => +id)).then(() => {
|
||
$rootScope.$broadcast('dialogs_multiupdate', noIDsDialogs);
|
||
|
||
for(let peerID in noIDsDialogs) {
|
||
$rootScope.$broadcast('dialog_unread', {peerID: +peerID});
|
||
}
|
||
});
|
||
//}, 10e3);
|
||
}
|
||
|
||
const count = (dialogsResult as MessagesDialogs.messagesDialogsSlice).count;
|
||
|
||
if(!dialogsResult.dialogs.length ||
|
||
!count ||
|
||
dialogs.length >= count) {
|
||
this.dialogsStorage.allDialogsLoaded[folderID] = true;
|
||
}
|
||
|
||
if(hasPrepend) {
|
||
this.scheduleHandleNewDialogs();
|
||
} else {
|
||
$rootScope.$broadcast('dialogs_multiupdate', {});
|
||
}
|
||
|
||
return count;
|
||
});
|
||
}
|
||
|
||
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] || {
|
||
_: 'messageEmpty',
|
||
id: messageID,
|
||
deleted: true,
|
||
pFlags: {}
|
||
};
|
||
}
|
||
|
||
public getMessagePeer(message: any): number {
|
||
var toID = message.peer_id && appPeersManager.getPeerID(message.peer_id) || 0;
|
||
|
||
return toID;
|
||
}
|
||
|
||
public getDialogByPeerID(peerID: number): [Dialog, number] | [] {
|
||
return this.dialogsStorage.getDialog(peerID);
|
||
}
|
||
|
||
public reloadConversation(peerID: number | number[]) {
|
||
[].concat(peerID).forEach(peerID => {
|
||
if(!this.reloadConversationsPeers.includes(peerID)) {
|
||
this.reloadConversationsPeers.push(peerID);
|
||
this.log('will reloadConversation', peerID);
|
||
}
|
||
});
|
||
|
||
if(this.reloadConversationsPromise) return this.reloadConversationsPromise;
|
||
return this.reloadConversationsPromise = new Promise((resolve, reject) => {
|
||
setTimeout(() => {
|
||
const peers = this.reloadConversationsPeers.map(peerID => appPeersManager.getInputDialogPeerByID(peerID));
|
||
this.reloadConversationsPeers.length = 0;
|
||
|
||
apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => {
|
||
this.applyConversations(result);
|
||
resolve();
|
||
}, reject).finally(() => {
|
||
this.reloadConversationsPromise = null;
|
||
});
|
||
}, 0);
|
||
});
|
||
}
|
||
|
||
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 savePinnedMessage(peerID: number, mid: number) {
|
||
if(!mid) {
|
||
delete this.pinnedMessages[peerID];
|
||
} else {
|
||
this.pinnedMessages[peerID] = mid;
|
||
|
||
if(!this.messagesStorage.hasOwnProperty(mid)) {
|
||
this.wrapSingleMessage(mid).then(() => {
|
||
$rootScope.$broadcast('peer_pinned_message', peerID);
|
||
});
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
$rootScope.$broadcast('peer_pinned_message', peerID);
|
||
}
|
||
|
||
public getPinnedMessage(peerID: number) {
|
||
return this.getMessage(this.pinnedMessages[peerID] || 0);
|
||
}
|
||
|
||
public updatePinnedMessage(peerID: number, msgID: number) {
|
||
apiManager.invokeApi('messages.updatePinnedMessage', {
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
id: msgID
|
||
}).then(updates => {
|
||
/////this.log('pinned updates:', updates);
|
||
apiUpdatesManager.processUpdateMessage(updates);
|
||
});
|
||
}
|
||
|
||
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[], options: {
|
||
isEdited?: boolean
|
||
} = {}) {
|
||
let albums: Set<string>;
|
||
messages.forEach((message) => {
|
||
if(message.pFlags === undefined) {
|
||
message.pFlags = {};
|
||
}
|
||
|
||
if(message._ == 'messageEmpty') {
|
||
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);
|
||
|
||
const mid = appMessagesIDsManager.getFullMessageID(message.id, channelID);
|
||
message.mid = mid;
|
||
|
||
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];
|
||
if(dialog && mid > 0) {
|
||
if(mid > dialog[message.pFlags.out
|
||
? 'read_outbox_max_id'
|
||
: 'read_inbox_max_id']) {
|
||
message.pFlags.unread = true;
|
||
}
|
||
}
|
||
// this.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id'])
|
||
|
||
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);
|
||
}
|
||
|
||
message.date -= serverTimeManager.serverTimeOffset;
|
||
|
||
const myID = appUsersManager.getSelf().id;
|
||
|
||
message.peerID = peerID;
|
||
if(message.peerID == myID/* && !message.from_id && !message.fwd_from */) {
|
||
message.fromID = message.fwd_from?.from_id ? appPeersManager.getPeerID(message.fwd_from.from_id) : myID;
|
||
} else {
|
||
message.fromID = message.pFlags.post || (!message.pFlags.out && !message.from_id) ? peerID : appPeersManager.getPeerID(message.from_id);
|
||
}
|
||
|
||
const fwdHeader = message.fwd_from;
|
||
if(fwdHeader) {
|
||
//if(peerID == myID) {
|
||
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;
|
||
}
|
||
|
||
/* 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 {
|
||
apiMessage.fwdPostID = fwdHeader.channel_post;
|
||
} */
|
||
|
||
message.fwdFromID = appPeersManager.getPeerID(fwdHeader.from_id);
|
||
|
||
fwdHeader.date -= serverTimeManager.serverTimeOffset;
|
||
}
|
||
|
||
if(message.via_bot_id > 0) {
|
||
message.viaBotID = message.via_bot_id;
|
||
}
|
||
|
||
const mediaContext: ReferenceContext = {
|
||
type: 'message',
|
||
messageID: mid
|
||
};
|
||
|
||
if(message.media) {
|
||
switch(message.media._) {
|
||
case 'messageMediaEmpty':
|
||
delete message.media;
|
||
break;
|
||
case 'messageMediaPhoto':
|
||
if(message.media.ttl_seconds) {
|
||
message.media = {_: 'messageMediaUnsupportedWeb'};
|
||
} else {
|
||
message.media.photo = appPhotosManager.savePhoto(message.media.photo, mediaContext);
|
||
//appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
|
||
}
|
||
break;
|
||
case 'messageMediaPoll':
|
||
message.media.poll = appPollsManager.savePoll(message.media.poll, message.media.results);
|
||
break;
|
||
case 'messageMediaDocument':
|
||
if(message.media.ttl_seconds) {
|
||
message.media = {_: 'messageMediaUnsupportedWeb'};
|
||
} else {
|
||
message.media.document = appDocsManager.saveDoc(message.media.document, mediaContext); // 11.04.2020 warning
|
||
}
|
||
|
||
|
||
break;
|
||
case 'messageMediaWebPage':
|
||
/* if(apiMessage.media.webpage.document) {
|
||
appDocsManager.saveDoc(apiMessage.media.webpage.document, mediaContext);
|
||
} */
|
||
appWebPagesManager.saveWebPage(message.media.webpage, message.mid, mediaContext);
|
||
break;
|
||
/*case 'messageMediaGame':
|
||
AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext);
|
||
apiMessage.media.handleMessage = true;
|
||
break; */
|
||
case 'messageMediaInvoice':
|
||
message.media = {_: 'messageMediaUnsupportedWeb'};
|
||
break;
|
||
case 'messageMediaGeoLive':
|
||
message.media._ = 'messageMediaGeo';
|
||
break;
|
||
}
|
||
}
|
||
|
||
if(message.action) {
|
||
let migrateFrom: number;
|
||
let migrateTo: number;
|
||
switch(message.action._) {
|
||
//case 'messageActionChannelEditPhoto':
|
||
case 'messageActionChatEditPhoto':
|
||
message.action.photo = appPhotosManager.savePhoto(message.action.photo, mediaContext);
|
||
//appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext);
|
||
if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода.
|
||
message.action._ = 'messageActionChannelEditPhoto';
|
||
}
|
||
break;
|
||
|
||
case 'messageActionChatEditTitle':
|
||
if(isBroadcast) {
|
||
message.action._ = 'messageActionChannelEditTitle';
|
||
}
|
||
break;
|
||
|
||
case 'messageActionChatDeletePhoto':
|
||
if(isBroadcast) {
|
||
message.action._ = 'messageActionChannelDeletePhoto';
|
||
}
|
||
break;
|
||
|
||
case 'messageActionChatAddUser':
|
||
if(message.action.users.length == 1) {
|
||
message.action.user_id = message.action.users[0];
|
||
if(message.fromID == message.action.user_id) {
|
||
if(isChannel) {
|
||
message.action._ = 'messageActionChatJoined';
|
||
} else {
|
||
message.action._ = 'messageActionChatReturn';
|
||
}
|
||
}
|
||
} else if(message.action.users.length > 1) {
|
||
message.action._ = 'messageActionChatAddUsers';
|
||
}
|
||
break;
|
||
|
||
case 'messageActionChatDeleteUser':
|
||
if(message.fromID == message.action.user_id) {
|
||
message.action._ = 'messageActionChatLeave';
|
||
}
|
||
break;
|
||
|
||
case 'messageActionChannelMigrateFrom':
|
||
migrateFrom = -message.action.chat_id;
|
||
migrateTo = -channelID;
|
||
break
|
||
|
||
case 'messageActionChatMigrateTo':
|
||
migrateFrom = -channelID;
|
||
migrateTo = -message.action.channel_id;
|
||
break;
|
||
|
||
case 'messageActionHistoryClear':
|
||
//apiMessage.deleted = true;
|
||
message.clear_history = true;
|
||
delete message.pFlags.out;
|
||
delete message.pFlags.unread;
|
||
break;
|
||
|
||
case 'messageActionPhoneCall':
|
||
delete message.fromID;
|
||
message.action.type =
|
||
(message.pFlags.out ? 'out_' : 'in_') +
|
||
(
|
||
message.action.reason._ == 'phoneCallDiscardReasonMissed' ||
|
||
message.action.reason._ == 'phoneCallDiscardReasonBusy'
|
||
? '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);
|
||
}
|
||
|
||
//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>';
|
||
} 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;
|
||
}
|
||
|
||
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-i-d="${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) {
|
||
if(!this.migratedFromTo[migrateFrom] &&
|
||
!this.migratedToFrom[migrateTo] &&
|
||
appChatsManager.hasChat(-migrateTo)) {
|
||
const fromChat = appChatsManager.getChat(-migrateFrom);
|
||
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]});
|
||
}
|
||
|
||
$rootScope.$broadcast('dialog_migrate', {migrateFrom, migrateTo});
|
||
}, 100);
|
||
}
|
||
}
|
||
}
|
||
|
||
public canMessageBeEdited(message: any, kind: 'text' | 'poll') {
|
||
const goodMedias = [
|
||
'messageMediaPhoto',
|
||
'messageMediaDocument',
|
||
'messageMediaWebPage',
|
||
'messageMediaPending'
|
||
];
|
||
|
||
if(kind == 'poll') {
|
||
goodMedias.push('messageMediaPoll');
|
||
}
|
||
|
||
if(message._ != 'message' ||
|
||
message.deleted ||
|
||
message.fwd_from ||
|
||
message.via_bot_id ||
|
||
message.media && goodMedias.indexOf(message.media._) == -1 ||
|
||
message.fromID && appUsersManager.isBot(message.fromID)) {
|
||
return false;
|
||
}
|
||
|
||
if(message.media &&
|
||
message.media._ == 'messageMediaDocument' &&
|
||
message.media.document.sticker) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
public 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;
|
||
}
|
||
|
||
if(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];
|
||
if(message) {
|
||
return message.peerID > 0 || message.fromID == $rootScope.myID || appChatsManager.hasRights(message.peerID, 'deleteRevoke');
|
||
} else {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) {
|
||
// * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages
|
||
|
||
appUsersManager.saveApiUsers(dialogsResult.users);
|
||
appChatsManager.saveApiChats(dialogsResult.chats);
|
||
this.saveMessages(dialogsResult.messages);
|
||
|
||
//this.log('applyConversation', dialogsResult);
|
||
|
||
const updatedDialogs: {[peerID: number]: Dialog} = {};
|
||
let hasUpdated = false;
|
||
(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;
|
||
}
|
||
}
|
||
|
||
/* const d = Object.assign({}, dialog);
|
||
if(peerID == 239602833) {
|
||
this.log.error('applyConversation lun', dialog, d);
|
||
} */
|
||
|
||
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;
|
||
} */
|
||
|
||
this.saveConversation(dialog);
|
||
|
||
if(wasDialogBefore) {
|
||
$rootScope.$broadcast('dialog_top', dialog);
|
||
} else {
|
||
updatedDialogs[peerID] = dialog;
|
||
hasUpdated = true;
|
||
}
|
||
} else {
|
||
const dropped = this.dialogsStorage.dropDialog(peerID);
|
||
if(dropped.length) {
|
||
$rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: dropped[0]});
|
||
}
|
||
}
|
||
|
||
if(this.newUpdatesAfterReloadToHandle[peerID] !== undefined) {
|
||
for(const i in this.newUpdatesAfterReloadToHandle[peerID]) {
|
||
const update = this.newUpdatesAfterReloadToHandle[peerID][i];
|
||
this.handleUpdate(update);
|
||
}
|
||
|
||
delete this.newUpdatesAfterReloadToHandle[peerID];
|
||
}
|
||
});
|
||
|
||
if(hasUpdated) {
|
||
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs);
|
||
}
|
||
}
|
||
|
||
public saveConversation(dialog: Dialog, folderID = 0) {
|
||
const peerID = appPeersManager.getPeerID(dialog.peer);
|
||
if(!peerID) {
|
||
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);
|
||
|
||
let mid: number, message;
|
||
if(dialog.top_message) {
|
||
mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
|
||
message = this.getMessage(mid);
|
||
} else {
|
||
mid = this.tempID--;
|
||
message = {
|
||
_: 'message',
|
||
id: mid,
|
||
mid: mid,
|
||
from_id: appPeersManager.getOutputPeer(appUsersManager.getSelf().id),
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
deleted: true,
|
||
pFlags: {out: true},
|
||
date: 0,
|
||
message: ''
|
||
};
|
||
this.saveMessages([message]);
|
||
}
|
||
|
||
if(!message?.pFlags) {
|
||
this.log.error('saveConversation no message:', dialog, message);
|
||
}
|
||
|
||
if(!channelID && peerID < 0) {
|
||
const chat = appChatsManager.getChat(-peerID);
|
||
if(chat && chat.migrated_to && chat.pFlags.deactivated) {
|
||
const migratedToPeer = appPeersManager.getPeerID(chat.migrated_to);
|
||
this.migratedFromTo[peerID] = migratedToPeer;
|
||
this.migratedToFrom[migratedToPeer] = peerID;
|
||
return;
|
||
}
|
||
}
|
||
|
||
dialog.top_message = mid;
|
||
dialog.read_inbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_inbox_max_id, channelID);
|
||
dialog.read_outbox_max_id = appMessagesIDsManager.getFullMessageID(dialog.read_outbox_max_id, channelID);
|
||
|
||
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;
|
||
|
||
this.dialogsStorage.generateIndexForDialog(dialog);
|
||
this.dialogsStorage.pushDialog(dialog, message.date);
|
||
|
||
// 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;
|
||
}
|
||
|
||
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) {
|
||
dialog.unread_count++;
|
||
} */
|
||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||
$rootScope.$broadcast('history_reply_markup', {peerID});
|
||
}
|
||
} else if(!historyStorage.history.length && !historyStorage.pending.length) {
|
||
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid);
|
||
}
|
||
|
||
if(channelID && dialog.pts) {
|
||
apiUpdatesManager.addChannelState(channelID, dialog.pts);
|
||
}
|
||
|
||
//if(this.filtersStorage.inited) {
|
||
//this.filtersStorage.processDialog(dialog);
|
||
//}
|
||
}
|
||
|
||
public mergeReplyKeyboard(historyStorage: HistoryStorage, message: any) {
|
||
// this.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup)
|
||
if(!message.reply_markup &&
|
||
!message.pFlags?.out &&
|
||
!message.action) {
|
||
return false;
|
||
}
|
||
if(message.reply_markup &&
|
||
message.reply_markup._ == 'replyInlineMarkup') {
|
||
return false;
|
||
}
|
||
var messageReplyMarkup = message.reply_markup;
|
||
var lastReplyMarkup = historyStorage.reply_markup;
|
||
if(messageReplyMarkup) {
|
||
if(lastReplyMarkup && lastReplyMarkup.mid >= message.mid) {
|
||
return false;
|
||
}
|
||
|
||
if(messageReplyMarkup.pFlags.selective) {
|
||
return false;
|
||
}
|
||
|
||
if(historyStorage.maxOutID &&
|
||
message.mid < historyStorage.maxOutID &&
|
||
messageReplyMarkup.pFlags.single_use) {
|
||
messageReplyMarkup.pFlags.hidden = true;
|
||
}
|
||
messageReplyMarkup = Object.assign({
|
||
mid: message.mid
|
||
}, messageReplyMarkup);
|
||
if(messageReplyMarkup._ != 'replyKeyboardHide') {
|
||
messageReplyMarkup.fromID = appPeersManager.getPeerID(message.from_id);
|
||
}
|
||
historyStorage.reply_markup = messageReplyMarkup;
|
||
// this.log('set', historyStorage.reply_markup)
|
||
return true;
|
||
}
|
||
|
||
if(message.pFlags.out) {
|
||
if(lastReplyMarkup) {
|
||
if(lastReplyMarkup.pFlags.single_use &&
|
||
!lastReplyMarkup.pFlags.hidden &&
|
||
(message.mid > lastReplyMarkup.mid || message.mid < 0) &&
|
||
message.message) {
|
||
lastReplyMarkup.pFlags.hidden = true;
|
||
// this.log('set', historyStorage.reply_markup)
|
||
return true;
|
||
}
|
||
} else if(!historyStorage.maxOutID ||
|
||
message.mid > historyStorage.maxOutID) {
|
||
historyStorage.maxOutID = message.mid;
|
||
}
|
||
}
|
||
|
||
if(message.action &&
|
||
message.action._ == 'messageActionChatDeleteUser' &&
|
||
(lastReplyMarkup
|
||
? message.action.user_id == lastReplyMarkup.fromID
|
||
: appUsersManager.isBot(message.action.user_id)
|
||
)
|
||
) {
|
||
historyStorage.reply_markup = {
|
||
_: 'replyKeyboardHide',
|
||
mid: message.mid,
|
||
pFlags: {}
|
||
};
|
||
// this.log('set', historyStorage.reply_markup)
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public getSearch(peerID = 0, query: string = '', inputFilter: {
|
||
_?: MyInputMessagesFilter
|
||
} = {_: 'inputMessagesFilterEmpty'}, maxID: number, limit = 20, offsetRate = 0, backLimit = 0): Promise<{
|
||
count: number,
|
||
next_rate: number,
|
||
history: number[]
|
||
}> {
|
||
//peerID = peerID ? parseInt(peerID) : 0;
|
||
const foundMsgs: number[] = [];
|
||
const useSearchCache = !query;
|
||
const newSearchFilter = {peer: peerID, filter: inputFilter};
|
||
const sameSearchCache = useSearchCache && deepEqual(this.lastSearchFilter, newSearchFilter);
|
||
|
||
if(useSearchCache && !sameSearchCache) {
|
||
// this.log.warn(dT(), 'new search filter', lastSearchFilter, newSearchFilter)
|
||
this.lastSearchFilter = newSearchFilter;
|
||
this.lastSearchResults = [];
|
||
}
|
||
|
||
//this.log(dT(), 'search', useSearchCache, sameSearchCache, this.lastSearchResults, maxID);
|
||
|
||
if(peerID && !maxID && !query) {
|
||
var historyStorage = this.historiesStorage[peerID];
|
||
|
||
if(historyStorage !== undefined && historyStorage.history.length) {
|
||
var neededContents: {
|
||
[messageMediaType: string]: boolean
|
||
} = {},
|
||
neededDocTypes: string[] = [], excludeDocTypes: string[] = [];
|
||
|
||
switch(inputFilter._) {
|
||
case 'inputMessagesFilterPhotos':
|
||
neededContents['messageMediaPhoto'] = true;
|
||
break;
|
||
|
||
case 'inputMessagesFilterPhotoVideo':
|
||
neededContents['messageMediaPhoto'] = true;
|
||
neededContents['messageMediaDocument'] = true;
|
||
neededDocTypes.push('video');
|
||
break;
|
||
|
||
case 'inputMessagesFilterVideo':
|
||
neededContents['messageMediaDocument'] = true;
|
||
neededDocTypes.push('video');
|
||
break;
|
||
|
||
case 'inputMessagesFilterDocument':
|
||
neededContents['messageMediaDocument'] = true;
|
||
excludeDocTypes.push('video');
|
||
break;
|
||
|
||
case 'inputMessagesFilterVoice':
|
||
neededContents['messageMediaDocument'] = true;
|
||
neededDocTypes.push('voice');
|
||
break;
|
||
|
||
case 'inputMessagesFilterRoundVoice':
|
||
neededContents['messageMediaDocument'] = true;
|
||
neededDocTypes.push('round', 'voice');
|
||
break;
|
||
|
||
case 'inputMessagesFilterRoundVideo':
|
||
neededContents['messageMediaDocument'] = true;
|
||
neededDocTypes.push('round');
|
||
break;
|
||
|
||
case 'inputMessagesFilterMusic':
|
||
neededContents['messageMediaDocument'] = true;
|
||
neededDocTypes.push('audio');
|
||
break;
|
||
|
||
case 'inputMessagesFilterUrl':
|
||
neededContents['url'] = true;
|
||
break;
|
||
|
||
case 'inputMessagesFilterChatPhotos':
|
||
neededContents['avatar'] = true;
|
||
break;
|
||
|
||
/* case 'inputMessagesFilterMyMentions':
|
||
neededContents['mentioned'] = true;
|
||
break; */
|
||
|
||
default:
|
||
return Promise.resolve({
|
||
count: 0,
|
||
next_rate: 0,
|
||
history: [] as number[]
|
||
});
|
||
}
|
||
|
||
for(let i = 0, length = historyStorage.history.length; i < length; i++) {
|
||
const message = this.messagesStorage[historyStorage.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;
|
||
}
|
||
|
||
if(found) {
|
||
foundMsgs.push(message.mid);
|
||
if(foundMsgs.length >= limit) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// this.log.warn(dT(), 'before append', foundMsgs)
|
||
if(foundMsgs.length < limit && this.lastSearchResults.length && sameSearchCache) {
|
||
var minID = foundMsgs.length ? foundMsgs[foundMsgs.length - 1] : false;
|
||
for(let i = 0; i < this.lastSearchResults.length; i++) {
|
||
if(minID === false || this.lastSearchResults[i] < minID) {
|
||
foundMsgs.push(this.lastSearchResults[i]);
|
||
if(foundMsgs.length >= limit) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// this.log.warn(dT(), 'after append', foundMsgs)
|
||
}
|
||
|
||
if(foundMsgs.length) {
|
||
if(foundMsgs.length < limit) {
|
||
maxID = foundMsgs[foundMsgs.length - 1];
|
||
limit = limit - foundMsgs.length;
|
||
} else {
|
||
if(useSearchCache) {
|
||
this.lastSearchResults = listMergeSorted(this.lastSearchResults, foundMsgs)
|
||
}
|
||
|
||
return Promise.resolve({
|
||
count: 0,
|
||
next_rate: 0,
|
||
history: foundMsgs
|
||
});
|
||
}
|
||
}
|
||
|
||
let apiPromise: Promise<any>;
|
||
if(peerID || !query) {
|
||
apiPromise = apiManager.invokeApi('messages.search', {
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
q: query || '',
|
||
filter: (inputFilter || {_: 'inputMessagesFilterEmpty'}) as any as MessagesFilter,
|
||
min_date: 0,
|
||
max_date: 0,
|
||
limit,
|
||
offset_id: appMessagesIDsManager.getMessageLocalID(maxID) || 0,
|
||
add_offset: backLimit ? -backLimit : 0,
|
||
max_id: 0,
|
||
min_id: 0,
|
||
hash: 0
|
||
}, {
|
||
//timeout: APITIMEOUT,
|
||
noErrorBox: true
|
||
});
|
||
} else {
|
||
var offsetDate = 0;
|
||
var offsetPeerID = 0;
|
||
var offsetID = 0;
|
||
var offsetMessage = maxID && this.getMessage(maxID);
|
||
|
||
if(offsetMessage && offsetMessage.date) {
|
||
offsetDate = offsetMessage.date + ServerTimeManager.serverTimeOffset;
|
||
offsetID = offsetMessage.id;
|
||
offsetPeerID = this.getMessagePeer(offsetMessage);
|
||
}
|
||
|
||
apiPromise = apiManager.invokeApi('messages.searchGlobal', {
|
||
q: query,
|
||
filter: (inputFilter || {_: 'inputMessagesFilterEmpty'}) as any as MessagesFilter,
|
||
min_date: 0,
|
||
max_date: 0,
|
||
offset_rate: offsetRate,
|
||
offset_peer: appPeersManager.getInputPeerByID(offsetPeerID),
|
||
offset_id: appMessagesIDsManager.getMessageLocalID(offsetID),
|
||
limit
|
||
}, {
|
||
//timeout: APITIMEOUT,
|
||
noErrorBox: true
|
||
});
|
||
}
|
||
|
||
return apiPromise.then((searchResult: any) => {
|
||
appUsersManager.saveApiUsers(searchResult.users);
|
||
appChatsManager.saveApiChats(searchResult.chats);
|
||
this.saveMessages(searchResult.messages);
|
||
|
||
this.log('messages.search result:', inputFilter, searchResult);
|
||
|
||
const foundCount: number = searchResult.count || (foundMsgs.length + searchResult.messages.length);
|
||
|
||
searchResult.messages.forEach((message: any) => {
|
||
const peerID = this.getMessagePeer(message);
|
||
if(peerID < 0) {
|
||
const chat = appChatsManager.getChat(-peerID);
|
||
if(chat.migrated_to) {
|
||
this.migrateChecks(peerID, -chat.migrated_to.channel_id);
|
||
}
|
||
}
|
||
foundMsgs.push(message.mid);
|
||
});
|
||
|
||
if(useSearchCache &&
|
||
(!maxID || sameSearchCache && this.lastSearchResults.indexOf(maxID) >= 0)) {
|
||
this.lastSearchResults = listMergeSorted(this.lastSearchResults, foundMsgs);
|
||
}
|
||
// this.log(dT(), 'after API', foundMsgs, lastSearchResults)
|
||
|
||
return {
|
||
count: foundCount,
|
||
next_rate: searchResult.next_rate,
|
||
history: foundMsgs
|
||
};
|
||
});
|
||
}
|
||
|
||
handleNewMessages = () => {
|
||
clearTimeout(this.newMessagesHandlePromise);
|
||
this.newMessagesHandlePromise = 0;
|
||
|
||
$rootScope.$broadcast('history_multiappend', this.newMessagesToHandle);
|
||
this.newMessagesToHandle = {};
|
||
};
|
||
|
||
handleNewDialogs = () => {
|
||
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];
|
||
} else {
|
||
this.dialogsStorage.pushDialog(dialog);
|
||
if(!appPeersManager.isChannel(+peerID)) {
|
||
newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
//this.log('after order:', this.dialogsStorage[0].map(d => d.peerID));
|
||
|
||
if(newMaxSeenID != 0) {
|
||
this.incrementMaxSeenID(newMaxSeenID);
|
||
}
|
||
|
||
$rootScope.$broadcast('dialogs_multiupdate', this.newDialogsToHandle as any);
|
||
this.newDialogsToHandle = {};
|
||
};
|
||
|
||
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) {
|
||
// console.trace('start read')
|
||
const isChannel = appPeersManager.isChannel(peerID);
|
||
const historyStorage = this.historiesStorage[peerID];
|
||
const foundDialog = this.getDialogByPeerID(peerID)[0];
|
||
|
||
if(!foundDialog || !foundDialog.unread_count) {
|
||
if(!historyStorage || !historyStorage.history.length) {
|
||
return Promise.resolve(false);
|
||
}
|
||
|
||
let foundUnread = !!historyStorage.history.find(messageID => {
|
||
const message = this.messagesStorage[messageID];
|
||
return message && !message.pFlags.out && message.pFlags.unread;
|
||
});
|
||
|
||
if(!foundUnread) {
|
||
return Promise.resolve(false);
|
||
}
|
||
}
|
||
|
||
if(isChannel) {
|
||
maxID = appMessagesIDsManager.getMessageLocalID(maxID);
|
||
}
|
||
|
||
if(!historyStorage.readMaxID || maxID > historyStorage.readMaxID) {
|
||
historyStorage.readMaxID = maxID;
|
||
}
|
||
|
||
if(historyStorage.readPromise) {
|
||
return historyStorage.readPromise;
|
||
}
|
||
|
||
let apiPromise: Promise<boolean>;
|
||
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;
|
||
});
|
||
} else {
|
||
apiPromise = apiManager.invokeApi('messages.readHistory', {
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
max_id: maxID
|
||
}).then((affectedMessages) => {
|
||
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(() => {
|
||
delete historyStorage.readPromise;
|
||
|
||
if(historyStorage.readMaxID > maxID) {
|
||
this.readHistory(peerID, historyStorage.readMaxID);
|
||
} else {
|
||
delete historyStorage.readMaxID;
|
||
}
|
||
});
|
||
|
||
return historyStorage.readPromise = apiPromise;
|
||
}
|
||
|
||
public readMessages(messageIDs: number[]) {
|
||
const splitted = appMessagesIDsManager.splitMessageIDsByChannels(messageIDs);
|
||
Object.keys(splitted.msgIDs).forEach((channelID: number | string) => {
|
||
channelID = +channelID;
|
||
const msgIDs = splitted.msgIDs[channelID];
|
||
|
||
if(channelID > 0) {
|
||
apiManager.invokeApi('channels.readMessageContents', {
|
||
channel: appChatsManager.getChannelInput(channelID),
|
||
id: msgIDs
|
||
}).then(() => {
|
||
apiUpdatesManager.processUpdateMessage({
|
||
_: 'updateShort',
|
||
update: {
|
||
_: 'updateChannelReadMessagesContents',
|
||
channel_id: channelID,
|
||
messages: msgIDs
|
||
}
|
||
});
|
||
});
|
||
} else {
|
||
apiManager.invokeApi('messages.readMessageContents', {
|
||
id: msgIDs
|
||
}).then((affectedMessages) => {
|
||
apiUpdatesManager.processUpdateMessage({
|
||
_: 'updateShort',
|
||
update: {
|
||
_: 'updateReadMessagesContents',
|
||
messages: msgIDs,
|
||
pts: affectedMessages.pts,
|
||
pts_count: affectedMessages.pts_count
|
||
}
|
||
});
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
public handleUpdate(update: Update) {
|
||
this.log.debug('AMM: handleUpdate:', update._);
|
||
switch(update._) {
|
||
case 'updateMessageID': {
|
||
const randomID = update.random_id;
|
||
const pendingData = this.pendingByRandomID[randomID];
|
||
//this.log('AMM updateMessageID:', update, pendingData);
|
||
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];
|
||
if(message) {
|
||
const historyStorage = this.historiesStorage[peerID];
|
||
const pos = historyStorage.pending.indexOf(tempID);
|
||
if(pos != -1) {
|
||
historyStorage.pending.splice(pos, 1);
|
||
}
|
||
delete this.messagesStorage[tempID];
|
||
|
||
this.finalizePendingMessageCallbacks(tempID, mid);
|
||
} else {
|
||
this.pendingByMessageID[mid] = randomID;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 'updateNewMessage':
|
||
case 'updateNewChannelMessage': {
|
||
const message = update.message as MyMessage;
|
||
const peerID = this.getMessagePeer(message);
|
||
const foundDialog = this.getDialogByPeerID(peerID);
|
||
|
||
if(!foundDialog.length) {
|
||
this.newDialogsToHandle[peerID] = {reload: true};
|
||
this.scheduleHandleNewDialogs();
|
||
if(this.newUpdatesAfterReloadToHandle[peerID] === undefined) {
|
||
this.newUpdatesAfterReloadToHandle[peerID] = [];
|
||
}
|
||
this.newUpdatesAfterReloadToHandle[peerID].push(update);
|
||
break;
|
||
}
|
||
|
||
if(update._ == 'updateNewChannelMessage') {
|
||
const chat = appChatsManager.getChat(-peerID);
|
||
if(chat.pFlags && (chat.pFlags.left || chat.pFlags.kicked)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
this.saveMessages([message]);
|
||
// this.log.warn(dT(), 'message unread', message.mid, message.pFlags.unread)
|
||
|
||
let historyStorage = this.historiesStorage[peerID];
|
||
if(historyStorage === undefined) {
|
||
historyStorage = this.historiesStorage[peerID] = {
|
||
count: null,
|
||
history: [],
|
||
pending: []
|
||
};
|
||
}
|
||
|
||
const history = message.mid > 0 ? historyStorage.history : historyStorage.pending;
|
||
if(history.indexOf(message.mid) != -1) {
|
||
return false;
|
||
}
|
||
const topMsgID = history[0];
|
||
history.unshift(message.mid);
|
||
if(message.mid > 0 && message.mid < topMsgID) {
|
||
history.sort((a, b) => {
|
||
return b - a;
|
||
});
|
||
}
|
||
|
||
if(message.mid > 0 &&
|
||
historyStorage.count !== null) {
|
||
historyStorage.count++;
|
||
}
|
||
|
||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||
$rootScope.$broadcast('history_reply_markup', {peerID});
|
||
}
|
||
|
||
if(!message.pFlags.out && message.from_id) {
|
||
appUsersManager.forceUserOnline(appPeersManager.getPeerID(message.from_id));
|
||
}
|
||
|
||
const randomID = this.pendingByMessageID[message.mid];
|
||
let pendingMessage: any;
|
||
|
||
if(randomID) {
|
||
if(pendingMessage = this.finalizePendingMessage(randomID, message)) {
|
||
$rootScope.$broadcast('history_update', {peerID, mid: message.mid});
|
||
}
|
||
|
||
delete this.pendingByMessageID[message.mid];
|
||
}
|
||
|
||
if(!pendingMessage) {
|
||
if(this.newMessagesToHandle[peerID] === undefined) {
|
||
this.newMessagesToHandle[peerID] = [];
|
||
}
|
||
|
||
this.newMessagesToHandle[peerID].push(message.mid);
|
||
if(!this.newMessagesHandlePromise) {
|
||
this.newMessagesHandlePromise = window.setTimeout(this.handleNewMessages, 0);
|
||
}
|
||
}
|
||
|
||
const inboxUnread = !message.pFlags.out && message.pFlags.unread;
|
||
const dialog = foundDialog[0];
|
||
dialog.top_message = message.mid;
|
||
if(inboxUnread) {
|
||
dialog.unread_count++;
|
||
}
|
||
if(!dialog.pFlags.pinned || !dialog.index) {
|
||
dialog.index = this.dialogsStorage.generateDialogIndex(message.date);
|
||
}
|
||
|
||
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});
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case 'updateFolderPeers': { // only 0 and 1 folders
|
||
this.log('updateFolderPeers', update);
|
||
const peers = update.folder_peers;
|
||
|
||
this.scheduleHandleNewDialogs();
|
||
peers.forEach((folderPeer: any) => {
|
||
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);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case 'updatePinnedDialogs': {
|
||
const folderID = update.folder_id ?? 0;
|
||
|
||
this.log('updatePinnedDialogs', update);
|
||
const newPinned: {[peerID: number]: true} = {};
|
||
if(!update.order) {
|
||
apiManager.invokeApi('messages.getPinnedDialogs', {
|
||
folder_id: folderID
|
||
}).then((dialogsResult) => {
|
||
dialogsResult.dialogs.reverse();
|
||
this.applyConversations(dialogsResult);
|
||
|
||
dialogsResult.dialogs.forEach((dialog) => {
|
||
newPinned[dialog.peerID] = true;
|
||
});
|
||
|
||
this.dialogsStorage.getFolder(folderID).forEach((dialog) => {
|
||
const peerID = dialog.peerID;
|
||
if(dialog.pFlags.pinned && !newPinned[peerID]) {
|
||
this.newDialogsToHandle[peerID] = {reload: true};
|
||
this.scheduleHandleNewDialogs();
|
||
}
|
||
});
|
||
});
|
||
|
||
break;
|
||
}
|
||
|
||
//this.log('before order:', this.dialogsStorage[0].map(d => d.peerID));
|
||
|
||
this.dialogsStorage.pinnedOrders[folderID].length = 0;
|
||
let willHandle = false;
|
||
update.order.reverse(); // index must be higher
|
||
update.order.forEach((peer: any) => {
|
||
const peerID = appPeersManager.getPeerID(peer.peer);
|
||
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();
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
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);
|
||
if(this.messagesStorage[mid] === undefined) {
|
||
break;
|
||
}
|
||
|
||
// console.trace(dT(), 'edit message', message)
|
||
this.saveMessages([message]/* , {isEdited: true} */);
|
||
safeReplaceObject(this.messagesStorage[mid], message);
|
||
|
||
const dialog = this.getDialogByPeerID(peerID)[0];
|
||
const isTopMessage = dialog && dialog.top_message == mid;
|
||
// @ts-ignore
|
||
if(message.clear_history) { // that's will never happen
|
||
if(isTopMessage) {
|
||
$rootScope.$broadcast('dialog_flush', {peerID: peerID});
|
||
}
|
||
} else {
|
||
$rootScope.$broadcast('message_edit', {
|
||
peerID,
|
||
mid,
|
||
justMedia: false
|
||
});
|
||
|
||
const groupID = (message as Message.message).grouped_id;
|
||
if(this.pinnedMessages[peerID]) {
|
||
let pinnedMid: number;
|
||
if(groupID) {
|
||
const mids = this.getMidsByAlbum(groupID);
|
||
pinnedMid = mids.find(mid => this.pinnedMessages[peerID] == mid);
|
||
} else if(this.pinnedMessages[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);
|
||
}
|
||
}
|
||
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;
|
||
|
||
//this.log.warn(dT(), 'read', peerID, isOut ? 'out' : 'in', maxID)
|
||
|
||
if(peerID > 0 && isOut) {
|
||
appUsersManager.forceUserOnline(peerID);
|
||
}
|
||
|
||
for(let i = 0, length = history.length; i < length; i++) {
|
||
const messageID = history[i];
|
||
if(messageID > maxID) {
|
||
continue;
|
||
}
|
||
|
||
const message = this.messagesStorage[messageID];
|
||
if(!message) {
|
||
continue;
|
||
}
|
||
|
||
if(message.pFlags.out != isOut) {
|
||
continue;
|
||
}
|
||
|
||
if(!message.pFlags.unread) {
|
||
break;
|
||
}
|
||
|
||
// this.log.warn('read', messageID, message.pFlags.unread, message)
|
||
if(message && message.pFlags.unread) {
|
||
delete message.pFlags.unread;
|
||
if(!foundAffected) {
|
||
foundAffected = true;
|
||
}
|
||
|
||
if(!message.pFlags.out) {
|
||
if(foundDialog) {
|
||
newUnreadCount = --foundDialog.unread_count;
|
||
}
|
||
//NotificationsManager.cancel('msg' + messageID); // warning
|
||
}
|
||
}
|
||
}
|
||
|
||
if(foundDialog) {
|
||
if(!isOut && newUnreadCount && foundDialog.top_message <= maxID) {
|
||
newUnreadCount = foundDialog.unread_count = 0;
|
||
}
|
||
|
||
foundDialog[isOut ? 'read_outbox_max_id' : 'read_inbox_max_id'] = maxID;
|
||
}
|
||
|
||
// 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});
|
||
//}
|
||
|
||
if(foundAffected) {
|
||
$rootScope.$broadcast('messages_read');
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 'updateChannelReadMessagesContents': {
|
||
const channelID: number = update.channel_id;
|
||
const newMessages: number[] = [];
|
||
update.messages.forEach((msgID: number) => {
|
||
newMessages.push(appMessagesIDsManager.getFullMessageID(msgID, channelID));
|
||
});
|
||
update.messages = newMessages;
|
||
}
|
||
|
||
case 'updateReadMessagesContents': {
|
||
const messages: number[] = update.messages;
|
||
for(const messageID of messages) {
|
||
const message = this.messagesStorage[messageID];
|
||
if(message) {
|
||
delete message.pFlags.media_unread;
|
||
}
|
||
}
|
||
|
||
$rootScope.$broadcast('messages_media_read', messages);
|
||
break;
|
||
}
|
||
|
||
case 'updateChannelAvailableMessages': {
|
||
const channelID: number = update.channel_id;
|
||
const messages: number[] = [];
|
||
const peerID: number = -channelID;
|
||
const history = (this.historiesStorage[peerID] || {}).history || [];
|
||
if(history.length) {
|
||
history.forEach((msgID: number) => {
|
||
if(!update.available_min_id ||
|
||
appMessagesIDsManager.getMessageLocalID(msgID) <= update.available_min_id) {
|
||
messages.push(msgID);
|
||
}
|
||
});
|
||
}
|
||
|
||
(update as any as Update.updateDeleteChannelMessages).messages = messages;
|
||
}
|
||
|
||
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];
|
||
if(message) {
|
||
const peerID = this.getMessagePeer(message);
|
||
const history = historiesUpdated[peerID] || (historiesUpdated[peerID] = {count: 0, unread: 0, msgs: {}});
|
||
|
||
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});
|
||
}
|
||
}
|
||
|
||
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.pinnedMessages[peerID] == mid) {
|
||
this.savePinnedMessage(peerID, 0);
|
||
}
|
||
|
||
const peerMessagesToHandle = this.newMessagesToHandle[peerID];
|
||
if(peerMessagesToHandle && peerMessagesToHandle.length) {
|
||
const peerMessagesHandlePos = peerMessagesToHandle.indexOf(mid);
|
||
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];
|
||
if(historyStorage !== undefined) {
|
||
const newHistory = historyStorage.history.filter(mid => !updatedData.msgs[mid]);
|
||
const newPending = historyStorage.pending.filter(mid => !updatedData.msgs[mid]);
|
||
historyStorage.history = newHistory;
|
||
if(updatedData.count &&
|
||
historyStorage.count !== null &&
|
||
historyStorage.count > 0) {
|
||
historyStorage.count -= updatedData.count;
|
||
if(historyStorage.count < 0) {
|
||
historyStorage.count = 0;
|
||
}
|
||
}
|
||
|
||
historyStorage.pending = newPending;
|
||
|
||
$rootScope.$broadcast('history_delete', {peerID, msgs: updatedData.msgs});
|
||
}
|
||
|
||
const foundDialog = this.getDialogByPeerID(peerID)[0];
|
||
if(foundDialog) {
|
||
if(updatedData.unread) {
|
||
foundDialog.unread_count -= updatedData.unread;
|
||
|
||
$rootScope.$broadcast('dialog_unread', {
|
||
peerID,
|
||
count: foundDialog.unread_count
|
||
});
|
||
}
|
||
|
||
if(updatedData.msgs[foundDialog.top_message]) {
|
||
this.reloadConversation(peerID);
|
||
}
|
||
}
|
||
});
|
||
break;
|
||
}
|
||
|
||
case 'updateChannel': {
|
||
const channelID: number = update.channel_id;
|
||
const peerID = -channelID;
|
||
const channel = appChatsManager.getChat(channelID);
|
||
|
||
const needDialog = channel._ == 'channel' && (!channel.pFlags.left && !channel.pFlags.kicked);
|
||
const foundDialog = this.getDialogByPeerID(peerID);
|
||
const hasDialog = foundDialog.length > 0;
|
||
|
||
const canViewHistory = channel._ == 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked);
|
||
const hasHistory = this.historiesStorage[peerID] !== undefined;
|
||
|
||
if(canViewHistory != hasHistory) {
|
||
delete this.historiesStorage[peerID];
|
||
$rootScope.$broadcast('history_forbidden', peerID);
|
||
}
|
||
|
||
if(hasDialog != needDialog) {
|
||
if(needDialog) {
|
||
this.reloadConversation(-channelID);
|
||
} else {
|
||
if(foundDialog[0]) {
|
||
this.dialogsStorage.dropDialog(peerID);
|
||
$rootScope.$broadcast('dialog_drop', {peerID: peerID, dialog: foundDialog[0]});
|
||
}
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
// @ts-ignore
|
||
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);
|
||
});
|
||
|
||
break;
|
||
}
|
||
|
||
case 'updateChannelMessageViews': {
|
||
const views = update.views;
|
||
const mid = appMessagesIDsManager.getFullMessageID(update.id, update.channel_id);
|
||
const message = this.getMessage(mid);
|
||
if(message && message.views && message.views < views) {
|
||
message.views = views;
|
||
$rootScope.$broadcast('message_views', {mid, views});
|
||
}
|
||
break;
|
||
}
|
||
|
||
case 'updateServiceNotification': {
|
||
this.log('updateServiceNotification', update);
|
||
const fromID = 777000;
|
||
const peerID = fromID;
|
||
const messageID = this.tempID--;
|
||
const message: any = {
|
||
_: 'message',
|
||
id: messageID,
|
||
from_id: appPeersManager.getOutputPeer(fromID),
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
pFlags: {unread: true},
|
||
date: (update.inbox_date || tsNow(true)) + serverTimeManager.serverTimeOffset,
|
||
message: update.message,
|
||
media: update.media,
|
||
entities: update.entities
|
||
};
|
||
if(!appUsersManager.hasUser(fromID)) {
|
||
appUsersManager.saveApiUsers([{
|
||
_: 'user',
|
||
id: fromID,
|
||
pFlags: {verified: true},
|
||
access_hash: 0,
|
||
first_name: 'Telegram',
|
||
phone: '42777'
|
||
}]);
|
||
}
|
||
this.saveMessages([message]);
|
||
|
||
if(update.inbox_date) {
|
||
this.pendingTopMsgs[peerID] = messageID;
|
||
this.handleUpdate({
|
||
_: 'updateNewMessage',
|
||
message: message
|
||
} as any);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
// 'updateChannelPinnedMessage' will be handled by appProfileManager
|
||
case 'updateChatPinnedMessage':
|
||
case 'updateUserPinnedMessage': {
|
||
// hz nado li tut appMessagesIDsManager.getFullMessageID(update.max_id, channelID);
|
||
const peerID = appPeersManager.getPeerID(update);
|
||
this.savePinnedMessage(peerID, update.id);
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
public finalizePendingMessage(randomID: number, finalMessage: any) {
|
||
var pendingData = this.pendingByRandomID[randomID];
|
||
// this.log('pdata', randomID, pendingData)
|
||
|
||
if(pendingData) {
|
||
var peerID = pendingData[0];
|
||
var tempID = pendingData[1];
|
||
var historyStorage = this.historiesStorage[peerID],
|
||
message;
|
||
|
||
// this.log('pending', randomID, historyStorage.pending)
|
||
var pos = historyStorage.pending.indexOf(tempID);
|
||
if(pos != -1) {
|
||
historyStorage.pending.splice(pos, 1);
|
||
}
|
||
|
||
if(message = this.messagesStorage[tempID]) {
|
||
delete message.pending;
|
||
delete message.error;
|
||
delete message.random_id;
|
||
delete message.send;
|
||
|
||
$rootScope.$broadcast('messages_pending');
|
||
}
|
||
|
||
delete this.messagesStorage[tempID];
|
||
|
||
this.finalizePendingMessageCallbacks(tempID, finalMessage.mid);
|
||
|
||
return message;
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
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];
|
||
}
|
||
|
||
$rootScope.$broadcast('message_sent', {tempID, mid});
|
||
}
|
||
|
||
public incrementMaxSeenID(maxID: number) {
|
||
if(!maxID || !(!this.maxSeenID || maxID > this.maxSeenID)) {
|
||
return false;
|
||
}
|
||
|
||
this.maxSeenID = maxID;
|
||
|
||
AppStorage.set({max_seen_msg: maxID});
|
||
|
||
apiManager.invokeApi('messages.receivedMessages', {
|
||
max_id: maxID
|
||
});
|
||
}
|
||
|
||
public getHistory(peerID: number, maxID = 0, limit: number, backLimit?: number) {
|
||
if(this.migratedFromTo[peerID]) {
|
||
peerID = this.migratedFromTo[peerID];
|
||
}
|
||
|
||
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]) {
|
||
isMigrated = true;
|
||
if(maxID && maxID < appMessagesIDsManager.fullMsgIDModulus) {
|
||
reqPeerID = this.migratedToFrom[peerID];
|
||
}
|
||
}
|
||
|
||
if(maxID > 0) {
|
||
offsetNotFound = true;
|
||
for(; offset < historyStorage.history.length; offset++) {
|
||
if(maxID > historyStorage.history[offset]) {
|
||
offsetNotFound = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(!offsetNotFound && (
|
||
historyStorage.count !== null && historyStorage.history.length == historyStorage.count ||
|
||
historyStorage.history.length >= offset + limit
|
||
)) {
|
||
if(backLimit) {
|
||
backLimit = Math.min(offset, backLimit);
|
||
offset = Math.max(0, offset - backLimit);
|
||
limit += backLimit;
|
||
} else {
|
||
limit = limit;
|
||
}
|
||
|
||
let history = historyStorage.history.slice(offset, offset + limit);
|
||
if(!maxID && historyStorage.pending.length) {
|
||
history = historyStorage.pending.slice().concat(history);
|
||
}
|
||
|
||
return this.wrapHistoryResult({
|
||
count: historyStorage.count,
|
||
history: history,
|
||
unreadOffset: unreadOffset,
|
||
unreadSkip: unreadSkip
|
||
});
|
||
}
|
||
|
||
if(offsetNotFound) {
|
||
offset = 0;
|
||
}
|
||
if((backLimit || unreadSkip || maxID) && historyStorage.history.indexOf(maxID) == -1) {
|
||
if(backLimit) {
|
||
offset = -backLimit;
|
||
limit += backLimit;
|
||
}
|
||
|
||
return this.requestHistory(reqPeerID, maxID, limit, offset).then((historyResult) => {
|
||
historyStorage.count = historyResult.count || historyResult.messages.length;
|
||
if(isMigrated) {
|
||
historyStorage.count++;
|
||
}
|
||
|
||
let history: number[] = [];
|
||
historyResult.messages.forEach((message: any) => {
|
||
history.push(message.mid);
|
||
});
|
||
|
||
if(!maxID && historyStorage.pending.length) {
|
||
history = historyStorage.pending.slice().concat(history);
|
||
}
|
||
|
||
return this.wrapHistoryResult({
|
||
count: historyStorage.count,
|
||
history: history,
|
||
unreadOffset: unreadOffset,
|
||
unreadSkip: unreadSkip
|
||
});
|
||
});
|
||
}
|
||
|
||
return this.fillHistoryStorage(peerID, maxID, limit, historyStorage).then(() => {
|
||
offset = 0;
|
||
if(maxID > 0) {
|
||
for(offset = 0; offset < historyStorage.history.length; offset++) {
|
||
if(maxID > historyStorage.history[offset]) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
let history = historyStorage.history.slice(backLimit ? Math.max(offset - backLimit, 0) : offset, offset + limit);
|
||
if(!maxID && historyStorage.pending.length) {
|
||
history = historyStorage.pending.slice().concat(history);
|
||
}
|
||
|
||
return this.wrapHistoryResult({
|
||
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) => {
|
||
historyStorage.count = historyResult.count || historyResult.messages.length;
|
||
|
||
if(!maxID && historyResult.messages.length) {
|
||
maxID = historyResult.messages[0].mid + 1;
|
||
}
|
||
|
||
let offset = 0;
|
||
if(maxID > 0) {
|
||
for(; offset < historyStorage.history.length; offset++) {
|
||
if(maxID > historyStorage.history[offset]) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
const wasTotalCount = historyStorage.history.length;
|
||
|
||
historyStorage.history.splice(offset, historyStorage.history.length - offset);
|
||
historyResult.messages.forEach((message: any) => {
|
||
if(this.mergeReplyKeyboard(historyStorage, message)) {
|
||
$rootScope.$broadcast('history_reply_markup', {peerID});
|
||
}
|
||
|
||
historyStorage.history.push(message.mid);
|
||
});
|
||
|
||
const totalCount = historyStorage.history.length;
|
||
fullLimit -= (totalCount - wasTotalCount);
|
||
|
||
const migratedNextPeer = this.migratedFromTo[peerID];
|
||
const migratedPrevPeer = this.migratedToFrom[peerID]
|
||
const isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined;
|
||
|
||
if(isMigrated) {
|
||
historyStorage.count = Math.max(historyStorage.count, totalCount) + 1;
|
||
}
|
||
|
||
if(fullLimit > 0) {
|
||
maxID = historyStorage.history[totalCount - 1];
|
||
if(isMigrated) {
|
||
if(!historyResult.messages.length) {
|
||
if(migratedPrevPeer) {
|
||
maxID = 0;
|
||
peerID = migratedPrevPeer;
|
||
} else {
|
||
historyStorage.count = totalCount;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return this.fillHistoryStorage(peerID, maxID, fullLimit, historyStorage);
|
||
} else if(totalCount < historyStorage.count) {
|
||
return this.fillHistoryStorage(peerID, maxID, fullLimit, historyStorage);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
});
|
||
}
|
||
|
||
public wrapHistoryResult(result: HistoryResult) {
|
||
if(result.unreadOffset) {
|
||
for(let i = result.history.length - 1; i >= 0; i--) {
|
||
const message = this.messagesStorage[result.history[i]];
|
||
if(message && !message.pFlags.out && message.pFlags.unread) {
|
||
result.unreadOffset = i + 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
public requestHistory(peerID: number, maxID: number, limit = 0, offset = 0, offsetDate = 0): Promise<any> {
|
||
const isChannel = appPeersManager.isChannel(peerID);
|
||
|
||
//console.trace('requestHistory', peerID, maxID, limit, offset);
|
||
|
||
$rootScope.$broadcast('history_request');
|
||
|
||
return apiManager.invokeApi('messages.getHistory', {
|
||
peer: appPeersManager.getInputPeerByID(peerID),
|
||
offset_id: maxID ? appMessagesIDsManager.getMessageLocalID(maxID) : 0,
|
||
offset_date: offsetDate,
|
||
add_offset: offset,
|
||
limit: limit,
|
||
max_id: 0,
|
||
min_id: 0,
|
||
hash: 0
|
||
}, {
|
||
//timeout: APITIMEOUT,
|
||
noErrorBox: true
|
||
}).then((historyResult) => {
|
||
this.log('requestHistory result:', peerID, historyResult, maxID, limit, offset);
|
||
|
||
if(historyResult._ == 'messages.messagesNotModified') {
|
||
return historyResult;
|
||
}
|
||
|
||
appUsersManager.saveApiUsers(historyResult.users);
|
||
appChatsManager.saveApiChats(historyResult.chats);
|
||
this.saveMessages(historyResult.messages);
|
||
|
||
if(isChannel) {
|
||
apiUpdatesManager.addChannelState(-peerID, (historyResult as MessagesMessages.messagesChannelMessages).pts);
|
||
}
|
||
|
||
let length = historyResult.messages.length;
|
||
if(length && historyResult.messages[length - 1].deleted) {
|
||
historyResult.messages.splice(length - 1, 1);
|
||
length--;
|
||
(historyResult as MessagesMessages.messagesMessagesSlice).count--;
|
||
}
|
||
|
||
// 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)) {
|
||
return historyResult;
|
||
} */
|
||
return historyResult;
|
||
|
||
/* return appProfileManager.getProfile(peerID).then((userFull: any) => {
|
||
var description = userFull.bot_info && userFull.bot_info.description;
|
||
if(description) {
|
||
var messageID = this.tempID--;
|
||
var message = {
|
||
_: 'messageService',
|
||
id: messageID,
|
||
from_id: peerID,
|
||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||
pFlags: {},
|
||
date: tsNow(true) + serverTimeManager.serverTimeOffset,
|
||
action: {
|
||
_: 'messageActionBotIntro',
|
||
description: description
|
||
}
|
||
}
|
||
|
||
this.saveMessages([message]);
|
||
historyResult.messages.push(message);
|
||
if(historyResult.count) {
|
||
historyResult.count++;
|
||
}
|
||
}
|
||
|
||
return historyResult;
|
||
}); */
|
||
}, (error) => {
|
||
switch (error.type) {
|
||
case 'CHANNEL_PRIVATE':
|
||
let channel = appChatsManager.getChat(-peerID);
|
||
channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};
|
||
apiUpdatesManager.processUpdateMessage({
|
||
_: 'updates',
|
||
updates: [{
|
||
_: 'updateChannel',
|
||
channel_id: -peerID
|
||
}],
|
||
chats: [channel],
|
||
users: []
|
||
});
|
||
break;
|
||
}
|
||
|
||
throw error;
|
||
});
|
||
}
|
||
|
||
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]);
|
||
}));
|
||
});
|
||
|
||
Promise.all(promises).finally(() => {
|
||
this.fetchSingleMessagesPromise = null;
|
||
if(this.needSingleMessages.length) this.fetchSingleMessages();
|
||
resolve();
|
||
});
|
||
}, 0);
|
||
});
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
public setTyping(peerID: number, _action: any): Promise<boolean> {
|
||
if(!$rootScope.myID) 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>;
|
||
}
|
||
}
|
||
|
||
const appMessagesManager = new AppMessagesManager();
|
||
export default appMessagesManager;
|