mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-02 08:13:37 +01:00
Use protopiler for protocol buffers
Co-authored-by: Jamie Kyle <jamie@signal.org>
This commit is contained in:
@@ -280,12 +280,11 @@ export default class AccountManager extends EventTarget {
|
||||
}
|
||||
const encrypted = encryptDeviceName(name, identityKey.publicKey);
|
||||
|
||||
const proto = new Proto.DeviceName();
|
||||
proto.ephemeralPublic = encrypted.ephemeralPublic.serialize();
|
||||
proto.syntheticIv = encrypted.syntheticIv;
|
||||
proto.ciphertext = encrypted.ciphertext;
|
||||
|
||||
const bytes = Proto.DeviceName.encode(proto).finish();
|
||||
const bytes = Proto.DeviceName.encode({
|
||||
ephemeralPublic: encrypted.ephemeralPublic.serialize(),
|
||||
syntheticIv: encrypted.syntheticIv,
|
||||
ciphertext: encrypted.ciphertext,
|
||||
});
|
||||
return Bytes.toBase64(bytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@ import type { ContactAvatarType } from '../types/Avatar.std.js';
|
||||
import type { AttachmentType } from '../types/Attachment.std.js';
|
||||
import type { AciString } from '../types/ServiceId.std.js';
|
||||
import { computeHash } from '../Crypto.node.js';
|
||||
import { dropNull } from '../util/dropNull.std.js';
|
||||
import { fromAciUuidBytesOrString } from '../util/ServiceId.node.js';
|
||||
import * as Bytes from '../Bytes.std.js';
|
||||
import { decryptAttachmentV2ToSink } from '../AttachmentCrypto.node.js';
|
||||
|
||||
import Avatar = Proto.ContactDetails.IAvatar;
|
||||
import Avatar = Proto.ContactDetails.Avatar.Params;
|
||||
import { stringToMIMEType } from '../types/MIME.std.js';
|
||||
|
||||
const log = createLogger('ContactsParser');
|
||||
@@ -32,7 +31,7 @@ type OptionalFields = {
|
||||
|
||||
type MessageWithAvatar<Message extends OptionalFields> = Omit<
|
||||
Message,
|
||||
'avatar' | 'toJSON' | 'aci' | 'aciBinary'
|
||||
'avatar' | 'toJSON' | 'aci' | 'aciBinary' | 'expireTimer'
|
||||
> & {
|
||||
aci: AciString;
|
||||
avatar?: ContactAvatarType;
|
||||
@@ -41,7 +40,8 @@ type MessageWithAvatar<Message extends OptionalFields> = Omit<
|
||||
number?: string | undefined;
|
||||
};
|
||||
|
||||
export type ContactDetailsWithAvatar = MessageWithAvatar<Proto.IContactDetails>;
|
||||
export type ContactDetailsWithAvatar =
|
||||
MessageWithAvatar<Proto.ContactDetails.Params>;
|
||||
|
||||
export async function parseContactsV2(
|
||||
attachment: AttachmentType
|
||||
@@ -105,7 +105,11 @@ async function prepareContact(
|
||||
? DurationInSeconds.fromSeconds(proto.expireTimer)
|
||||
: undefined;
|
||||
|
||||
const aci = fromAciUuidBytesOrString(aciBinary, rawAci, 'ContactBuffer.aci');
|
||||
const aci = fromAciUuidBytesOrString(
|
||||
aciBinary,
|
||||
rawAci ?? '',
|
||||
'ContactBuffer.aci'
|
||||
);
|
||||
|
||||
if ((Bytes.isNotEmpty(aciBinary) || rawAci) && aci == null) {
|
||||
log.warn('ParseContactsTransform: Dropping contact with invalid aci');
|
||||
@@ -132,9 +136,8 @@ async function prepareContact(
|
||||
return {
|
||||
...proto,
|
||||
expireTimer,
|
||||
expireTimerVersion: proto.expireTimerVersion ?? null,
|
||||
aci,
|
||||
avatar,
|
||||
number: dropNull(proto.number),
|
||||
number: proto.number ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary.std.js'
|
||||
import { Zone } from '../util/Zone.std.js';
|
||||
import * as durations from '../util/durations/index.std.js';
|
||||
import { DurationInSeconds } from '../util/durations/index.std.js';
|
||||
import { isKnownProtoEnumMember } from '../util/isKnownProtoEnumMember.std.js';
|
||||
import { Address } from '../types/Address.std.js';
|
||||
import { QualifiedAddress } from '../types/QualifiedAddress.std.js';
|
||||
import { normalizeStoryDistributionId } from '../types/StoryDistributionId.std.js';
|
||||
@@ -78,10 +79,11 @@ import createTaskWithTimeout from './TaskWithTimeout.std.js';
|
||||
import {
|
||||
processAttachment,
|
||||
processDataMessage,
|
||||
processBodyRange,
|
||||
processGroupV2Context,
|
||||
processPreview,
|
||||
} from './processDataMessage.preload.js';
|
||||
import { processSyncMessage } from './processSyncMessage.node.js';
|
||||
import { processSent } from './processSyncMessage.node.js';
|
||||
import type { EventHandler } from './EventTarget.std.js';
|
||||
import EventTarget from './EventTarget.std.js';
|
||||
import type { IncomingWebSocketRequest } from './WebsocketResources.preload.js';
|
||||
@@ -97,7 +99,6 @@ import type {
|
||||
ProcessedEnvelope,
|
||||
ProcessedPreview,
|
||||
ProcessedSent,
|
||||
ProcessedSyncMessage,
|
||||
UnprocessedType,
|
||||
} from './Types.d.ts';
|
||||
import type {
|
||||
@@ -173,6 +174,8 @@ import {
|
||||
MessageRequestResponseSource,
|
||||
} from '../types/MessageRequestResponseEvent.std.js';
|
||||
|
||||
import { toNumber } from '../util/toNumber.std.js';
|
||||
|
||||
const { isBoolean, isNumber, isString, noop, omit } = lodash;
|
||||
|
||||
const log = createLogger('MessageReceiver');
|
||||
@@ -412,12 +415,11 @@ export default class MessageReceiver
|
||||
const plaintext = request.body;
|
||||
|
||||
const decoded = Proto.Envelope.decode(plaintext);
|
||||
const serverTimestamp = decoded.serverTimestamp?.toNumber() ?? 0;
|
||||
const serverTimestamp = toNumber(decoded.serverTimestamp) ?? 0;
|
||||
|
||||
const ourAci = this.#storage.user.getCheckedAci();
|
||||
|
||||
const { content } = decoded;
|
||||
strictAssert(content != null, 'Content is required for envelopes');
|
||||
|
||||
const envelope: ProcessedEnvelope = {
|
||||
// Make non-private envelope IDs dashless so they don't get redacted
|
||||
@@ -432,7 +434,9 @@ export default class MessageReceiver
|
||||
),
|
||||
|
||||
// Proto.Envelope fields
|
||||
type: decoded.type ?? Proto.Envelope.Type.UNKNOWN,
|
||||
type: isKnownProtoEnumMember(Proto.Envelope.Type, decoded.type)
|
||||
? decoded.type
|
||||
: Proto.Envelope.Type.UNKNOWN,
|
||||
source: undefined,
|
||||
sourceServiceId: fromServiceIdBinaryOrString(
|
||||
decoded.sourceServiceIdBinary,
|
||||
@@ -451,8 +455,8 @@ export default class MessageReceiver
|
||||
decoded.updatedPni,
|
||||
'MessageReceiver.handleRequest.updatedPni'
|
||||
),
|
||||
timestamp: decoded.clientTimestamp?.toNumber() ?? 0,
|
||||
content,
|
||||
timestamp: toNumber(decoded.clientTimestamp) ?? 0,
|
||||
content: content ?? new Uint8Array(0),
|
||||
serverGuid:
|
||||
(Bytes.isNotEmpty(decoded.serverGuidBinary)
|
||||
? bytesToUuid(decoded.serverGuidBinary)
|
||||
@@ -1473,7 +1477,10 @@ export default class MessageReceiver
|
||||
let inProgressMessageType = '';
|
||||
try {
|
||||
const content = Proto.Content.decode(plaintext);
|
||||
if (!wasEncrypted && Bytes.isEmpty(content.decryptionErrorMessage)) {
|
||||
if (
|
||||
!wasEncrypted &&
|
||||
Bytes.isEmpty(content.content?.decryptionErrorMessage)
|
||||
) {
|
||||
log.warn(
|
||||
`${logId}: dropping plaintext envelope without decryption error message`
|
||||
);
|
||||
@@ -1504,8 +1511,8 @@ export default class MessageReceiver
|
||||
}
|
||||
|
||||
isGroupV2 =
|
||||
Boolean(content.dataMessage?.groupV2) ||
|
||||
Boolean(content.storyMessage?.group);
|
||||
Boolean(content.content?.dataMessage?.groupV2) ||
|
||||
Boolean(content.content?.storyMessage?.group);
|
||||
|
||||
if (
|
||||
wasEncrypted &&
|
||||
@@ -1520,12 +1527,12 @@ export default class MessageReceiver
|
||||
);
|
||||
}
|
||||
|
||||
const isStoryReply = Boolean(content.dataMessage?.storyContext);
|
||||
const isStoryReply = Boolean(content.content?.dataMessage?.storyContext);
|
||||
const isGroupStoryReply = Boolean(
|
||||
isStoryReply && content.dataMessage?.groupV2
|
||||
isStoryReply && content.content?.dataMessage?.groupV2
|
||||
);
|
||||
const isStory = Boolean(content.storyMessage);
|
||||
const isDeleteForEveryone = Boolean(content.dataMessage?.delete);
|
||||
const isStory = Boolean(content.content?.storyMessage);
|
||||
const isDeleteForEveryone = Boolean(content.content?.dataMessage?.delete);
|
||||
|
||||
if (
|
||||
envelope.story &&
|
||||
@@ -1581,12 +1588,12 @@ export default class MessageReceiver
|
||||
|
||||
// Some sync messages have to be fully processed in the middle of
|
||||
// decryption queue since subsequent envelopes use their key material.
|
||||
const { syncMessage } = content;
|
||||
if (wasEncrypted && syncMessage?.pniChangeNumber) {
|
||||
const syncMessage = content.content?.syncMessage;
|
||||
if (wasEncrypted && syncMessage?.content?.pniChangeNumber) {
|
||||
inProgressMessageType = 'pni change number';
|
||||
await this.#handlePNIChangeNumber(
|
||||
envelope,
|
||||
syncMessage.pniChangeNumber
|
||||
syncMessage.content.pniChangeNumber
|
||||
);
|
||||
this.#removeFromCache(envelope);
|
||||
return { plaintext: undefined, envelope };
|
||||
@@ -2111,9 +2118,9 @@ export default class MessageReceiver
|
||||
const ev = new SentEvent(
|
||||
{
|
||||
envelopeId: envelope.id,
|
||||
destinationE164: dropNull(destinationE164),
|
||||
destinationE164: destinationE164 ?? '',
|
||||
destinationServiceId,
|
||||
timestamp: timestamp.toNumber(),
|
||||
timestamp: toNumber(timestamp),
|
||||
serverTimestamp: envelope.serverTimestamp,
|
||||
device: envelope.sourceDevice,
|
||||
unidentifiedStatus,
|
||||
@@ -2121,7 +2128,7 @@ export default class MessageReceiver
|
||||
isRecipientUpdate: Boolean(isRecipientUpdate),
|
||||
receivedAtCounter: envelope.receivedAtCounter,
|
||||
receivedAtDate: envelope.receivedAtDate,
|
||||
expirationStartTimestamp: expirationStartTimestamp?.toNumber(),
|
||||
expirationStartTimestamp: toNumber(expirationStartTimestamp) ?? 0,
|
||||
},
|
||||
this.#removeFromCache.bind(this, envelope)
|
||||
);
|
||||
@@ -2130,7 +2137,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleStoryMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
msg: Proto.IStoryMessage,
|
||||
msg: Proto.StoryMessage,
|
||||
sentMessage?: ProcessedSent
|
||||
): Promise<void> {
|
||||
const envelopeId = getEnvelopeId(envelope);
|
||||
@@ -2155,16 +2162,17 @@ export default class MessageReceiver
|
||||
const attachments: Array<ProcessedAttachment> = [];
|
||||
let preview: ReadonlyArray<ProcessedPreview> | undefined;
|
||||
|
||||
if (msg.fileAttachment) {
|
||||
const attachment = processAttachment(msg.fileAttachment);
|
||||
if (msg.attachment?.fileAttachment != null) {
|
||||
const attachment = processAttachment(msg.attachment.fileAttachment);
|
||||
attachments.push(attachment);
|
||||
}
|
||||
|
||||
if (msg.textAttachment) {
|
||||
if (msg.attachment?.textAttachment != null) {
|
||||
// If a text attachment has a link preview we remove it from the
|
||||
// textAttachment data structure and instead process the preview and add
|
||||
// it as a "preview" property for the message attributes.
|
||||
const { text, preview: unprocessedPreview } = msg.textAttachment;
|
||||
const { text, preview: unprocessedPreview } =
|
||||
msg.attachment.textAttachment;
|
||||
if (unprocessedPreview) {
|
||||
preview = processPreview([unprocessedPreview]);
|
||||
} else if (!text) {
|
||||
@@ -2174,10 +2182,18 @@ export default class MessageReceiver
|
||||
attachments.push({
|
||||
size: text?.length ?? 0,
|
||||
contentType: TEXT_ATTACHMENT,
|
||||
textAttachment: omit(msg.textAttachment, 'preview'),
|
||||
textAttachment: {
|
||||
...omit(msg.attachment.textAttachment, 'preview'),
|
||||
textStyle: isKnownProtoEnumMember(
|
||||
Proto.TextAttachment.Style,
|
||||
msg.attachment.textAttachment.textStyle
|
||||
)
|
||||
? msg.attachment.textAttachment.textStyle
|
||||
: 0,
|
||||
},
|
||||
blurHash: generateBlurHash(
|
||||
(msg.textAttachment.color ||
|
||||
msg.textAttachment.gradient?.startColor) ??
|
||||
(msg.attachment.textAttachment.background?.color ||
|
||||
msg.attachment.textAttachment.background?.gradient?.startColor) ??
|
||||
undefined
|
||||
),
|
||||
});
|
||||
@@ -2204,7 +2220,9 @@ export default class MessageReceiver
|
||||
const message: ProcessedDataMessage = {
|
||||
attachments,
|
||||
|
||||
bodyRanges: filterAndClean(msg.bodyRanges),
|
||||
bodyRanges: filterAndClean(
|
||||
msg.bodyRanges.map(processBodyRange).filter(isNotNil)
|
||||
),
|
||||
preview,
|
||||
canReplyToStory: Boolean(msg.allowsReplies),
|
||||
expireTimer: DurationInSeconds.DAY,
|
||||
@@ -2238,6 +2256,8 @@ export default class MessageReceiver
|
||||
return {
|
||||
destinationServiceId,
|
||||
isAllowedToReplyToStory: Boolean(isAllowedToReply),
|
||||
destinationPniIdentityKey: undefined,
|
||||
unidentified: false,
|
||||
};
|
||||
})
|
||||
.filter(isNotNil),
|
||||
@@ -2298,6 +2318,7 @@ export default class MessageReceiver
|
||||
destinationServiceId,
|
||||
isAllowedToReplyToStory:
|
||||
isAllowedToReply.has(destinationServiceId),
|
||||
unidentified: false,
|
||||
})
|
||||
),
|
||||
message,
|
||||
@@ -2341,7 +2362,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleEditMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
msg: Proto.IEditMessage
|
||||
msg: Proto.EditMessage
|
||||
): Promise<void> {
|
||||
const logId = `handleEditMessage(${getEnvelopeId(envelope)})`;
|
||||
log.info(logId);
|
||||
@@ -2391,7 +2412,7 @@ export default class MessageReceiver
|
||||
),
|
||||
message: {
|
||||
...message,
|
||||
editedMessageTimestamp: msg.targetSentTimestamp.toNumber(),
|
||||
editedMessageTimestamp: toNumber(msg.targetSentTimestamp),
|
||||
},
|
||||
receivedAtCounter: envelope.receivedAtCounter,
|
||||
receivedAtDate: envelope.receivedAtDate,
|
||||
@@ -2403,7 +2424,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleDataMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
msg: Proto.IDataMessage
|
||||
msg: Proto.DataMessage
|
||||
): Promise<void> {
|
||||
const logId = `handleDataMessage/${getEnvelopeId(envelope)}`;
|
||||
log.info(logId);
|
||||
@@ -2577,47 +2598,50 @@ export default class MessageReceiver
|
||||
const envelope = await this.#maybeUpdateTimestamp(incomingEnvelope);
|
||||
|
||||
if (
|
||||
content.decryptionErrorMessage &&
|
||||
Bytes.isNotEmpty(content.decryptionErrorMessage)
|
||||
content.content?.decryptionErrorMessage &&
|
||||
Bytes.isNotEmpty(content.content.decryptionErrorMessage)
|
||||
) {
|
||||
this.#handleDecryptionError(envelope, content.decryptionErrorMessage);
|
||||
return;
|
||||
}
|
||||
if (content.syncMessage) {
|
||||
await this.#handleSyncMessage(
|
||||
this.#handleDecryptionError(
|
||||
envelope,
|
||||
processSyncMessage(content.syncMessage)
|
||||
content.content.decryptionErrorMessage
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (content.dataMessage) {
|
||||
await this.#handleDataMessage(envelope, content.dataMessage);
|
||||
if (content.content?.syncMessage) {
|
||||
await this.#handleSyncMessage(envelope, content.content.syncMessage);
|
||||
return;
|
||||
}
|
||||
if (content.nullMessage) {
|
||||
if (content.content?.dataMessage) {
|
||||
await this.#handleDataMessage(envelope, content.content.dataMessage);
|
||||
return;
|
||||
}
|
||||
if (content.content?.nullMessage) {
|
||||
this.#handleNullMessage(envelope);
|
||||
return;
|
||||
}
|
||||
if (content.callMessage) {
|
||||
await this.#handleCallingMessage(envelope, content.callMessage);
|
||||
if (content.content?.callMessage) {
|
||||
await this.#handleCallingMessage(envelope, content.content.callMessage);
|
||||
return;
|
||||
}
|
||||
if (content.receiptMessage) {
|
||||
await this.#handleReceiptMessage(envelope, content.receiptMessage);
|
||||
if (content.content?.receiptMessage) {
|
||||
await this.#handleReceiptMessage(
|
||||
envelope,
|
||||
content.content.receiptMessage
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (content.typingMessage) {
|
||||
this.#handleTypingMessage(envelope, content.typingMessage);
|
||||
if (content.content?.typingMessage) {
|
||||
this.#handleTypingMessage(envelope, content.content.typingMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.storyMessage) {
|
||||
await this.#handleStoryMessage(envelope, content.storyMessage);
|
||||
if (content.content?.storyMessage) {
|
||||
await this.#handleStoryMessage(envelope, content.content.storyMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (content.editMessage) {
|
||||
await this.#handleEditMessage(envelope, content.editMessage);
|
||||
if (content.content?.editMessage) {
|
||||
await this.#handleEditMessage(envelope, content.content.editMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2710,7 +2734,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handlePniSignatureMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
pniSignatureMessage: Proto.IPniSignatureMessage
|
||||
pniSignatureMessage: Proto.PniSignatureMessage
|
||||
): Promise<void> {
|
||||
const envelopeId = getEnvelopeId(envelope);
|
||||
const logId = `handlePniSignatureMessage/${envelopeId}`;
|
||||
@@ -2754,7 +2778,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleCallingMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
callingMessage: Proto.ICallMessage
|
||||
callingMessage: Proto.CallMessage
|
||||
): Promise<void> {
|
||||
logUnexpectedUrgentValue(envelope, 'callingMessage');
|
||||
|
||||
@@ -2778,7 +2802,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleReceiptMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
receiptMessage: Proto.IReceiptMessage
|
||||
receiptMessage: Proto.ReceiptMessage
|
||||
): Promise<void> {
|
||||
strictAssert(receiptMessage.timestamp, 'Receipt message without timestamp');
|
||||
|
||||
@@ -2808,7 +2832,7 @@ export default class MessageReceiver
|
||||
const logId = getEnvelopeId(envelope);
|
||||
|
||||
const receipts = receiptMessage.timestamp.map(rawTimestamp => ({
|
||||
timestamp: rawTimestamp?.toNumber(),
|
||||
timestamp: toNumber(rawTimestamp),
|
||||
source: envelope.source,
|
||||
sourceServiceId: envelope.sourceServiceId,
|
||||
sourceDevice: envelope.sourceDevice,
|
||||
@@ -2828,7 +2852,7 @@ export default class MessageReceiver
|
||||
|
||||
#handleTypingMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
typingMessage: Proto.ITypingMessage
|
||||
typingMessage: Proto.TypingMessage
|
||||
): void {
|
||||
this.#removeFromCache(envelope);
|
||||
|
||||
@@ -2836,7 +2860,7 @@ export default class MessageReceiver
|
||||
|
||||
if (envelope.timestamp && typingMessage.timestamp) {
|
||||
const envelopeTimestamp = envelope.timestamp;
|
||||
const typingTimestamp = typingMessage.timestamp?.toNumber();
|
||||
const typingTimestamp = toNumber(typingMessage.timestamp);
|
||||
|
||||
if (typingTimestamp !== envelopeTimestamp) {
|
||||
log.warn(
|
||||
@@ -2874,7 +2898,7 @@ export default class MessageReceiver
|
||||
typing: {
|
||||
groupV2Id: groupV2IdString,
|
||||
typingMessage,
|
||||
timestamp: timestamp?.toNumber() ?? Date.now(),
|
||||
timestamp: toNumber(timestamp) ?? Date.now(),
|
||||
started: action === Proto.TypingMessage.Action.STARTED,
|
||||
stopped: action === Proto.TypingMessage.Action.STOPPED,
|
||||
},
|
||||
@@ -2891,7 +2915,7 @@ export default class MessageReceiver
|
||||
}
|
||||
|
||||
#isInvalidGroupData(
|
||||
message: Proto.IDataMessage,
|
||||
message: Proto.DataMessage,
|
||||
envelope: ProcessedEnvelope
|
||||
): boolean {
|
||||
const { groupV2 } = message;
|
||||
@@ -2920,7 +2944,7 @@ export default class MessageReceiver
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#getGroupId(message: Proto.IDataMessage): string | undefined {
|
||||
#getGroupId(message: Proto.DataMessage): string | undefined {
|
||||
if (message.groupV2) {
|
||||
strictAssert(message.groupV2.masterKey, 'Missing groupV2.masterKey');
|
||||
const { id } = deriveGroupFields(message.groupV2.masterKey);
|
||||
@@ -2939,7 +2963,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleSyncMessage(
|
||||
envelope: UnsealedEnvelope,
|
||||
syncMessage: ProcessedSyncMessage
|
||||
syncMessage: Proto.SyncMessage
|
||||
): Promise<void> {
|
||||
const ourNumber = this.#storage.user.getNumber();
|
||||
const ourAci = this.#storage.user.getCheckedAci();
|
||||
@@ -2956,8 +2980,8 @@ export default class MessageReceiver
|
||||
if (envelope.sourceDevice == ourDeviceId) {
|
||||
throw new Error('Received sync message from our own device');
|
||||
}
|
||||
if (syncMessage.sent) {
|
||||
const sentMessage = syncMessage.sent;
|
||||
if (syncMessage.content?.sent) {
|
||||
const sentMessage = processSent(syncMessage.content.sent);
|
||||
|
||||
if (sentMessage.editMessage) {
|
||||
return this.#handleSentEditMessage(envelope, sentMessage);
|
||||
@@ -3016,23 +3040,23 @@ export default class MessageReceiver
|
||||
log.info(
|
||||
'sent message to',
|
||||
this.#getDestination(sentMessage),
|
||||
sentMessage.timestamp?.toNumber(),
|
||||
toNumber(sentMessage.timestamp),
|
||||
'from',
|
||||
getEnvelopeId(envelope)
|
||||
);
|
||||
|
||||
return this.#handleSentMessage(envelope, sentMessage);
|
||||
}
|
||||
if (syncMessage.contacts) {
|
||||
if (syncMessage.content?.contacts) {
|
||||
// Note: this method will download attachment and thus might block
|
||||
// message processing, but we would like to fully process contact sync
|
||||
// before moving on since it updates conversation state.
|
||||
return this.#handleContacts(envelope, syncMessage.contacts);
|
||||
return this.#handleContacts(envelope, syncMessage.content.contacts);
|
||||
}
|
||||
if (syncMessage.blocked) {
|
||||
return this.#handleBlocked(envelope, syncMessage.blocked);
|
||||
if (syncMessage.content?.blocked) {
|
||||
return this.#handleBlocked(envelope, syncMessage.content.blocked);
|
||||
}
|
||||
if (syncMessage.request) {
|
||||
if (syncMessage.content?.request) {
|
||||
log.info('Got SyncMessage Request');
|
||||
this.#removeFromCache(envelope);
|
||||
return;
|
||||
@@ -3040,13 +3064,16 @@ export default class MessageReceiver
|
||||
if (syncMessage.read && syncMessage.read.length) {
|
||||
return this.#handleRead(envelope, syncMessage.read);
|
||||
}
|
||||
if (syncMessage.verified) {
|
||||
if (syncMessage.content?.verified) {
|
||||
log.info('Got verified sync message, dropping');
|
||||
this.#removeFromCache(envelope);
|
||||
return;
|
||||
}
|
||||
if (syncMessage.configuration) {
|
||||
return this.#handleConfiguration(envelope, syncMessage.configuration);
|
||||
if (syncMessage.content?.configuration) {
|
||||
return this.#handleConfiguration(
|
||||
envelope,
|
||||
syncMessage.content.configuration
|
||||
);
|
||||
}
|
||||
if (
|
||||
syncMessage.stickerPackOperation &&
|
||||
@@ -3057,46 +3084,58 @@ export default class MessageReceiver
|
||||
syncMessage.stickerPackOperation
|
||||
);
|
||||
}
|
||||
if (syncMessage.viewOnceOpen) {
|
||||
return this.#handleViewOnceOpen(envelope, syncMessage.viewOnceOpen);
|
||||
}
|
||||
if (syncMessage.messageRequestResponse) {
|
||||
return this.#handleMessageRequestResponse(
|
||||
if (syncMessage.content?.viewOnceOpen) {
|
||||
return this.#handleViewOnceOpen(
|
||||
envelope,
|
||||
syncMessage.messageRequestResponse
|
||||
syncMessage.content.viewOnceOpen
|
||||
);
|
||||
}
|
||||
if (syncMessage.fetchLatest) {
|
||||
return this.#handleFetchLatest(envelope, syncMessage.fetchLatest);
|
||||
if (syncMessage.content?.messageRequestResponse) {
|
||||
return this.#handleMessageRequestResponse(
|
||||
envelope,
|
||||
syncMessage.content.messageRequestResponse
|
||||
);
|
||||
}
|
||||
if (syncMessage.keys) {
|
||||
return this.#handleKeys(envelope, syncMessage.keys);
|
||||
if (syncMessage.content?.fetchLatest) {
|
||||
return this.#handleFetchLatest(envelope, syncMessage.content.fetchLatest);
|
||||
}
|
||||
if (syncMessage.content?.keys) {
|
||||
return this.#handleKeys(envelope, syncMessage.content.keys);
|
||||
}
|
||||
if (syncMessage.viewed && syncMessage.viewed.length) {
|
||||
return this.#handleViewed(envelope, syncMessage.viewed);
|
||||
}
|
||||
if (syncMessage.callEvent) {
|
||||
return this.#handleCallEvent(envelope, syncMessage.callEvent);
|
||||
if (syncMessage.content?.callEvent) {
|
||||
return this.#handleCallEvent(envelope, syncMessage.content.callEvent);
|
||||
}
|
||||
if (syncMessage.callLinkUpdate) {
|
||||
return this.#handleCallLinkUpdate(envelope, syncMessage.callLinkUpdate);
|
||||
}
|
||||
if (syncMessage.callLogEvent) {
|
||||
return this.#handleCallLogEvent(envelope, syncMessage.callLogEvent);
|
||||
}
|
||||
if (syncMessage.deleteForMe) {
|
||||
return this.#handleDeleteForMeSync(envelope, syncMessage.deleteForMe);
|
||||
}
|
||||
if (syncMessage.deviceNameChange) {
|
||||
return this.#handleDeviceNameChangeSync(
|
||||
if (syncMessage.content?.callLinkUpdate) {
|
||||
return this.#handleCallLinkUpdate(
|
||||
envelope,
|
||||
syncMessage.deviceNameChange
|
||||
syncMessage.content.callLinkUpdate
|
||||
);
|
||||
}
|
||||
if (syncMessage.attachmentBackfillResponse) {
|
||||
if (syncMessage.content?.callLogEvent) {
|
||||
return this.#handleCallLogEvent(
|
||||
envelope,
|
||||
syncMessage.content.callLogEvent
|
||||
);
|
||||
}
|
||||
if (syncMessage.content?.deleteForMe) {
|
||||
return this.#handleDeleteForMeSync(
|
||||
envelope,
|
||||
syncMessage.content.deleteForMe
|
||||
);
|
||||
}
|
||||
if (syncMessage.content?.deviceNameChange) {
|
||||
return this.#handleDeviceNameChangeSync(
|
||||
envelope,
|
||||
syncMessage.content.deviceNameChange
|
||||
);
|
||||
}
|
||||
if (syncMessage.content?.attachmentBackfillResponse) {
|
||||
return this.#handleAttachmentBackfillResponse(
|
||||
envelope,
|
||||
syncMessage.attachmentBackfillResponse
|
||||
syncMessage.content.attachmentBackfillResponse
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3148,7 +3187,7 @@ export default class MessageReceiver
|
||||
const ev = new SentEvent(
|
||||
{
|
||||
envelopeId: envelope.id,
|
||||
destinationE164: dropNull(destinationE164),
|
||||
destinationE164: destinationE164 ?? '',
|
||||
destinationServiceId,
|
||||
timestamp: envelope.timestamp,
|
||||
serverTimestamp: envelope.serverTimestamp,
|
||||
@@ -3156,12 +3195,12 @@ export default class MessageReceiver
|
||||
unidentifiedStatus,
|
||||
message: {
|
||||
...message,
|
||||
editedMessageTimestamp: editMessage.targetSentTimestamp.toNumber(),
|
||||
editedMessageTimestamp: toNumber(editMessage.targetSentTimestamp),
|
||||
},
|
||||
isRecipientUpdate: Boolean(isRecipientUpdate),
|
||||
receivedAtCounter: envelope.receivedAtCounter,
|
||||
receivedAtDate: envelope.receivedAtDate,
|
||||
expirationStartTimestamp: expirationStartTimestamp?.toNumber(),
|
||||
expirationStartTimestamp: toNumber(expirationStartTimestamp) ?? 0,
|
||||
},
|
||||
this.#removeFromCache.bind(this, envelope)
|
||||
);
|
||||
@@ -3170,7 +3209,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleConfiguration(
|
||||
envelope: ProcessedEnvelope,
|
||||
configuration: Proto.SyncMessage.IConfiguration
|
||||
configuration: Proto.SyncMessage.Configuration
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('got configuration sync message', logId);
|
||||
@@ -3186,7 +3225,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleViewOnceOpen(
|
||||
envelope: ProcessedEnvelope,
|
||||
sync: Proto.SyncMessage.IViewOnceOpen
|
||||
sync: Proto.SyncMessage.ViewOnceOpen
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('got view once open sync message', logId);
|
||||
@@ -3200,7 +3239,7 @@ export default class MessageReceiver
|
||||
sync.senderAci,
|
||||
'handleViewOnceOpen.senderUuid'
|
||||
),
|
||||
timestamp: sync.timestamp?.toNumber(),
|
||||
timestamp: toNumber(sync.timestamp) ?? 0,
|
||||
envelopeTimestamp: envelope.timestamp,
|
||||
},
|
||||
this.#removeFromCache.bind(this, envelope)
|
||||
@@ -3211,7 +3250,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleMessageRequestResponse(
|
||||
envelope: ProcessedEnvelope,
|
||||
sync: Proto.SyncMessage.IMessageRequestResponse
|
||||
sync: Proto.SyncMessage.MessageRequestResponse
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('got message request response sync message', logId);
|
||||
@@ -3239,7 +3278,12 @@ export default class MessageReceiver
|
||||
sync.threadAci,
|
||||
'handleMessageRequestResponse.threadUuid'
|
||||
),
|
||||
messageRequestResponseType: sync.type,
|
||||
messageRequestResponseType: isKnownProtoEnumMember(
|
||||
Proto.SyncMessage.MessageRequestResponse.Type,
|
||||
sync.type
|
||||
)
|
||||
? sync.type
|
||||
: Proto.SyncMessage.MessageRequestResponse.Type.UNKNOWN,
|
||||
groupV2Id: groupV2IdString,
|
||||
receivedAtCounter: envelope.receivedAtCounter,
|
||||
receivedAtMs: envelope.receivedAtDate,
|
||||
@@ -3253,7 +3297,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleFetchLatest(
|
||||
envelope: ProcessedEnvelope,
|
||||
sync: Proto.SyncMessage.IFetchLatest
|
||||
sync: Proto.SyncMessage.FetchLatest
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('got fetch latest sync message', logId);
|
||||
@@ -3270,7 +3314,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleKeys(
|
||||
envelope: ProcessedEnvelope,
|
||||
sync: Proto.SyncMessage.IKeys
|
||||
sync: Proto.SyncMessage.Keys
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('got keys sync message', logId);
|
||||
@@ -3300,7 +3344,7 @@ export default class MessageReceiver
|
||||
lastResortKyberPreKey,
|
||||
registrationId,
|
||||
newE164,
|
||||
}: Proto.SyncMessage.IPniChangeNumber
|
||||
}: Proto.SyncMessage.PniChangeNumber
|
||||
): Promise<void> {
|
||||
const ourAci = this.#storage.user.getCheckedAci();
|
||||
|
||||
@@ -3345,7 +3389,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleStickerPackOperation(
|
||||
envelope: ProcessedEnvelope,
|
||||
operations: Array<Proto.SyncMessage.IStickerPackOperation>
|
||||
operations: Array<Proto.SyncMessage.StickerPackOperation>
|
||||
): Promise<void> {
|
||||
const ENUM = Proto.SyncMessage.StickerPackOperation.Type;
|
||||
const logId = getEnvelopeId(envelope);
|
||||
@@ -3369,7 +3413,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleRead(
|
||||
envelope: ProcessedEnvelope,
|
||||
read: Array<Proto.SyncMessage.IRead>
|
||||
read: Array<Proto.SyncMessage.Read>
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleRead', logId);
|
||||
@@ -3382,7 +3426,7 @@ export default class MessageReceiver
|
||||
return {
|
||||
envelopeId: envelope.id,
|
||||
envelopeTimestamp: envelope.timestamp,
|
||||
timestamp: timestamp?.toNumber(),
|
||||
timestamp: toNumber(timestamp) ?? 0,
|
||||
senderAci: fromAciUuidBytesOrString(
|
||||
senderAciBinary,
|
||||
rawSenderAci,
|
||||
@@ -3404,7 +3448,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleViewed(
|
||||
envelope: ProcessedEnvelope,
|
||||
viewed: ReadonlyArray<Proto.SyncMessage.IViewed>
|
||||
viewed: ReadonlyArray<Proto.SyncMessage.Viewed>
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleViewed', logId);
|
||||
@@ -3415,7 +3459,7 @@ export default class MessageReceiver
|
||||
const { timestamp, senderAci: rawSenderAci, senderAciBinary } = data;
|
||||
|
||||
return {
|
||||
timestamp: timestamp?.toNumber(),
|
||||
timestamp: toNumber(timestamp) ?? 0,
|
||||
senderAci: fromAciUuidBytesOrString(
|
||||
senderAciBinary,
|
||||
rawSenderAci,
|
||||
@@ -3437,7 +3481,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleCallEvent(
|
||||
envelope: ProcessedEnvelope,
|
||||
callEvent: Proto.SyncMessage.ICallEvent
|
||||
callEvent: Proto.SyncMessage.CallEvent
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleCallEvent', logId);
|
||||
@@ -3466,7 +3510,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleCallLinkUpdate(
|
||||
envelope: ProcessedEnvelope,
|
||||
callLinkUpdate: Proto.SyncMessage.ICallLinkUpdate
|
||||
callLinkUpdate: Proto.SyncMessage.CallLinkUpdate
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleCallLinkUpdate', logId);
|
||||
@@ -3509,7 +3553,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleCallLogEvent(
|
||||
envelope: ProcessedEnvelope,
|
||||
callLogEvent: Proto.SyncMessage.ICallLogEvent
|
||||
callLogEvent: Proto.SyncMessage.CallLogEvent
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleCallLogEvent', logId);
|
||||
@@ -3534,7 +3578,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleDeleteForMeSync(
|
||||
envelope: ProcessedEnvelope,
|
||||
deleteSync: Proto.SyncMessage.IDeleteForMe
|
||||
deleteSync: Proto.SyncMessage.DeleteForMe
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleDeleteForMeSync', logId);
|
||||
@@ -3733,7 +3777,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleAttachmentBackfillResponse(
|
||||
envelope: ProcessedEnvelope,
|
||||
response: Proto.SyncMessage.IAttachmentBackfillResponse
|
||||
response: Proto.SyncMessage.AttachmentBackfillResponse
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info('handleAttachmentBackfillResponse', logId);
|
||||
@@ -3770,14 +3814,19 @@ export default class MessageReceiver
|
||||
);
|
||||
|
||||
let eventData: AttachmentBackfillResponseSyncEventData;
|
||||
if (response.error != null) {
|
||||
if (response.data?.error != null) {
|
||||
eventData = {
|
||||
error: response.error,
|
||||
error: isKnownProtoEnumMember(
|
||||
Proto.SyncMessage.AttachmentBackfillResponse.Error,
|
||||
response.data.error
|
||||
)
|
||||
? response.data.error
|
||||
: 0,
|
||||
targetMessage,
|
||||
targetConversation,
|
||||
};
|
||||
} else {
|
||||
const { attachments } = response;
|
||||
const attachments = response.data?.attachments;
|
||||
strictAssert(
|
||||
attachments != null,
|
||||
'MessageReceiver.handleAttachmentBackfillResponse: no attachments'
|
||||
@@ -3811,7 +3860,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleDeviceNameChangeSync(
|
||||
envelope: ProcessedEnvelope,
|
||||
deviceNameChange: Proto.SyncMessage.IDeviceNameChange
|
||||
deviceNameChange: Proto.SyncMessage.DeviceNameChange
|
||||
): Promise<void> {
|
||||
const logId = `handleDeviceNameChangeSync: ${getEnvelopeId(envelope)}`;
|
||||
log.info(logId);
|
||||
@@ -3844,7 +3893,7 @@ export default class MessageReceiver
|
||||
|
||||
async #handleContacts(
|
||||
envelope: ProcessedEnvelope,
|
||||
contactSyncProto: Proto.SyncMessage.IContacts
|
||||
contactSyncProto: Proto.SyncMessage.Contacts
|
||||
): Promise<void> {
|
||||
const logId = getEnvelopeId(envelope);
|
||||
log.info(`handleContacts ${logId}`);
|
||||
@@ -3870,7 +3919,7 @@ export default class MessageReceiver
|
||||
// proper before/after logic can be applied within that function.
|
||||
async #handleBlocked(
|
||||
envelope: ProcessedEnvelope,
|
||||
blocked: Proto.SyncMessage.IBlocked
|
||||
blocked: Proto.SyncMessage.Blocked
|
||||
): Promise<void> {
|
||||
const logId = `handleBlocked(${getEnvelopeId(envelope)})`;
|
||||
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
|
||||
@@ -4042,7 +4091,7 @@ export default class MessageReceiver
|
||||
|
||||
#processDecrypted(
|
||||
envelope: ProcessedEnvelope,
|
||||
decrypted: Proto.IDataMessage
|
||||
decrypted: Proto.DataMessage
|
||||
): ProcessedDataMessage {
|
||||
return processDataMessage(decrypted, envelope.timestamp);
|
||||
}
|
||||
@@ -4078,10 +4127,14 @@ function envelopeTypeToCiphertextType(type: number | undefined): number {
|
||||
}
|
||||
|
||||
function processAddressableMessage(
|
||||
target: Proto.IAddressableMessage,
|
||||
target: Proto.AddressableMessage,
|
||||
logId: string
|
||||
): AddressableMessage | undefined {
|
||||
const sentAt = target.sentTimestamp?.toNumber();
|
||||
if (target.author == null) {
|
||||
log.error(`${logId}/processAddressableMessage: no author`);
|
||||
return undefined;
|
||||
}
|
||||
const sentAt = toNumber(target.sentTimestamp);
|
||||
if (!isNumber(sentAt)) {
|
||||
log.warn(
|
||||
`${logId}/processAddressableMessage: No sentTimestamp found! Dropping AddressableMessage.`
|
||||
@@ -4089,7 +4142,16 @@ function processAddressableMessage(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { authorServiceId: rawAuthorServiceId, authorServiceIdBinary } = target;
|
||||
if (target.author.authorE164 != null) {
|
||||
return {
|
||||
type: 'e164' as const,
|
||||
authorE164: target.author.authorE164,
|
||||
sentAt,
|
||||
};
|
||||
}
|
||||
|
||||
const { authorServiceId: rawAuthorServiceId, authorServiceIdBinary } =
|
||||
target.author;
|
||||
|
||||
const authorServiceId = fromServiceIdBinaryOrString(
|
||||
authorServiceIdBinary,
|
||||
@@ -4117,13 +4179,6 @@ function processAddressableMessage(
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
if (target.authorE164) {
|
||||
return {
|
||||
type: 'e164' as const,
|
||||
authorE164: target.authorE164,
|
||||
sentAt,
|
||||
};
|
||||
}
|
||||
|
||||
log.warn(
|
||||
`${logId}/processAddressableMessage: No author field found! Dropping AddressableMessage.`
|
||||
@@ -4132,15 +4187,19 @@ function processAddressableMessage(
|
||||
}
|
||||
|
||||
function processConversationIdentifier(
|
||||
target: Proto.IConversationIdentifier,
|
||||
target: Proto.ConversationIdentifier,
|
||||
logId: string
|
||||
): ConversationIdentifier | undefined {
|
||||
if (target.identifier == null) {
|
||||
log.error(`${logId}/processConversationIdentifier: no identifier`);
|
||||
return undefined;
|
||||
}
|
||||
const {
|
||||
threadServiceId: rawThreadServiceId,
|
||||
threadServiceIdBinary,
|
||||
threadGroupId,
|
||||
threadE164,
|
||||
} = target;
|
||||
} = target.identifier;
|
||||
|
||||
const threadServiceId = fromServiceIdBinaryOrString(
|
||||
threadServiceIdBinary,
|
||||
@@ -4186,13 +4245,20 @@ function processConversationIdentifier(
|
||||
}
|
||||
|
||||
function processBackfilledAttachment(
|
||||
data: Proto.SyncMessage.AttachmentBackfillResponse.IAttachmentData
|
||||
data: Proto.SyncMessage.AttachmentBackfillResponse.AttachmentData
|
||||
): AttachmentBackfillAttachmentType {
|
||||
if (data.status != null) {
|
||||
return { status: data.status };
|
||||
if (data.data?.status != null) {
|
||||
return {
|
||||
status: isKnownProtoEnumMember(
|
||||
Proto.SyncMessage.AttachmentBackfillResponse.AttachmentData.Status,
|
||||
data.data.status
|
||||
)
|
||||
? data.data.status
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const attachment = processAttachment(data.attachment);
|
||||
const attachment = processAttachment(data.data?.attachment);
|
||||
strictAssert(
|
||||
attachment != null,
|
||||
'MessageReceiver.handleAttachmentBackfillResponse: ' +
|
||||
|
||||
@@ -8,14 +8,12 @@
|
||||
import lodash from 'lodash';
|
||||
|
||||
import { z } from 'zod';
|
||||
import type {
|
||||
CiphertextMessage,
|
||||
PlaintextContent,
|
||||
} from '@signalapp/libsignal-client';
|
||||
import type { CiphertextMessage } from '@signalapp/libsignal-client';
|
||||
import {
|
||||
ErrorCode,
|
||||
LibSignalErrorBase,
|
||||
CiphertextMessageType,
|
||||
PlaintextContent,
|
||||
ProtocolAddress,
|
||||
sealedSenderEncrypt,
|
||||
SenderCertificate,
|
||||
@@ -129,7 +127,7 @@ export default class OutgoingMessage {
|
||||
|
||||
serviceIds: ReadonlyArray<ServiceIdString>;
|
||||
|
||||
message: Proto.Content | PlaintextContent;
|
||||
message: Proto.Content.Params | PlaintextContent;
|
||||
|
||||
callback: (result: CallbackResultType) => void;
|
||||
|
||||
@@ -177,20 +175,14 @@ export default class OutgoingMessage {
|
||||
contentHint: number;
|
||||
groupId: string | undefined;
|
||||
serviceIds: ReadonlyArray<ServiceIdString>;
|
||||
message: Proto.Content | Proto.DataMessage | PlaintextContent;
|
||||
message: Proto.Content.Params | PlaintextContent;
|
||||
options?: OutgoingMessageOptionsType;
|
||||
sendLogCallback?: SendLogCallbackType;
|
||||
story?: boolean;
|
||||
timestamp: number;
|
||||
urgent: boolean;
|
||||
}) {
|
||||
if (message instanceof Proto.DataMessage) {
|
||||
const content = new Proto.Content();
|
||||
content.dataMessage = message;
|
||||
this.message = content;
|
||||
} else {
|
||||
this.message = message;
|
||||
}
|
||||
this.message = message;
|
||||
|
||||
this.timestamp = timestamp;
|
||||
this.serviceIds = serviceIds;
|
||||
@@ -222,17 +214,13 @@ export default class OutgoingMessage {
|
||||
let editMessage: Uint8Array | undefined;
|
||||
let hasPniSignatureMessage = false;
|
||||
|
||||
if (proto instanceof Proto.Content) {
|
||||
if (proto.dataMessage) {
|
||||
dataMessage = Proto.DataMessage.encode(proto.dataMessage).finish();
|
||||
} else if (proto.editMessage) {
|
||||
editMessage = Proto.EditMessage.encode(proto.editMessage).finish();
|
||||
if (!(proto instanceof PlaintextContent)) {
|
||||
if (proto.content?.dataMessage) {
|
||||
dataMessage = Proto.DataMessage.encode(proto.content.dataMessage);
|
||||
} else if (proto.content?.editMessage) {
|
||||
editMessage = Proto.EditMessage.encode(proto.content.editMessage);
|
||||
}
|
||||
hasPniSignatureMessage = Boolean(proto.pniSignatureMessage);
|
||||
} else if (proto instanceof Proto.DataMessage) {
|
||||
dataMessage = Proto.DataMessage.encode(proto).finish();
|
||||
} else if (proto instanceof Proto.EditMessage) {
|
||||
editMessage = Proto.EditMessage.encode(proto).finish();
|
||||
}
|
||||
|
||||
this.callback({
|
||||
@@ -371,21 +359,21 @@ export default class OutgoingMessage {
|
||||
if (!this.plaintext) {
|
||||
const { message } = this;
|
||||
|
||||
if (message instanceof Proto.Content) {
|
||||
this.plaintext = padMessage(Proto.Content.encode(message).finish());
|
||||
} else {
|
||||
if (message instanceof PlaintextContent) {
|
||||
this.plaintext = message.serialize();
|
||||
} else {
|
||||
this.plaintext = padMessage(Proto.Content.encode(message));
|
||||
}
|
||||
}
|
||||
return this.plaintext;
|
||||
}
|
||||
|
||||
getContentProtoBytes(): Uint8Array | undefined {
|
||||
if (this.message instanceof Proto.Content) {
|
||||
return new Uint8Array(Proto.Content.encode(this.message).finish());
|
||||
if (this.message instanceof PlaintextContent) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return Proto.Content.encode(this.message);
|
||||
}
|
||||
|
||||
async getCiphertextMessage({
|
||||
@@ -399,16 +387,16 @@ export default class OutgoingMessage {
|
||||
}): Promise<CiphertextMessage> {
|
||||
const { message } = this;
|
||||
|
||||
if (message instanceof Proto.Content) {
|
||||
return signalEncrypt(
|
||||
this.getPlaintext(),
|
||||
protocolAddress,
|
||||
sessionStore,
|
||||
identityKeyStore
|
||||
);
|
||||
if (message instanceof PlaintextContent) {
|
||||
return message.asCiphertextMessage();
|
||||
}
|
||||
|
||||
return message.asCiphertextMessage();
|
||||
return signalEncrypt(
|
||||
this.getPlaintext(),
|
||||
protocolAddress,
|
||||
sessionStore,
|
||||
identityKeyStore
|
||||
);
|
||||
}
|
||||
|
||||
async doSendMessage(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
34
ts/textsecure/Types.d.ts
vendored
34
ts/textsecure/Types.d.ts
vendored
@@ -16,6 +16,7 @@ import type { MIMEType } from '../types/MIME.std.js';
|
||||
import type { DurationInSeconds } from '../util/durations/index.std.js';
|
||||
import type { AnyPaymentEvent } from '../types/Payment.std.js';
|
||||
import type { RawBodyRange } from '../types/BodyRange.std.js';
|
||||
import type { StoryMessageRecipientsType } from '../types/Stories.std.js';
|
||||
|
||||
export {
|
||||
IdentityKeyType,
|
||||
@@ -158,7 +159,7 @@ export type ProcessedAvatar = {
|
||||
isProfile: boolean;
|
||||
};
|
||||
|
||||
export type ProcessedContact = Omit<Proto.DataMessage.IContact, 'avatar'> & {
|
||||
export type ProcessedContact = Omit<Proto.DataMessage.Contact, 'avatar'> & {
|
||||
avatar?: ProcessedAvatar;
|
||||
};
|
||||
|
||||
@@ -219,7 +220,7 @@ export type ProcessedAdminDelete = Readonly<{
|
||||
|
||||
export type ProcessedBodyRange = RawBodyRange;
|
||||
|
||||
export type ProcessedGroupCallUpdate = Proto.DataMessage.IGroupCallUpdate;
|
||||
export type ProcessedGroupCallUpdate = Proto.DataMessage.GroupCallUpdate;
|
||||
|
||||
export type ProcessedGiftBadge = {
|
||||
expiration: number;
|
||||
@@ -273,37 +274,26 @@ export type ProcessedDataMessage = {
|
||||
canReplyToStory?: boolean;
|
||||
};
|
||||
|
||||
export type ProcessedUnidentifiedDeliveryStatus = Omit<
|
||||
Proto.SyncMessage.Sent.IUnidentifiedDeliveryStatus,
|
||||
'destinationAci' | 'destinationPni'
|
||||
> & {
|
||||
export type ProcessedUnidentifiedDeliveryStatus = Readonly<{
|
||||
destinationServiceId?: ServiceIdString;
|
||||
isAllowedToReplyToStory?: boolean;
|
||||
};
|
||||
|
||||
export type ProcessedStoryMessageRecipient = Omit<
|
||||
Proto.SyncMessage.Sent.IStoryMessageRecipient,
|
||||
'destinationAci' | 'destinationPni'
|
||||
> & {
|
||||
destinationServiceId?: ServiceIdString;
|
||||
};
|
||||
destinationPniIdentityKey?: Uint8Array;
|
||||
unidentified?: boolean;
|
||||
}>;
|
||||
|
||||
export type ProcessedSent = Omit<
|
||||
Proto.SyncMessage.ISent,
|
||||
Proto.SyncMessage.Sent,
|
||||
| '$unknown'
|
||||
| 'destinationId'
|
||||
| 'unidentifiedStatus'
|
||||
| 'storyMessageRecipients'
|
||||
| 'destinationAci'
|
||||
| 'destinationPni'
|
||||
| 'destinationServiceId'
|
||||
| 'destinationServiceIdBinary'
|
||||
> & {
|
||||
destinationId?: string;
|
||||
destinationServiceId?: ServiceIdString;
|
||||
unidentifiedStatus?: Array<ProcessedUnidentifiedDeliveryStatus>;
|
||||
storyMessageRecipients?: Array<ProcessedStoryMessageRecipient>;
|
||||
};
|
||||
|
||||
export type ProcessedSyncMessage = Omit<Proto.ISyncMessage, 'sent'> & {
|
||||
sent?: ProcessedSent;
|
||||
storyMessageRecipients?: StoryMessageRecipientsType;
|
||||
};
|
||||
|
||||
export type CustomError = Error & {
|
||||
|
||||
@@ -3427,9 +3427,9 @@ export async function callLinkCreateAuth(
|
||||
}
|
||||
|
||||
export async function submitCallQualitySurvey(
|
||||
survey: Proto.ISubmitCallQualitySurveyRequest
|
||||
survey: Proto.SubmitCallQualitySurveyRequest.Params
|
||||
): Promise<void> {
|
||||
const data = Proto.SubmitCallQualitySurveyRequest.encode(survey).finish();
|
||||
const data = Proto.SubmitCallQualitySurveyRequest.encode(survey);
|
||||
await _ajax({
|
||||
call: 'callQualitySurvey',
|
||||
contentType: 'application/octet-stream',
|
||||
@@ -4327,7 +4327,7 @@ export async function getGroupCredentials({
|
||||
|
||||
export async function getExternalGroupCredential(
|
||||
options: GroupCredentialsType
|
||||
): Promise<Proto.IExternalGroupCredential> {
|
||||
): Promise<Proto.ExternalGroupCredential.Params> {
|
||||
const basicAuth = generateGroupAuth(
|
||||
options.groupPublicParamsHex,
|
||||
options.authCredentialPresentationHex
|
||||
@@ -4346,7 +4346,7 @@ export async function getExternalGroupCredential(
|
||||
return Proto.ExternalGroupCredential.decode(response);
|
||||
}
|
||||
|
||||
function verifyAttributes(attributes: Proto.IAvatarUploadAttributes) {
|
||||
function verifyAttributes(attributes: Proto.AvatarUploadAttributes.Params) {
|
||||
const { key, credential, acl, algorithm, date, policy, signature } =
|
||||
attributes;
|
||||
|
||||
@@ -4581,14 +4581,14 @@ export function confirmPaypalBoostPayment(
|
||||
}
|
||||
|
||||
export async function createGroup(
|
||||
group: Proto.IGroup,
|
||||
group: Proto.Group.Params,
|
||||
options: GroupCredentialsType
|
||||
): Promise<Proto.IGroupResponse> {
|
||||
): Promise<Proto.GroupResponse.Params> {
|
||||
const basicAuth = generateGroupAuth(
|
||||
options.groupPublicParamsHex,
|
||||
options.authCredentialPresentationHex
|
||||
);
|
||||
const data = Proto.Group.encode(group).finish();
|
||||
const data = Proto.Group.encode(group);
|
||||
|
||||
const response = await _ajax({
|
||||
basicAuth,
|
||||
@@ -4606,7 +4606,7 @@ export async function createGroup(
|
||||
|
||||
export async function getGroup(
|
||||
options: GroupCredentialsType
|
||||
): Promise<Proto.IGroupResponse> {
|
||||
): Promise<Proto.GroupResponse.Params> {
|
||||
const basicAuth = generateGroupAuth(
|
||||
options.groupPublicParamsHex,
|
||||
options.authCredentialPresentationHex
|
||||
@@ -4655,15 +4655,15 @@ export async function getGroupFromLink(
|
||||
}
|
||||
|
||||
export async function modifyGroup(
|
||||
changes: Proto.GroupChange.IActions,
|
||||
changes: Proto.GroupChange.Actions.Params,
|
||||
options: GroupCredentialsType,
|
||||
inviteLinkBase64?: string
|
||||
): Promise<Proto.IGroupChangeResponse> {
|
||||
): Promise<Proto.GroupChangeResponse.Params> {
|
||||
const basicAuth = generateGroupAuth(
|
||||
options.groupPublicParamsHex,
|
||||
options.authCredentialPresentationHex
|
||||
);
|
||||
const data = Proto.GroupChange.Actions.encode(changes).finish();
|
||||
const data = Proto.GroupChange.Actions.encode(changes);
|
||||
const safeInviteLinkPassword = inviteLinkBase64
|
||||
? toWebSafeBase64(inviteLinkBase64)
|
||||
: undefined;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
isPniString,
|
||||
} from '../types/ServiceId.std.js';
|
||||
import type { StoryDistributionIdString } from '../types/StoryDistributionId.std.js';
|
||||
import type { StoryMessageRecipientsType } from '../types/Stories.std.js';
|
||||
import type {
|
||||
ProcessedEnvelope,
|
||||
ProcessedDataMessage,
|
||||
@@ -32,7 +33,7 @@ export class EmptyEvent extends Event {
|
||||
}
|
||||
|
||||
export type TypingEventData = Readonly<{
|
||||
typingMessage: Proto.ITypingMessage;
|
||||
typingMessage: Proto.TypingMessage;
|
||||
timestamp: number;
|
||||
started: boolean;
|
||||
stopped: boolean;
|
||||
@@ -290,7 +291,7 @@ export class ViewEvent extends ConfirmableEvent {
|
||||
|
||||
export class ConfigurationEvent extends ConfirmableEvent {
|
||||
constructor(
|
||||
public readonly configuration: Proto.SyncMessage.IConfiguration,
|
||||
public readonly configuration: Proto.SyncMessage.Configuration,
|
||||
confirm: ConfirmCallback
|
||||
) {
|
||||
super('configuration', confirm);
|
||||
@@ -325,7 +326,7 @@ export type MessageRequestResponseOptions = {
|
||||
envelopeId: string;
|
||||
threadE164?: string;
|
||||
threadAci?: AciString;
|
||||
messageRequestResponseType: Proto.SyncMessage.IMessageRequestResponse['type'];
|
||||
messageRequestResponseType: Proto.SyncMessage.MessageRequestResponse.Type;
|
||||
groupId?: string;
|
||||
groupV2Id?: string;
|
||||
receivedAtCounter: number;
|
||||
@@ -378,7 +379,7 @@ export class MessageRequestResponseEvent extends ConfirmableEvent {
|
||||
|
||||
export class FetchLatestEvent extends ConfirmableEvent {
|
||||
constructor(
|
||||
public readonly eventType: Proto.SyncMessage.IFetchLatest['type'],
|
||||
public readonly eventType: Proto.SyncMessage.FetchLatest['type'],
|
||||
confirm: ConfirmCallback
|
||||
) {
|
||||
super('fetchLatest', confirm);
|
||||
@@ -641,7 +642,7 @@ export class CallLogEventSyncEvent extends ConfirmableEvent {
|
||||
|
||||
export type StoryRecipientUpdateData = Readonly<{
|
||||
destinationServiceId: ServiceIdString;
|
||||
storyMessageRecipients: Array<Proto.SyncMessage.Sent.IStoryMessageRecipient>;
|
||||
storyMessageRecipients: StoryMessageRecipientsType;
|
||||
timestamp: number;
|
||||
}>;
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import Long from 'long';
|
||||
import { ReceiptCredentialPresentation } from '@signalapp/libsignal-client/zkgroup.js';
|
||||
import lodash from 'lodash';
|
||||
|
||||
import { assertDev, strictAssert } from '../util/assert.std.js';
|
||||
import { dropNull, shallowDropNull } from '../util/dropNull.std.js';
|
||||
import { dropNull } from '../util/dropNull.std.js';
|
||||
import {
|
||||
fromAciUuidBytes,
|
||||
fromAciUuidBytesOrString,
|
||||
} from '../util/ServiceId.node.js';
|
||||
import { getTimestampFromLong } from '../util/timestampLongUtils.std.js';
|
||||
import { isKnownProtoEnumMember } from '../util/isKnownProtoEnumMember.std.js';
|
||||
import { SignalService as Proto } from '../protobuf/index.std.js';
|
||||
import { deriveGroupFields } from '../groups.preload.js';
|
||||
import * as Bytes from '../Bytes.std.js';
|
||||
@@ -36,6 +36,7 @@ import type {
|
||||
ProcessedUnpinMessage,
|
||||
} from './Types.d.ts';
|
||||
import { GiftBadgeStates } from '../types/GiftBadgeStates.std.js';
|
||||
import type { RawBodyRange } from '../types/BodyRange.std.js';
|
||||
import {
|
||||
APPLICATION_OCTET_STREAM,
|
||||
stringToMIMEType,
|
||||
@@ -54,6 +55,8 @@ import { partitionBodyAndNormalAttachments } from '../util/Attachment.std.js';
|
||||
import { isNotNil } from '../util/isNotNil.std.js';
|
||||
import { createLogger } from '../logging/log.std.js';
|
||||
|
||||
import { toNumber } from '../util/toNumber.std.js';
|
||||
|
||||
const { isNumber } = lodash;
|
||||
|
||||
const log = createLogger('processDataMessage');
|
||||
@@ -62,24 +65,22 @@ const FLAGS = Proto.DataMessage.Flags;
|
||||
export const ATTACHMENT_MAX = 32;
|
||||
|
||||
export function processAttachment(
|
||||
attachment: Proto.IAttachmentPointer
|
||||
attachment: Proto.AttachmentPointer
|
||||
): ProcessedAttachment;
|
||||
export function processAttachment(
|
||||
attachment?: Proto.IAttachmentPointer | null
|
||||
attachment?: Proto.AttachmentPointer | null
|
||||
): ProcessedAttachment | undefined;
|
||||
|
||||
export function processAttachment(
|
||||
attachment?: Proto.IAttachmentPointer | null
|
||||
attachment?: Proto.AttachmentPointer | null
|
||||
): ProcessedAttachment | undefined {
|
||||
const attachmentWithoutNulls = shallowDropNull(attachment);
|
||||
if (!attachmentWithoutNulls) {
|
||||
if (attachment == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
cdnId,
|
||||
cdnKey,
|
||||
cdnNumber,
|
||||
attachmentIdentifier,
|
||||
clientUuid,
|
||||
key,
|
||||
size,
|
||||
@@ -93,15 +94,14 @@ export function processAttachment(
|
||||
height,
|
||||
caption,
|
||||
blurHash,
|
||||
} = attachmentWithoutNulls;
|
||||
|
||||
const hasCdnId = Long.isLong(cdnId) ? !cdnId.isZero() : Boolean(cdnId);
|
||||
} = attachment;
|
||||
|
||||
if (!isNumber(size)) {
|
||||
throw new Error('Missing size on incoming attachment!');
|
||||
}
|
||||
|
||||
let uploadTimestamp = attachmentWithoutNulls.uploadTimestamp?.toNumber();
|
||||
let uploadTimestamp: number | undefined =
|
||||
toNumber(attachment.uploadTimestamp) ?? 0;
|
||||
|
||||
// Make sure uploadTimestamp is not set to an obviously wrong future value (we use
|
||||
// uploadTimestamp to determine whether to re-use CDN pointers)
|
||||
@@ -111,17 +111,20 @@ export function processAttachment(
|
||||
}
|
||||
|
||||
return {
|
||||
cdnKey,
|
||||
cdnNumber,
|
||||
chunkSize,
|
||||
fileName,
|
||||
flags,
|
||||
width,
|
||||
height,
|
||||
caption,
|
||||
blurHash,
|
||||
cdnKey: attachmentIdentifier?.cdnKey,
|
||||
cdnNumber: cdnNumber ?? 0,
|
||||
chunkSize: chunkSize ?? 0,
|
||||
fileName: fileName ?? '',
|
||||
flags: flags ?? 0,
|
||||
width: width ?? 0,
|
||||
height: height ?? 0,
|
||||
caption: caption ?? '',
|
||||
blurHash: blurHash ?? '',
|
||||
uploadTimestamp,
|
||||
cdnId: hasCdnId ? String(cdnId) : undefined,
|
||||
cdnId:
|
||||
attachmentIdentifier?.cdnId === 0n
|
||||
? undefined
|
||||
: attachmentIdentifier?.cdnId?.toString(),
|
||||
clientUuid: Bytes.isNotEmpty(clientUuid)
|
||||
? bytesToUuid(clientUuid)
|
||||
: undefined,
|
||||
@@ -138,7 +141,7 @@ export function processAttachment(
|
||||
}
|
||||
|
||||
export function processGroupV2Context(
|
||||
groupV2?: Proto.IGroupContextV2 | null
|
||||
groupV2?: Proto.GroupContextV2 | null
|
||||
): ProcessedGroupV2Context | undefined {
|
||||
if (!groupV2) {
|
||||
return undefined;
|
||||
@@ -149,7 +152,7 @@ export function processGroupV2Context(
|
||||
|
||||
return {
|
||||
masterKey: Bytes.toBase64(groupV2.masterKey),
|
||||
revision: dropNull(groupV2.revision),
|
||||
revision: groupV2.revision ?? 0,
|
||||
groupChange: groupV2.groupChange
|
||||
? Bytes.toBase64(groupV2.groupChange)
|
||||
: undefined,
|
||||
@@ -160,28 +163,28 @@ export function processGroupV2Context(
|
||||
}
|
||||
|
||||
export function processPayment(
|
||||
payment?: Proto.DataMessage.IPayment | null
|
||||
payment?: Proto.DataMessage.Payment | null
|
||||
): AnyPaymentEvent | undefined {
|
||||
if (!payment) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (payment.notification != null) {
|
||||
if (payment.Item?.notification != null) {
|
||||
return {
|
||||
kind: PaymentEventKind.Notification,
|
||||
note: payment.notification.note ?? null,
|
||||
note: payment.Item.notification.note ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
if (payment.activation != null) {
|
||||
if (payment.Item?.activation != null) {
|
||||
if (
|
||||
payment.activation.type ===
|
||||
payment.Item.activation.type ===
|
||||
Proto.DataMessage.Payment.Activation.Type.REQUEST
|
||||
) {
|
||||
return { kind: PaymentEventKind.ActivationRequest };
|
||||
}
|
||||
if (
|
||||
payment.activation.type ===
|
||||
payment.Item.activation.type ===
|
||||
Proto.DataMessage.Payment.Activation.Type.ACTIVATED
|
||||
) {
|
||||
return { kind: PaymentEventKind.Activation };
|
||||
@@ -192,7 +195,7 @@ export function processPayment(
|
||||
}
|
||||
|
||||
export function processQuote(
|
||||
quote?: Proto.DataMessage.IQuote | null
|
||||
quote?: Proto.DataMessage.Quote | null
|
||||
): ProcessedQuote | undefined {
|
||||
if (!quote) {
|
||||
return undefined;
|
||||
@@ -206,25 +209,29 @@ export function processQuote(
|
||||
);
|
||||
|
||||
return {
|
||||
id: quote.id?.toNumber(),
|
||||
id: toNumber(quote.id) ?? 0,
|
||||
authorAci,
|
||||
text: dropNull(quote.text),
|
||||
text: quote.text ?? '',
|
||||
attachments: (quote.attachments ?? []).slice(0, 1).map(attachment => {
|
||||
return {
|
||||
contentType: attachment.contentType
|
||||
? stringToMIMEType(attachment.contentType)
|
||||
: APPLICATION_OCTET_STREAM,
|
||||
fileName: dropNull(attachment.fileName),
|
||||
fileName: attachment.fileName ?? '',
|
||||
thumbnail: processAttachment(attachment.thumbnail),
|
||||
};
|
||||
}),
|
||||
bodyRanges: filterAndClean(quote.bodyRanges),
|
||||
type: quote.type || Proto.DataMessage.Quote.Type.NORMAL,
|
||||
bodyRanges: filterAndClean(
|
||||
quote.bodyRanges.map(processBodyRange).filter(isNotNil)
|
||||
),
|
||||
type: isKnownProtoEnumMember(Proto.DataMessage.Quote.Type, quote.type)
|
||||
? quote.type
|
||||
: Proto.DataMessage.Quote.Type.NORMAL,
|
||||
};
|
||||
}
|
||||
|
||||
export function processStoryContext(
|
||||
storyContext?: Proto.DataMessage.IStoryContext | null
|
||||
storyContext?: Proto.DataMessage.StoryContext | null
|
||||
): ProcessedStoryContext | undefined {
|
||||
if (!storyContext) {
|
||||
return undefined;
|
||||
@@ -248,7 +255,7 @@ export function processStoryContext(
|
||||
}
|
||||
|
||||
export function processContact(
|
||||
contact?: ReadonlyArray<Proto.DataMessage.IContact> | null
|
||||
contact?: ReadonlyArray<Proto.DataMessage.Contact> | null
|
||||
): ReadonlyArray<ProcessedContact> | undefined {
|
||||
if (!contact) {
|
||||
return undefined;
|
||||
@@ -276,13 +283,13 @@ function isLinkPreviewDateValid(value: unknown): value is number {
|
||||
);
|
||||
}
|
||||
|
||||
function cleanLinkPreviewDate(value?: Long | null): number | undefined {
|
||||
const result = value?.toNumber();
|
||||
function cleanLinkPreviewDate(value?: bigint | null): number | undefined {
|
||||
const result = toNumber(value);
|
||||
return isLinkPreviewDateValid(result) ? result : undefined;
|
||||
}
|
||||
|
||||
export function processPreview(
|
||||
preview?: ReadonlyArray<Proto.IPreview> | null
|
||||
preview?: ReadonlyArray<Proto.Preview> | null
|
||||
): ReadonlyArray<ProcessedPreview> | undefined {
|
||||
if (!preview) {
|
||||
return undefined;
|
||||
@@ -290,17 +297,17 @@ export function processPreview(
|
||||
|
||||
return preview.slice(0, 1).map(item => {
|
||||
return {
|
||||
url: dropNull(item.url),
|
||||
title: dropNull(item.title),
|
||||
url: item.url ?? '',
|
||||
title: item.title ?? '',
|
||||
image: item.image ? processAttachment(item.image) : undefined,
|
||||
description: dropNull(item.description),
|
||||
description: item.description ?? '',
|
||||
date: cleanLinkPreviewDate(item.date),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function processSticker(
|
||||
sticker?: Proto.DataMessage.ISticker | null
|
||||
sticker?: Proto.DataMessage.Sticker | null
|
||||
): ProcessedSticker | undefined {
|
||||
if (!sticker) {
|
||||
return undefined;
|
||||
@@ -309,14 +316,14 @@ export function processSticker(
|
||||
return {
|
||||
packId: sticker.packId ? Bytes.toHex(sticker.packId) : undefined,
|
||||
packKey: sticker.packKey ? Bytes.toBase64(sticker.packKey) : undefined,
|
||||
stickerId: dropNull(sticker.stickerId),
|
||||
emoji: dropNull(sticker.emoji),
|
||||
stickerId: sticker.stickerId ?? 0,
|
||||
emoji: sticker.emoji ?? '',
|
||||
data: processAttachment(sticker.data),
|
||||
};
|
||||
}
|
||||
|
||||
export function processReaction(
|
||||
reaction?: Proto.DataMessage.IReaction | null
|
||||
reaction?: Proto.DataMessage.Reaction | null
|
||||
): ProcessedReaction | undefined {
|
||||
if (!reaction) {
|
||||
return undefined;
|
||||
@@ -331,32 +338,37 @@ export function processReaction(
|
||||
);
|
||||
|
||||
return {
|
||||
emoji: dropNull(reaction.emoji),
|
||||
emoji: reaction.emoji ?? '',
|
||||
remove: Boolean(reaction.remove),
|
||||
targetAuthorAci,
|
||||
targetTimestamp: reaction.targetSentTimestamp?.toNumber(),
|
||||
targetTimestamp: toNumber(reaction.targetSentTimestamp) ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function processPinMessage(
|
||||
pinMessage?: Proto.DataMessage.IPinMessage | null
|
||||
pinMessage?: Proto.DataMessage.PinMessage | null
|
||||
): ProcessedPinMessage | undefined {
|
||||
if (pinMessage == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const targetSentTimestamp = pinMessage.targetSentTimestamp?.toNumber();
|
||||
const targetSentTimestamp = toNumber(pinMessage.targetSentTimestamp);
|
||||
strictAssert(targetSentTimestamp, 'Missing targetSentTimestamp');
|
||||
|
||||
const targetAuthorAci = fromAciUuidBytes(pinMessage.targetAuthorAciBinary);
|
||||
strictAssert(targetAuthorAci, 'Missing targetAuthorAciBinary');
|
||||
|
||||
let pinDuration: DurationInSeconds | null;
|
||||
if (pinMessage.pinDurationForever) {
|
||||
if (pinMessage.pinDuration?.pinDurationForever) {
|
||||
pinDuration = null;
|
||||
} else {
|
||||
strictAssert(pinMessage.pinDurationSeconds, 'Missing pinDurationSeconds');
|
||||
pinDuration = DurationInSeconds.fromSeconds(pinMessage.pinDurationSeconds);
|
||||
strictAssert(
|
||||
pinMessage.pinDuration?.pinDurationSeconds,
|
||||
'Missing pinDurationSeconds'
|
||||
);
|
||||
pinDuration = DurationInSeconds.fromSeconds(
|
||||
pinMessage.pinDuration.pinDurationSeconds
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -367,21 +379,21 @@ export function processPinMessage(
|
||||
}
|
||||
|
||||
export function processPollCreate(
|
||||
pollCreate?: Proto.DataMessage.IPollCreate | null
|
||||
pollCreate?: Proto.DataMessage.PollCreate | null
|
||||
): ProcessedPollCreate | undefined {
|
||||
if (!pollCreate) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
question: dropNull(pollCreate.question),
|
||||
question: pollCreate.question ?? '',
|
||||
options: pollCreate.options?.filter(isNotNil) || [],
|
||||
allowMultiple: Boolean(pollCreate.allowMultiple),
|
||||
};
|
||||
}
|
||||
|
||||
export function processPollVote(
|
||||
pollVote?: Proto.DataMessage.IPollVote | null
|
||||
pollVote?: Proto.DataMessage.PollVote | null
|
||||
): ProcessedPollVote | undefined {
|
||||
if (!pollVote) {
|
||||
return undefined;
|
||||
@@ -395,44 +407,44 @@ export function processPollVote(
|
||||
|
||||
return {
|
||||
targetAuthorAci,
|
||||
targetTimestamp: pollVote.targetSentTimestamp?.toNumber(),
|
||||
targetTimestamp: toNumber(pollVote.targetSentTimestamp) ?? 0,
|
||||
optionIndexes: pollVote.optionIndexes?.filter(isNotNil) || [],
|
||||
voteCount: pollVote.voteCount || 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function processPollTerminate(
|
||||
pollTerminate?: Proto.DataMessage.IPollTerminate | null
|
||||
pollTerminate?: Proto.DataMessage.PollTerminate | null
|
||||
): ProcessedPollTerminate | undefined {
|
||||
if (!pollTerminate) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
targetTimestamp: pollTerminate.targetSentTimestamp?.toNumber(),
|
||||
targetTimestamp: toNumber(pollTerminate.targetSentTimestamp) ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function processDelete(
|
||||
del?: Proto.DataMessage.IDelete | null
|
||||
del?: Proto.DataMessage.Delete | null
|
||||
): ProcessedDelete | undefined {
|
||||
if (!del) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
targetSentTimestamp: del.targetSentTimestamp?.toNumber(),
|
||||
targetSentTimestamp: toNumber(del.targetSentTimestamp) ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function processAdminDelete(
|
||||
adminDelete?: Proto.DataMessage.IAdminDelete | null
|
||||
adminDelete?: Proto.DataMessage.AdminDelete | null
|
||||
): ProcessedAdminDelete | undefined {
|
||||
if (!adminDelete) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const targetSentTimestamp = adminDelete.targetSentTimestamp?.toNumber();
|
||||
const targetSentTimestamp = toNumber(adminDelete.targetSentTimestamp);
|
||||
strictAssert(targetSentTimestamp, 'AdminDelete missing targetSentTimestamp');
|
||||
|
||||
const targetAuthorAci = fromAciUuidBytes(adminDelete.targetAuthorAciBinary);
|
||||
@@ -445,7 +457,7 @@ export function processAdminDelete(
|
||||
}
|
||||
|
||||
export function processGiftBadge(
|
||||
giftBadge: Proto.DataMessage.IGiftBadge | null | undefined
|
||||
giftBadge: Proto.DataMessage.GiftBadge | null | undefined
|
||||
): ProcessedGiftBadge | undefined {
|
||||
if (
|
||||
!giftBadge ||
|
||||
@@ -471,13 +483,13 @@ export function processGiftBadge(
|
||||
}
|
||||
|
||||
export function processUnpinMessage(
|
||||
unpinMessage?: Proto.DataMessage.IUnpinMessage | null
|
||||
unpinMessage?: Proto.DataMessage.UnpinMessage | null
|
||||
): ProcessedUnpinMessage | undefined {
|
||||
if (unpinMessage == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const targetSentTimestamp = unpinMessage.targetSentTimestamp?.toNumber();
|
||||
const targetSentTimestamp = toNumber(unpinMessage.targetSentTimestamp);
|
||||
strictAssert(targetSentTimestamp, 'Missing targetSentTimestamp');
|
||||
|
||||
const targetAuthorAci = fromAciUuidBytes(unpinMessage.targetAuthorAciBinary);
|
||||
@@ -490,7 +502,7 @@ export function processUnpinMessage(
|
||||
}
|
||||
|
||||
export function processDataMessage(
|
||||
message: Proto.IDataMessage,
|
||||
message: Proto.DataMessage,
|
||||
envelopeTimestamp: number,
|
||||
|
||||
// Only for testing
|
||||
@@ -507,7 +519,7 @@ export function processDataMessage(
|
||||
throw new Error('Missing timestamp on dataMessage');
|
||||
}
|
||||
|
||||
const timestamp = message.timestamp?.toNumber();
|
||||
const timestamp = toNumber(message.timestamp);
|
||||
|
||||
if (envelopeTimestamp !== timestamp) {
|
||||
throw new Error(
|
||||
@@ -517,7 +529,7 @@ export function processDataMessage(
|
||||
}
|
||||
|
||||
const processedAttachments = message.attachments
|
||||
?.map((attachment: Proto.IAttachmentPointer) => ({
|
||||
?.map((attachment: Proto.AttachmentPointer) => ({
|
||||
...processAttachment(attachment),
|
||||
downloadPath: doCreateName(),
|
||||
}))
|
||||
@@ -529,7 +541,7 @@ export function processDataMessage(
|
||||
);
|
||||
|
||||
const result: ProcessedDataMessage = {
|
||||
body: dropNull(message.body),
|
||||
body: message.body ?? '',
|
||||
bodyAttachment,
|
||||
attachments,
|
||||
groupV2: processGroupV2Context(message.groupV2),
|
||||
@@ -546,7 +558,7 @@ export function processDataMessage(
|
||||
contact: processContact(message.contact),
|
||||
preview: processPreview(message.preview),
|
||||
sticker: processSticker(message.sticker),
|
||||
requiredProtocolVersion: dropNull(message.requiredProtocolVersion),
|
||||
requiredProtocolVersion: message.requiredProtocolVersion ?? 0,
|
||||
isViewOnce: Boolean(message.isViewOnce),
|
||||
reaction: processReaction(message.reaction),
|
||||
pinMessage: processPinMessage(message.pinMessage),
|
||||
@@ -555,7 +567,9 @@ export function processDataMessage(
|
||||
pollTerminate: processPollTerminate(message.pollTerminate),
|
||||
delete: processDelete(message.delete),
|
||||
adminDelete: processAdminDelete(message.adminDelete),
|
||||
bodyRanges: filterAndClean(message.bodyRanges),
|
||||
bodyRanges: filterAndClean(
|
||||
message.bodyRanges.map(processBodyRange).filter(isNotNil)
|
||||
),
|
||||
groupCallUpdate: dropNull(message.groupCallUpdate),
|
||||
storyContext: processStoryContext(message.storyContext),
|
||||
giftBadge: processGiftBadge(message.giftBadge),
|
||||
@@ -605,3 +619,36 @@ export function processDataMessage(
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function processBodyRange(
|
||||
proto: Proto.BodyRange
|
||||
): RawBodyRange | undefined {
|
||||
if (proto.associatedValue == null) {
|
||||
return undefined;
|
||||
}
|
||||
if (proto.associatedValue.style) {
|
||||
return {
|
||||
start: proto.start ?? 0,
|
||||
length: proto.length ?? 0,
|
||||
style: isKnownProtoEnumMember(
|
||||
Proto.BodyRange.Style,
|
||||
proto.associatedValue.style
|
||||
)
|
||||
? proto.associatedValue.style
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const mentionAci = fromAciUuidBytesOrString(
|
||||
proto.associatedValue.mentionAciBinary,
|
||||
proto.associatedValue.mentionAci,
|
||||
'BodyRange.mentionAci'
|
||||
);
|
||||
strictAssert(mentionAci != null, 'Expected mentionAci');
|
||||
|
||||
return {
|
||||
start: proto.start ?? 0,
|
||||
length: proto.length ?? 0,
|
||||
mentionAci,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
import type { SignalService as Proto } from '../protobuf/index.std.js';
|
||||
import type { ServiceIdString } from '../types/ServiceId.std.js';
|
||||
import { normalizeStoryDistributionId } from '../types/StoryDistributionId.std.js';
|
||||
import { fromServiceIdBinaryOrString } from '../util/ServiceId.node.js';
|
||||
import type { ProcessedSent, ProcessedSyncMessage } from './Types.d.ts';
|
||||
import type { ProcessedSent } from './Types.d.ts';
|
||||
|
||||
type ProtoServiceId = Readonly<{
|
||||
destinationServiceId?: string | null;
|
||||
@@ -33,21 +34,18 @@ function processProtoWithDestinationServiceId<Input extends ProtoServiceId>(
|
||||
};
|
||||
}
|
||||
|
||||
function processSent(
|
||||
sent?: Proto.SyncMessage.ISent | null
|
||||
): ProcessedSent | undefined {
|
||||
if (!sent) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function processSent(sent: Proto.SyncMessage.Sent): ProcessedSent {
|
||||
const {
|
||||
destinationServiceId: rawDestinationServiceId,
|
||||
destinationServiceIdBinary,
|
||||
unidentifiedStatus,
|
||||
storyMessageRecipients,
|
||||
$unknown,
|
||||
...remaining
|
||||
} = sent;
|
||||
|
||||
void $unknown;
|
||||
|
||||
return {
|
||||
...remaining,
|
||||
|
||||
@@ -57,19 +55,31 @@ function processSent(
|
||||
'processSent'
|
||||
),
|
||||
unidentifiedStatus: unidentifiedStatus
|
||||
? unidentifiedStatus.map(processProtoWithDestinationServiceId)
|
||||
? unidentifiedStatus
|
||||
.map(processProtoWithDestinationServiceId)
|
||||
.map(({ unidentified, destinationPniIdentityKey, ...rest }) => {
|
||||
return {
|
||||
...rest,
|
||||
unidentified: unidentified ?? false,
|
||||
destinationPniIdentityKey: destinationPniIdentityKey ?? undefined,
|
||||
};
|
||||
})
|
||||
: undefined,
|
||||
storyMessageRecipients: storyMessageRecipients
|
||||
? storyMessageRecipients.map(processProtoWithDestinationServiceId)
|
||||
? storyMessageRecipients
|
||||
.map(processProtoWithDestinationServiceId)
|
||||
.map(recipient => {
|
||||
return {
|
||||
isAllowedToReply: recipient.isAllowedToReply ?? false,
|
||||
destinationServiceId: recipient.destinationServiceId,
|
||||
distributionListIds: recipient.distributionListIds.map(id => {
|
||||
return normalizeStoryDistributionId(
|
||||
id,
|
||||
'processSent.storyMessageRecipients'
|
||||
);
|
||||
}),
|
||||
};
|
||||
})
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function processSyncMessage(
|
||||
syncMessage: Proto.ISyncMessage
|
||||
): ProcessedSyncMessage {
|
||||
return {
|
||||
...syncMessage,
|
||||
sent: processSent(syncMessage.sent),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user