From 5869717cd3c79c10c5ec4436263a5269d189d75d Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Tue, 16 May 2023 10:37:12 -0700 Subject: [PATCH] Use correct timestamp for receipts of edited messages --- ts/jobs/helpers/sendNormalMessage.ts | 20 +++------ ts/messageModifiers/Deletes.ts | 6 ++- ts/messageModifiers/Edits.ts | 4 +- ts/messageModifiers/MessageReceipts.ts | 3 +- ts/messageModifiers/Reactions.ts | 11 +++-- ts/messageModifiers/ReadSyncs.ts | 6 ++- ts/messageModifiers/ViewSyncs.ts | 10 +++-- ts/models/conversations.ts | 3 +- ts/models/messages.ts | 5 +-- ts/reactions/enqueueReactionForSend.ts | 6 ++- ts/sql/Interface.ts | 4 +- ts/sql/Server.ts | 14 +++---- .../82-edited-messages-read-index.ts | 29 +++++++++++++ ts/sql/migrations/index.ts | 2 + ts/state/ducks/conversations.ts | 5 ++- ts/util/getMessageSentTimestamp.ts | 42 +++++++++++++++++++ ts/util/getMessageSentTimestampSet.ts | 17 ++++++++ ts/util/handleEditMessage.ts | 4 +- ts/util/makeQuote.ts | 5 ++- ts/util/markConversationRead.ts | 2 +- ts/util/sendEditedMessage.ts | 10 +++-- 21 files changed, 156 insertions(+), 52 deletions(-) create mode 100644 ts/sql/migrations/82-edited-messages-read-index.ts create mode 100644 ts/util/getMessageSentTimestamp.ts create mode 100644 ts/util/getMessageSentTimestampSet.ts diff --git a/ts/jobs/helpers/sendNormalMessage.ts b/ts/jobs/helpers/sendNormalMessage.ts index 18fd1f9882db..f6db071aa96d 100644 --- a/ts/jobs/helpers/sendNormalMessage.ts +++ b/ts/jobs/helpers/sendNormalMessage.ts @@ -15,6 +15,7 @@ import { SignalService as Proto } from '../../protobuf'; import { handleMessageSend } from '../../util/handleMessageSend'; import { findAndFormatContact } from '../../util/findAndFormatContact'; import { uploadAttachment } from '../../util/uploadAttachment'; +import { getMessageSentTimestamp } from '../../util/getMessageSentTimestamp'; import type { CallbackResultType } from '../../textsecure/Types.d'; import { isSent } from '../../messages/MessageSendState'; import { isOutgoing, canReact } from '../../state/selectors/message'; @@ -499,22 +500,11 @@ async function getMessageSendData({ storyContext?: StoryContextType; }> { const editMessageTimestamp = message.get('editMessageTimestamp'); - const sentAt = message.get('sent_at'); - const timestamp = message.get('timestamp'); - - let mainMessageTimestamp: number; - if (sentAt) { - mainMessageTimestamp = sentAt; - } else if (timestamp) { - log.error('message lacked sent_at. Falling back to timestamp'); - mainMessageTimestamp = timestamp; - } else { - log.error( - 'message lacked sent_at and timestamp. Falling back to current time' - ); - mainMessageTimestamp = Date.now(); - } + const mainMessageTimestamp = getMessageSentTimestamp(message.attributes, { + includeEdits: false, + log, + }); const messageTimestamp = editMessageTimestamp || mainMessageTimestamp; const storyId = message.get('storyId'); diff --git a/ts/messageModifiers/Deletes.ts b/ts/messageModifiers/Deletes.ts index 0a58f16e70b9..e745ef861410 100644 --- a/ts/messageModifiers/Deletes.ts +++ b/ts/messageModifiers/Deletes.ts @@ -10,6 +10,7 @@ import * as log from '../logging/log'; import * as Errors from '../types/errors'; import { deleteForEveryone } from '../util/deleteForEveryone'; import { drop } from '../util/drop'; +import { getMessageSentTimestampSet } from '../util/getMessageSentTimestampSet'; export type DeleteAttributesType = { targetSentTimestamp: number; @@ -31,10 +32,11 @@ export class Deletes extends Collection { } forMessage(message: MessageModel): Array { + const sentTimestamps = getMessageSentTimestampSet(message.attributes); const matchingDeletes = this.filter(item => { return ( - item.get('targetSentTimestamp') === message.get('sent_at') && - item.get('fromId') === getContactId(message.attributes) + item.get('fromId') === getContactId(message.attributes) && + sentTimestamps.has(item.get('targetSentTimestamp')) ); }); diff --git a/ts/messageModifiers/Edits.ts b/ts/messageModifiers/Edits.ts index 56b744b4caa1..21e3a61cbac8 100644 --- a/ts/messageModifiers/Edits.ts +++ b/ts/messageModifiers/Edits.ts @@ -9,6 +9,7 @@ import { drop } from '../util/drop'; import { filter, size } from '../util/iterables'; import { getContactId } from '../messages/helpers'; import { handleEditMessage } from '../util/handleEditMessage'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; export type EditAttributesType = { conversationId: string; @@ -20,9 +21,10 @@ export type EditAttributesType = { const edits = new Set(); export function forMessage(message: MessageModel): Array { + const sentAt = getMessageSentTimestamp(message.attributes, { log }); const matchingEdits = filter(edits, item => { return ( - item.targetSentTimestamp === message.get('sent_at') && + item.targetSentTimestamp === sentAt && item.fromId === getContactId(message.attributes) ); }); diff --git a/ts/messageModifiers/MessageReceipts.ts b/ts/messageModifiers/MessageReceipts.ts index 5386069a49f8..a022ed21991c 100644 --- a/ts/messageModifiers/MessageReceipts.ts +++ b/ts/messageModifiers/MessageReceipts.ts @@ -24,6 +24,7 @@ import dataInterface from '../sql/Client'; import * as log from '../logging/log'; import { getSourceUuid } from '../messages/helpers'; import { queueUpdateMessage } from '../util/messageBatcher'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; const { deleteSentProtoRecipient } = dataInterface; @@ -159,7 +160,7 @@ export class MessageReceipts extends Collection { return []; } - const sentAt = message.get('sent_at'); + const sentAt = getMessageSentTimestamp(message.attributes, { log }); const receipts = this.filter( receipt => receipt.get('messageSentAt') === sentAt ); diff --git a/ts/messageModifiers/Reactions.ts b/ts/messageModifiers/Reactions.ts index e44ef2760c4c..6fdfe4aa4ac5 100644 --- a/ts/messageModifiers/Reactions.ts +++ b/ts/messageModifiers/Reactions.ts @@ -16,6 +16,7 @@ import { getContactId, getContact } from '../messages/helpers'; import { isDirectConversation, isMe } from '../util/whatTypeOfConversation'; import { isOutgoing, isStory } from '../state/selectors/message'; import { strictAssert } from '../util/assert'; +import { getMessageSentTimestampSet } from '../util/getMessageSentTimestampSet'; export class ReactionModel extends Model {} @@ -31,9 +32,10 @@ export class Reactions extends Collection { } forMessage(message: MessageModel): Array { + const sentTimestamps = getMessageSentTimestampSet(message.attributes); if (isOutgoing(message.attributes)) { - const outgoingReactions = this.filter( - item => item.get('targetTimestamp') === message.get('sent_at') + const outgoingReactions = this.filter(item => + sentTimestamps.has(item.get('targetTimestamp')) ); if (outgoingReactions.length > 0) { @@ -44,14 +46,15 @@ export class Reactions extends Collection { } const senderId = getContactId(message.attributes); - const sentAt = message.get('sent_at'); const reactionsBySource = this.filter(re => { const targetSender = window.ConversationController.lookupOrCreate({ uuid: re.get('targetAuthorUuid'), reason: 'Reactions.forMessage', }); const targetTimestamp = re.get('targetTimestamp'); - return targetSender?.id === senderId && targetTimestamp === sentAt; + return ( + targetSender?.id === senderId && sentTimestamps.has(targetTimestamp) + ); }); if (reactionsBySource.length > 0) { diff --git a/ts/messageModifiers/ReadSyncs.ts b/ts/messageModifiers/ReadSyncs.ts index 7038e0b0360d..ded90293c972 100644 --- a/ts/messageModifiers/ReadSyncs.ts +++ b/ts/messageModifiers/ReadSyncs.ts @@ -13,6 +13,7 @@ import * as log from '../logging/log'; import * as Errors from '../types/errors'; import { StartupQueue } from '../util/StartupQueue'; import { queueUpdateMessage } from '../util/messageBatcher'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; export type ReadSyncAttributesType = { senderId: string; @@ -66,10 +67,13 @@ export class ReadSyncs extends Collection { uuid: message.get('sourceUuid'), reason: 'ReadSyncs.forMessage', }); + const messageTimestamp = getMessageSentTimestamp(message.attributes, { + log, + }); const sync = this.find(item => { return ( item.get('senderId') === sender?.id && - item.get('timestamp') === message.get('sent_at') + item.get('timestamp') === messageTimestamp ); }); if (sync) { diff --git a/ts/messageModifiers/ViewSyncs.ts b/ts/messageModifiers/ViewSyncs.ts index 44f46b2eb059..29f455ae01be 100644 --- a/ts/messageModifiers/ViewSyncs.ts +++ b/ts/messageModifiers/ViewSyncs.ts @@ -16,6 +16,7 @@ import { queueAttachmentDownloads } from '../util/queueAttachmentDownloads'; import * as log from '../logging/log'; import { GiftBadgeStates } from '../components/conversation/Message'; import { queueUpdateMessage } from '../util/messageBatcher'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; export type ViewSyncAttributesType = { senderId: string; @@ -44,17 +45,18 @@ export class ViewSyncs extends Collection { uuid: message.get('sourceUuid'), reason: 'ViewSyncs.forMessage', }); + const messageTimestamp = getMessageSentTimestamp(message.attributes, { + log, + }); const syncs = this.filter(item => { return ( item.get('senderId') === sender?.id && - item.get('timestamp') === message.get('sent_at') + item.get('timestamp') === messageTimestamp ); }); if (syncs.length) { log.info( - `Found ${syncs.length} early view sync(s) for message ${message.get( - 'sent_at' - )}` + `Found ${syncs.length} early view sync(s) for message ${messageTimestamp}` ); this.remove(syncs); } diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index 9f50781dea63..edc46fc04d34 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -29,6 +29,7 @@ import { memoizeByThis } from '../util/memoizeByThis'; import { getInitials } from '../util/getInitials'; import { normalizeUuid } from '../util/normalizeUuid'; import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import type { AttachmentType, ThumbnailType } from '../types/Attachment'; import { toDayMillis } from '../util/timestamp'; import { isVoiceMessage } from '../types/Attachment'; @@ -2321,7 +2322,7 @@ export class ConversationModel extends window.Backbone conversationId, senderE164: m.source, senderUuid: m.sourceUuid, - timestamp: m.sent_at, + timestamp: getMessageSentTimestamp(m, { log }), isDirectConversation: isDirectConversation(this.attributes), })), }); diff --git a/ts/models/messages.ts b/ts/models/messages.ts index 77f4a17314b1..7d5a206cb460 100644 --- a/ts/models/messages.ts +++ b/ts/models/messages.ts @@ -48,6 +48,7 @@ import type { import { SendMessageProtoError } from '../textsecure/Errors'; import * as expirationTimer from '../util/expirationTimer'; import { getUserLanguages } from '../util/userLanguages'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import type { ReactionType } from '../types/Reactions'; import { UUID, UUIDKind } from '../types/UUID'; @@ -1802,9 +1803,7 @@ export class MessageModel extends window.Backbone.Model { ); const isEditedMessage = Boolean(this.get('editHistory')); - const mainMessageTimestamp = this.get('sent_at') || this.get('timestamp'); - const timestamp = - this.get('editMessageTimestamp') || mainMessageTimestamp; + const timestamp = getMessageSentTimestamp(this.attributes, { log }); const encodedContent = isEditedMessage ? { diff --git a/ts/reactions/enqueueReactionForSend.ts b/ts/reactions/enqueueReactionForSend.ts index 12bea35bcfb6..eea1afb6da6d 100644 --- a/ts/reactions/enqueueReactionForSend.ts +++ b/ts/reactions/enqueueReactionForSend.ts @@ -9,8 +9,10 @@ import { strictAssert } from '../util/assert'; import { isDirectConversation } from '../util/whatTypeOfConversation'; import { incrementMessageCounter } from '../util/incrementMessageCounter'; import { repeat, zipObject } from '../util/iterables'; +import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import { SendStatus } from '../messages/MessageSendState'; import { UUID } from '../types/UUID'; +import * as log from '../logging/log'; export async function enqueueReactionForSend({ emoji, @@ -30,7 +32,9 @@ export async function enqueueReactionForSend({ `enqueueReactionForSend: message ${message.idForLogging()} had no source UUID` ); - const targetTimestamp = message.get('sent_at') || message.get('timestamp'); + const targetTimestamp = getMessageSentTimestamp(message.attributes, { + log, + }); strictAssert( targetTimestamp, `enqueueReactionForSend: message ${message.idForLogging()} had no timestamp` diff --git a/ts/sql/Interface.ts b/ts/sql/Interface.ts index 2348b0056b06..05b3a4c725db 100644 --- a/ts/sql/Interface.ts +++ b/ts/sql/Interface.ts @@ -389,7 +389,7 @@ export type FTSOptimizationStateType = Readonly<{ }>; export type EditedMessageType = Readonly<{ - fromId: string; + conversationId: string; messageId: string; sentAt: number; readStatus: MessageType['readStatus']; @@ -523,7 +523,7 @@ export type DataInterface = { storyId?: string; }) => Promise; getUnreadEditedMessagesAndMarkRead: (options: { - fromId: string; + conversationId: string; newestUnreadAt: number; }) => Promise; getUnreadReactionsAndMarkRead: (options: { diff --git a/ts/sql/Server.ts b/ts/sql/Server.ts index 12af3ee24247..81fd7a783adf 100644 --- a/ts/sql/Server.ts +++ b/ts/sql/Server.ts @@ -5630,7 +5630,7 @@ async function removeAllProfileKeyCredentials(): Promise { async function saveEditedMessage( mainMessage: MessageType, ourUuid: UUIDStringType, - { fromId, messageId, readStatus, sentAt }: EditedMessageType + { conversationId, messageId, readStatus, sentAt }: EditedMessageType ): Promise { const db = getInstance(); @@ -5644,12 +5644,12 @@ async function saveEditedMessage( const [query, params] = sql` INSERT INTO edited_messages ( - fromId, + conversationId, messageId, sentAt, readStatus ) VALUES ( - ${fromId}, + ${conversationId}, ${messageId}, ${sentAt}, ${readStatus} @@ -5675,10 +5675,10 @@ async function _getAllEditedMessages(): Promise< } async function getUnreadEditedMessagesAndMarkRead({ - fromId, + conversationId, newestUnreadAt, }: { - fromId: string; + conversationId: string; newestUnreadAt: number; }): Promise { const db = getInstance(); @@ -5695,7 +5695,7 @@ async function getUnreadEditedMessagesAndMarkRead({ ON messages.id = edited_messages.messageId WHERE edited_messages.readStatus = ${ReadStatus.Unread} AND - edited_messages.fromId = ${fromId} AND + edited_messages.conversationId = ${conversationId} AND received_at <= ${newestUnreadAt} ORDER BY messages.received_at DESC, messages.sent_at DESC; `; @@ -5711,7 +5711,7 @@ async function getUnreadEditedMessagesAndMarkRead({ readStatus = ${ReadStatus.Read} WHERE readStatus = ${ReadStatus.Unread} AND - fromId = ${fromId} AND + conversationId = ${conversationId} AND sentAt <= ${newestSentAt}; `; diff --git a/ts/sql/migrations/82-edited-messages-read-index.ts b/ts/sql/migrations/82-edited-messages-read-index.ts new file mode 100644 index 000000000000..0747c19f4a78 --- /dev/null +++ b/ts/sql/migrations/82-edited-messages-read-index.ts @@ -0,0 +1,29 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { Database } from '@signalapp/better-sqlite3'; + +import type { LoggerType } from '../../types/Logging'; + +export default function updateToSchemaVersion82( + currentVersion: number, + db: Database, + logger: LoggerType +): void { + if (currentVersion >= 82) { + return; + } + + db.transaction(() => { + db.exec(` + ALTER TABLE edited_messages DROP COLUMN fromId; + ALTER TABLE edited_messages ADD COLUMN conversationId STRING; + + CREATE INDEX edited_messages_unread ON edited_messages (readStatus, conversationId); + `); + + db.pragma('user_version = 82'); + })(); + + logger.info('updateToSchemaVersion82: success!'); +} diff --git a/ts/sql/migrations/index.ts b/ts/sql/migrations/index.ts index 1c38dd9cc1e6..2f333a4ddef0 100644 --- a/ts/sql/migrations/index.ts +++ b/ts/sql/migrations/index.ts @@ -57,6 +57,7 @@ import updateToSchemaVersion78 from './78-merge-receipt-jobs'; import updateToSchemaVersion79 from './79-paging-lightbox'; import updateToSchemaVersion80 from './80-edited-messages'; import updateToSchemaVersion81 from './81-contact-removed-notification'; +import updateToSchemaVersion82 from './82-edited-messages-read-index'; function updateToSchemaVersion1( currentVersion: number, @@ -1984,6 +1985,7 @@ export const SCHEMA_VERSIONS = [ updateToSchemaVersion80, updateToSchemaVersion81, + updateToSchemaVersion82, ]; export function updateSchema(db: Database, logger: LoggerType): void { diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 02ad0893d2d1..89e799b22b56 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -138,6 +138,7 @@ import { DAY } from '../../util/durations'; import { isNotNil } from '../../util/isNotNil'; import { PanelType } from '../../types/Panels'; import { startConversation } from '../../util/startConversation'; +import { getMessageSentTimestamp } from '../../util/getMessageSentTimestamp'; import { UUIDKind } from '../../types/UUID'; import { removeLinkPreview } from '../../services/LinkPreview'; import type { @@ -1822,7 +1823,7 @@ export const markViewed = (messageId: string): void => { const senderE164 = message.get('source'); const senderUuid = message.get('sourceUuid'); - const timestamp = message.get('sent_at'); + const timestamp = getMessageSentTimestamp(message.attributes, { log }); message.set(messageUpdaterMarkViewed(message.attributes, Date.now())); @@ -2985,7 +2986,7 @@ function deleteMessagesForEveryone( await sendDeleteForEveryoneMessage(conversation.attributes, { id: message.id, - timestamp: message.get('sent_at'), + timestamp: getMessageSentTimestamp(message.attributes, { log }), }); } catch (error) { hasError = true; diff --git a/ts/util/getMessageSentTimestamp.ts b/ts/util/getMessageSentTimestamp.ts new file mode 100644 index 000000000000..a6aaf3827b83 --- /dev/null +++ b/ts/util/getMessageSentTimestamp.ts @@ -0,0 +1,42 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { MessageAttributesType } from '../model-types.d'; +import type { LoggerType } from '../types/Logging'; +import { assertDev } from './assert'; + +export type GetMessageSentTimestampOptionsType = Readonly<{ + includeEdits?: boolean; + log: LoggerType; +}>; + +export function getMessageSentTimestamp( + { + editMessageTimestamp, + sent_at: sentAt, + timestamp, + }: Pick< + MessageAttributesType, + 'editMessageTimestamp' | 'sent_at' | 'timestamp' + >, + { includeEdits = true, log }: GetMessageSentTimestampOptionsType +): number { + if (includeEdits && editMessageTimestamp) { + return editMessageTimestamp; + } + + if (sentAt) { + return sentAt; + } + + if (timestamp) { + log.error('message lacked sent_at. Falling back to timestamp'); + return timestamp; + } + + assertDev( + false, + 'message lacked sent_at and timestamp. Falling back to current time' + ); + return Date.now(); +} diff --git a/ts/util/getMessageSentTimestampSet.ts b/ts/util/getMessageSentTimestampSet.ts new file mode 100644 index 000000000000..0d8dc59a782c --- /dev/null +++ b/ts/util/getMessageSentTimestampSet.ts @@ -0,0 +1,17 @@ +// Copyright 2023 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import type { MessageAttributesType } from '../model-types.d'; + +export function getMessageSentTimestampSet({ + sent_at: sentAt, + editHistory, +}: Pick< + MessageAttributesType, + 'sent_at' | 'editHistory' +>): ReadonlySet { + return new Set([ + sentAt, + ...(editHistory?.map(({ timestamp }) => timestamp) ?? []), + ]); +} diff --git a/ts/util/handleEditMessage.ts b/ts/util/handleEditMessage.ts index 9a56f5f1ba15..b28ca1ea6189 100644 --- a/ts/util/handleEditMessage.ts +++ b/ts/util/handleEditMessage.ts @@ -240,7 +240,7 @@ export async function handleEditMessage( window.Whisper.deliveryReceiptQueue.add(() => { window.Whisper.deliveryReceiptBatcher.add({ messageId: mainMessage.id, - conversationId: editAttributes.fromId, + conversationId: editAttributes.conversationId, senderE164: editAttributes.message.source, senderUuid: editAttributes.message.sourceUuid, timestamp: editAttributes.message.timestamp, @@ -263,7 +263,7 @@ export async function handleEditMessage( mainMessageModel.attributes, window.textsecure.storage.user.getCheckedUuid().toString(), { - fromId: editAttributes.fromId, + conversationId: editAttributes.conversationId, messageId: mainMessage.id, readStatus, sentAt: upgradedEditedMessageData.timestamp, diff --git a/ts/util/makeQuote.ts b/ts/util/makeQuote.ts index a50a8fea7789..4510cb12a71f 100644 --- a/ts/util/makeQuote.ts +++ b/ts/util/makeQuote.ts @@ -14,8 +14,10 @@ import { getContact } from '../messages/helpers'; import { getQuoteBodyText } from './getQuoteBodyText'; import { isGIF } from '../types/Attachment'; import { isGiftBadge, isTapToView } from '../state/selectors/message'; +import * as log from '../logging/log'; import { map, take, collect } from './iterables'; import { strictAssert } from './assert'; +import { getMessageSentTimestamp } from './getMessageSentTimestamp'; export async function makeQuote( quotedMessage: MessageAttributesType @@ -27,14 +29,13 @@ export async function makeQuote( const { attachments, bodyRanges, - editMessageTimestamp, id: messageId, payment, preview, sticker, } = quotedMessage; - const quoteId = editMessageTimestamp || quotedMessage.sent_at; + const quoteId = getMessageSentTimestamp(quotedMessage, { log }); return { authorUuid: contact.get('uuid'), diff --git a/ts/util/markConversationRead.ts b/ts/util/markConversationRead.ts index dc61b00c2cfc..0b50e4e1e621 100644 --- a/ts/util/markConversationRead.ts +++ b/ts/util/markConversationRead.ts @@ -45,7 +45,7 @@ export async function markConversationRead( includeStoryReplies: !isGroup(conversationAttrs), }), window.Signal.Data.getUnreadEditedMessagesAndMarkRead({ - fromId: conversationId, + conversationId, newestUnreadAt, }), window.Signal.Data.getUnreadReactionsAndMarkRead({ diff --git a/ts/util/sendEditedMessage.ts b/ts/util/sendEditedMessage.ts index 9d705475b6ca..98e9baabb20b 100644 --- a/ts/util/sendEditedMessage.ts +++ b/ts/util/sendEditedMessage.ts @@ -30,6 +30,7 @@ import { isSignalConversation } from './isSignalConversation'; import { strictAssert } from './assert'; import { timeAndLogIfTooLong } from './timeAndLogIfTooLong'; import { makeQuote } from './makeQuote'; +import { getMessageSentTimestamp } from './getMessageSentTimestamp'; const SEND_REPORT_THRESHOLD_MS = 25; @@ -82,9 +83,12 @@ export async function sendEditedMessage( } const timestamp = Date.now(); - const targetSentTimestamp = - targetMessage.attributes.editMessageTimestamp ?? - targetMessage.attributes.timestamp; + const targetSentTimestamp = getMessageSentTimestamp( + targetMessage.attributes, + { + log, + } + ); log.info(`${idLog}: edited(${timestamp}) original(${targetSentTimestamp})`);