Retry delivery and read receipts for up to 24 hours
This commit is contained in:
parent
e81821f4a6
commit
f9e98836b0
15 changed files with 316 additions and 243 deletions
116
ts/util/sendReceipts.ts
Normal file
116
ts/util/sendReceipts.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { chunk } from 'lodash';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import type { Receipt } from '../types/Receipt';
|
||||
import { ReceiptType } from '../types/Receipt';
|
||||
import { getSendOptions } from './getSendOptions';
|
||||
import { handleMessageSend } from './handleMessageSend';
|
||||
import { isConversationAccepted } from './isConversationAccepted';
|
||||
import { map } from './iterables';
|
||||
import { missingCaseError } from './missingCaseError';
|
||||
|
||||
const CHUNK_SIZE = 100;
|
||||
|
||||
export async function sendReceipts({
|
||||
log,
|
||||
receipts,
|
||||
type,
|
||||
}: Readonly<{
|
||||
log: LoggerType;
|
||||
receipts: ReadonlyArray<Receipt>;
|
||||
type: ReceiptType;
|
||||
}>): Promise<void> {
|
||||
let requiresUserSetting: boolean;
|
||||
let methodName:
|
||||
| 'sendDeliveryReceipt'
|
||||
| 'sendReadReceipt'
|
||||
| 'sendViewedReceipt';
|
||||
switch (type) {
|
||||
case ReceiptType.Delivery:
|
||||
requiresUserSetting = false;
|
||||
methodName = 'sendDeliveryReceipt';
|
||||
break;
|
||||
case ReceiptType.Read:
|
||||
requiresUserSetting = true;
|
||||
methodName = 'sendReadReceipt';
|
||||
break;
|
||||
case ReceiptType.Viewed:
|
||||
requiresUserSetting = true;
|
||||
methodName = 'sendViewedReceipt';
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(type);
|
||||
}
|
||||
|
||||
if (requiresUserSetting && !window.storage.get('read-receipt-setting')) {
|
||||
log.info('requires user setting. Not sending these receipts');
|
||||
return;
|
||||
}
|
||||
|
||||
const receiptsBySenderId: Map<string, Array<Receipt>> = receipts.reduce(
|
||||
(result, receipt) => {
|
||||
const { senderE164, senderUuid } = receipt;
|
||||
if (!senderE164 && !senderUuid) {
|
||||
log.error('no sender E164 or UUID. Skipping this receipt');
|
||||
return result;
|
||||
}
|
||||
|
||||
const senderId = window.ConversationController.ensureContactIds({
|
||||
e164: senderE164,
|
||||
uuid: senderUuid,
|
||||
});
|
||||
if (!senderId) {
|
||||
throw new Error(
|
||||
'no conversation found with that E164/UUID. Cannot send this receipt'
|
||||
);
|
||||
}
|
||||
|
||||
const existingGroup = result.get(senderId);
|
||||
if (existingGroup) {
|
||||
existingGroup.push(receipt);
|
||||
} else {
|
||||
result.set(senderId, [receipt]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
new Map()
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
map(receiptsBySenderId, async ([senderId, receiptsForSender]) => {
|
||||
const sender = window.ConversationController.get(senderId);
|
||||
if (!sender) {
|
||||
throw new Error(
|
||||
'despite having a conversation ID, no conversation was found'
|
||||
);
|
||||
}
|
||||
|
||||
if (!isConversationAccepted(sender.attributes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sendOptions = await getSendOptions(sender.attributes);
|
||||
|
||||
const batches = chunk(receiptsForSender, CHUNK_SIZE);
|
||||
await Promise.all(
|
||||
map(batches, async batch => {
|
||||
const timestamps = batch.map(receipt => receipt.timestamp);
|
||||
const messageIds = batch.map(receipt => receipt.messageId);
|
||||
|
||||
await handleMessageSend(
|
||||
window.textsecure.messaging[methodName]({
|
||||
senderE164: sender.get('e164'),
|
||||
senderUuid: sender.get('uuid'),
|
||||
timestamps,
|
||||
options: sendOptions,
|
||||
}),
|
||||
{ messageIds, sendType: type }
|
||||
);
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue