Refactor target message for delivery receipt processing

This commit is contained in:
trevor-signal 2023-12-08 15:35:31 -05:00 committed by GitHub
parent e724f36b79
commit c2b1d76e6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 40 deletions

View file

@ -15,6 +15,7 @@ import * as Errors from '../types/errors';
import { import {
SendActionType, SendActionType,
SendStatus, SendStatus,
UNDELIVERED_SEND_STATUSES,
sendStateReducer, sendStateReducer,
} from '../messages/MessageSendState'; } from '../messages/MessageSendState';
import type { DeleteSentProtoRecipientOptionsType } from '../sql/Interface'; import type { DeleteSentProtoRecipientOptionsType } from '../sql/Interface';
@ -25,6 +26,7 @@ import { queueUpdateMessage } from '../util/messageBatcher';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import { getMessageIdForLogging } from '../util/idForLogging'; import { getMessageIdForLogging } from '../util/idForLogging';
import { generateCacheKey } from './generateCacheKey'; import { generateCacheKey } from './generateCacheKey';
import { getPropForTimestamp } from '../util/editHelpers';
const { deleteSentProtoRecipient } = dataInterface; const { deleteSentProtoRecipient } = dataInterface;
@ -38,7 +40,7 @@ export type MessageReceiptAttributesType = {
envelopeId: string; envelopeId: string;
messageSentAt: number; messageSentAt: number;
receiptTimestamp: number; receiptTimestamp: number;
removeFromMessageReceiverCache: () => unknown; removeFromMessageReceiverCache: () => void;
sourceConversationId: string; sourceConversationId: string;
sourceDevice: number; sourceDevice: number;
sourceServiceId: ServiceIdString; sourceServiceId: ServiceIdString;
@ -89,45 +91,74 @@ function remove(receipt: MessageReceiptAttributesType): void {
receipt.removeFromMessageReceiverCache(); receipt.removeFromMessageReceiverCache();
} }
async function getTargetMessage( function getTargetMessage({
sourceId: string, sourceConversationId,
serviceId: ServiceIdString, messages,
messages: ReadonlyArray<MessageAttributesType> targetTimestamp,
): Promise<MessageModel | null> { }: {
sourceConversationId: string;
messages: ReadonlyArray<MessageAttributesType>;
targetTimestamp: number;
}): MessageModel | null {
if (messages.length === 0) { if (messages.length === 0) {
return null; return null;
} }
const message = messages.find(
item =>
(isOutgoing(item) || isStory(item)) && sourceId === item.conversationId
);
if (message) {
return window.MessageCache.__DEPRECATED$register(
message.id,
message,
'MessageReceipts.getTargetMessage 1'
);
}
const groups = await window.Signal.Data.getAllGroupsInvolvingServiceId( const matchingMessages = messages
serviceId .filter(msg => isOutgoing(msg) || isStory(msg))
); .filter(msg => {
const sendStateByConversationId = getPropForTimestamp({
message: msg,
prop: 'sendStateByConversationId',
targetTimestamp,
log,
});
const ids = groups.map(item => item.id); const isRecipient = Object.hasOwn(
ids.push(sourceId); sendStateByConversationId ?? {},
sourceConversationId
);
if (!isRecipient) {
return false;
}
const target = messages.find( const sendStatus =
item => sendStateByConversationId?.[sourceConversationId]?.status;
(isOutgoing(item) || isStory(item)) && ids.includes(item.conversationId)
); if (
if (!target) { sendStatus === undefined ||
UNDELIVERED_SEND_STATUSES.includes(sendStatus)
) {
log.warn(`
MessageReceipts.getTargetMessage: received receipt for undelivered message,
status: ${sendStatus},
sourceConversationId: ${sourceConversationId},
message: ${getMessageIdForLogging(message)}.
`);
return false;
}
return true;
});
if (matchingMessages.length === 0) {
return null; return null;
} }
if (matchingMessages.length > 1) {
log.warn(`
MessageReceipts.getTargetMessage: multiple (${matchingMessages.length})
matching messages for receipt,
sentAt=${targetTimestamp},
sourceConversationId=${sourceConversationId}
`);
}
const message = matchingMessages[0];
return window.MessageCache.__DEPRECATED$register( return window.MessageCache.__DEPRECATED$register(
target.id, message.id,
target, message,
'MessageReceipts.getTargetMessage 2' 'MessageReceipts.getTargetMessage'
); );
} }
@ -367,11 +398,11 @@ export async function onReceipt(
messageSentAt messageSentAt
); );
const message = await getTargetMessage( const message = getTargetMessage({
sourceConversationId, sourceConversationId,
sourceServiceId, messages,
messages targetTimestamp: receipt.messageSentAt,
); });
if (message) { if (message) {
await updateMessageSendState(receipt, message); await updateMessageSendState(receipt, message);

View file

@ -39,6 +39,11 @@ export const parseMessageSendStatus = makeEnumParser(
SendStatus.Pending SendStatus.Pending
); );
export const UNDELIVERED_SEND_STATUSES = [
SendStatus.Pending,
SendStatus.Failed,
];
const STATUS_NUMBERS: Record<SendStatus, number> = { const STATUS_NUMBERS: Record<SendStatus, number> = {
[SendStatus.Failed]: 0, [SendStatus.Failed]: 0,
[SendStatus.Pending]: 1, [SendStatus.Pending]: 1,

View file

@ -5,9 +5,10 @@ import { isNumber, sortBy } from 'lodash';
import { strictAssert } from './assert'; import { strictAssert } from './assert';
import type { EditHistoryType } from '../model-types'; import type { EditHistoryType, MessageAttributesType } from '../model-types';
import type { MessageModel } from '../models/messages';
import type { LoggerType } from '../types/Logging'; import type { LoggerType } from '../types/Logging';
import { getMessageIdForLogging } from './idForLogging';
import type { MessageModel } from '../models/messages';
// The tricky bit for this function is if we are on our second+ attempt to send a given // The tricky bit for this function is if we are on our second+ attempt to send a given
// edit, we're still sending that edit. // edit, we're still sending that edit.
@ -51,13 +52,18 @@ export function getPropForTimestamp<T extends keyof EditHistoryType>({
targetTimestamp, targetTimestamp,
}: { }: {
log: LoggerType; log: LoggerType;
message: MessageModel; message: MessageModel | MessageAttributesType;
prop: T; prop: T;
targetTimestamp: number; targetTimestamp: number;
}): EditHistoryType[T] { }): EditHistoryType[T] {
const logId = `getPropForTimestamp(${message.idForLogging()}, target=${targetTimestamp}})`; const attributes =
message instanceof window.Whisper.Message ? message.attributes : message;
const editHistory = message.get('editHistory'); const logId = `getPropForTimestamp(${getMessageIdForLogging(
attributes
)}, target=${targetTimestamp}})`;
const { editHistory } = attributes;
const targetEdit = editHistory?.find( const targetEdit = editHistory?.find(
item => item.timestamp === targetTimestamp item => item.timestamp === targetTimestamp
); );
@ -65,7 +71,7 @@ export function getPropForTimestamp<T extends keyof EditHistoryType>({
if (editHistory) { if (editHistory) {
log.warn(`${logId}: No edit found, using top-level data`); log.warn(`${logId}: No edit found, using top-level data`);
} }
return message.get(prop); return attributes[prop];
} }
return targetEdit[prop]; return targetEdit[prop];