signal-desktop/ts/jobs/helpers/sendCallingMessage.ts
Scott Nonnenberg 783c71999a
Send call messages with conversationJobQueue
Co-authored-by: trevor-signal <trevor@signal.org>
2024-04-16 14:55:09 -07:00

149 lines
4.1 KiB
TypeScript

// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { handleMessageSend } from '../../util/handleMessageSend';
import { getSendOptions } from '../../util/getSendOptions';
import {
isDirectConversation,
isGroup,
} from '../../util/whatTypeOfConversation';
import { SignalService as Proto } from '../../protobuf';
import {
handleMultipleSendErrors,
maybeExpandErrors,
} from './handleMultipleSendErrors';
import type { ConversationModel } from '../../models/conversations';
import type {
ConversationQueueJobBundle,
CallingMessageJobData,
} from '../conversationJobQueue';
import { isConversationUnregistered } from '../../util/isConversationUnregistered';
import {
OutgoingIdentityKeyError,
UnregisteredUserError,
} from '../../textsecure/Errors';
import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { sendContentMessageToGroup } from '../../util/sendToGroup';
import * as Bytes from '../../Bytes';
import { getValidRecipients } from './getValidRecipients';
export async function sendCallingMessage(
conversation: ConversationModel,
{
isFinalAttempt,
messaging,
shouldContinue,
timestamp,
timeRemaining,
log,
}: ConversationQueueJobBundle,
data: CallingMessageJobData
): Promise<void> {
const logId = `sendCallingMessage(${conversation.idForLogging()}.${timestamp})`;
if (!shouldContinue) {
log.info(`${logId}: Ran out of time. Giving up.`);
return;
}
log.info(`${logId}: Starting send`);
if (
isDirectConversation(conversation.attributes) &&
isConversationUnregistered(conversation.attributes)
) {
log.warn(`${logId}: Direct conversation is unregistered; refusing to send`);
return;
}
const {
protoBase64,
urgent,
recipients: jobRecipients,
isPartialSend,
} = data;
const recipients = getValidRecipients(
jobRecipients || conversation.getRecipients(),
{ log, logId }
);
const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id,
untrustedServiceIds,
});
throw new Error(
`${logId}: Blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.`
);
}
if (recipients.length === 0) {
log.warn(`${logId}: Giving up because there are no valid recipients.`);
return;
}
const sendType = 'callingMessage';
const sendOptions = await getSendOptions(conversation.attributes);
const callingMessage = Proto.CallingMessage.decode(
Bytes.fromBase64(protoBase64)
);
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
try {
if (isGroup(conversation.attributes)) {
await handleMessageSend(
sendContentMessageToGroup({
contentHint: ContentHint.DEFAULT,
contentMessage: new Proto.Content({ callingMessage }),
isPartialSend,
messageId: undefined,
recipients,
sendOptions,
sendTarget: conversation.toSenderKeyTarget(),
sendType,
timestamp,
urgent,
}),
{ messageIds: [], sendType }
);
} else {
const sendTarget = conversation.getSendTarget();
if (!sendTarget) {
log.error(`${logId}: Direct conversation send target is falsy`);
return;
}
await handleMessageSend(
messaging.sendCallingMessage(
sendTarget,
callingMessage,
timestamp,
urgent,
sendOptions
),
{ messageIds: [], sendType }
);
}
} catch (error: unknown) {
if (
error instanceof OutgoingIdentityKeyError ||
error instanceof UnregisteredUserError
) {
log.info(
`${logId}: Send failure was OutgoingIdentityKeyError or UnregisteredUserError. Cancelling job.`
);
return;
}
await handleMultipleSendErrors({
errors: maybeExpandErrors(error),
isFinalAttempt,
log,
timeRemaining,
toThrow: error,
});
}
}