[Refactoring] Rework sent message handling (#2763)

This commit is contained in:
Alexander Zinchuk 2023-03-19 22:30:35 -05:00
parent 7bccdedf2a
commit bfbd9bd2ed
6 changed files with 97 additions and 87 deletions

1
package-lock.json generated
View File

@ -105,7 +105,6 @@
}
},
"dev/eslint-multitab": {
"name": "eslint-plugin-eslint-multitab-tt",
"version": "0.0.0",
"dev": true,
"license": "ISC",

View File

@ -1,6 +1,5 @@
import BigInt from 'big-integer';
import type { Api as GramJs } from '../../lib/gramjs';
import type { ApiMessage } from '../types';
import { omitVirtualClassFields } from './apiBuilders/helpers';
import { DATA_BROADCAST_CHANNEL_NAME } from '../../config';
import { constructors } from '../../lib/gramjs/tl';
@ -10,7 +9,6 @@ import { throttle } from '../../util/schedulers';
const IS_MULTITAB_SUPPORTED = 'BroadcastChannel' in self;
export interface LocalDb {
localMessages: Record<string, ApiMessage>;
// Used for loading avatars and media through in-memory Gram JS instances.
chats: Record<string, GramJs.Chat | GramJs.Channel>;
users: Record<string, GramJs.User>;

View File

@ -232,7 +232,7 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
}
if (!shouldIgnoreUpdates) {
handleUpdatesFromRequest(request, result);
handleUpdates(result);
}
return shouldReturnTrue ? result && true : result;
@ -256,7 +256,7 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
}
}
function handleUpdatesFromRequest<T extends GramJs.AnyRequest>(request: T, result: T['__response']) {
export function handleUpdates(result: {}) {
let manyUpdates;
let singleUpdate;
@ -279,10 +279,10 @@ function handleUpdatesFromRequest<T extends GramJs.AnyRequest>(request: T, resul
injectUpdateEntities(manyUpdates);
manyUpdates.updates.forEach((update) => {
updater(update, request);
updater(update);
});
} else if (singleUpdate) {
updater(singleUpdate, request);
updater(singleUpdate);
}
}

View File

@ -30,7 +30,7 @@ import {
SUPPORTED_IMAGE_CONTENT_TYPES,
SUPPORTED_VIDEO_CONTENT_TYPES,
} from '../../../config';
import { invokeRequest, uploadFile } from './client';
import { handleUpdates, invokeRequest, uploadFile } from './client';
import {
buildApiMessage,
buildLocalForwardedMessage,
@ -38,6 +38,8 @@ import {
buildWebPage,
buildApiSponsoredMessage,
buildApiFormattedText,
buildMessageTextContent,
buildMessageMediaContent,
} from '../apiBuilders/messages';
import { buildApiUser } from '../apiBuilders/users';
import {
@ -54,8 +56,8 @@ import {
buildSendMessageAction,
buildInputPollFromExisting,
buildInputTextWithEntities,
buildMessageFromUpdate,
} from '../gramjsBuilders';
import localDb from '../localDb';
import { buildApiChatFromPreview, buildApiSendAsPeerId } from '../apiBuilders/chats';
import { fetchFile } from '../../../util/files';
import {
@ -287,7 +289,6 @@ export function sendMessage(
}, FAST_SEND_TIMEOUT);
const randomId = generateRandomBigInt();
localDb.localMessages[String(randomId)] = localMessage;
if (groupedId) {
return sendGroupedMedia({
@ -339,7 +340,7 @@ export function sendMessage(
const RequestClass = media ? GramJs.messages.SendMedia : GramJs.messages.SendMessage;
try {
await invokeRequest(new RequestClass({
const update = await invokeRequest(new RequestClass({
clearDraft: true,
message: text || '',
entities: entities ? entities.map(buildMtpMessageEntity) : undefined,
@ -353,7 +354,8 @@ export function sendMessage(
...(noWebPage && { noWebpage: noWebPage }),
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
...(shouldUpdateStickerSetsOrder && { updateStickersetsOrder: shouldUpdateStickerSetsOrder }),
}), true, true);
}), false, true, true);
if (update) handleLocalMessageUpdate(localMessage, update);
} catch (error: any) {
onUpdate({
'@type': 'updateMessageSendFailed',
@ -371,6 +373,7 @@ export function sendMessage(
const groupedUploads: Record<string, {
counter: number;
singleMediaByIndex: Record<number, GramJs.InputSingleMedia>;
localMessages: Record<string, ApiMessage>;
}> = {};
function sendGroupedMedia(
@ -406,6 +409,7 @@ function sendGroupedMedia(
groupedUploads[groupedId] = {
counter: 0,
singleMediaByIndex: {},
localMessages: {},
};
}
@ -453,15 +457,16 @@ function sendGroupedMedia(
message: text || '',
entities: entities ? entities.map(buildMtpMessageEntity) : undefined,
});
groupedUploads[groupedId].localMessages[randomId.toString()] = localMessage;
if (Object.keys(groupedUploads[groupedId].singleMediaByIndex).length < groupedUploads[groupedId].counter) {
return;
}
const { singleMediaByIndex } = groupedUploads[groupedId];
const { singleMediaByIndex, localMessages } = groupedUploads[groupedId];
delete groupedUploads[groupedId];
await invokeRequest(new GramJs.messages.SendMultiMedia({
const update = await invokeRequest(new GramJs.messages.SendMultiMedia({
clearDraft: true,
peer: buildInputPeer(chat.id, chat.accessHash),
multiMedia: Object.values(singleMediaByIndex), // Object keys are usually ordered
@ -470,7 +475,9 @@ function sendGroupedMedia(
...(isSilent && { silent: isSilent }),
...(scheduledAt && { scheduleDate: scheduledAt }),
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
}), true);
}), false, undefined, true);
if (update) handleMultipleLocalMessagesUpdate(localMessages, update);
})();
return queue;
@ -554,9 +561,6 @@ export async function editMessage({
message: messageUpdate,
});
// TODO Revise intersecting with scheduled
localDb.localMessages[message.id] = { ...message, ...messageUpdate };
const mtpEntities = entities && entities.map(buildMtpMessageEntity);
await invokeRequest(new GramJs.messages.EditMessage({
@ -1267,6 +1271,7 @@ export async function forwardMessages({
}) {
const messageIds = messages.map(({ id }) => id);
const randomIds = messages.map(generateRandomBigInt);
const localMessages: Record<string, ApiMessage> = {};
messages.forEach((message, index) => {
const localMessage = buildLocalForwardedMessage({
@ -1278,7 +1283,7 @@ export async function forwardMessages({
noCaptions,
isCurrentUserPremium,
});
localDb.localMessages[String(randomIds[index])] = localMessage;
localMessages[randomIds[index].toString()] = localMessage;
onUpdate({
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
@ -1288,7 +1293,7 @@ export async function forwardMessages({
});
});
await invokeRequest(new GramJs.messages.ForwardMessages({
const update = await invokeRequest(new GramJs.messages.ForwardMessages({
fromPeer: buildInputPeer(fromChat.id, fromChat.accessHash),
toPeer: buildInputPeer(toChat.id, toChat.accessHash),
randomId: randomIds,
@ -1300,7 +1305,8 @@ export async function forwardMessages({
...(toThreadId && { topMsgId: toThreadId }),
...(scheduledAt && { scheduleDate: scheduledAt }),
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
}), true);
}), false, undefined, true);
if (update) handleMultipleLocalMessagesUpdate(localMessages, update);
}
export async function findFirstMessageIdAfterDate({
@ -1632,3 +1638,74 @@ export async function translateText(params: TranslateTextParams) {
return formattedText;
}
function handleMultipleLocalMessagesUpdate(
localMessages: Record<string, ApiMessage>, update: GramJs.TypeUpdates,
) {
if (!('updates' in update)) return;
update.updates.forEach((u) => {
if (u instanceof GramJs.UpdateMessageID) {
const localMessage = localMessages[u.randomId.toString()];
handleLocalMessageUpdate(localMessage, u);
}
});
}
function handleLocalMessageUpdate(localMessage: ApiMessage, update: GramJs.TypeUpdates) {
let messageUpdate;
if (update instanceof GramJs.UpdateShortSentMessage || update instanceof GramJs.UpdateMessageID) {
messageUpdate = update;
} else if ('updates' in update) {
messageUpdate = update.updates.find((u): u is GramJs.UpdateMessageID => u instanceof GramJs.UpdateMessageID);
}
if (!messageUpdate) {
handleUpdates(update);
return;
}
let newContent: ApiMessage['content'] | undefined;
if (messageUpdate instanceof GramJs.UpdateShortSentMessage) {
if (localMessage.content.text && messageUpdate.entities) {
newContent = {
text: buildMessageTextContent(localMessage.content.text.text, messageUpdate.entities),
};
}
if (messageUpdate.media) {
newContent = {
...newContent,
...buildMessageMediaContent(messageUpdate.media),
};
}
const mtpMessage = buildMessageFromUpdate(messageUpdate.id, localMessage.chatId, messageUpdate);
if (isMessageWithMedia(mtpMessage)) {
addMessageToLocalDb(mtpMessage);
}
}
// Edge case for "Send When Online"
const isSentBefore = 'date' in messageUpdate && messageUpdate.date * 1000 < Date.now() + getServerTimeOffset() * 1000;
onUpdate({
'@type': localMessage.isScheduled && !isSentBefore
? 'updateScheduledMessageSendSucceeded'
: 'updateMessageSendSucceeded',
chatId: localMessage.chatId,
localId: localMessage.id,
message: {
...localMessage,
...(newContent && {
content: {
...localMessage.content,
...newContent,
},
}),
id: messageUpdate.id,
sendingState: undefined,
...('date' in messageUpdate && { date: messageUpdate.date }),
},
});
handleUpdates(update);
}

View File

@ -12,7 +12,6 @@ import {
buildApiMessageFromShort,
buildApiMessageFromShortChat,
buildMessageMediaContent,
buildMessageTextContent,
buildPoll,
buildPollResults,
buildApiMessageFromNotification,
@ -116,7 +115,7 @@ function dispatchUserAndChatUpdates(entities: (GramJs.TypeUser | GramJs.TypeChat
});
}
export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
export function updater(update: Update) {
if (update instanceof connection.UpdateServerTimeOffset) {
setServerTimeOffset(update.timeOffset);
@ -440,70 +439,8 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
message,
});
}
} else if ((
originRequest instanceof GramJs.messages.SendMessage
|| originRequest instanceof GramJs.messages.SendMedia
|| originRequest instanceof GramJs.messages.SendMultiMedia
|| originRequest instanceof GramJs.messages.ForwardMessages
) && (
update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage
)) {
let randomId;
if ('randomId' in update) {
randomId = update.randomId;
} else if ('randomId' in originRequest) {
randomId = originRequest.randomId;
}
const localMessage = randomId && localDb.localMessages[String(randomId)];
if (!localMessage) {
throw new Error('Local message not found');
}
let newContent: ApiMessage['content'] | undefined;
if (update instanceof GramJs.UpdateShortSentMessage) {
if (localMessage.content.text && update.entities) {
newContent = {
text: buildMessageTextContent(localMessage.content.text.text, update.entities),
};
}
if (update.media) {
newContent = {
...newContent,
...buildMessageMediaContent(update.media),
};
}
const mtpMessage = buildMessageFromUpdate(update.id, localMessage.chatId, update);
if (isMessageWithMedia(mtpMessage)) {
addMessageToLocalDb(mtpMessage);
}
}
} else if (update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage) {
sentMessageIds.add(update.id);
// Edge case for "Send When Online"
const isAlreadySent = 'date' in update && update.date * 1000 < Date.now() + getServerTimeOffset() * 1000;
onUpdate({
'@type': localMessage.isScheduled && !isAlreadySent
? 'updateScheduledMessageSendSucceeded'
: 'updateMessageSendSucceeded',
chatId: localMessage.chatId,
localId: localMessage.id,
message: {
...localMessage,
...(newContent && {
content: {
...localMessage.content,
...newContent,
},
}),
id: update.id,
sendingState: undefined,
...('date' in update && { date: update.date }),
},
});
} else if (update instanceof GramJs.UpdateReadMessagesContents) {
onUpdate({
'@type': 'updateCommonBoxMessages',

View File

@ -25,7 +25,6 @@ let worker: Worker;
const requestStates = new Map<string, RequestStates>();
const requestStatesByCallback = new Map<AnyToVoidFunction, RequestStates>();
const savedLocalDb: LocalDb = {
localMessages: {},
chats: {},
users: {},
messages: {},