diff --git a/ts/jobs/helpers/sendStory.ts b/ts/jobs/helpers/sendStory.ts index b6f70a1d6..361f7da89 100644 --- a/ts/jobs/helpers/sendStory.ts +++ b/ts/jobs/helpers/sendStory.ts @@ -71,10 +71,11 @@ export async function sendStory( return; } + const messageTimestamp = message.get('timestamp'); const messageConversation = message.getConversation(); if (messageConversation !== conversation) { log.error( - `stories.sendStory(${messageId}): Message conversation '${messageConversation?.idForLogging()}' does not match job conversation ${conversation.idForLogging()}` + `stories.sendStory(${messageTimestamp}): Message conversation '${messageConversation?.idForLogging()}' does not match job conversation ${conversation.idForLogging()}` ); return; } @@ -84,7 +85,7 @@ export async function sendStory( if (!attachment) { log.info( - `stories.sendStory(${messageId}): message does not have any attachments to send. Giving up on sending it` + `stories.sendStory(${messageTimestamp}): message does not have any attachments to send. Giving up on sending it` ); return; } @@ -163,17 +164,18 @@ export async function sendStory( return; } + const messageTimestamp = message.get('timestamp'); const messageConversation = message.getConversation(); if (messageConversation !== conversation) { log.error( - `stories.sendStory(${messageId}): Message conversation '${messageConversation?.idForLogging()}' does not match job conversation ${conversation.idForLogging()}` + `stories.sendStory(${messageTimestamp}): Message conversation '${messageConversation?.idForLogging()}' does not match job conversation ${conversation.idForLogging()}` ); return; } if (message.isErased() || message.get('deletedForEveryone')) { log.info( - `stories.sendStory(${messageId}): message was erased. Giving up on sending it` + `stories.sendStory(${messageTimestamp}): message was erased. Giving up on sending it` ); return; } @@ -185,7 +187,7 @@ export async function sendStory( if (!receiverId) { log.info( - `stories.sendStory(${messageId}): did not get a valid recipient ID for message. Giving up on sending it` + `stories.sendStory(${messageTimestamp}): did not get a valid recipient ID for message. Giving up on sending it` ); return; } @@ -212,7 +214,7 @@ export async function sendStory( if (!shouldContinue) { log.info( - `stories.sendStory(${messageId}): ran out of time. Giving up on sending it` + `stories.sendStory(${messageTimestamp}): ran out of time. Giving up on sending it` ); await markMessageFailed(message, [ new Error('Message send ran out of time'), @@ -242,7 +244,7 @@ export async function sendStory( } ); throw new Error( - `stories.sendStory(${messageId}): sending blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` + `stories.sendStory(${messageTimestamp}): sending blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` ); } @@ -266,7 +268,7 @@ export async function sendStory( ); log.info( - `stories.sendStory(${messageId}): sending story to ${receiverId}` + `stories.sendStory(${messageTimestamp}): sending story to ${receiverId}` ); const storyMessage = new Proto.StoryMessage(); @@ -314,6 +316,7 @@ export async function sendStory( sendOptions, sendTarget, sendType: 'story', + story: true, timestamp: message.get('timestamp'), urgent: false, }); diff --git a/ts/sql/Server.ts b/ts/sql/Server.ts index 6dd75c347..3c12e86f2 100644 --- a/ts/sql/Server.ts +++ b/ts/sql/Server.ts @@ -4556,7 +4556,7 @@ async function getStoryDistributionWithMembers( id: string ): Promise { const db = getInstance(); - const storyDistribution = prepare( + const storyDistribution: StoryDistributionForDatabase | undefined = prepare( db, 'SELECT * FROM storyDistributions WHERE id = $id;' ).get({ @@ -4575,7 +4575,7 @@ async function getStoryDistributionWithMembers( }); return { - ...storyDistribution, + ...hydrateStoryDistribution(storyDistribution), members: members.map(({ uuid }) => uuid), }; } diff --git a/ts/textsecure/OutgoingMessage.ts b/ts/textsecure/OutgoingMessage.ts index 7761fa951..2f5922f63 100644 --- a/ts/textsecure/OutgoingMessage.ts +++ b/ts/textsecure/OutgoingMessage.ts @@ -134,6 +134,8 @@ export default class OutgoingMessage { urgent: boolean; + story?: boolean; + recipients: Record>; sendLogCallback?: SendLogCallbackType; @@ -147,6 +149,7 @@ export default class OutgoingMessage { options, sendLogCallback, server, + story, timestamp, urgent, }: { @@ -158,6 +161,7 @@ export default class OutgoingMessage { options?: OutgoingMessageOptionsType; sendLogCallback?: SendLogCallbackType; server: WebAPIType; + story?: boolean; timestamp: number; urgent: boolean; }) { @@ -175,6 +179,7 @@ export default class OutgoingMessage { this.contentHint = contentHint; this.groupId = groupId; this.callback = callback; + this.story = story; this.urgent = urgent; this.identifiersCompleted = 0; @@ -310,11 +315,17 @@ export default class OutgoingMessage { identifier, jsonData, timestamp, - { accessKey, online: this.online, urgent: this.urgent } + { + accessKey, + online: this.online, + story: this.story, + urgent: this.urgent, + } ); } else { promise = this.server.sendMessages(identifier, jsonData, timestamp, { online: this.online, + story: this.story, urgent: this.urgent, }); } diff --git a/ts/textsecure/SendMessage.ts b/ts/textsecure/SendMessage.ts index 34b818a26..345658331 100644 --- a/ts/textsecure/SendMessage.ts +++ b/ts/textsecure/SendMessage.ts @@ -1203,6 +1203,7 @@ export default class MessageSender { proto, recipients, sendLogCallback, + story, timestamp, urgent, }: Readonly<{ @@ -1213,6 +1214,7 @@ export default class MessageSender { proto: Proto.Content | Proto.DataMessage | PlaintextContent; recipients: ReadonlyArray; sendLogCallback?: SendLogCallbackType; + story?: boolean; timestamp: number; urgent: boolean; }>): void { @@ -1233,6 +1235,7 @@ export default class MessageSender { options, sendLogCallback, server: this.server, + story, timestamp, urgent, }); @@ -2220,6 +2223,7 @@ export default class MessageSender { proto, recipients, sendLogCallback, + story, timestamp = Date.now(), urgent, }: Readonly<{ @@ -2229,6 +2233,7 @@ export default class MessageSender { proto: Proto.Content; recipients: ReadonlyArray; sendLogCallback?: SendLogCallbackType; + story?: boolean; timestamp: number; urgent: boolean; }>): Promise { @@ -2269,6 +2274,7 @@ export default class MessageSender { proto, recipients: identifiers, sendLogCallback, + story, timestamp, urgent, }); @@ -2341,6 +2347,7 @@ export default class MessageSender { groupId, identifiers, throwIfNotInDatabase, + story, urgent, }: Readonly<{ contentHint: number; @@ -2348,6 +2355,7 @@ export default class MessageSender { groupId: string | undefined; identifiers: ReadonlyArray; throwIfNotInDatabase?: boolean; + story?: boolean; urgent: boolean; }>, options?: Readonly @@ -2380,6 +2388,7 @@ export default class MessageSender { proto: contentMessage, recipients: identifiers, sendLogCallback, + story, timestamp, urgent, }); diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index 54b881277..597c227e5 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -917,13 +917,18 @@ export type WebAPIType = { destination: string, messageArray: ReadonlyArray, timestamp: number, - options: { online?: boolean; urgent?: boolean } + options: { online?: boolean; story?: boolean; urgent?: boolean } ) => Promise; sendMessagesUnauth: ( destination: string, messageArray: ReadonlyArray, timestamp: number, - options: { accessKey?: string; online?: boolean; urgent?: boolean } + options: { + accessKey?: string; + online?: boolean; + story?: boolean; + urgent?: boolean; + } ) => Promise; sendWithSenderKey: ( payload: Uint8Array, @@ -2084,12 +2089,19 @@ export function initialize({ accessKey, online, urgent = true, - }: { accessKey?: string; online?: boolean; urgent?: boolean } + story = false, + }: { + accessKey?: string; + online?: boolean; + story?: boolean; + urgent?: boolean; + } ) { const jsonData = { messages, timestamp, online: Boolean(online), + story, urgent, }; @@ -2108,12 +2120,17 @@ export function initialize({ destination: string, messages: ReadonlyArray, timestamp: number, - { online, urgent = true }: { online?: boolean; urgent?: boolean } + { + online, + urgent = true, + story = false, + }: { online?: boolean; story?: boolean; urgent?: boolean } ) { const jsonData = { messages, timestamp, online: Boolean(online), + story, urgent, }; diff --git a/ts/util/sendStoryMessage.ts b/ts/util/sendStoryMessage.ts index 240d03887..ff09f1481 100644 --- a/ts/util/sendStoryMessage.ts +++ b/ts/util/sendStoryMessage.ts @@ -17,7 +17,6 @@ import { conversationJobQueue, conversationQueueJobEnum, } from '../jobs/conversationJobQueue'; -import { formatJobForInsert } from '../jobs/formatJobForInsert'; import { getRecipients } from './getRecipients'; import { getSignalConnections } from './getSignalConnections'; import { incrementMessageCounter } from './incrementMessageCounter'; @@ -144,6 +143,8 @@ export async function sendStoryMessage( ); } + // Note: we use the same sent_at for these messages because we want de-duplication + // on the receiver side. return window.Signal.Migrations.upgradeMessageSchema({ attachments, conversationId: ourConversation.id, @@ -247,7 +248,9 @@ export async function sendStoryMessage( ourConversation.addSingleMessage(model, { isJustSent: true }); - log.info(`stories.sendStoryMessage: saving message ${message.id}`); + log.info( + `stories.sendStoryMessage: saving message ${messageAttributes.timestamp}` + ); return dataInterface.saveMessage(message.attributes, { forceSave: true, ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), @@ -258,18 +261,12 @@ export async function sendStoryMessage( // * Send to the distribution lists // * Place into job queue // * Save the job - await conversationJobQueue.add( - { - type: conversationQueueJobEnum.enum.Story, - conversationId: ourConversation.id, - messageIds: distributionListMessages.map(m => m.id), - timestamp, - }, - async jobToInsert => { - log.info(`stories.sendStoryMessage: saving job ${jobToInsert.id}`); - await dataInterface.insertJob(formatJobForInsert(jobToInsert)); - } - ); + await conversationJobQueue.add({ + type: conversationQueueJobEnum.enum.Story, + conversationId: ourConversation.id, + messageIds: distributionListMessages.map(m => m.id), + timestamp, + }); // * Send to groups // * Save the message models @@ -301,7 +298,9 @@ export async function sendStoryMessage( const conversation = message.getConversation(); conversation?.addSingleMessage(model, { isJustSent: true }); - log.info(`stories.sendStoryMessage: saving message ${message.id}`); + log.info( + `stories.sendStoryMessage: saving message ${messageAttributes.timestamp}` + ); await dataInterface.saveMessage(message.attributes, { forceSave: true, jobToInsert, diff --git a/ts/util/sendToGroup.ts b/ts/util/sendToGroup.ts index 8578691e2..f94f1cb5c 100644 --- a/ts/util/sendToGroup.ts +++ b/ts/util/sendToGroup.ts @@ -156,6 +156,7 @@ export async function sendContentMessageToGroup({ sendOptions, sendTarget, sendType, + story, timestamp, urgent, }: { @@ -168,6 +169,7 @@ export async function sendContentMessageToGroup({ sendOptions?: SendOptionsType; sendTarget: SenderKeyTargetType; sendType: SendTypesType; + story?: boolean; timestamp: number; urgent: boolean; }): Promise { @@ -199,6 +201,7 @@ export async function sendContentMessageToGroup({ sendOptions, sendTarget, sendType, + story, timestamp, urgent, }); @@ -235,6 +238,7 @@ export async function sendContentMessageToGroup({ proto: contentMessage, recipients, sendLogCallback, + story, timestamp, urgent, }); @@ -253,6 +257,7 @@ export async function sendToGroupViaSenderKey(options: { sendOptions?: SendOptionsType; sendTarget: SenderKeyTargetType; sendType: SendTypesType; + story?: boolean; timestamp: number; urgent: boolean; }): Promise { @@ -267,6 +272,7 @@ export async function sendToGroupViaSenderKey(options: { sendOptions, sendTarget, sendType, + story, timestamp, urgent, } = options; @@ -433,6 +439,7 @@ export async function sendToGroupViaSenderKey(options: { distributionId, groupId, identifiers: newToMemberUuids, + story, urgent, }, sendOptions ? { ...sendOptions, online: false } : undefined