Telegram Web, preconfigured for usage in I2P.
http://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3974 lines
131 KiB
3974 lines
131 KiB
/*! |
|
* Webogram v0.7.0 - messaging web application for MTProto |
|
* https://github.com/zhukov/webogram |
|
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> |
|
* https://github.com/zhukov/webogram/blob/master/LICENSE |
|
*/ |
|
|
|
'use strict' |
|
|
|
angular.module('myApp.services') |
|
|
|
.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppDocsManager, AppStickersManager, AppMessagesIDsManager, DraftsManager, AppWebPagesManager, AppGamesManager, MtpApiManager, MtpApiFileManager, ServerTimeManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) { |
|
var messagesStorage = {} |
|
var messagesForHistory = {} |
|
var messagesForDialogs = {} |
|
var historiesStorage = {} |
|
var dialogsStorage = {count: null, dialogs: []} |
|
var pendingByRandomID = {} |
|
var pendingByMessageID = {} |
|
var pendingAfterMsgs = {} |
|
var pendingTopMsgs = {} |
|
var sendFilePromise = $q.when() |
|
var tempID = -1 |
|
var tempFinalizeCallbacks = {} |
|
|
|
var dialogsIndex = SearchIndexManager.createIndex() |
|
var cachedResults = {query: false} |
|
|
|
var lastSearchFilter = {} |
|
var lastSearchResults = [] |
|
|
|
var needSingleMessages = [] |
|
var fetchSingleMessagesTimeout = false |
|
|
|
var incrementedMessageViews = {} |
|
var needIncrementMessageViews = [] |
|
var incrementMessageViewsTimeout = false |
|
|
|
var maxSeenID = false |
|
Storage.get('max_seen_msg').then(function (maxID) { |
|
if (maxID && |
|
!AppMessagesIDsManager.getMessageIDInfo(maxID)[1]) { |
|
maxSeenID = maxID |
|
} |
|
}) |
|
|
|
var dateOrTimeFilter = $filter('dateOrTime') |
|
var fwdMessagesPluralize = _.pluralize('conversation_forwarded_X_messages') |
|
var gameScorePluralize = _.pluralize('conversation_scored_X') |
|
|
|
NotificationsManager.start() |
|
|
|
var allDialogsLoaded = false |
|
var dialogsOffsetDate = 0 |
|
var pinnedIndex = 0 |
|
var dialogsNum = 0 |
|
|
|
var migratedFromTo = {} |
|
var migratedToFrom = {} |
|
|
|
function getConversations (query, offsetIndex, limit) { |
|
var curDialogStorage = dialogsStorage |
|
var isSearch = angular.isString(query) && query.length |
|
|
|
if (isSearch) { |
|
if (!limit || cachedResults.query !== query) { |
|
cachedResults.query = query |
|
|
|
var results = SearchIndexManager.search(query, dialogsIndex) |
|
|
|
cachedResults.dialogs = [] |
|
angular.forEach(dialogsStorage.dialogs, function (dialog) { |
|
if (results[dialog.peerID]) { |
|
cachedResults.dialogs.push(dialog) |
|
} |
|
}) |
|
cachedResults.count = cachedResults.dialogs.length |
|
} |
|
curDialogStorage = cachedResults |
|
} else { |
|
cachedResults.query = false |
|
} |
|
|
|
var offset = 0 |
|
if (offsetIndex > 0) { |
|
for (offset = 0; offset < curDialogStorage.dialogs.length; offset++) { |
|
if (offsetIndex > curDialogStorage.dialogs[offset].index) { |
|
break |
|
} |
|
} |
|
} |
|
|
|
limit = limit || 20 |
|
|
|
if ( |
|
isSearch || |
|
allDialogsLoaded || |
|
curDialogStorage.dialogs.length >= offset + limit |
|
) { |
|
return $q.when({ |
|
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit) |
|
}) |
|
} |
|
|
|
return getTopMessages(limit).then(function () { |
|
offset = 0 |
|
if (offsetIndex > 0) { |
|
for (offset = 0; offset < curDialogStorage.dialogs.length; offset++) { |
|
if (offsetIndex > curDialogStorage.dialogs[offset].index) { |
|
break |
|
} |
|
} |
|
} |
|
return { |
|
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit) |
|
} |
|
}) |
|
} |
|
|
|
function getConversation(peerID) { |
|
var foundDialog = getDialogByPeerID(peerID) |
|
if (foundDialog.length) { |
|
return $q.when(foundDialog[0]) |
|
} |
|
return $q.when({ |
|
peerID: peerID, |
|
top_message: 0, |
|
index: generateDialogIndex(generateDialogPinnedDate()), |
|
pFlags: {} |
|
}) |
|
} |
|
|
|
function getDialogByPeerID (peerID) { |
|
for (var i = 0; i < dialogsStorage.dialogs.length; i++) { |
|
if (dialogsStorage.dialogs[i].peerID == peerID) { |
|
return [dialogsStorage.dialogs[i], i] |
|
} |
|
} |
|
|
|
return [] |
|
} |
|
|
|
function saveConversation (dialog) { |
|
var peerID = AppPeersManager.getPeerID(dialog.peer) |
|
if (!peerID) { |
|
return false |
|
} |
|
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0 |
|
var peerText = AppPeersManager.getPeerSearchText(peerID) |
|
SearchIndexManager.indexObject(peerID, peerText, dialogsIndex) |
|
|
|
var isMegagroup = AppPeersManager.isMegagroup(channelID) |
|
if (dialog.top_message) { |
|
var mid = AppMessagesIDsManager.getFullMessageID(dialog.top_message, channelID) |
|
var message = getMessage(mid) |
|
} else { |
|
var mid = tempID-- |
|
var message = { |
|
_: 'message', |
|
id: mid, |
|
mid: mid, |
|
from_id: AppUsersManager.getSelf().id, |
|
to_id: AppPeersManager.getOutputPeer(peerID), |
|
deleted: true, |
|
flags: 0, |
|
pFlags: {unread: false, out: true}, |
|
date: 0, |
|
message: '' |
|
} |
|
saveMessages([message]) |
|
} |
|
var offsetDate = message.date |
|
|
|
if (!channelID && peerID < 0) { |
|
var chat = AppChatsManager.getChat(-peerID) |
|
if (chat && chat.migrated_to && chat.pFlags.deactivated) { |
|
var migratedToPeer = AppPeersManager.getPeerID(chat.migrated_to) |
|
migratedFromTo[peerID] = migratedToPeer |
|
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) |
|
|
|
var topDate = message.date |
|
if (channelID) { |
|
var channel = AppChatsManager.getChat(channelID) |
|
if (!topDate || channel.date && channel.date > topDate) { |
|
topDate = channel.date |
|
} |
|
} |
|
var savedDraft = DraftsManager.saveDraft(peerID, dialog.draft) |
|
if (savedDraft && savedDraft.date > topDate) { |
|
topDate = savedDraft.date |
|
} |
|
if (dialog.pFlags.pinned) { |
|
topDate = generateDialogPinnedDate() |
|
} |
|
|
|
dialog.index = generateDialogIndex(topDate) |
|
dialog.peerID = peerID |
|
|
|
pushDialogToStorage(dialog, offsetDate) |
|
|
|
// Because we saved message without dialog present |
|
var unreadKey = message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id' |
|
if (message.mid && message.mid > dialog[unreadKey]) { |
|
message.pFlags.unread = true |
|
} |
|
|
|
if (historiesStorage[peerID] === undefined && |
|
!message.deleted) { |
|
var historyStorage = {count: null, history: [], pending: []} |
|
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid) |
|
if (mid < 0 && message.pFlags.unread) { |
|
dialog.unread_count++ |
|
} |
|
historiesStorage[peerID] = historyStorage |
|
if (mergeReplyKeyboard(historyStorage, message)) { |
|
$rootScope.$broadcast('history_reply_markup', {peerID: peerID}) |
|
} |
|
} |
|
|
|
NotificationsManager.savePeerSettings(peerID, dialog.notify_settings) |
|
|
|
if (channelID && dialog.pts) { |
|
ApiUpdatesManager.addChannelState(channelID, dialog.pts) |
|
} |
|
|
|
if ( |
|
Config.Modes.packed && |
|
!channelID && |
|
dialog.unread_count > 0 && |
|
maxSeenID && |
|
dialog.top_message > maxSeenID && |
|
message.pFlags.unread && |
|
!message.pFlags.out |
|
) { |
|
var notifyPeer = message.flags & 16 ? message.from_id : peerID |
|
NotificationsManager.getPeerMuted(notifyPeer).then(function (muted) { |
|
if (!muted) { |
|
notifyAboutMessage(message) |
|
} |
|
}) |
|
} |
|
} |
|
|
|
function getTopMessages (limit) { |
|
var first = true |
|
var dialogs = dialogsStorage.dialogs |
|
var offsetDate = 0 |
|
var offsetID = 0 |
|
var offsetPeerID = 0 |
|
var offsetIndex = 0 |
|
var flags = 0 |
|
if (dialogsOffsetDate) { |
|
offsetDate = dialogsOffsetDate + ServerTimeManager.serverTimeOffset |
|
offsetIndex = dialogsOffsetDate * 0x10000 |
|
flags |= 1 |
|
} |
|
return MtpApiManager.invokeApi('messages.getDialogs', { |
|
flags: flags, |
|
offset_date: offsetDate, |
|
offset_id: AppMessagesIDsManager.getMessageLocalID(offsetID), |
|
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID), |
|
limit: limit |
|
}, { |
|
timeout: 300 |
|
}).then(function (dialogsResult) { |
|
if (!offsetDate) { |
|
TelegramMeWebService.setAuthorized(true) |
|
} |
|
|
|
AppUsersManager.saveApiUsers(dialogsResult.users) |
|
AppChatsManager.saveApiChats(dialogsResult.chats) |
|
saveMessages(dialogsResult.messages) |
|
|
|
var maxSeenIdIncremented = offsetDate ? true : false |
|
var hasPrepend = false |
|
dialogsResult.dialogs.reverse() |
|
angular.forEach(dialogsResult.dialogs, function (dialog) { |
|
saveConversation(dialog) |
|
if (offsetIndex && dialog.index > offsetIndex) { |
|
newDialogsToHandle[dialog.peerID] = dialog |
|
hasPrepend = true |
|
} |
|
|
|
if (!maxSeenIdIncremented && |
|
!AppPeersManager.isChannel(AppPeersManager.getPeerID(dialog.peer))) { |
|
incrementMaxSeenID(dialog.top_message) |
|
maxSeenIdIncremented = true |
|
} |
|
}) |
|
dialogsResult.dialogs.reverse() |
|
|
|
if (!dialogsResult.dialogs.length || |
|
!dialogsResult.count || |
|
dialogs.length >= dialogsResult.count) { |
|
allDialogsLoaded = true |
|
} |
|
|
|
if (hasPrepend && |
|
!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} else { |
|
$rootScope.$broadcast('dialogs_multiupdate', {}) |
|
} |
|
}) |
|
} |
|
|
|
function generateDialogPinnedDate() { |
|
return 0x7fffff00 + ((pinnedIndex++) & 0xff) |
|
} |
|
|
|
function generateDialogIndex (date) { |
|
if (date === undefined) { |
|
date = tsNow(true) + ServerTimeManager.serverTimeOffset |
|
} |
|
return (date * 0x10000) + ((++dialogsNum) & 0xFFFF) |
|
} |
|
|
|
function pushDialogToStorage (dialog, offsetDate) { |
|
var dialogs = dialogsStorage.dialogs |
|
var pos = getDialogByPeerID(dialog.peerID)[1] |
|
if (pos !== undefined) { |
|
dialogs.splice(pos, 1) |
|
} |
|
|
|
if (offsetDate && |
|
!dialog.pFlags.pinned && |
|
(!dialogsOffsetDate || offsetDate < dialogsOffsetDate)) { |
|
if (pos !== undefined) { |
|
// So the dialog jumped to the last position |
|
return false |
|
} |
|
dialogsOffsetDate = offsetDate |
|
} |
|
|
|
var index = dialog.index |
|
var i |
|
var len = dialogs.length |
|
if (!len || index < dialogs[len - 1].index) { |
|
dialogs.push(dialog) |
|
} |
|
else if (index >= dialogs[0].index) { |
|
dialogs.unshift(dialog) |
|
} |
|
else { |
|
for (i = 0; i < len; i++) { |
|
if (index > dialogs[i].index) { |
|
dialogs.splice(i, 0, dialog) |
|
break |
|
} |
|
} |
|
} |
|
} |
|
|
|
function requestHistory (peerID, maxID, limit, offset) { |
|
var isChannel = AppPeersManager.isChannel(peerID) |
|
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID) |
|
|
|
return MtpApiManager.invokeApi('messages.getHistory', { |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
offset_id: maxID ? AppMessagesIDsManager.getMessageLocalID(maxID) : 0, |
|
add_offset: offset || 0, |
|
limit: limit || 0 |
|
}, { |
|
timeout: 300, |
|
noErrorBox: true |
|
}).then(function (historyResult) { |
|
AppUsersManager.saveApiUsers(historyResult.users) |
|
AppChatsManager.saveApiChats(historyResult.chats) |
|
saveMessages(historyResult.messages) |
|
|
|
if (isChannel) { |
|
ApiUpdatesManager.addChannelState(-peerID, historyResult.pts) |
|
} |
|
|
|
var length = historyResult.messages.length |
|
if (length && |
|
historyResult.messages[length - 1].deleted) { |
|
historyResult.messages.splice(length - 1, 1) |
|
length-- |
|
historyResult.count-- |
|
} |
|
|
|
if ( |
|
peerID < 0 || |
|
!AppUsersManager.isBot(peerID) || |
|
(length == limit && limit < historyResult.count) |
|
) { |
|
return historyResult |
|
} |
|
|
|
return AppProfileManager.getProfile(peerID).then(function (userFull) { |
|
var description = userFull.bot_info && userFull.bot_info.description |
|
if (description) { |
|
var messageID = tempID-- |
|
var message = { |
|
_: 'messageService', |
|
id: messageID, |
|
from_id: peerID, |
|
to_id: AppPeersManager.getOutputPeer(peerID), |
|
flags: 0, |
|
pFlags: {}, |
|
date: tsNow(true) + ServerTimeManager.serverTimeOffset, |
|
action: { |
|
_: 'messageActionBotIntro', |
|
description: description |
|
} |
|
} |
|
saveMessages([message]) |
|
historyResult.messages.push(message) |
|
if (historyResult.count) { |
|
historyResult.count++ |
|
} |
|
} |
|
return historyResult |
|
}) |
|
}, function (error) { |
|
switch (error.type) { |
|
case 'CHANNEL_PRIVATE': |
|
var channel = AppChatsManager.getChat(-peerID) |
|
channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title} |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updates', |
|
updates: [{ |
|
_: 'updateChannel', |
|
channel_id: -peerID |
|
}], |
|
chats: [channel], |
|
users: [] |
|
}) |
|
break |
|
} |
|
return $q.reject(error) |
|
}) |
|
} |
|
|
|
function fillHistoryStorage (peerID, maxID, fullLimit, historyStorage) { |
|
// console.log('fill history storage', peerID, maxID, fullLimit, angular.copy(historyStorage)) |
|
var offset = (migratedFromTo[peerID] && !maxID) ? 1 : 0 |
|
return requestHistory(peerID, maxID, fullLimit, offset).then(function (historyResult) { |
|
historyStorage.count = historyResult.count || historyResult.messages.length |
|
|
|
var offset = 0 |
|
if (!maxID && historyResult.messages.length) { |
|
maxID = historyResult.messages[0].mid + 1 |
|
} |
|
if (maxID > 0) { |
|
for (offset = 0; offset < historyStorage.history.length; offset++) { |
|
if (maxID > historyStorage.history[offset]) { |
|
break |
|
} |
|
} |
|
} |
|
|
|
var wasTotalCount = historyStorage.history.length |
|
|
|
historyStorage.history.splice(offset, historyStorage.history.length - offset) |
|
angular.forEach(historyResult.messages, function (message) { |
|
if (mergeReplyKeyboard(historyStorage, message)) { |
|
$rootScope.$broadcast('history_reply_markup', {peerID: peerID}) |
|
} |
|
historyStorage.history.push(message.mid) |
|
}) |
|
|
|
var totalCount = historyStorage.history.length |
|
fullLimit -= (totalCount - wasTotalCount) |
|
|
|
var migratedNextPeer = migratedFromTo[peerID] |
|
var migratedPrevPeer = migratedToFrom[peerID] |
|
var isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined |
|
|
|
if (isMigrated) { |
|
historyStorage.count = Math.max(historyStorage.count, totalCount) + 1 |
|
} |
|
|
|
if (fullLimit > 0) { |
|
maxID = historyStorage.history[totalCount - 1] |
|
if (isMigrated) { |
|
if (!historyResult.messages.length) { |
|
if (migratedPrevPeer) { |
|
maxID = 0 |
|
peerID = migratedPrevPeer |
|
} else { |
|
historyStorage.count = totalCount |
|
return true |
|
} |
|
} |
|
return fillHistoryStorage(peerID, maxID, fullLimit, historyStorage) |
|
} |
|
else if (totalCount < historyStorage.count) { |
|
return fillHistoryStorage(peerID, maxID, fullLimit, historyStorage) |
|
} |
|
} |
|
return true |
|
}) |
|
} |
|
|
|
function wrapHistoryResult (peerID, result) { |
|
var unreadOffset = result.unreadOffset |
|
if (unreadOffset) { |
|
var i |
|
var message |
|
for (i = result.history.length - 1; i >= 0; i--) { |
|
message = messagesStorage[result.history[i]] |
|
if (message && !message.pFlags.out && message.pFlags.unread) { |
|
result.unreadOffset = i + 1 |
|
break |
|
} |
|
} |
|
} |
|
return $q.when(result) |
|
} |
|
|
|
function migrateChecks (migrateFrom, migrateTo) { |
|
if (!migratedFromTo[migrateFrom] && |
|
!migratedToFrom[migrateTo] && |
|
AppChatsManager.hasChat(-migrateTo)) { |
|
var fromChat = AppChatsManager.getChat(-migrateFrom) |
|
if (fromChat && |
|
fromChat.migrated_to && |
|
fromChat.migrated_to.channel_id == -migrateTo) { |
|
migratedFromTo[migrateFrom] = migrateTo |
|
migratedToFrom[migrateTo] = migrateFrom |
|
|
|
$timeout(function () { |
|
var foundDialog = getDialogByPeerID(migrateFrom) |
|
if (foundDialog.length) { |
|
dialogsStorage.dialogs.splice(foundDialog[1], 1) |
|
$rootScope.$broadcast('dialog_drop', {peerID: migrateFrom}) |
|
} |
|
$rootScope.$broadcast('dialog_migrate', {migrateFrom: migrateFrom, migrateTo: migrateTo}) |
|
}, 100) |
|
} |
|
} |
|
} |
|
|
|
function convertMigratedPeer (peerID) { |
|
if (migratedFromTo[peerID]) { |
|
return migratedFromTo[peerID] |
|
} |
|
} |
|
|
|
function getHistory (peerID, maxID, limit, backLimit, prerendered) { |
|
if (migratedFromTo[peerID]) { |
|
peerID = migratedFromTo[peerID] |
|
} |
|
var historyStorage = historiesStorage[peerID] |
|
var offset = 0 |
|
var offsetNotFound = false |
|
var unreadOffset = false |
|
var unreadSkip = false |
|
|
|
prerendered = prerendered ? Math.min(50, prerendered) : 0 |
|
|
|
if (historyStorage === undefined) { |
|
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []} |
|
} |
|
|
|
if (maxID < 0) { |
|
maxID = 0 |
|
} |
|
var isMigrated = false |
|
var reqPeerID = peerID |
|
if (migratedToFrom[peerID]) { |
|
isMigrated = true |
|
if (maxID && maxID < AppMessagesIDsManager.fullMsgIDModulus) { |
|
reqPeerID = migratedToFrom[peerID] |
|
} |
|
} |
|
|
|
if (!limit && !maxID) { |
|
var foundDialog = getDialogByPeerID(peerID)[0] |
|
if (foundDialog && foundDialog.unread_count > 1) { |
|
var unreadCount = foundDialog.unread_count |
|
if (unreadSkip = (unreadCount > 50)) { |
|
if (foundDialog.read_inbox_max_id) { |
|
maxID = foundDialog.read_inbox_max_id |
|
backLimit = 16 |
|
unreadOffset = 16 |
|
limit = 4 |
|
} else { |
|
limit = 20 |
|
unreadOffset = 16 |
|
offset = unreadCount - unreadOffset |
|
} |
|
} else { |
|
limit = Math.max(10, prerendered, unreadCount + 2) |
|
unreadOffset = unreadCount |
|
} |
|
} |
|
else if (Config.Mobile) { |
|
limit = 20 |
|
} |
|
} |
|
if (maxID > 0) { |
|
offsetNotFound = true |
|
for (offset = 0; offset < historyStorage.history.length; offset++) { |
|
if (maxID > historyStorage.history[offset]) { |
|
offsetNotFound = false |
|
break |
|
} |
|
} |
|
} |
|
|
|
if (!offsetNotFound && ( |
|
historyStorage.count !== null && historyStorage.history.length == historyStorage.count || |
|
historyStorage.history.length >= offset + (limit || 1) |
|
)) { |
|
if (backLimit) { |
|
backLimit = Math.min(offset, backLimit) |
|
offset = Math.max(0, offset - backLimit) |
|
limit += backLimit |
|
} else { |
|
limit = limit || (offset ? 20 : (prerendered || 5)) |
|
} |
|
var history = historyStorage.history.slice(offset, offset + limit) |
|
if (!maxID && historyStorage.pending.length) { |
|
history = historyStorage.pending.slice().concat(history) |
|
} |
|
return wrapHistoryResult(peerID, { |
|
count: historyStorage.count, |
|
history: history, |
|
unreadOffset: unreadOffset, |
|
unreadSkip: unreadSkip |
|
}) |
|
} |
|
|
|
if (!backLimit && !limit) { |
|
limit = prerendered || 20 |
|
} |
|
if (offsetNotFound) { |
|
offset = 0 |
|
} |
|
if (backLimit || unreadSkip || maxID && historyStorage.history.indexOf(maxID) == -1) { |
|
if (backLimit) { |
|
offset = -backLimit |
|
limit += backLimit |
|
} |
|
return requestHistory(reqPeerID, maxID, limit, offset).then(function (historyResult) { |
|
historyStorage.count = historyResult.count || historyResult.messages.length |
|
if (isMigrated) { |
|
historyStorage.count++ |
|
} |
|
|
|
var history = [] |
|
angular.forEach(historyResult.messages, function (message) { |
|
history.push(message.mid) |
|
}) |
|
if (!maxID && historyStorage.pending.length) { |
|
history = historyStorage.pending.slice().concat(history) |
|
} |
|
|
|
return wrapHistoryResult(peerID, { |
|
count: historyStorage.count, |
|
history: history, |
|
unreadOffset: unreadOffset, |
|
unreadSkip: unreadSkip |
|
}) |
|
}) |
|
} |
|
|
|
return fillHistoryStorage(peerID, maxID, limit, historyStorage).then(function () { |
|
offset = 0 |
|
if (maxID > 0) { |
|
for (offset = 0; offset < historyStorage.history.length; offset++) { |
|
if (maxID > historyStorage.history[offset]) { |
|
break |
|
} |
|
} |
|
} |
|
|
|
var history = historyStorage.history.slice(offset, offset + limit) |
|
if (!maxID && historyStorage.pending.length) { |
|
history = historyStorage.pending.slice().concat(history) |
|
} |
|
|
|
return wrapHistoryResult(peerID, { |
|
count: historyStorage.count, |
|
history: history, |
|
unreadOffset: unreadOffset, |
|
unreadSkip: unreadSkip |
|
}) |
|
}) |
|
} |
|
|
|
function getReplyKeyboard (peerID) { |
|
return (historiesStorage[peerID] || {}).reply_markup || false |
|
} |
|
|
|
function mergeReplyKeyboard (historyStorage, message) { |
|
// console.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup) |
|
if (!message.reply_markup && |
|
!message.pFlags.out && |
|
!message.action) { |
|
return false |
|
} |
|
if (message.reply_markup && |
|
message.reply_markup._ == 'replyInlineMarkup') { |
|
return false |
|
} |
|
var messageReplyMarkup = message.reply_markup |
|
var lastReplyMarkup = historyStorage.reply_markup |
|
if (messageReplyMarkup) { |
|
if (lastReplyMarkup && lastReplyMarkup.mid >= message.mid) { |
|
return false |
|
} |
|
if (messageReplyMarkup.pFlags.selective && |
|
!(message.flags & 16)) { |
|
return false |
|
} |
|
if (historyStorage.maxOutID && |
|
message.mid < historyStorage.maxOutID && |
|
messageReplyMarkup.pFlags.single_use) { |
|
messageReplyMarkup.pFlags.hidden = true |
|
} |
|
messageReplyMarkup = angular.extend({ |
|
mid: message.mid |
|
}, messageReplyMarkup) |
|
if (messageReplyMarkup._ != 'replyKeyboardHide') { |
|
messageReplyMarkup.fromID = message.from_id |
|
} |
|
historyStorage.reply_markup = messageReplyMarkup |
|
// console.log('set', historyStorage.reply_markup) |
|
return true |
|
} |
|
|
|
if (message.pFlags.out) { |
|
if (lastReplyMarkup) { |
|
if (lastReplyMarkup.pFlags.single_use && |
|
!lastReplyMarkup.pFlags.hidden && |
|
(message.mid > lastReplyMarkup.mid || message.mid < 0) && |
|
message.message) { |
|
lastReplyMarkup.pFlags.hidden = true |
|
// console.log('set', historyStorage.reply_markup) |
|
return true |
|
} |
|
} else if (!historyStorage.maxOutID || |
|
message.mid > historyStorage.maxOutID) { |
|
historyStorage.maxOutID = message.mid |
|
} |
|
} |
|
|
|
if (message.action && |
|
message.action._ == 'messageActionChatDeleteUser' && |
|
(lastReplyMarkup |
|
? message.action.user_id == lastReplyMarkup.fromID |
|
: AppUsersManager.isBot(message.action.user_id) |
|
) |
|
) { |
|
historyStorage.reply_markup = { |
|
_: 'replyKeyboardHide', |
|
mid: message.mid, |
|
flags: 0, |
|
pFlags: {} |
|
} |
|
// console.log('set', historyStorage.reply_markup) |
|
return true |
|
} |
|
|
|
return false |
|
} |
|
|
|
function getSearch (peerID, query, inputFilter, maxID, limit) { |
|
peerID = peerID ? parseInt(peerID) : 0 |
|
var foundMsgs = [] |
|
var useSearchCache = !query |
|
var newSearchFilter = {peer: peerID, filter: inputFilter} |
|
var sameSearchCache = useSearchCache && angular.equals(lastSearchFilter, newSearchFilter) |
|
|
|
if (useSearchCache && !sameSearchCache) { |
|
// console.warn(dT(), 'new search filter', lastSearchFilter, newSearchFilter) |
|
lastSearchFilter = newSearchFilter |
|
lastSearchResults = [] |
|
} |
|
|
|
// console.log(dT(), 'search', useSearchCache, sameSearchCache, lastSearchResults, maxID) |
|
|
|
if (peerID && !maxID && !query) { |
|
var historyStorage = historiesStorage[peerID] |
|
|
|
if (historyStorage !== undefined && historyStorage.history.length) { |
|
var neededContents = {}, |
|
neededDocType |
|
var neededLimit = limit || 20, |
|
i |
|
var message |
|
|
|
switch (inputFilter._) { |
|
case 'inputMessagesFilterPhotos': |
|
neededContents['messageMediaPhoto'] = true |
|
break |
|
|
|
case 'inputMessagesFilterPhotoVideo': |
|
neededContents['messageMediaPhoto'] = true |
|
neededContents['messageMediaDocument'] = true |
|
neededDocType = 'video' |
|
break |
|
|
|
case 'inputMessagesFilterVideo': |
|
neededContents['messageMediaDocument'] = true |
|
neededDocType = 'video' |
|
break |
|
|
|
case 'inputMessagesFilterDocument': |
|
neededContents['messageMediaDocument'] = true |
|
neededDocType = false |
|
break |
|
|
|
case 'inputMessagesFilterVoice': |
|
neededContents['messageMediaDocument'] = true |
|
neededDocType = 'voice' |
|
break |
|
|
|
case 'inputMessagesFilterRoundVideo': |
|
neededContents['messageMediaDocument'] = true |
|
neededDocType = 'round' |
|
break |
|
|
|
case 'inputMessagesFilterMusic': |
|
neededContents['messageMediaDocument'] = true |
|
neededDocType = 'audio' |
|
break |
|
|
|
case 'inputMessagesFilterUrl': |
|
neededContents['url'] = true |
|
break |
|
|
|
case 'inputMessagesFilterMyMentions': |
|
neededContents['mentioned'] = true |
|
break |
|
|
|
default: |
|
return $q.when({ |
|
count: 0, |
|
history: [] |
|
}) |
|
} |
|
for (i = 0; i < historyStorage.history.length; i++) { |
|
message = messagesStorage[historyStorage.history[i]] |
|
if (message.media && neededContents[message.media._]) { |
|
if (neededDocType !== undefined && |
|
message.media._ == 'messageMediaDocument' && |
|
message.media.document.type != neededDocType) { |
|
continue |
|
} |
|
foundMsgs.push(message.mid) |
|
if (foundMsgs.length >= neededLimit) { |
|
break |
|
} |
|
} |
|
} |
|
} |
|
|
|
// console.warn(dT(), 'before append', foundMsgs) |
|
if (foundMsgs.length < neededLimit && lastSearchResults.length && sameSearchCache) { |
|
var minID = foundMsgs.length ? foundMsgs[foundMsgs.length - 1] : false |
|
for (var i = 0; i < lastSearchResults.length; i++) { |
|
if (minID === false || lastSearchResults[i] < minID) { |
|
foundMsgs.push(lastSearchResults[i]) |
|
if (foundMsgs.length >= neededLimit) { |
|
break |
|
} |
|
} |
|
} |
|
} |
|
// console.warn(dT(), 'after append', foundMsgs) |
|
} |
|
|
|
if (foundMsgs.length || limit == 1000) { |
|
if (useSearchCache) { |
|
lastSearchResults = listMergeSorted(lastSearchResults, foundMsgs) |
|
} |
|
|
|
return $q.when({ |
|
count: null, |
|
history: foundMsgs |
|
}) |
|
} |
|
|
|
var apiPromise |
|
|
|
if (peerID || !query) { |
|
apiPromise = MtpApiManager.invokeApi('messages.search', { |
|
flags: 0, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
q: query || '', |
|
filter: inputFilter || {_: 'inputMessagesFilterEmpty'}, |
|
min_date: 0, |
|
max_date: 0, |
|
limit: limit || 20, |
|
offset_id: AppMessagesIDsManager.getMessageLocalID(maxID) || 0, |
|
add_offset: 0, |
|
max_id: 0, |
|
min_id: 0 |
|
}, { |
|
timeout: 300, |
|
noErrorBox: true |
|
}) |
|
} else { |
|
var offsetDate = 0 |
|
var offsetPeerID = 0 |
|
var offsetID = 0 |
|
var offsetMessage = maxID && getMessage(maxID) |
|
|
|
if (offsetMessage && offsetMessage.date) { |
|
offsetDate = offsetMessage.date + ServerTimeManager.serverTimeOffset |
|
offsetID = offsetMessage.id |
|
offsetPeerID = getMessagePeer(offsetMessage) |
|
} |
|
apiPromise = MtpApiManager.invokeApi('messages.searchGlobal', { |
|
q: query, |
|
offset_date: offsetDate, |
|
offset_peer: AppPeersManager.getInputPeerByID(offsetPeerID), |
|
offset_id: AppMessagesIDsManager.getMessageLocalID(offsetID), |
|
limit: limit || 20 |
|
}, { |
|
timeout: 300, |
|
noErrorBox: true |
|
}) |
|
} |
|
|
|
return apiPromise.then(function (searchResult) { |
|
AppUsersManager.saveApiUsers(searchResult.users) |
|
AppChatsManager.saveApiChats(searchResult.chats) |
|
saveMessages(searchResult.messages) |
|
|
|
var foundCount = searchResult.count || searchResult.messages.length |
|
|
|
foundMsgs = [] |
|
angular.forEach(searchResult.messages, function (message) { |
|
var peerID = getMessagePeer(message) |
|
if (peerID < 0) { |
|
var chat = AppChatsManager.getChat(-peerID) |
|
if (chat.migrated_to) { |
|
migrateChecks(peerID, -chat.migrated_to.channel_id) |
|
} |
|
} |
|
foundMsgs.push(message.mid) |
|
}) |
|
|
|
if (useSearchCache && |
|
(!maxID || sameSearchCache && lastSearchResults.indexOf(maxID) >= 0)) { |
|
lastSearchResults = listMergeSorted(lastSearchResults, foundMsgs) |
|
} |
|
// console.log(dT(), 'after API', foundMsgs, lastSearchResults) |
|
|
|
return { |
|
count: foundCount, |
|
history: foundMsgs |
|
} |
|
}, function (error) { |
|
if (error.code == 400) { |
|
error.handled = true |
|
} |
|
return $q.reject(error) |
|
}) |
|
} |
|
|
|
function getMessage (messageID) { |
|
return messagesStorage[messageID] || { |
|
_: 'messageEmpty', |
|
deleted: true, |
|
pFlags: {out: false, unread: false} |
|
} |
|
} |
|
|
|
function canMessageBeEdited(message) { |
|
var goodMedias = [ |
|
'messageMediaPhoto', |
|
'messageMediaDocument', |
|
'messageMediaWebPage', |
|
'messageMediaPending' |
|
] |
|
if (message._ != 'message' || |
|
message.deleted || |
|
message.fwd_from || |
|
message.via_bot_id || |
|
message.media && goodMedias.indexOf(message.media._) == -1 || |
|
message.fromID && AppUsersManager.isBot(message.fromID)) { |
|
return false |
|
} |
|
if (message.media && |
|
message.media._ == 'messageMediaDocument' && |
|
message.media.document.sticker) { |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
function canEditMessage(messageID) { |
|
if (!messagesStorage[messageID]) { |
|
return false |
|
} |
|
var message = messagesStorage[messageID] |
|
if (!message || |
|
!message.canBeEdited) { |
|
return false |
|
} |
|
if (getMessagePeer(message) == AppUsersManager.getSelf().id) { |
|
return true |
|
} |
|
if (message.date < tsNow(true) - 2 * 86400 || |
|
!message.pFlags.out) { |
|
return false |
|
} |
|
return true |
|
} |
|
|
|
function canReportMessage(messageID) { |
|
if (!messagesStorage[messageID]) { |
|
return false |
|
} |
|
if (!canEditMessage(messageID)) { |
|
var message = messagesStorage[messageID] |
|
if (!message.pFlags.out) { |
|
return true |
|
} |
|
} |
|
return false |
|
} |
|
|
|
function getMessageEditData(messageID) { |
|
if (!canEditMessage(messageID)) { |
|
return $q.reject() |
|
} |
|
var message = getMessage(messageID) |
|
if (message.media && |
|
message.media._ != 'messageMediaEmpty' && |
|
message.media._ != 'messageMediaWebPage') { |
|
|
|
return $q.when({ |
|
caption: true, |
|
text: typeof message.media.caption === 'string' ? message.media.caption : '' |
|
}) |
|
} |
|
|
|
var text = typeof message.message === 'string' ? message.message : '' |
|
var entities = RichTextProcessor.parseEntities(text) |
|
var serverEntities = message.entities || [] |
|
entities = RichTextProcessor.mergeEntities(entities, serverEntities) |
|
|
|
text = RichTextProcessor.wrapDraftText(text, {entities: entities}) |
|
|
|
return $q.when({ |
|
caption: false, |
|
text: text |
|
}) |
|
} |
|
|
|
function canRevokeMessage(messageID) { |
|
if (messageID <= 0 || |
|
!messagesStorage[messageID]) { |
|
return false |
|
} |
|
|
|
var message = messagesStorage[messageID] |
|
if (message._ != 'message' || |
|
message.deleted) { |
|
return false |
|
} |
|
|
|
var peerID = getMessagePeer(message) |
|
if (peerID < 0 && !AppChatsManager.isChannel(-peerID)) { |
|
var chat = AppChatsManager.getChat(-peerID) |
|
if (chat.pFlags.creator || |
|
chat.pFlags.admins_enabled && chat.pFlags.admin) { |
|
return true |
|
} |
|
} |
|
|
|
if (!message.pFlags.out || |
|
message.date < tsNow(true) - 2 * 86400) { |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
function deleteMessages (messageIDs, revoke) { |
|
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(messageIDs) |
|
var promises = [] |
|
angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { |
|
var promise |
|
if (channelID > 0) { |
|
var channel = AppChatsManager.getChat(channelID) |
|
if (!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) { |
|
var goodMsgIDs = [] |
|
if (channel.pFlags.editor || channel.pFlags.megagroup) { |
|
angular.forEach(msgIDs, function (msgID, i) { |
|
var message = getMessage(splitted.mids[channelID][i]) |
|
if (message.pFlags.out) { |
|
goodMsgIDs.push(msgID) |
|
} |
|
}) |
|
} |
|
if (!goodMsgIDs.length) { |
|
return |
|
} |
|
msgIDs = goodMsgIDs |
|
} |
|
promise = MtpApiManager.invokeApi('channels.deleteMessages', { |
|
channel: AppChatsManager.getChannelInput(channelID), |
|
id: msgIDs |
|
}).then(function (affectedMessages) { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updateDeleteChannelMessages', |
|
channel_id: channelID, |
|
messages: msgIDs, |
|
pts: affectedMessages.pts, |
|
pts_count: affectedMessages.pts_count |
|
} |
|
}) |
|
}) |
|
} else { |
|
var flags = 0 |
|
if (revoke) { |
|
flags |= 1 |
|
} |
|
promise = MtpApiManager.invokeApi('messages.deleteMessages', { |
|
flags: flags, |
|
id: msgIDs |
|
}).then(function (affectedMessages) { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updateDeleteMessages', |
|
messages: msgIDs, |
|
pts: affectedMessages.pts, |
|
pts_count: affectedMessages.pts_count |
|
} |
|
}) |
|
}) |
|
} |
|
promises.push(promise) |
|
}) |
|
|
|
return $q.all(promises) |
|
} |
|
|
|
function getMessageShareLink (fullMsgID) { |
|
var info = AppMessagesIDsManager.getMessageIDInfo(fullMsgID) |
|
var msgID = info[0] |
|
var channelID = info[1] |
|
if (!channelID) { |
|
return $q.reject() |
|
} |
|
var message = getMessage(fullMsgID) |
|
var channel = AppChatsManager.getChat(channelID) |
|
if (!message || |
|
!message.pFlags || |
|
!message.pFlags.post || |
|
!channel.username) { |
|
return $q.reject() |
|
} |
|
return MtpApiManager.invokeApi('channels.exportMessageLink', { |
|
channel: AppChatsManager.getChannelInput(channelID), |
|
id: msgID |
|
}).then(function (exportedMessageLink) { |
|
return exportedMessageLink.link |
|
}) |
|
} |
|
|
|
function readHistory (peerID) { |
|
// console.trace('start read') |
|
var isChannel = AppPeersManager.isChannel(peerID) |
|
var historyStorage = historiesStorage[peerID] |
|
var foundDialog = getDialogByPeerID(peerID)[0] |
|
|
|
if (!foundDialog || !foundDialog.unread_count) { |
|
if (!historyStorage || !historyStorage.history.length) { |
|
return false |
|
} |
|
|
|
var messageID, |
|
message |
|
var foundUnread = false |
|
for (i = historyStorage.history.length; i >= 0; i--) { |
|
messageID = historyStorage.history[i] |
|
message = messagesStorage[messageID] |
|
if (message && !message.pFlags.out && message.pFlags.unread) { |
|
foundUnread = true |
|
break |
|
} |
|
} |
|
if (!foundUnread) { |
|
return false |
|
} |
|
} |
|
|
|
if (historyStorage.readPromise) { |
|
return historyStorage.readPromise |
|
} |
|
|
|
var apiPromise |
|
if (isChannel) { |
|
apiPromise = MtpApiManager.invokeApi('channels.readHistory', { |
|
channel: AppChatsManager.getChannelInput(-peerID), |
|
max_id: 0 |
|
}) |
|
} else { |
|
apiPromise = MtpApiManager.invokeApi('messages.readHistory', { |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
max_id: 0 |
|
}).then(function (affectedMessages) { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updatePts', |
|
pts: affectedMessages.pts, |
|
pts_count: affectedMessages.pts_count |
|
} |
|
}) |
|
}) |
|
} |
|
|
|
historyStorage.readPromise = apiPromise.then(function () { |
|
if (foundDialog) { |
|
// console.log('done read history', peerID) |
|
foundDialog.unread_count = 0 |
|
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: 0}) |
|
$rootScope.$broadcast('messages_read') |
|
if (historyStorage && historyStorage.history.length) { |
|
foundDialog.read_inbox_max_id = historyStorage.history[0] |
|
} |
|
} |
|
})['finally'](function () { |
|
delete historyStorage.readPromise |
|
}) |
|
|
|
if (historyStorage && historyStorage.history.length) { |
|
var messageID |
|
var message, i |
|
var peerID, foundDialog |
|
var dialog |
|
for (i = 0; i < historyStorage.history.length; i++) { |
|
messageID = historyStorage.history[i] |
|
message = messagesStorage[messageID] |
|
if (message && !message.pFlags.out) { |
|
message.pFlags.unread = false |
|
if (messagesForHistory[messageID]) { |
|
messagesForHistory[messageID].pFlags.unread = false |
|
} |
|
if (messagesForDialogs[messageID]) { |
|
messagesForDialogs[messageID].pFlags.unread = false |
|
} |
|
NotificationsManager.cancel('msg' + messageID) |
|
} |
|
} |
|
} |
|
|
|
NotificationsManager.soundReset(AppPeersManager.getPeerString(peerID)) |
|
|
|
return historyStorage.readPromise |
|
} |
|
|
|
function readMessages (messageIDs) { |
|
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(messageIDs) |
|
angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { |
|
if (channelID > 0) { |
|
MtpApiManager.invokeApi('channels.readMessageContents', { |
|
channel: AppChatsManager.getChannelInput(channelID), |
|
id: msgIDs |
|
}).then(function () { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updateChannelReadMessagesContents', |
|
channel_id: channelID, |
|
messages: msgIDs |
|
} |
|
}) |
|
}) |
|
} else { |
|
MtpApiManager.invokeApi('messages.readMessageContents', { |
|
id: msgIDs |
|
}).then(function (affectedMessages) { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updateReadMessagesContents', |
|
messages: msgIDs, |
|
pts: affectedMessages.pts, |
|
pts_count: affectedMessages.pts_count |
|
} |
|
}) |
|
}) |
|
} |
|
}) |
|
|
|
|
|
} |
|
|
|
function doFlushHistory (inputPeer, justClear) { |
|
var flags = 0 |
|
if (justClear) { |
|
flags |= 1 |
|
} |
|
return MtpApiManager.invokeApi('messages.deleteHistory', { |
|
flags: flags, |
|
peer: inputPeer, |
|
max_id: 0 |
|
}).then(function (affectedHistory) { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updatePts', |
|
pts: affectedHistory.pts, |
|
pts_count: affectedHistory.pts_count |
|
} |
|
}) |
|
if (!affectedHistory.offset) { |
|
return true |
|
} |
|
return doFlushHistory(inputPeer, justClear) |
|
}) |
|
} |
|
|
|
function flushHistory (peerID, justClear) { |
|
if (AppPeersManager.isChannel(peerID)) { |
|
return getHistory(peerID, false, 1).then(function (historyResult) { |
|
var channelID = -peerID |
|
var maxID = AppMessagesIDsManager.getMessageLocalID(historyResult.history[0] || 0) |
|
return MtpApiManager.invokeApi('channels.deleteHistory', { |
|
channel: AppChatsManager.getChannelInput(channelID), |
|
max_id: maxID |
|
}).then(function () { |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updateChannelAvailableMessages', |
|
channel_id: channelID, |
|
available_min_id: maxID |
|
} |
|
}) |
|
return true |
|
}) |
|
}) |
|
} |
|
return doFlushHistory(AppPeersManager.getInputPeerByID(peerID), justClear).then(function () { |
|
if (justClear) { |
|
$rootScope.$broadcast('dialog_flush', {peerID: peerID}) |
|
} else { |
|
var foundDialog = getDialogByPeerID(peerID) |
|
if (foundDialog[0]) { |
|
dialogsStorage.dialogs.splice(foundDialog[1], 1) |
|
} |
|
delete historiesStorage[peerID] |
|
$rootScope.$broadcast('dialog_drop', {peerID: peerID}) |
|
} |
|
}) |
|
} |
|
|
|
function saveMessages (apiMessages, options) { |
|
options = options || {} |
|
angular.forEach(apiMessages, function (apiMessage) { |
|
if (apiMessage.pFlags === undefined) { |
|
apiMessage.pFlags = {} |
|
} |
|
if (!apiMessage.pFlags.out) { |
|
apiMessage.pFlags.out = false |
|
} |
|
if (!apiMessage.pFlags.unread) { |
|
apiMessage.pFlags.unread = false |
|
} |
|
if (apiMessage._ == 'messageEmpty') { |
|
return |
|
} |
|
|
|
var peerID = getMessagePeer(apiMessage) |
|
var isChannel = apiMessage.to_id._ == 'peerChannel' |
|
var channelID = isChannel ? -peerID : 0 |
|
var isBroadcast = isChannel && AppChatsManager.isBroadcast(channelID) |
|
|
|
var mid = AppMessagesIDsManager.getFullMessageID(apiMessage.id, channelID) |
|
apiMessage.mid = mid |
|
|
|
var dialog = getDialogByPeerID(peerID)[0] |
|
if (dialog && mid > 0) { |
|
var dialogKey = apiMessage.pFlags.out |
|
? 'read_outbox_max_id' |
|
: 'read_inbox_max_id' |
|
|
|
apiMessage.pFlags.unread = mid > dialog[dialogKey] |
|
} |
|
else if (options.isNew) { |
|
apiMessage.pFlags.unread = true |
|
} |
|
// console.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) |
|
|
|
if (apiMessage.reply_to_msg_id) { |
|
apiMessage.reply_to_mid = AppMessagesIDsManager.getFullMessageID(apiMessage.reply_to_msg_id, channelID) |
|
} |
|
|
|
apiMessage.date -= ServerTimeManager.serverTimeOffset |
|
|
|
apiMessage.peerID = peerID |
|
apiMessage.fromID = apiMessage.pFlags.post ? peerID : apiMessage.from_id |
|
|
|
var fwdHeader = apiMessage.fwd_from |
|
if (fwdHeader) { |
|
if (peerID == AppUsersManager.getSelf().id) { |
|
if (fwdHeader.saved_from_peer && fwdHeader.saved_from_msg_id) { |
|
var savedFromPeerID = AppPeersManager.getPeerID(fwdHeader.saved_from_peer) |
|
var savedFromMid = AppMessagesIDsManager.getFullMessageID(fwdHeader.saved_from_msg_id, AppPeersManager.isChannel(savedFromPeerID) ? -savedFromPeerID : 0) |
|
apiMessage.savedFrom = savedFromPeerID + '_' + savedFromMid |
|
} |
|
apiMessage.fromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id |
|
} else { |
|
apiMessage.fwdFromID = fwdHeader.channel_id ? -fwdHeader.channel_id : fwdHeader.from_id |
|
apiMessage.fwdPostID = fwdHeader.channel_post |
|
} |
|
fwdHeader.date -= ServerTimeManager.serverTimeOffset |
|
} |
|
|
|
if (apiMessage.via_bot_id > 0) { |
|
apiMessage.viaBotID = apiMessage.via_bot_id |
|
} |
|
|
|
var mediaContext = { |
|
user_id: apiMessage.fromID, |
|
date: apiMessage.date |
|
} |
|
|
|
if (apiMessage.media) { |
|
switch (apiMessage.media._) { |
|
case 'messageMediaEmpty': |
|
delete apiMessage.media |
|
break |
|
case 'messageMediaPhoto': |
|
if (apiMessage.media.ttl_seconds) { |
|
apiMessage.media = {_: 'messageMediaUnsupportedWeb'} |
|
} else { |
|
AppPhotosManager.savePhoto(apiMessage.media.photo, mediaContext) |
|
} |
|
break |
|
case 'messageMediaDocument': |
|
if (apiMessage.media.ttl_seconds) { |
|
apiMessage.media = {_: 'messageMediaUnsupportedWeb'} |
|
} else { |
|
AppDocsManager.saveDoc(apiMessage.media.document, mediaContext) |
|
} |
|
break |
|
case 'messageMediaWebPage': |
|
AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.mid, mediaContext) |
|
break |
|
case 'messageMediaGame': |
|
AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext) |
|
apiMessage.media.handleMessage = true |
|
break |
|
case 'messageMediaInvoice': |
|
apiMessage.media = {_: 'messageMediaUnsupportedWeb'} |
|
break |
|
case 'messageMediaGeoLive': |
|
apiMessage.media._ = 'messageMediaGeo' |
|
break |
|
} |
|
} |
|
if (apiMessage.action) { |
|
var migrateFrom |
|
var migrateTo |
|
switch (apiMessage.action._) { |
|
case 'messageActionChatEditPhoto': |
|
AppPhotosManager.savePhoto(apiMessage.action.photo, mediaContext) |
|
if (isBroadcast) { |
|
apiMessage.action._ = 'messageActionChannelEditPhoto' |
|
} |
|
break |
|
|
|
case 'messageActionChatEditTitle': |
|
if (isBroadcast) { |
|
apiMessage.action._ = 'messageActionChannelEditTitle' |
|
} |
|
break |
|
|
|
case 'messageActionChatDeletePhoto': |
|
if (isBroadcast) { |
|
apiMessage.action._ = 'messageActionChannelDeletePhoto' |
|
} |
|
break |
|
|
|
case 'messageActionChatAddUser': |
|
if (apiMessage.action.users.length == 1) { |
|
apiMessage.action.user_id = apiMessage.action.users[0] |
|
if (apiMessage.fromID == apiMessage.action.user_id) { |
|
if (isChannel) { |
|
apiMessage.action._ = 'messageActionChatJoined' |
|
} else { |
|
apiMessage.action._ = 'messageActionChatReturn' |
|
} |
|
} |
|
} |
|
else if (apiMessage.action.users.length > 1) { |
|
apiMessage.action._ = 'messageActionChatAddUsers' |
|
} |
|
break |
|
|
|
case 'messageActionChatDeleteUser': |
|
if (apiMessage.fromID == apiMessage.action.user_id) { |
|
apiMessage.action._ = 'messageActionChatLeave' |
|
} |
|
break |
|
|
|
case 'messageActionChannelMigrateFrom': |
|
migrateFrom = -apiMessage.action.chat_id |
|
migrateTo = -channelID |
|
break |
|
|
|
case 'messageActionChatMigrateTo': |
|
migrateFrom = -channelID |
|
migrateTo = -apiMessage.action.channel_id |
|
break |
|
|
|
case 'messageActionHistoryClear': |
|
apiMessage.deleted = true |
|
apiMessage.clear_history = true |
|
apiMessage.pFlags.out = false |
|
apiMessage.pFlags.unread = false |
|
break |
|
|
|
case 'messageActionPhoneCall': |
|
delete apiMessage.fromID |
|
apiMessage.action.type = |
|
(apiMessage.pFlags.out ? 'out_' : 'in_') + |
|
( |
|
apiMessage.action.reason._ == 'phoneCallDiscardReasonMissed' || |
|
apiMessage.action.reason._ == 'phoneCallDiscardReasonBusy' |
|
? 'missed' |
|
: 'ok' |
|
) |
|
break |
|
} |
|
|
|
if (migrateFrom && |
|
migrateTo && |
|
!migratedFromTo[migrateFrom] && |
|
!migratedToFrom[migrateTo]) { |
|
migrateChecks(migrateFrom, migrateTo) |
|
} |
|
} |
|
|
|
if (apiMessage.message && apiMessage.message.length) { |
|
var myEntities = RichTextProcessor.parseEntities(apiMessage.message) |
|
var apiEntities = apiMessage.entities || [] |
|
apiMessage.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !apiMessage.pending) |
|
} |
|
|
|
apiMessage.canBeEdited = canMessageBeEdited(apiMessage) |
|
|
|
if (!options.isEdited) { |
|
messagesStorage[mid] = apiMessage |
|
} |
|
}) |
|
} |
|
|
|
function sendText (peerID, text, options) { |
|
if (!angular.isString(text)) { |
|
return |
|
} |
|
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID |
|
options = options || {} |
|
var entities = options.entities || [] |
|
if (!options.viaBotID) { |
|
text = RichTextProcessor.parseMarkdown(text, entities) |
|
} |
|
if (!text.length) { |
|
return |
|
} |
|
|
|
var sendEntites = getInputEntities(entities) |
|
var messageID = tempID-- |
|
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)] |
|
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString() |
|
var historyStorage = historiesStorage[peerID] |
|
var flags = 0 |
|
var pFlags = {} |
|
var replyToMsgID = options.replyToMsgID |
|
var isChannel = AppPeersManager.isChannel(peerID) |
|
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID) |
|
var asChannel = isChannel && !isMegagroup ? true : false |
|
var message |
|
|
|
if (historyStorage === undefined) { |
|
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []} |
|
} |
|
|
|
var fromID = AppUsersManager.getSelf().id |
|
if (peerID != fromID) { |
|
flags |= 2 |
|
pFlags.out = true |
|
if (!isChannel && !AppUsersManager.isBot(peerID)) { |
|
flags |= 1 |
|
pFlags.unread = true |
|
} |
|
} |
|
if (replyToMsgID) { |
|
flags |= 8 |
|
} |
|
if (asChannel) { |
|
fromID = 0 |
|
pFlags.post = true |
|
} else { |
|
flags |= 256 |
|
} |
|
message = { |
|
_: 'message', |
|
id: messageID, |
|
from_id: fromID, |
|
to_id: AppPeersManager.getOutputPeer(peerID), |
|
flags: flags, |
|
pFlags: pFlags, |
|
date: tsNow(true) + ServerTimeManager.serverTimeOffset, |
|
message: text, |
|
random_id: randomIDS, |
|
reply_to_msg_id: replyToMsgID, |
|
via_bot_id: options.viaBotID, |
|
reply_markup: options.reply_markup, |
|
entities: entities, |
|
views: asChannel && 1, |
|
pending: true |
|
} |
|
|
|
var toggleError = function (on) { |
|
var historyMessage = 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 = function () { |
|
toggleError(false) |
|
var sentRequestOptions = {} |
|
if (pendingAfterMsgs[peerID]) { |
|
sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID |
|
} |
|
var flags = 0 |
|
if (replyToMsgID) { |
|
flags |= 1 |
|
} |
|
if (asChannel) { |
|
flags |= 16 |
|
} |
|
if (options.clearDraft) { |
|
flags |= 128 |
|
} |
|
var apiPromise |
|
if (options.viaBotID) { |
|
apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', { |
|
flags: flags, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
random_id: randomID, |
|
reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID), |
|
query_id: options.queryID, |
|
id: options.resultID |
|
}, sentRequestOptions) |
|
} else { |
|
if (sendEntites.length) { |
|
flags |= 8 |
|
} |
|
apiPromise = MtpApiManager.invokeApi('messages.sendMessage', { |
|
flags: flags, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
message: text, |
|
random_id: randomID, |
|
reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID), |
|
entities: sendEntites |
|
}, sentRequestOptions) |
|
} |
|
// console.log(flags, entities) |
|
apiPromise.then(function (updates) { |
|
if (updates._ == 'updateShortSentMessage') { |
|
message.flags = updates.flags |
|
message.date = updates.date |
|
message.id = updates.id |
|
message.media = updates.media |
|
message.entities = updates.entities |
|
updates = { |
|
_: 'updates', |
|
users: [], |
|
chats: [], |
|
seq: 0, |
|
updates: [{ |
|
_: 'updateMessageID', |
|
random_id: randomIDS, |
|
id: updates.id |
|
}, { |
|
_: isChannel |
|
? 'updateNewChannelMessage' |
|
: 'updateNewMessage', |
|
message: message, |
|
pts: updates.pts, |
|
pts_count: updates.pts_count |
|
}] |
|
} |
|
} |
|
else if (updates.updates) { |
|
angular.forEach(updates.updates, function (update) { |
|
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) |
|
}, function (error) { |
|
toggleError(true) |
|
})['finally'](function () { |
|
if (pendingAfterMsgs[peerID] === sentRequestOptions) { |
|
delete pendingAfterMsgs[peerID] |
|
} |
|
}) |
|
|
|
pendingAfterMsgs[peerID] = sentRequestOptions |
|
} |
|
|
|
saveMessages([message]) |
|
historyStorage.pending.unshift(messageID) |
|
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true}) |
|
|
|
setZeroTimeout(message.send) |
|
// setTimeout(function () { |
|
// message.send() |
|
// }, 5000) |
|
|
|
if (options.clearDraft) { |
|
DraftsManager.clearDraft(peerID) |
|
} |
|
|
|
pendingByRandomID[randomIDS] = [peerID, messageID] |
|
} |
|
|
|
function sendFile (peerID, file, options) { |
|
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID |
|
options = options || {} |
|
var messageID = tempID-- |
|
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)] |
|
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString() |
|
var historyStorage = historiesStorage[peerID] |
|
var flags = 0 |
|
var pFlags = {} |
|
var replyToMsgID = options.replyToMsgID |
|
var isChannel = AppPeersManager.isChannel(peerID) |
|
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID) |
|
var asChannel = isChannel && !isMegagroup ? true : false |
|
var attachType, apiFileName |
|
var realFileName |
|
|
|
if (!options.isMedia) { |
|
attachType = 'document' |
|
apiFileName = 'document.' + file.type.split('/')[1] |
|
} else if (['image/jpeg', 'image/png', 'image/bmp'].indexOf(file.type) >= 0) { |
|
attachType = 'photo' |
|
apiFileName = 'photo.' + file.type.split('/')[1] |
|
} else if (file.type.substr(0, 6) == 'audio/' || ['video/ogg'].indexOf(file.type) >= 0) { |
|
attachType = 'audio' |
|
apiFileName = 'audio.' + (file.type.split('/')[1] == 'ogg' ? 'ogg' : 'mp3') |
|
} else if (file.type.substr(0, 6) == 'video/') { |
|
attachType = 'video' |
|
apiFileName = 'video.mp4' |
|
} else { |
|
attachType = 'document' |
|
apiFileName = 'document.' + file.type.split('/')[1] |
|
} |
|
|
|
// console.log(attachType, apiFileName, file.type) |
|
|
|
if (historyStorage === undefined) { |
|
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []} |
|
} |
|
|
|
var fromID = AppUsersManager.getSelf().id |
|
if (peerID != fromID) { |
|
flags |= 2 |
|
pFlags.out = true |
|
if (!isChannel && !AppUsersManager.isBot(peerID)) { |
|
flags |= 1 |
|
pFlags.unread = true |
|
} |
|
} |
|
if (replyToMsgID) { |
|
flags |= 8 |
|
} |
|
if (asChannel) { |
|
fromID = 0 |
|
pFlags.post = true |
|
} else { |
|
flags |= 256 |
|
} |
|
var media = { |
|
_: 'messageMediaPending', |
|
type: attachType, |
|
file_name: file.name || apiFileName, |
|
size: file.size, |
|
progress: {percent: 1, total: file.size} |
|
} |
|
|
|
var message = { |
|
_: 'message', |
|
id: messageID, |
|
from_id: fromID, |
|
to_id: AppPeersManager.getOutputPeer(peerID), |
|
flags: flags, |
|
pFlags: pFlags, |
|
date: tsNow(true) + ServerTimeManager.serverTimeOffset, |
|
message: '', |
|
media: media, |
|
random_id: randomIDS, |
|
reply_to_msg_id: replyToMsgID, |
|
views: asChannel && 1, |
|
pending: true |
|
} |
|
|
|
var toggleError = function (on) { |
|
var historyMessage = messagesForHistory[messageID] |
|
if (on) { |
|
message.error = true |
|
if (historyMessage) { |
|
historyMessage.error = true |
|
} |
|
} else { |
|
delete message.error |
|
if (historyMessage) { |
|
delete historyMessage.error |
|
} |
|
} |
|
$rootScope.$broadcast('messages_pending') |
|
} |
|
|
|
var uploaded = false, |
|
uploadPromise |
|
|
|
message.send = function () { |
|
var sendFileDeferred = $q.defer() |
|
|
|
sendFilePromise.then(function () { |
|
if (!uploaded || message.error) { |
|
uploaded = false |
|
uploadPromise = MtpApiFileManager.uploadFile(file) |
|
} |
|
|
|
uploadPromise.then(function (inputFile) { |
|
inputFile.name = apiFileName |
|
uploaded = true |
|
var inputMedia |
|
switch (attachType) { |
|
case 'photo': |
|
inputMedia = {_: 'inputMediaUploadedPhoto', flags: 0, file: inputFile} |
|
break |
|
|
|
case 'document': |
|
default: |
|
inputMedia = {_: 'inputMediaUploadedDocument', file: inputFile, mime_type: file.type, caption: '', attributes: [ |
|
{_: 'documentAttributeFilename', file_name: file.name} |
|
]} |
|
} |
|
var flags = 0 |
|
if (replyToMsgID) { |
|
flags |= 1 |
|
} |
|
if (asChannel) { |
|
flags |= 16 |
|
} |
|
MtpApiManager.invokeApi('messages.sendMedia', { |
|
flags: flags, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
media: inputMedia, |
|
random_id: randomID, |
|
reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID) |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
}, function (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) |
|
}) |
|
}, function (error) { |
|
toggleError(true) |
|
}, function (progress) { |
|
// console.log('upload progress', progress) |
|
media.progress.done = progress.done |
|
media.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total)) |
|
$rootScope.$broadcast('history_update', {peerID: peerID}) |
|
}) |
|
|
|
media.progress.cancel = function () { |
|
if (!uploaded) { |
|
sendFileDeferred.resolve() |
|
uploadPromise.cancel() |
|
cancelPendingMessage(randomIDS) |
|
} |
|
} |
|
|
|
uploadPromise['finally'](function () { |
|
sendFileDeferred.resolve() |
|
}) |
|
}) |
|
|
|
sendFilePromise = sendFileDeferred.promise |
|
} |
|
|
|
saveMessages([message]) |
|
historyStorage.pending.unshift(messageID) |
|
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true}) |
|
|
|
setZeroTimeout(message.send) |
|
|
|
pendingByRandomID[randomIDS] = [peerID, messageID] |
|
} |
|
|
|
function sendOther (peerID, inputMedia, options) { |
|
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID |
|
options = options || {} |
|
|
|
var messageID = tempID-- |
|
var randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)] |
|
var randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString() |
|
var historyStorage = historiesStorage[peerID] |
|
var replyToMsgID = options.replyToMsgID |
|
var isChannel = AppPeersManager.isChannel(peerID) |
|
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID) |
|
var asChannel = isChannel && !isMegagroup ? true : false |
|
|
|
if (historyStorage === undefined) { |
|
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []} |
|
} |
|
|
|
var fromID = AppUsersManager.getSelf().id |
|
var media |
|
switch (inputMedia._) { |
|
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 |
|
} |
|
|
|
var flags = 0 |
|
var pFlags = {} |
|
if (peerID != fromID) { |
|
flags |= 2 |
|
pFlags.out = true |
|
if (!AppUsersManager.isBot(peerID)) { |
|
flags |= 1 |
|
pFlags.unread = true |
|
} |
|
} |
|
if (replyToMsgID) { |
|
flags |= 8 |
|
} |
|
if (asChannel) { |
|
fromID = 0 |
|
pFlags.post = true |
|
} else { |
|
flags |= 256 |
|
} |
|
|
|
var message = { |
|
_: 'message', |
|
id: messageID, |
|
from_id: fromID, |
|
to_id: AppPeersManager.getOutputPeer(peerID), |
|
flags: flags, |
|
pFlags: pFlags, |
|
date: tsNow(true) + ServerTimeManager.serverTimeOffset, |
|
message: '', |
|
media: media, |
|
random_id: randomIDS, |
|
reply_to_msg_id: replyToMsgID, |
|
via_bot_id: options.viaBotID, |
|
reply_markup: options.reply_markup, |
|
views: asChannel && 1, |
|
pending: true |
|
} |
|
|
|
var toggleError = function (on) { |
|
var historyMessage = 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 = function () { |
|
var flags = 0 |
|
if (replyToMsgID) { |
|
flags |= 1 |
|
} |
|
if (asChannel) { |
|
flags |= 16 |
|
} |
|
if (options.clearDraft) { |
|
flags |= 128 |
|
} |
|
|
|
var sentRequestOptions = {} |
|
if (pendingAfterMsgs[peerID]) { |
|
sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID |
|
} |
|
|
|
var apiPromise |
|
if (options.viaBotID) { |
|
apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', { |
|
flags: flags, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
random_id: randomID, |
|
reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID), |
|
query_id: options.queryID, |
|
id: options.resultID |
|
}, sentRequestOptions) |
|
} else { |
|
apiPromise = MtpApiManager.invokeApi('messages.sendMedia', { |
|
flags: flags, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
media: inputMedia, |
|
random_id: randomID, |
|
reply_to_msg_id: AppMessagesIDsManager.getMessageLocalID(replyToMsgID) |
|
}, sentRequestOptions) |
|
} |
|
apiPromise.then(function (updates) { |
|
if (updates.updates) { |
|
angular.forEach(updates.updates, function (update) { |
|
if (update._ == 'updateDraftMessage') { |
|
update.local = true |
|
} |
|
}) |
|
} |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
}, function (error) { |
|
toggleError(true) |
|
})['finally'](function () { |
|
if (pendingAfterMsgs[peerID] === sentRequestOptions) { |
|
delete pendingAfterMsgs[peerID] |
|
} |
|
}) |
|
pendingAfterMsgs[peerID] = sentRequestOptions |
|
} |
|
|
|
saveMessages([message]) |
|
historyStorage.pending.unshift(messageID) |
|
$rootScope.$broadcast('history_append', {peerID: peerID, messageID: messageID, my: true}) |
|
|
|
setZeroTimeout(message.send) |
|
|
|
if (options.clearDraft) { |
|
DraftsManager.clearDraft(peerID) |
|
} |
|
|
|
pendingByRandomID[randomIDS] = [peerID, messageID] |
|
} |
|
|
|
function forwardMessages (peerID, mids, options) { |
|
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID |
|
mids = mids.sort() |
|
options = options || {} |
|
|
|
var flags = 0 |
|
var isChannel = AppPeersManager.isChannel(peerID) |
|
var isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID) |
|
var asChannel = isChannel && !isMegagroup ? true : false |
|
|
|
if (asChannel) { |
|
flags |= 16 |
|
} |
|
if (options.withMyScore) { |
|
flags |= 256 |
|
} |
|
|
|
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids) |
|
var promises = [] |
|
angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { |
|
var len = msgIDs.length |
|
var randomIDs = [] |
|
for (var i = 0; i < len; i++) { |
|
randomIDs.push([nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]) |
|
} |
|
var sentRequestOptions = {} |
|
if (pendingAfterMsgs[peerID]) { |
|
sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID |
|
} |
|
var promise = MtpApiManager.invokeApi('messages.forwardMessages', { |
|
flags: flags, |
|
from_peer: AppPeersManager.getInputPeerByID(-channelID), |
|
id: msgIDs, |
|
random_id: randomIDs, |
|
to_peer: AppPeersManager.getInputPeerByID(peerID) |
|
}, sentRequestOptions).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
})['finally'](function () { |
|
if (pendingAfterMsgs[peerID] === sentRequestOptions) { |
|
delete pendingAfterMsgs[peerID] |
|
} |
|
}) |
|
pendingAfterMsgs[peerID] = sentRequestOptions |
|
promises.push(promise) |
|
}) |
|
|
|
return $q.all(promises) |
|
} |
|
|
|
function reportMessages (mids, reason) { |
|
mids = mids.sort() |
|
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids) |
|
var promises = [] |
|
angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { |
|
var peerID = -channelID || getMessagePeer(getMessage(msgIDs[0])) |
|
var promise = MtpApiManager.invokeApi('messages.report', { |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
id: msgIDs, |
|
reason: reason |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
}) |
|
promises.push(promise) |
|
}) |
|
|
|
return $q.all(promises) |
|
} |
|
|
|
function startBot (botID, chatID, startParam) { |
|
var peerID = chatID ? -chatID : botID |
|
if (startParam) { |
|
var randomID = bigint(nextRandomInt(0xFFFFFFFF)).shiftLeft(32).add(bigint(nextRandomInt(0xFFFFFFFF))).toString() |
|
|
|
return MtpApiManager.invokeApi('messages.startBot', { |
|
bot: AppUsersManager.getUserInput(botID), |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
random_id: randomID, |
|
start_param: startParam |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
}) |
|
} |
|
|
|
if (chatID) { |
|
if (AppChatsManager.isChannel(chatID)) { |
|
return MtpApiManager.invokeApi('channels.inviteToChannel', { |
|
channel: AppChatsManager.getChannelInput(chatID), |
|
users: [AppUsersManager.getUserInput(botID)] |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
sendText(peerID, '/start@' + bot.username) |
|
}, function (error) { |
|
if (error && error.type == 'USER_ALREADY_PARTICIPANT') { |
|
var bot = AppUsersManager.getUser(botID) |
|
sendText(peerID, '/start@' + bot.username) |
|
error.handled = true |
|
} |
|
}) |
|
} else { |
|
return MtpApiManager.invokeApi('messages.addChatUser', { |
|
chat_id: AppChatsManager.getChatInput(chatID), |
|
user_id: AppUsersManager.getUserInput(botID) |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
sendText(peerID, '/start@' + bot.username) |
|
}, function (error) { |
|
if (error && error.type == 'USER_ALREADY_PARTICIPANT') { |
|
var bot = AppUsersManager.getUser(botID) |
|
sendText(peerID, '/start@' + bot.username) |
|
error.handled = true |
|
} |
|
}) |
|
} |
|
} |
|
|
|
return sendText(peerID, '/start') |
|
} |
|
|
|
function shareGame (botID, peerID, inputGame) { |
|
var randomID = bigint(nextRandomInt(0xFFFFFFFF)).shiftLeft(32).add(bigint(nextRandomInt(0xFFFFFFFF))).toString() |
|
return MtpApiManager.invokeApi('messages.sendMedia', { |
|
flags: 0, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
media: { |
|
_: 'inputMediaGame', |
|
id: inputGame |
|
}, |
|
random_id: randomID |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
}) |
|
} |
|
|
|
function cancelPendingMessage (randomID) { |
|
var pendingData = pendingByRandomID[randomID] |
|
|
|
console.log('pending', randomID, pendingData) |
|
|
|
if (pendingData) { |
|
var peerID = pendingData[0] |
|
var tempID = pendingData[1] |
|
var historyStorage = historiesStorage[peerID] |
|
var pos = historyStorage.pending.indexOf(tempID) |
|
|
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updateShort', |
|
update: { |
|
_: 'updateDeleteMessages', |
|
messages: [tempID] |
|
} |
|
}) |
|
|
|
if (pos != -1) { |
|
historyStorage.pending.splice(pos, 1) |
|
} |
|
|
|
delete messagesForHistory[tempID] |
|
delete messagesStorage[tempID] |
|
|
|
return true |
|
} |
|
|
|
return false |
|
} |
|
|
|
function finalizePendingMessage (randomID, finalMessage) { |
|
var pendingData = pendingByRandomID[randomID] |
|
// console.log('pdata', randomID, pendingData) |
|
|
|
if (pendingData) { |
|
var peerID = pendingData[0] |
|
var tempID = pendingData[1] |
|
var historyStorage = historiesStorage[peerID], |
|
message, |
|
historyMessage |
|
|
|
// console.log('pending', randomID, historyStorage.pending) |
|
var pos = historyStorage.pending.indexOf(tempID) |
|
if (pos != -1) { |
|
historyStorage.pending.splice(pos, 1) |
|
} |
|
|
|
if (message = messagesStorage[tempID]) { |
|
delete message.pending |
|
delete message.error |
|
delete message.random_id |
|
delete message.send |
|
} |
|
|
|
if (historyMessage = messagesForHistory[tempID]) { |
|
messagesForHistory[finalMessage.mid] = angular.extend(historyMessage, wrapForHistory(finalMessage.mid)) |
|
delete historyMessage.pending |
|
delete historyMessage.error |
|
delete historyMessage.random_id |
|
delete historyMessage.send |
|
|
|
$rootScope.$broadcast('messages_pending') |
|
} |
|
|
|
delete messagesForHistory[tempID] |
|
delete messagesStorage[tempID] |
|
|
|
finalizePendingMessageCallbacks(tempID, finalMessage.mid) |
|
|
|
return message |
|
} |
|
|
|
return false |
|
} |
|
|
|
function finalizePendingMessageCallbacks(tempID, mid) { |
|
var callbacks = tempFinalizeCallbacks[tempID] |
|
console.warn(dT(), callbacks, tempID) |
|
if (callbacks !== undefined) { |
|
angular.forEach(callbacks, function (callback) { |
|
callback(mid) |
|
}) |
|
delete tempFinalizeCallbacks[tempID] |
|
} |
|
} |
|
|
|
function getInputEntities(entities) { |
|
var sendEntites = angular.copy(entities) |
|
angular.forEach(sendEntites, function (entity) { |
|
if (entity._ == 'messageEntityMentionName') { |
|
entity._ = 'inputMessageEntityMentionName' |
|
entity.user_id = AppUsersManager.getUserInput(entity.user_id) |
|
} |
|
}) |
|
return sendEntites |
|
} |
|
|
|
function editMessage(messageID, text) { |
|
if (!angular.isString(text) || |
|
!canEditMessage(messageID)) { |
|
return $q.reject() |
|
} |
|
if (messageID < 0) { |
|
if (tempFinalizeCallbacks[messageID] === undefined) { |
|
tempFinalizeCallbacks[messageID] = {} |
|
} |
|
var deferred = $q.defer() |
|
tempFinalizeCallbacks[messageID].edit = function (mid) { |
|
console.log('invoke callback', mid) |
|
editMessage(mid, text).then(function (result) { |
|
deferred.resolve(result) |
|
}, function (error) { |
|
deferred.reject(error) |
|
}) |
|
} |
|
return deferred.promise |
|
} |
|
var entities = [] |
|
text = RichTextProcessor.parseMarkdown(text, entities) |
|
|
|
var message = getMessage(messageID) |
|
var peerID = getMessagePeer(message) |
|
var flags = 8 | (1 << 11) |
|
|
|
return MtpApiManager.invokeApi('messages.editMessage', { |
|
flags: flags, |
|
peer: AppPeersManager.getInputPeerByID(peerID), |
|
id: AppMessagesIDsManager.getMessageLocalID(messageID), |
|
message: text, |
|
entities: getInputEntities(entities) |
|
}).then(function (updates) { |
|
ApiUpdatesManager.processUpdateMessage(updates) |
|
}, function (error) { |
|
if (error && |
|
error.type == 'MESSAGE_NOT_MODIFIED') { |
|
error.handled = true |
|
return |
|
} |
|
if (error && |
|
error.type == 'MESSAGE_EMPTY') { |
|
error.handled = true |
|
} |
|
return $q.reject(error) |
|
}) |
|
} |
|
|
|
function getMessagePeer (message) { |
|
var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0 |
|
|
|
if (toID < 0) { |
|
return toID |
|
} else if (message.pFlags && message.pFlags.out || message.flags & 2) { |
|
return toID |
|
} |
|
return message.from_id |
|
} |
|
|
|
function wrapForDialog (msgID, dialog) { |
|
var useCache = msgID && dialog !== undefined |
|
var unreadCount = dialog && dialog.unread_count |
|
|
|
if (useCache && messagesForDialogs[msgID] !== undefined) { |
|
delete messagesForDialogs[msgID].typing |
|
messagesForDialogs[msgID].unreadCount = unreadCount |
|
return messagesForDialogs[msgID] |
|
} |
|
|
|
var message = angular.copy(messagesStorage[msgID]) |
|
|
|
if (!message || !message.to_id) { |
|
if (dialog && dialog.peerID) { |
|
message = { |
|
_: 'message', |
|
to_id: AppPeersManager.getOutputPeer(dialog.peerID), |
|
deleted: true, |
|
date: tsNow(true), |
|
pFlags: {out: true} |
|
} |
|
} else { |
|
return message |
|
} |
|
} |
|
|
|
message.peerID = getMessagePeer(message) |
|
message.peerData = AppPeersManager.getPeer(message.peerID) |
|
message.peerString = AppPeersManager.getPeerString(message.peerID) |
|
message.unreadCount = unreadCount |
|
message.index = dialog && dialog.index || (message.date * 0x10000) |
|
message.pinned = dialog && dialog.pFlags.pinned || false |
|
|
|
if (message._ == 'messageService' && message.action.user_id) { |
|
message.action.user = AppUsersManager.getUser(message.action.user_id) |
|
} |
|
|
|
if (message.message && message.message.length) { |
|
message.richMessage = RichTextProcessor.wrapRichText(message.message.substr(0, 128), {noLinks: true, noLinebreaks: true}) |
|
} |
|
|
|
message.dateText = dateOrTimeFilter(message.date) |
|
|
|
if (useCache) { |
|
message.draft = DraftsManager.getServerDraft(message.peerID) |
|
messagesForDialogs[msgID] = message |
|
} |
|
|
|
return message |
|
} |
|
|
|
function wrapSingleMessage (msgID) { |
|
if (messagesStorage[msgID]) { |
|
return wrapForDialog(msgID) |
|
} |
|
if (needSingleMessages.indexOf(msgID) == -1) { |
|
needSingleMessages.push(msgID) |
|
if (fetchSingleMessagesTimeout === false) { |
|
fetchSingleMessagesTimeout = setTimeout(fetchSingleMessages, 100) |
|
} |
|
} |
|
return {mid: msgID, loading: true} |
|
} |
|
|
|
function clearDialogCache (msgID) { |
|
delete messagesForDialogs[msgID] |
|
} |
|
|
|
function wrapForHistory (msgID) { |
|
if (messagesForHistory[msgID] !== undefined) { |
|
return messagesForHistory[msgID] |
|
} |
|
|
|
var message = angular.copy(messagesStorage[msgID]) || {id: msgID} |
|
|
|
if (message.media && message.media.progress !== undefined) { |
|
message.media.progress = messagesStorage[msgID].media.progress |
|
} |
|
|
|
var fromUser = message.from_id && AppUsersManager.getUser(message.from_id) |
|
var fromBot = fromUser && fromUser.pFlags.bot && fromUser.username || false |
|
var withBot = (fromBot || |
|
message.to_id && ( |
|
message.to_id.chat_id || |
|
message.to_id.user_id && AppUsersManager.isBot(message.to_id.user_id) |
|
) |
|
) |
|
|
|
if (message.media) { |
|
if (message.media.caption && |
|
message.media.caption.length) { |
|
message.media.rCaption = RichTextProcessor.wrapRichText(message.media.caption, { |
|
noCommands: !withBot, |
|
fromBot: fromBot |
|
}) |
|
} |
|
|
|
switch (message.media._) { |
|
case 'messageMediaPhoto': |
|
message.media.photo = AppPhotosManager.wrapForHistory(message.media.photo.id) |
|
break |
|
|
|
case 'messageMediaDocument': |
|
message.media.document = AppDocsManager.wrapForHistory(message.media.document.id) |
|
break |
|
|
|
case 'messageMediaGeo': |
|
var mapUrl = 'https://maps.google.com/?q=' + message.media.geo['lat'] + ',' + message.media.geo['long'] |
|
message.media.mapUrl = $sce.trustAsResourceUrl(mapUrl) |
|
break |
|
|
|
case 'messageMediaVenue': |
|
var mapUrl |
|
if (message.media.provider == 'foursquare' && |
|
message.media.venue_id) { |
|
mapUrl = 'https://foursquare.com/v/' + encodeURIComponent(message.media.venue_id) |
|
} else { |
|
mapUrl = 'https://maps.google.com/?q=' + message.media.geo['lat'] + ',' + message.media.geo['long'] |
|
} |
|
message.media.mapUrl = $sce.trustAsResourceUrl(mapUrl) |
|
break |
|
|
|
case 'messageMediaContact': |
|
message.media.rFullName = RichTextProcessor.wrapRichText( |
|
message.media.first_name + ' ' + (message.media.last_name || ''), |
|
{noLinks: true, noLinebreaks: true} |
|
) |
|
break |
|
|
|
case 'messageMediaWebPage': |
|
if (!message.media.webpage || |
|
message.media.webpage._ == 'webPageEmpty') { |
|
delete message.media |
|
break |
|
} |
|
message.media.webpage = AppWebPagesManager.wrapForHistory(message.media.webpage.id) |
|
break |
|
|
|
case 'messageMediaGame': |
|
message.media.game = AppGamesManager.wrapForHistory(message.media.game.id) |
|
break |
|
} |
|
} |
|
else if (message.action) { |
|
switch (message.action._) { |
|
case 'messageActionChatEditPhoto': |
|
case 'messageActionChannelEditPhoto': |
|
message.action.photo = AppPhotosManager.wrapForHistory(message.action.photo.id) |
|
break |
|
|
|
case 'messageActionChatCreate': |
|
case 'messageActionChatEditTitle': |
|
case 'messageActionChannelCreate': |
|
case 'messageActionChannelEditTitle': |
|
message.action.rTitle = RichTextProcessor.wrapRichText(message.action.title, {noLinebreaks: true}) || _('chat_title_deleted') |
|
break |
|
|
|
case 'messageActionBotIntro': |
|
message.action.rDescription = RichTextProcessor.wrapRichText(message.action.description, { |
|
noCommands: !withBot, |
|
fromBot: fromBot |
|
}) |
|
break |
|
} |
|
} |
|
|
|
return messagesForHistory[msgID] = message |
|
} |
|
|
|
function wrapReplyMarkup (replyMarkup, fromID) { |
|
if (!replyMarkup || |
|
replyMarkup._ == 'replyKeyboardHide') { |
|
return false |
|
} |
|
if (replyMarkup.wrapped) { |
|
return replyMarkup |
|
} |
|
var isInline = replyMarkup._ == 'replyInlineMarkup' |
|
var count = replyMarkup.rows && replyMarkup.rows.length || 0 |
|
if (!isInline && |
|
count > 0 && |
|
count <= 4 && |
|
!(replyMarkup.pFlags && replyMarkup.pFlags.resize)) { |
|
replyMarkup.splitCount = count |
|
} |
|
replyMarkup.wrapped = true |
|
angular.forEach(replyMarkup.rows, function (markupRow) { |
|
angular.forEach(markupRow.buttons, function (markupButton) { |
|
markupButton.rText = RichTextProcessor.wrapRichText(markupButton.text, {noLinks: true, noLinebreaks: true}) |
|
if (markupButton._ == 'keyboardButtonUrl') { |
|
var from = AppUsersManager.getUser(fromID) |
|
var unsafe = !(from && from.pFlags && from.pFlags.verified) |
|
markupButton.pUrl = RichTextProcessor.wrapUrl(markupButton.url, unsafe) |
|
} |
|
}) |
|
}) |
|
return replyMarkup |
|
} |
|
|
|
function wrapMessageText(msgID) { |
|
var message = getMessage(msgID) |
|
var fromUser = message.from_id && AppUsersManager.getUser(message.from_id) |
|
var fromBot = fromUser && fromUser.pFlags.bot && fromUser.username || false |
|
var toPeerID = AppPeersManager.getPeerID(message.to_id) |
|
var withBot = ( |
|
fromBot || |
|
AppPeersManager.isBot(toPeerID) || |
|
AppPeersManager.isAnyGroup(toPeerID) |
|
) |
|
|
|
var options = { |
|
noCommands: !withBot, |
|
fromBot: fromBot, |
|
entities: message.totalEntities |
|
} |
|
if (message.pFlags.mentioned) { |
|
var user = AppUsersManager.getSelf() |
|
if (user) { |
|
options.highlightUsername = user.username |
|
} |
|
} |
|
return RichTextProcessor.wrapRichText(message.message, options) |
|
} |
|
|
|
function fetchSingleMessages () { |
|
if (fetchSingleMessagesTimeout !== false) { |
|
clearTimeout(fetchSingleMessagesTimeout) |
|
fetchSingleMessagesTimeout = false |
|
} |
|
if (!needSingleMessages.length) { |
|
return |
|
} |
|
var mids = needSingleMessages.slice() |
|
needSingleMessages = [] |
|
|
|
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids) |
|
angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { |
|
var promise |
|
if (channelID > 0) { |
|
promise = MtpApiManager.invokeApi('channels.getMessages', { |
|
channel: AppChatsManager.getChannelInput(channelID), |
|
id: msgIDs |
|
}) |
|
} else { |
|
promise = MtpApiManager.invokeApi('messages.getMessages', { |
|
id: msgIDs |
|
}) |
|
} |
|
|
|
promise.then(function (getMessagesResult) { |
|
AppUsersManager.saveApiUsers(getMessagesResult.users) |
|
AppChatsManager.saveApiChats(getMessagesResult.chats) |
|
saveMessages(getMessagesResult.messages) |
|
|
|
$rootScope.$broadcast('messages_downloaded', splitted.mids[channelID]) |
|
}) |
|
}) |
|
} |
|
|
|
function incrementMessageViews () { |
|
if (incrementMessageViewsTimeout !== false) { |
|
clearTimeout(incrementMessageViewsTimeout) |
|
incrementMessageViewsTimeout = false |
|
} |
|
if (!needIncrementMessageViews.length) { |
|
return |
|
} |
|
var mids = needIncrementMessageViews.slice() |
|
needIncrementMessageViews = [] |
|
|
|
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids) |
|
angular.forEach(splitted.msgIDs, function (msgIDs, channelID) { |
|
// console.log('increment', msgIDs, channelID) |
|
MtpApiManager.invokeApi('messages.getMessagesViews', { |
|
peer: AppPeersManager.getInputPeerByID(-channelID), |
|
id: msgIDs, |
|
increment: true |
|
}).then(function (views) { |
|
if (channelID) { |
|
var mids = splitted.mids[channelID] |
|
var updates = [] |
|
for (var i = 0; i < mids.length; i++) { |
|
updates.push({ |
|
_: 'updateChannelMessageViews', |
|
channel_id: channelID, |
|
id: mids[i], |
|
views: views[i] |
|
}) |
|
} |
|
ApiUpdatesManager.processUpdateMessage({ |
|
_: 'updates', |
|
updates: updates, |
|
chats: [], |
|
users: [] |
|
}) |
|
} |
|
}) |
|
}) |
|
} |
|
|
|
function regroupWrappedHistory (history, limit) { |
|
if (!history || !history.length) { |
|
return false |
|
} |
|
var start = 0 |
|
var len = history.length |
|
var end = len, |
|
i, curDay |
|
var prevDay, curMessage |
|
var prevMessage, curGrouped |
|
var prevGrouped |
|
var wasUpdated = false |
|
var groupFwd = !Config.Mobile |
|
|
|
if (limit > 0) { |
|
end = Math.min(limit, len) |
|
} else if (limit < 0) { |
|
start = Math.max(0, end + limit) |
|
} |
|
|
|
for (i = start; i < end; i++) { |
|
if (history[i].deleted) { |
|
history.splice(i, 1) |
|
end-- |
|
continue |
|
} |
|
curMessage = history[i] |
|
curDay = Math.floor((curMessage.date + ServerTimeManager.midnightOffset) / 86400) |
|
|
|
prevGrouped = prevMessage && prevMessage.grouped |
|
curGrouped = curMessage.grouped |
|
|
|
if (curDay === prevDay) { |
|
if (curMessage.needDate) { |
|
delete curMessage.needDate |
|
wasUpdated = true |
|
} |
|
} else if (!i || prevMessage) { |
|
if (!curMessage.needDate) { |
|
curMessage.needDate = true |
|
wasUpdated = true |
|
} |
|
} |
|
|
|
if (curMessage.fwdFromID && |
|
curMessage.media && |
|
curMessage.media.document && |
|
(curMessage.media.document.sticker || curMessage.media.document.audioTitle) && |
|
(curMessage.fromID != (prevMessage || {}).fromID || !(prevMessage || {}).fwdFromID)) { |
|
delete curMessage.fwdFromID |
|
curMessage._ = 'message' |
|
} |
|
|
|
if (curMessage.views && |
|
!incrementedMessageViews[curMessage.mid]) { |
|
incrementedMessageViews[curMessage.mid] = true |
|
needIncrementMessageViews.push(curMessage.mid) |
|
if (incrementMessageViewsTimeout === false) { |
|
incrementMessageViewsTimeout = setTimeout(incrementMessageViews, 15000) |
|
} |
|
} |
|
|
|
if ((!AppPeersManager.isBroadcast(getMessagePeer(curMessage)) || curMessage.fwdFromID) && |
|
prevMessage && |
|
prevMessage.fromID == curMessage.fromID && |
|
!prevMessage.fwdFromID == !curMessage.fwdFromID && |
|
prevMessage.viaBotID == curMessage.viaBotID && |
|
!prevMessage.action && |
|
!curMessage.action && |
|
curMessage.date < prevMessage.date + 900) { |
|
var singleLine = curMessage.message && curMessage.message.length < 70 && curMessage.message.indexOf('\n') == -1 && !curMessage.reply_to_mid |
|
if (groupFwd && |
|
curMessage.fwdFromID && |
|
curMessage.fwdFromID == prevMessage.fwdFromID && |
|
curMessage.viaBotID == prevMessage.viaBotID) { |
|
curMessage.grouped = singleLine ? 'im_grouped_fwd_short' : 'im_grouped_fwd' |
|
} else { |
|
curMessage.grouped = !curMessage.fwdFromID && singleLine ? 'im_grouped_short' : 'im_grouped' |
|
} |
|
if (groupFwd && curMessage.fwdFromID) { |
|
if (!prevMessage.grouped) { |
|
prevMessage.grouped = 'im_grouped_fwd_start' |
|
} |
|
if (curMessage.grouped && i == len - 1) { |
|
curMessage.grouped += ' im_grouped_fwd_end' |
|
} |
|
} |
|
} else if (prevMessage || !i) { |
|
delete curMessage.grouped |
|
|
|
if (groupFwd && prevMessage && prevMessage.grouped && prevMessage.fwdFromID) { |
|
prevMessage.grouped += ' im_grouped_fwd_end' |
|
} |
|
} |
|
if (!wasUpdated && prevGrouped != (prevMessage && prevMessage.grouped)) { |
|
wasUpdated = true |
|
} |
|
prevMessage = curMessage |
|
prevDay = curDay |
|
} |
|
if (!wasUpdated && curGrouped != (prevMessage && prevMessage.grouped)) { |
|
wasUpdated = true |
|
} |
|
|
|
return wasUpdated |
|
} |
|
|
|
function getMessageThumb (message, thumbWidth, thumbHeight) { |
|
var thumbPhotoSize |
|
var sticker = false |
|
if (message.media) { |
|
switch (message.media._) { |
|
case 'messageMediaPhoto': |
|
thumbPhotoSize = AppPhotosManager.choosePhotoSize(message.media.photo, thumbWidth, thumbHeight) |
|
break |
|
|
|
case 'messageMediaDocument': |
|
thumbPhotoSize = message.media.document.thumb |
|
if (message.media.document.sticker) { |
|
sticker = true |
|
} |
|
break |
|
} |
|
} |
|
|
|
if (thumbPhotoSize && thumbPhotoSize._ != 'photoSizeEmpty') { |
|
var dim = calcImageInBox(thumbPhotoSize.w, thumbPhotoSize.h, thumbWidth, thumbHeight, true) |
|
|
|
var thumb = { |
|
width: dim.w, |
|
height: dim.h, |
|
location: thumbPhotoSize.location, |
|
size: thumbPhotoSize.size |
|
} |
|
if (sticker) { |
|
thumb.location.sticker = true |
|
} |
|
|
|
return thumb |
|
} |
|
|
|
return false |
|
} |
|
|
|
function incrementMaxSeenID (maxID) { |
|
if (!maxID || !(!maxSeenID || maxID > maxSeenID)) { |
|
return false |
|
} |
|
Storage.set({ |
|
max_seen_msg: maxID |
|
}) |
|
MtpApiManager.invokeApi('messages.receivedMessages', { |
|
max_id: maxID |
|
}) |
|
} |
|
|
|
function notifyAboutMessage (message, options) { |
|
options = options || {} |
|
|
|
var peerID = getMessagePeer(message) |
|
var peerString |
|
var notification = {} |
|
var notificationMessage = false, |
|
notificationPhoto |
|
|
|
var notifySettings = NotificationsManager.getNotifySettings() |
|
|
|
if (message.fwdFromID && options.fwd_count) { |
|
notificationMessage = fwdMessagesPluralize(options.fwd_count) |
|
} else if (message.message) { |
|
if (notifySettings.nopreview) { |
|
notificationMessage = _('conversation_message_sent') |
|
} else { |
|
notificationMessage = RichTextProcessor.wrapPlainText(message.message) |
|
} |
|
} else if (message.media) { |
|
var captionEmoji = false; |
|
switch (message.media._) { |
|
case 'messageMediaPhoto': |
|
notificationMessage = _('conversation_media_photo_raw') |
|
captionEmoji = '🖼' |
|
break |
|
case 'messageMediaDocument': |
|
switch (message.media.document.type) { |
|
case 'gif': |
|
notificationMessage = _('conversation_media_gif_raw') |
|
captionEmoji = '🎬' |
|
break |
|
case 'sticker': |
|
notificationMessage = _('conversation_media_sticker') |
|
var stickerEmoji = message.media.document.stickerEmojiRaw |
|
if (stickerEmoji !== undefined) { |
|
notificationMessage = RichTextProcessor.wrapPlainText(stickerEmoji) + ' ' + notificationMessage |
|
} |
|
break |
|
case 'video': |
|
notificationMessage = _('conversation_media_video_raw') |
|
captionEmoji = '📹' |
|
break |
|
case 'round': |
|
notificationMessage = _('conversation_media_round_raw') |
|
captionEmoji = '📹' |
|
break |
|
case 'voice': |
|
case 'audio': |
|
notificationMessage = _('conversation_media_audio_raw') |
|
break |
|
default: |
|
if (message.media.document.file_name) { |
|
notificationMessage = RichTextProcessor.wrapPlainText('📎 ' + message.media.document.file_name) |
|
} else { |
|
notificationMessage = _('conversation_media_document_raw') |
|
captionEmoji = '📎' |
|
} |
|
break |
|
} |
|
break |
|
|
|
case 'messageMediaGeo': |
|
case 'messageMediaVenue': |
|
notificationMessage = _('conversation_media_location_raw') |
|
captionEmoji = '📍' |
|
break |
|
case 'messageMediaContact': |
|
notificationMessage = _('conversation_media_contact_raw') |
|
break |
|
case 'messageMediaGame': |
|
notificationMessage = RichTextProcessor.wrapPlainText('🎮 ' + message.media.game.title) |
|
break |
|
case 'messageMediaUnsupported': |
|
notificationMessage = _('conversation_media_unsupported_raw') |
|
break |
|
default: |
|
notificationMessage = _('conversation_media_attachment_raw') |
|
break |
|
} |
|
|
|
if (captionEmoji !== false && |
|
message.media.caption) { |
|
notificationMessage = RichTextProcessor.wrapPlainText(captionEmoji + ' ' + message.media.caption) |
|
} |
|
} else if (message._ == 'messageService') { |
|
switch (message.action._) { |
|
case 'messageActionChatCreate': |
|
notificationMessage = _('conversation_group_created_raw') |
|
break |
|
case 'messageActionChatEditTitle': |
|
notificationMessage = _('conversation_group_renamed_raw') |
|
break |
|
case 'messageActionChatEditPhoto': |
|
notificationMessage = _('conversation_group_photo_updated_raw') |
|
break |
|
case 'messageActionChatDeletePhoto': |
|
notificationMessage = _('conversation_group_photo_removed_raw') |
|
break |
|
case 'messageActionChatAddUser': |
|
case 'messageActionChatAddUsers': |
|
notificationMessage = _('conversation_invited_user_message_raw') |
|
break |
|
case 'messageActionChatReturn': |
|
notificationMessage = _('conversation_returned_to_group_raw') |
|
break |
|
case 'messageActionChatJoined': |
|
notificationMessage = _('conversation_joined_group_raw') |
|
break |
|
case 'messageActionChatDeleteUser': |
|
notificationMessage = _('conversation_kicked_user_message_raw') |
|
break |
|
case 'messageActionChatLeave': |
|
notificationMessage = _('conversation_left_group_raw') |
|
break |
|
case 'messageActionChatJoinedByLink': |
|
notificationMessage = _('conversation_joined_by_link_raw') |
|
break |
|
case 'messageActionChannelCreate': |
|
notificationMessage = _('conversation_created_channel_raw') |
|
break |
|
case 'messageActionChannelEditTitle': |
|
notificationMessage = _('conversation_changed_channel_name_raw') |
|
break |
|
case 'messageActionChannelEditPhoto': |
|
notificationMessage = _('conversation_changed_channel_photo_raw') |
|
break |
|
case 'messageActionChannelDeletePhoto': |
|
notificationMessage = _('conversation_removed_channel_photo_raw') |
|
break |
|
case 'messageActionPinMessage': |
|
notificationMessage = _('conversation_pinned_message_raw') |
|
break |
|
case 'messageActionGameScore': |
|
notificationMessage = gameScorePluralize(message.action.score) |
|
break |
|
|
|
case 'messageActionPhoneCall': |
|
switch (message.action.type) { |
|
case 'out_missed': |
|
notificationMessage = _('message_service_phonecall_canceled_raw') |
|
break |
|
case 'in_missed': |
|
notificationMessage = _('message_service_phonecall_missed_raw') |
|
break |
|
case 'out_ok': |
|
notificationMessage = _('message_service_phonecall_outgoing_raw') |
|
break |
|
case 'in_ok': |
|
notificationMessage = _('message_service_phonecall_incoming_raw') |
|
break |
|
} |
|
break |
|
} |
|
} |
|
|
|
if (peerID > 0) { |
|
var fromUser = AppUsersManager.getUser(message.from_id) |
|
var fromPhoto = AppUsersManager.getUserPhoto(message.from_id) |
|
|
|
notification.title = (fromUser.first_name || '') + |
|
(fromUser.first_name && fromUser.last_name ? ' ' : '') + |
|
(fromUser.last_name || '') |
|
if (!notification.title) { |
|
notification.title = fromUser.phone || _('conversation_unknown_user_raw') |
|
} |
|
|
|
notificationPhoto = fromPhoto |
|
|
|
peerString = AppUsersManager.getUserString(peerID) |
|
} else { |
|
notification.title = AppChatsManager.getChat(-peerID).title || _('conversation_unknown_chat_raw') |
|
|
|
if (message.from_id > 0) { |
|
var fromUser = AppUsersManager.getUser(message.from_id) |
|
notification.title = (fromUser.first_name || fromUser.last_name || _('conversation_unknown_user_raw')) + |
|
' @ ' + |
|
notification.title |
|
} |
|
|
|
notificationPhoto = AppChatsManager.getChatPhoto(-peerID) |
|
|
|
peerString = AppChatsManager.getChatString(-peerID) |
|
} |
|
|
|
notification.title = RichTextProcessor.wrapPlainText(notification.title) |
|
|
|
notification.onclick = function () { |
|
$rootScope.$broadcast('history_focus', { |
|
peerString: peerString, |
|
messageID: message.flags & 16 ? message.mid : 0 |
|
}) |
|
} |
|
|
|
notification.message = notificationMessage |
|
notification.key = 'msg' + message.mid |
|
notification.tag = peerString |
|
notification.silent = message.pFlags.silent || false |
|
|
|
if (notificationPhoto.location && !notificationPhoto.location.empty) { |
|
MtpApiFileManager.downloadSmallFile(notificationPhoto.location, notificationPhoto.size).then(function (blob) { |
|
if (message.pFlags.unread) { |
|
notification.image = blob |
|
NotificationsManager.notify(notification) |
|
} |
|
}) |
|
} else { |
|
NotificationsManager.notify(notification) |
|
} |
|
} |
|
|
|
var newMessagesHandlePromise = false |
|
var newMessagesToHandle = {} |
|
var newDialogsHandlePromise = false |
|
var newDialogsToHandle = {} |
|
var notificationsHandlePromise = false |
|
var notificationsToHandle = {} |
|
var newUpdatesAfterReloadToHandle = {} |
|
|
|
function handleNewMessages () { |
|
$timeout.cancel(newMessagesHandlePromise) |
|
newMessagesHandlePromise = false |
|
$rootScope.$broadcast('history_multiappend', newMessagesToHandle) |
|
newMessagesToHandle = {} |
|
} |
|
|
|
function handleNewDialogs () { |
|
$timeout.cancel(newDialogsHandlePromise) |
|
newDialogsHandlePromise = false |
|
var newMaxSeenID = 0 |
|
angular.forEach(newDialogsToHandle, function (dialog, peerID) { |
|
if (dialog.reload) { |
|
reloadConversation(peerID) |
|
delete newDialogsToHandle[peerID] |
|
} else { |
|
pushDialogToStorage(dialog) |
|
if (!AppPeersManager.isChannel(peerID)) { |
|
newMaxSeenID = Math.max(newMaxSeenID, dialog.top_message || 0) |
|
} |
|
} |
|
}) |
|
if (newMaxSeenID !== false) { |
|
incrementMaxSeenID(newMaxSeenID) |
|
} |
|
$rootScope.$broadcast('dialogs_multiupdate', newDialogsToHandle) |
|
newDialogsToHandle = {} |
|
} |
|
|
|
function handleNotifications () { |
|
$timeout.cancel(notificationsHandlePromise) |
|
notificationsHandlePromise = false |
|
|
|
var timeout = $rootScope.idle.isIDLE && StatusManager.isOtherDeviceActive() ? 30000 : 1000 |
|
angular.forEach(notificationsToHandle, function (notifyPeerToHandle) { |
|
notifyPeerToHandle.isMutedPromise.then(function (muted) { |
|
var topMessage = notifyPeerToHandle.top_message |
|
if (muted || |
|
!topMessage.pFlags.unread) { |
|
return |
|
} |
|
setTimeout(function () { |
|
if (topMessage.pFlags.unread) { |
|
notifyAboutMessage(topMessage, { |
|
fwd_count: notifyPeerToHandle.fwd_count |
|
}) |
|
} |
|
}, timeout) |
|
}) |
|
}) |
|
|
|
notificationsToHandle = {} |
|
} |
|
|
|
function handleUpdate(update) { |
|
switch (update._) { |
|
case 'updateMessageID': |
|
var randomID = update.random_id |
|
var pendingData = pendingByRandomID[randomID] |
|
if (pendingData) { |
|
var peerID = pendingData[0] |
|
var tempID = pendingData[1] |
|
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0 |
|
var mid = AppMessagesIDsManager.getFullMessageID(update.id, channelID) |
|
var message = messagesStorage[mid] |
|
if (message) { |
|
var historyStorage = historiesStorage[peerID] |
|
var pos = historyStorage.pending.indexOf(tempID) |
|
if (pos != -1) { |
|
historyStorage.pending.splice(pos, 1) |
|
} |
|
delete messagesForHistory[tempID] |
|
delete messagesStorage[tempID] |
|
|
|
var msgs = {} |
|
msgs[tempID] = true |
|
|
|
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs}) |
|
|
|
finalizePendingMessageCallbacks(tempID, finalMessage.mid) |
|
} else { |
|
pendingByMessageID[mid] = randomID |
|
} |
|
} |
|
break |
|
|
|
case 'updateNewMessage': |
|
case 'updateNewChannelMessage': |
|
var message = update.message |
|
var peerID = getMessagePeer(message) |
|
var historyStorage = historiesStorage[peerID] |
|
var foundDialog = getDialogByPeerID(peerID) |
|
|
|
if (!foundDialog.length) { |
|
newDialogsToHandle[peerID] = {reload: true} |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
if (newUpdatesAfterReloadToHandle[peerID] === undefined) { |
|
newUpdatesAfterReloadToHandle[peerID] = [] |
|
} |
|
newUpdatesAfterReloadToHandle[peerID].push(update) |
|
break |
|
} |
|
|
|
if (update._ == 'updateNewChannelMessage') { |
|
var chat = AppChatsManager.getChat(-peerID) |
|
if (chat.pFlags && (chat.pFlags.left || chat.pFlags.kicked)) { |
|
break |
|
} |
|
} |
|
|
|
saveMessages([message], {isNew: true}) |
|
// console.warn(dT(), 'message unread', message.mid, message.pFlags.unread) |
|
|
|
if (historyStorage === undefined) { |
|
historyStorage = historiesStorage[peerID] = { |
|
count: null, |
|
history: [], |
|
pending: [] |
|
} |
|
} |
|
|
|
var history = message.mid > 0 ? historyStorage.history : historyStorage.pending |
|
if (history.indexOf(message.mid) != -1) { |
|
return false |
|
} |
|
var topMsgID = history[0] |
|
history.unshift(message.mid) |
|
if (message.mid > 0 && message.mid < topMsgID) { |
|
history.sort(function (a, b) { |
|
return b - a |
|
}) |
|
} |
|
if (message.mid > 0 && |
|
historyStorage.count !== null) { |
|
historyStorage.count++ |
|
} |
|
|
|
if (mergeReplyKeyboard(historyStorage, message)) { |
|
$rootScope.$broadcast('history_reply_markup', {peerID: peerID}) |
|
} |
|
|
|
if (!message.pFlags.out && message.from_id) { |
|
AppUsersManager.forceUserOnline(message.from_id) |
|
} |
|
|
|
var randomID = pendingByMessageID[message.mid], |
|
pendingMessage |
|
|
|
if (randomID) { |
|
if (pendingMessage = finalizePendingMessage(randomID, message)) { |
|
$rootScope.$broadcast('history_update', {peerID: peerID}) |
|
} |
|
delete pendingByMessageID[message.mid] |
|
} |
|
|
|
if (!pendingMessage) { |
|
if (newMessagesToHandle[peerID] === undefined) { |
|
newMessagesToHandle[peerID] = [] |
|
} |
|
newMessagesToHandle[peerID].push(message.mid) |
|
if (!newMessagesHandlePromise) { |
|
newMessagesHandlePromise = $timeout(handleNewMessages, 0) |
|
} |
|
} |
|
|
|
var inboxUnread = !message.pFlags.out && message.pFlags.unread |
|
var dialog = foundDialog[0] |
|
dialog.top_message = message.mid |
|
if (inboxUnread) { |
|
dialog.unread_count++ |
|
} |
|
if (!dialog.pFlags.pinned || !dialog.index) { |
|
dialog.index = generateDialogIndex(message.date) |
|
} |
|
|
|
newDialogsToHandle[peerID] = dialog |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
|
|
if (inboxUnread && |
|
($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) { |
|
var notifyPeer = message.flags & 16 ? message.from_id : peerID |
|
var notifyPeerToHandle = notificationsToHandle[notifyPeer] |
|
if (notifyPeerToHandle === undefined) { |
|
notifyPeerToHandle = notificationsToHandle[notifyPeer] = { |
|
isMutedPromise: NotificationsManager.getPeerMuted(notifyPeer), |
|
fwd_count: 0, |
|
from_id: 0 |
|
} |
|
} |
|
|
|
if (notifyPeerToHandle.from_id != message.from_id) { |
|
notifyPeerToHandle.from_id = message.from_id |
|
notifyPeerToHandle.fwd_count = 0 |
|
} |
|
if (message.fwdFromID) { |
|
notifyPeerToHandle.fwd_count++ |
|
} |
|
|
|
notifyPeerToHandle.top_message = message |
|
|
|
if (!notificationsHandlePromise) { |
|
notificationsHandlePromise = $timeout(handleNotifications, 1000) |
|
} |
|
} |
|
break |
|
|
|
case 'updateDialogPinned': |
|
var peerID = AppPeersManager.getPeerID(update.peer) |
|
var foundDialog = getDialogByPeerID(peerID) |
|
|
|
if (!foundDialog.length || !update.pFlags.pinned) { |
|
newDialogsToHandle[peerID] = {reload: true} |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
break |
|
} |
|
|
|
var dialog = foundDialog[0] |
|
dialog.index = generateDialogIndex(generateDialogPinnedDate()) |
|
dialog.pFlags.pinned = true |
|
break |
|
|
|
case 'updatePinnedDialogs': |
|
var changedDialogs = {} |
|
var newPinned = {} |
|
if (!update.order) { |
|
MtpApiManager.invokeApi('messages.getPinnedDialogs', {}).then(function (dialogsResult) { |
|
dialogsResult.dialogs.reverse() |
|
applyConversations(dialogsResult) |
|
angular.forEach(dialogsResult.dialogs, function (dialog) { |
|
newPinned[dialog.peerID] = true |
|
}) |
|
angular.forEach(dialogsStorage.dialogs, function (dialog) { |
|
var peerID = dialog.peerID |
|
if (dialog.pFlags.pinned && !newPinned[peerID]) { |
|
newDialogsToHandle[peerID] = {reload: true} |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
} |
|
}) |
|
}) |
|
break |
|
} |
|
update.order.reverse() |
|
angular.forEach(update.order, function (peer) { |
|
var peerID = AppPeersManager.getPeerID(peer) |
|
newPinned[peerID] = true |
|
|
|
var foundDialog = getDialogByPeerID(peerID) |
|
|
|
if (!foundDialog.length) { |
|
newDialogsToHandle[peerID] = {reload: true} |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
return |
|
} |
|
|
|
var dialog = foundDialog[0] |
|
dialog.index = generateDialogIndex(generateDialogPinnedDate()) |
|
dialog.pFlags.pinned = true |
|
|
|
newDialogsToHandle[peerID] = dialog |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
}) |
|
angular.forEach(dialogsStorage.dialogs, function (dialog) { |
|
var peerID = dialog.peerID |
|
if (dialog.pFlags.pinned && !newPinned[peerID]) { |
|
newDialogsToHandle[peerID] = {reload: true} |
|
if (!newDialogsHandlePromise) { |
|
newDialogsHandlePromise = $timeout(handleNewDialogs, 0) |
|
} |
|
} |
|
}) |
|
break |
|
|
|
case 'updateEditMessage': |
|
case 'updateEditChannelMessage': |
|
var message = update.message |
|
var peerID = getMessagePeer(message) |
|
var channelID = message.to_id._ == 'peerChannel' ? -peerID : 0 |
|
var mid = AppMessagesIDsManager.getFullMessageID(message.id, channelID) |
|
if (messagesStorage[mid] === undefined) { |
|
break |
|
} |
|
|
|
// console.trace(dT(), 'edit message', message) |
|
saveMessages([message], {isEdited: true}) |
|
safeReplaceObject(messagesStorage[mid], message) |
|
|
|
var wasForHistory = messagesForHistory[mid] |
|
if (wasForHistory !== undefined) { |
|
delete messagesForHistory[mid] |
|
var newForHistory = wrapForHistory(mid) |
|
safeReplaceObject(wasForHistory, newForHistory) |
|
messagesForHistory[mid] = wasForHistory |
|
} |
|
|
|
var foundDialog = getDialogByPeerID(peerID)[0] |
|
var isTopMessage = foundDialog && foundDialog.top_message == mid |
|
if (message.clear_history) { |
|
if (isTopMessage) { |
|
$rootScope.$broadcast('dialog_flush', {peerID: peerID}) |
|
} else { |
|
var msgs = {} |
|
msgs[mid] = true |
|
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: msgs}) |
|
} |
|
} else { |
|
$rootScope.$broadcast('message_edit', { |
|
peerID: peerID, |
|
id: message.id, |
|
mid: mid |
|
}) |
|
if (isTopMessage) { |
|
var updatedDialogs = {} |
|
updatedDialogs[peerID] = foundDialog |
|
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs) |
|
} |
|
} |
|
break |
|
|
|
case 'updateReadHistoryInbox': |
|
case 'updateReadHistoryOutbox': |
|
case 'updateReadChannelInbox': |
|
case 'updateReadChannelOutbox': |
|
var isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox' |
|
var channelID = update.channel_id |
|
var maxID = AppMessagesIDsManager.getFullMessageID(update.max_id, channelID) |
|
var peerID = channelID ? -channelID : AppPeersManager.getPeerID(update.peer) |
|
var foundDialog = getDialogByPeerID(peerID) |
|
var history = (historiesStorage[peerID] || {}).history || [] |
|
var newUnreadCount = false |
|
var length = history.length |
|
var foundAffected = false |
|
var messageID, message |
|
var i |
|
|
|
// console.warn(dT(), 'read', peerID, isOut ? 'out' : 'in', maxID) |
|
|
|
if (peerID > 0 && isOut) { |
|
AppUsersManager.forceUserOnline(peerID) |
|
} |
|
|
|
for (i = 0; i < length; i++) { |
|
messageID = history[i] |
|
if (messageID > maxID) { |
|
continue |
|
} |
|
message = messagesStorage[messageID] |
|
|
|
if (message.pFlags.out != isOut) { |
|
continue |
|
} |
|
if (!message.pFlags.unread) { |
|
break |
|
} |
|
// console.warn('read', messageID, message.pFlags.unread, message) |
|
if (message && message.pFlags.unread) { |
|
message.pFlags.unread = false |
|
if (messagesForHistory[messageID]) { |
|
messagesForHistory[messageID].pFlags.unread = false |
|
if (!foundAffected) { |
|
foundAffected = true |
|
} |
|
} |
|
if (messagesForDialogs[messageID]) { |
|
messagesForDialogs[messageID].pFlags.unread = false |
|
} |
|
if (!message.pFlags.out) { |
|
if (foundDialog[0]) { |
|
newUnreadCount = --foundDialog[0].unread_count |
|
} |
|
NotificationsManager.cancel('msg' + messageID) |
|
} |
|
} |
|
} |
|
if (foundDialog[0]) { |
|
if (!isOut && |
|
newUnreadCount && |
|
foundDialog[0].top_message <= maxID) { |
|
newUnreadCount = foundDialog[0].unread_count = 0 |
|
} |
|
var dialogKey = isOut ? 'read_outbox_max_id' : 'read_inbox_max_id' |
|
foundDialog[0][dialogKey] = maxID |
|
} |
|
|
|
if (newUnreadCount !== false) { |
|
// console.warn(dT(), 'cnt', peerID, newUnreadCount) |
|
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: newUnreadCount}) |
|
} |
|
if (foundAffected) { |
|
$rootScope.$broadcast('messages_read') |
|
} |
|
break |
|
|
|
case 'updateChannelReadMessagesContents': |
|
var channelID = update.channel_id |
|
var newMessages = [] |
|
angular.forEach(update.messages, function (msgID) { |
|
newMessages.push(AppMessagesIDsManager.getFullMessageID(msgID, channelID)) |
|
}) |
|
update.messages = newMessages |
|
|
|
case 'updateReadMessagesContents': |
|
var messages = update.messages |
|
var len = messages.length |
|
var i |
|
var messageID, message |
|
var historyMessage |
|
for (i = 0; i < len; i++) { |
|
messageID = messages[i] |
|
if (message = messagesStorage[messageID]) { |
|
delete message.pFlags.media_unread |
|
} |
|
if (historyMessage = messagesForHistory[messageID]) { |
|
delete historyMessage.pFlags.media_unread |
|
} |
|
} |
|
break |
|
|
|
case 'updateChannelAvailableMessages': |
|
var channelID = update.channel_id |
|
var messages = [] |
|
var peerID = -channelID |
|
var history = (historiesStorage[peerID] || {}).history || [] |
|
if (history.length) { |
|
angular.forEach(history, function (msgID) { |
|
if (!update.available_min_id || |
|
AppMessagesIDsManager.getMessageLocalID(msgID) <= update.available_min_id) { |
|
messages.push(msgID) |
|
} |
|
}) |
|
} |
|
update.messages = messages |
|
|
|
case 'updateDeleteMessages': |
|
case 'updateDeleteChannelMessages': |
|
var dialogsUpdated = {} |
|
var historiesUpdated = {} |
|
var channelID = update.channel_id |
|
var messageID |
|
var message, i |
|
var peerID, foundDialog |
|
var history |
|
var peerMessagesToHandle |
|
var peerMessagesHandlePos |
|
|
|
for (i = 0; i < update.messages.length; i++) { |
|
messageID = AppMessagesIDsManager.getFullMessageID(update.messages[i], channelID) |
|
message = messagesStorage[messageID] |
|
if (message) { |
|
peerID = getMessagePeer(message) |
|
history = historiesUpdated[peerID] || (historiesUpdated[peerID] = {count: 0, unread: 0, msgs: {}}) |
|
|
|
if (!message.pFlags.out && message.pFlags.unread) { |
|
history.unread++ |
|
NotificationsManager.cancel('msg' + messageID) |
|
} |
|
history.count++ |
|
history.msgs[messageID] = true |
|
|
|
if (messagesForHistory[messageID]) { |
|
messagesForHistory[messageID].deleted = true |
|
delete messagesForHistory[messageID] |
|
} |
|
if (messagesForDialogs[messageID]) { |
|
messagesForDialogs[messageID].deleted = true |
|
delete messagesForDialogs[messageID] |
|
} |
|
message.deleted = true |
|
messagesStorage[messageID] = { |
|
deleted: true, |
|
id: messageID, |
|
from_id: message.from_id, |
|
to_id: message.to_id, |
|
flags: message.flags, |
|
pFlags: message.pFlags, |
|
date: message.date |
|
} |
|
|
|
peerMessagesToHandle = newMessagesToHandle[peerID] |
|
if (peerMessagesToHandle && peerMessagesToHandle.length) { |
|
peerMessagesHandlePos = peerMessagesToHandle.indexOf(messageID) |
|
if (peerMessagesHandlePos != -1) { |
|
peerMessagesToHandle.splice(peerMessagesHandlePos) |
|
} |
|
} |
|
} |
|
} |
|
|
|
angular.forEach(historiesUpdated, function (updatedData, peerID) { |
|
var historyStorage = historiesStorage[peerID] |
|
if (historyStorage !== undefined) { |
|
var newHistory = [] |
|
var newPending = [] |
|
for (var i = 0; i < historyStorage.history.length; i++) { |
|
if (!updatedData.msgs[historyStorage.history[i]]) { |
|
newHistory.push(historyStorage.history[i]) |
|
} |
|
} |
|
historyStorage.history = newHistory |
|
if (updatedData.count && |
|
historyStorage.count !== null && |
|
historyStorage.count > 0) { |
|
historyStorage.count -= updatedData.count |
|
if (historyStorage.count < 0) { |
|
historyStorage.count = 0 |
|
} |
|
} |
|
|
|
for (var i = 0; i < historyStorage.pending.length; i++) { |
|
if (!updatedData.msgs[historyStorage.pending[i]]) { |
|
newPending.push(historyStorage.pending[i]) |
|
} |
|
} |
|
historyStorage.pending = newPending |
|
|
|
$rootScope.$broadcast('history_delete', {peerID: peerID, msgs: updatedData.msgs}) |
|
} |
|
|
|
var foundDialog = getDialogByPeerID(peerID)[0] |
|
if (foundDialog) { |
|
if (updatedData.unread) { |
|
foundDialog.unread_count -= updatedData.unread |
|
|
|
$rootScope.$broadcast('dialog_unread', { |
|
peerID: peerID, |
|
count: foundDialog.unread_count |
|
}) |
|
} |
|
|
|
if (updatedData.msgs[foundDialog.top_message]) { |
|
reloadConversation(peerID) |
|
} |
|
} |
|
}) |
|
break |
|
|
|
case 'updateChannel': |
|
var channelID = update.channel_id |
|
var peerID = -channelID |
|
var channel = AppChatsManager.getChat(channelID) |
|
|
|
var needDialog = channel._ == 'channel' && (!channel.pFlags.left && !channel.pFlags.kicked) |
|
var foundDialog = getDialogByPeerID(peerID) |
|
var hasDialog = foundDialog.length > 0 |
|
|
|
var canViewHistory = channel._ == 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked) && true || false |
|
var hasHistory = historiesStorage[peerID] !== undefined |
|
|
|
if (canViewHistory != hasHistory) { |
|
delete historiesStorage[peerID] |
|
$rootScope.$broadcast('history_forbidden', peerID) |
|
} |
|
if (hasDialog != needDialog) { |
|
if (needDialog) { |
|
reloadConversation(-channelID) |
|
} else { |
|
if (foundDialog[0]) { |
|
dialogsStorage.dialogs.splice(foundDialog[1], 1) |
|
$rootScope.$broadcast('dialog_drop', {peerID: peerID}) |
|
} |
|
} |
|
} |
|
break |
|
|
|
case 'updateChannelReload': |
|
var channelID = update.channel_id |
|
var peerID = -channelID |
|
var foundDialog = getDialogByPeerID(peerID) |
|
if (foundDialog[0]) { |
|
dialogsStorage.dialogs.splice(foundDialog[1], 1) |
|
} |
|
delete historiesStorage[peerID] |
|
reloadConversation(-channelID).then(function () { |
|
$rootScope.$broadcast('history_reload', peerID) |
|
}) |
|
break |
|
|
|
case 'updateChannelMessageViews': |
|
var views = update.views |
|
var mid = AppMessagesIDsManager.getFullMessageID(update.id, update.channel_id) |
|
var message = getMessage(mid) |
|
if (message && message.views && message.views < views) { |
|
message.views = views |
|
$rootScope.$broadcast('message_views', { |
|
mid: mid, |
|
views: views |
|
}) |
|
} |
|
break |
|
|
|
case 'updateServiceNotification': |
|
// update.inbox_date = tsNow(true) |
|
// update.pFlags = {popup: true} |
|
var fromID = 777000 |
|
var peerID = fromID |
|
var messageID = tempID-- |
|
var message = { |
|
_: 'message', |
|
id: messageID, |
|
from_id: fromID, |
|
to_id: AppPeersManager.getOutputPeer(peerID), |
|
flags: 0, |
|
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' |
|
}]) |
|
} |
|
saveMessages([message]) |
|
|
|
if (update.inbox_date) { |
|
pendingTopMsgs[peerID] = messageID |
|
handleUpdate({ |
|
_: 'updateNewMessage', |
|
message: message |
|
}) |
|
} |
|
if (update.pFlags.popup && update.message) { |
|
var historyMessage = wrapForHistory(messageID) |
|
ErrorService.show({error: {code: 400, type: 'UPDATE_SERVICE_NOTIFICATION'}, historyMessage: historyMessage}) |
|
} |
|
break |
|
} |
|
} |
|
|
|
$rootScope.$on('apiUpdate', function (e, update) { |
|
// if (update._ != 'updateUserStatus') { |
|
// console.log('on apiUpdate', update) |
|
// } |
|
handleUpdate(update) |
|
}) |
|
|
|
function reloadConversation (peerID) { |
|
return MtpApiManager.invokeApi('messages.getPeerDialogs', { |
|
peers: [ |
|
AppPeersManager.getInputPeerByID(peerID) |
|
] |
|
}).then(applyConversations) |
|
} |
|
|
|
function applyConversations(dialogsResult) { |
|
AppUsersManager.saveApiUsers(dialogsResult.users) |
|
AppChatsManager.saveApiChats(dialogsResult.chats) |
|
saveMessages(dialogsResult.messages) |
|
|
|
var updatedDialogs = {} |
|
var hasUpdated = false |
|
angular.forEach(dialogsResult.dialogs, function (dialog) { |
|
var peerID = AppPeersManager.getPeerID(dialog.peer) |
|
var topMessage = dialog.top_message |
|
var topPendingMesage = pendingTopMsgs[peerID] |
|
if (topPendingMesage) { |
|
if (!topMessage || getMessage(topPendingMesage).date > getMessage(topMessage).date) { |
|
dialog.top_message = topMessage = topPendingMesage |
|
} |
|
} |
|
if (topMessage) { |
|
var wasBefore = getDialogByPeerID(peerID).length > 0 |
|
saveConversation(dialog) |
|
if (wasBefore) { |
|
clearDialogCache(topMessage) |
|
$rootScope.$broadcast('dialog_top', dialog) |
|
} else { |
|
updatedDialogs[peerID] = dialog |
|
hasUpdated = true |
|
} |
|
} else { |
|
var foundDialog = getDialogByPeerID(peerID) |
|
if (foundDialog.length) { |
|
dialogsStorage.dialogs.splice(foundDialog[1], 1) |
|
$rootScope.$broadcast('dialog_drop', {peerID: peerID}) |
|
} |
|
} |
|
if (newUpdatesAfterReloadToHandle[peerID] !== undefined) { |
|
angular.forEach(newUpdatesAfterReloadToHandle[peerID], function (update) { |
|
handleUpdate(update) |
|
}) |
|
delete newUpdatesAfterReloadToHandle[peerID] |
|
} |
|
}) |
|
if (hasUpdated) { |
|
$rootScope.$broadcast('dialogs_multiupdate', updatedDialogs) |
|
} |
|
} |
|
|
|
$rootScope.$on('webpage_updated', function (e, eventData) { |
|
angular.forEach(eventData.msgs, function (msgID) { |
|
var historyMessage = messagesForHistory[msgID] |
|
if (historyMessage) { |
|
historyMessage.media = { |
|
_: 'messageMediaWebPage', |
|
webpage: AppWebPagesManager.wrapForHistory(eventData.id) |
|
} |
|
$rootScope.$broadcast('message_edit', { |
|
peerID: getMessagePeer(historyMessage), |
|
id: historyMessage.id, |
|
mid: msgID, |
|
justMedia: true |
|
}) |
|
} |
|
}) |
|
}) |
|
|
|
$rootScope.$on('draft_updated', function (e, eventData) { |
|
var peerID = eventData.peerID |
|
var draft = eventData.draft |
|
|
|
var dialog = getDialogByPeerID(peerID)[0] |
|
if (dialog) { |
|
var topDate |
|
if (draft && draft.date) { |
|
topDate = draft.date |
|
} else { |
|
var channelID = AppPeersManager.isChannel(peerID) ? -peerID : 0 |
|
var topDate = 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 = generateDialogIndex(topDate) |
|
} |
|
pushDialogToStorage(dialog) |
|
|
|
$rootScope.$broadcast('dialog_draft', { |
|
peerID: peerID, |
|
draft: draft, |
|
index: dialog.index |
|
}) |
|
} |
|
}) |
|
|
|
return { |
|
getConversations: getConversations, |
|
getConversation: getConversation, |
|
getHistory: getHistory, |
|
getSearch: getSearch, |
|
getMessage: getMessage, |
|
getReplyKeyboard: getReplyKeyboard, |
|
readHistory: readHistory, |
|
readMessages: readMessages, |
|
flushHistory: flushHistory, |
|
deleteMessages: deleteMessages, |
|
sendText: sendText, |
|
sendFile: sendFile, |
|
sendOther: sendOther, |
|
forwardMessages: forwardMessages, |
|
reportMessages: reportMessages, |
|
startBot: startBot, |
|
shareGame: shareGame, |
|
editMessage: editMessage, |
|
convertMigratedPeer: convertMigratedPeer, |
|
getMessagePeer: getMessagePeer, |
|
getMessageThumb: getMessageThumb, |
|
getMessageShareLink: getMessageShareLink, |
|
canMessageBeEdited: canMessageBeEdited, |
|
canEditMessage: canEditMessage, |
|
canReportMessage: canReportMessage, |
|
getMessageEditData: getMessageEditData, |
|
canRevokeMessage: canRevokeMessage, |
|
clearDialogCache: clearDialogCache, |
|
wrapForDialog: wrapForDialog, |
|
wrapForHistory: wrapForHistory, |
|
wrapReplyMarkup: wrapReplyMarkup, |
|
wrapSingleMessage: wrapSingleMessage, |
|
wrapMessageText: wrapMessageText, |
|
regroupWrappedHistory: regroupWrappedHistory |
|
} |
|
}) |
|
|
|
.service('AppMessagesIDsManager', function () { |
|
var channelLocals = {} |
|
var channelsByLocals = {} |
|
var channelCurLocal = 0 |
|
var fullMsgIDModulus = 4294967296 |
|
|
|
return { |
|
getFullMessageID: getFullMessageID, |
|
getMessageIDInfo: getMessageIDInfo, |
|
getMessageLocalID: getMessageLocalID, |
|
splitMessageIDsByChannels: splitMessageIDsByChannels, |
|
fullMsgIDModulus: fullMsgIDModulus |
|
} |
|
|
|
function getFullMessageID (msgID, channelID) { |
|
if (!channelID || msgID <= 0) { |
|
return msgID |
|
} |
|
msgID = getMessageLocalID(msgID) |
|
var localStart = channelLocals[channelID] |
|
if (!localStart) { |
|
localStart = (++channelCurLocal) * fullMsgIDModulus |
|
channelsByLocals[localStart] = channelID |
|
channelLocals[channelID] = localStart |
|
} |
|
|
|
return localStart + msgID |
|
} |
|
|
|
function getMessageIDInfo (fullMsgID) { |
|
if (fullMsgID < fullMsgIDModulus) { |
|
return [fullMsgID, 0] |
|
} |
|
var msgID = fullMsgID % fullMsgIDModulus |
|
var channelID = channelsByLocals[fullMsgID - msgID] |
|
|
|
return [msgID, channelID] |
|
} |
|
|
|
function getMessageLocalID (fullMsgID) { |
|
if (!fullMsgID) { |
|
return 0 |
|
} |
|
return fullMsgID % fullMsgIDModulus |
|
} |
|
|
|
function splitMessageIDsByChannels (mids) { |
|
var msgIDsByChannels = {} |
|
var midsByChannels = {} |
|
var i |
|
var mid, msgChannel |
|
var channelID |
|
for (i = 0; i < mids.length; i++) { |
|
mid = mids[i] |
|
msgChannel = getMessageIDInfo(mid) |
|
channelID = msgChannel[1] |
|
if (msgIDsByChannels[channelID] === undefined) { |
|
msgIDsByChannels[channelID] = [] |
|
midsByChannels[channelID] = [] |
|
} |
|
msgIDsByChannels[channelID].push(msgChannel[0]) |
|
midsByChannels[channelID].push(mid) |
|
} |
|
|
|
return { |
|
msgIDs: msgIDsByChannels, |
|
mids: midsByChannels |
|
} |
|
} |
|
})
|
|
|