2021-12-10 22:51:54 +00:00
|
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import * as log from '../logging/log';
|
|
|
|
import type { ConversationModel } from '../models/conversations';
|
|
|
|
import type {
|
|
|
|
CustomError,
|
|
|
|
MessageAttributesType,
|
|
|
|
QuotedMessageType,
|
|
|
|
} from '../model-types.d';
|
2023-08-10 16:43:33 +00:00
|
|
|
import type { ServiceIdString } from '../types/ServiceId';
|
2022-11-30 21:47:54 +00:00
|
|
|
import { PaymentEventKind } from '../types/Payment';
|
|
|
|
import type { AnyPaymentEvent } from '../types/Payment';
|
|
|
|
import type { LocalizerType } from '../types/Util';
|
|
|
|
import { missingCaseError } from '../util/missingCaseError';
|
2022-07-18 20:01:43 +00:00
|
|
|
|
|
|
|
export function isIncoming(
|
|
|
|
message: Pick<MessageAttributesType, 'type'>
|
|
|
|
): boolean {
|
|
|
|
return message.type === 'incoming';
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isOutgoing(
|
|
|
|
message: Pick<MessageAttributesType, 'type'>
|
|
|
|
): boolean {
|
|
|
|
return message.type === 'outgoing';
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isStory(message: Pick<MessageAttributesType, 'type'>): boolean {
|
|
|
|
return message.type === 'story';
|
|
|
|
}
|
2021-12-10 22:51:54 +00:00
|
|
|
|
2022-11-30 21:47:54 +00:00
|
|
|
export type MessageAttributesWithPaymentEvent = MessageAttributesType & {
|
|
|
|
payment: AnyPaymentEvent;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function messageHasPaymentEvent(
|
|
|
|
message: MessageAttributesType
|
|
|
|
): message is MessageAttributesWithPaymentEvent {
|
|
|
|
return message.payment != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getPaymentEventNotificationText(
|
|
|
|
payment: AnyPaymentEvent,
|
|
|
|
senderTitle: string,
|
|
|
|
conversationTitle: string | null,
|
|
|
|
senderIsMe: boolean,
|
|
|
|
i18n: LocalizerType
|
|
|
|
): string {
|
|
|
|
if (payment.kind === PaymentEventKind.Notification) {
|
|
|
|
return i18n('icu:payment-event-notification-label');
|
|
|
|
}
|
|
|
|
return getPaymentEventDescription(
|
|
|
|
payment,
|
|
|
|
senderTitle,
|
|
|
|
conversationTitle,
|
|
|
|
senderIsMe,
|
|
|
|
i18n
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getPaymentEventDescription(
|
|
|
|
payment: AnyPaymentEvent,
|
|
|
|
senderTitle: string,
|
|
|
|
conversationTitle: string | null,
|
|
|
|
senderIsMe: boolean,
|
|
|
|
i18n: LocalizerType
|
|
|
|
): string {
|
|
|
|
const { kind } = payment;
|
|
|
|
if (kind === PaymentEventKind.Notification) {
|
|
|
|
if (senderIsMe) {
|
|
|
|
if (conversationTitle != null) {
|
|
|
|
return i18n('icu:payment-event-notification-message-you-label', {
|
|
|
|
receiver: conversationTitle,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return i18n(
|
|
|
|
'icu:payment-event-notification-message-you-label-without-receiver'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return i18n('icu:payment-event-notification-message-label', {
|
|
|
|
sender: senderTitle,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (kind === PaymentEventKind.ActivationRequest) {
|
|
|
|
if (senderIsMe) {
|
|
|
|
if (conversationTitle != null) {
|
|
|
|
return i18n('icu:payment-event-activation-request-you-label', {
|
|
|
|
receiver: conversationTitle,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return i18n(
|
|
|
|
'icu:payment-event-activation-request-you-label-without-receiver'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return i18n('icu:payment-event-activation-request-label', {
|
|
|
|
sender: senderTitle,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (kind === PaymentEventKind.Activation) {
|
|
|
|
if (senderIsMe) {
|
|
|
|
return i18n('icu:payment-event-activated-you-label');
|
|
|
|
}
|
|
|
|
return i18n('icu:payment-event-activated-label', {
|
|
|
|
sender: senderTitle,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
throw missingCaseError(kind);
|
|
|
|
}
|
|
|
|
|
2021-12-10 22:51:54 +00:00
|
|
|
export function isQuoteAMatch(
|
|
|
|
message: MessageAttributesType | null | undefined,
|
|
|
|
conversationId: string,
|
2023-08-16 20:54:39 +00:00
|
|
|
quote: Pick<QuotedMessageType, 'id' | 'authorAci' | 'author'>
|
2021-12-10 22:51:54 +00:00
|
|
|
): message is MessageAttributesType {
|
|
|
|
if (!message) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-16 20:54:39 +00:00
|
|
|
const { authorAci, id } = quote;
|
2022-08-09 21:39:00 +00:00
|
|
|
const authorConversation = window.ConversationController.lookupOrCreate({
|
2021-12-10 22:51:54 +00:00
|
|
|
e164: 'author' in quote ? quote.author : undefined,
|
2023-08-16 20:54:39 +00:00
|
|
|
serviceId: authorAci,
|
2022-12-03 01:05:27 +00:00
|
|
|
reason: 'helpers.isQuoteAMatch',
|
2021-12-10 22:51:54 +00:00
|
|
|
});
|
|
|
|
|
2023-04-20 16:31:59 +00:00
|
|
|
const isSameTimestamp =
|
|
|
|
message.sent_at === id ||
|
|
|
|
message.editHistory?.some(({ timestamp }) => timestamp === id) ||
|
|
|
|
false;
|
|
|
|
|
2021-12-10 22:51:54 +00:00
|
|
|
return (
|
2023-04-20 16:31:59 +00:00
|
|
|
isSameTimestamp &&
|
2021-12-10 22:51:54 +00:00
|
|
|
message.conversationId === conversationId &&
|
2022-08-09 21:39:00 +00:00
|
|
|
getContactId(message) === authorConversation?.id
|
2021-12-10 22:51:54 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getContactId(
|
2023-08-16 20:54:39 +00:00
|
|
|
message: Pick<MessageAttributesType, 'type' | 'source' | 'sourceServiceId'>
|
2021-12-10 22:51:54 +00:00
|
|
|
): string | undefined {
|
|
|
|
const source = getSource(message);
|
2023-08-16 20:54:39 +00:00
|
|
|
const sourceServiceId = getSourceServiceId(message);
|
2021-12-10 22:51:54 +00:00
|
|
|
|
2023-08-16 20:54:39 +00:00
|
|
|
if (!source && !sourceServiceId) {
|
2021-12-10 22:51:54 +00:00
|
|
|
return window.ConversationController.getOurConversationId();
|
|
|
|
}
|
|
|
|
|
2022-08-09 21:39:00 +00:00
|
|
|
const conversation = window.ConversationController.lookupOrCreate({
|
2021-12-10 22:51:54 +00:00
|
|
|
e164: source,
|
2023-08-16 20:54:39 +00:00
|
|
|
serviceId: sourceServiceId,
|
2022-12-03 01:05:27 +00:00
|
|
|
reason: 'helpers.getContactId',
|
2021-12-10 22:51:54 +00:00
|
|
|
});
|
2022-08-09 21:39:00 +00:00
|
|
|
return conversation?.id;
|
2021-12-10 22:51:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getContact(
|
|
|
|
message: MessageAttributesType
|
|
|
|
): ConversationModel | undefined {
|
|
|
|
const id = getContactId(message);
|
|
|
|
return window.ConversationController.get(id);
|
|
|
|
}
|
|
|
|
|
2022-10-03 23:10:20 +00:00
|
|
|
export function getSource(
|
|
|
|
message: Pick<MessageAttributesType, 'type' | 'source'>
|
|
|
|
): string | undefined {
|
2022-03-04 21:14:52 +00:00
|
|
|
if (isIncoming(message) || isStory(message)) {
|
2021-12-10 22:51:54 +00:00
|
|
|
return message.source;
|
|
|
|
}
|
|
|
|
if (!isOutgoing(message)) {
|
|
|
|
log.warn('Message.getSource: Called for non-incoming/non-outgoing message');
|
|
|
|
}
|
|
|
|
|
|
|
|
return window.textsecure.storage.user.getNumber();
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getSourceDevice(
|
2022-10-03 23:10:20 +00:00
|
|
|
message: Pick<MessageAttributesType, 'type' | 'sourceDevice'>
|
2021-12-10 22:51:54 +00:00
|
|
|
): string | number | undefined {
|
|
|
|
const { sourceDevice } = message;
|
|
|
|
|
2022-03-04 21:14:52 +00:00
|
|
|
if (isIncoming(message) || isStory(message)) {
|
2021-12-10 22:51:54 +00:00
|
|
|
return sourceDevice;
|
|
|
|
}
|
|
|
|
if (!isOutgoing(message)) {
|
|
|
|
log.warn(
|
|
|
|
'Message.getSourceDevice: Called for non-incoming/non-outgoing message'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sourceDevice || window.textsecure.storage.user.getDeviceId();
|
|
|
|
}
|
|
|
|
|
2023-08-16 20:54:39 +00:00
|
|
|
export function getSourceServiceId(
|
|
|
|
message: Pick<MessageAttributesType, 'type' | 'sourceServiceId'>
|
2023-08-10 16:43:33 +00:00
|
|
|
): ServiceIdString | undefined {
|
2022-03-04 21:14:52 +00:00
|
|
|
if (isIncoming(message) || isStory(message)) {
|
2023-08-16 20:54:39 +00:00
|
|
|
return message.sourceServiceId;
|
2021-12-10 22:51:54 +00:00
|
|
|
}
|
|
|
|
if (!isOutgoing(message)) {
|
|
|
|
log.warn(
|
2023-08-16 20:54:39 +00:00
|
|
|
'Message.getSourceServiceId: Called for non-incoming/non-outgoing message'
|
2021-12-10 22:51:54 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-08-10 16:43:33 +00:00
|
|
|
return window.textsecure.storage.user.getAci();
|
2021-12-10 22:51:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export const isCustomError = (e: unknown): e is CustomError =>
|
|
|
|
e instanceof Error;
|