Import/export view-once messages

This commit is contained in:
Fedor Indutny 2024-10-29 11:16:09 -07:00 committed by GitHub
parent 2f6270c585
commit ce090a8a3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 66 additions and 3 deletions

View file

@ -194,7 +194,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: 'signalapp/Signal-Message-Backup-Tests'
ref: '996daf691d4162ce854845dc883c62adb1a3fe55'
ref: 'c84390838f65c6c0927c9bbcc3a240f986fc4a80'
path: 'backup-integration-tests'
- run: xvfb-run --auto-servernum npm run test-electron

View file

@ -342,6 +342,7 @@ message ChatItem {
ChatUpdateMessage updateMessage = 15;
PaymentNotification paymentNotification = 16;
GiftBadge giftBadge = 17;
ViewOnceMessage viewOnceMessage = 18;
}
}
@ -470,6 +471,12 @@ message GiftBadge {
State state = 2;
}
message ViewOnceMessage {
// Will be null for viewed messages
MessageAttachment attachment = 1;
repeated Reaction reactions = 2;
}
message ContactAttachment {
message Name {
optional string givenName = 1;

View file

@ -77,6 +77,7 @@ import {
isNormalBubble,
isPhoneNumberDiscovery,
isProfileChange,
isTapToView,
isUniversalTimerNotification,
isUnsupportedMessage,
isVerifiedChange,
@ -1060,7 +1061,12 @@ export class BackupExportStream extends Readable {
}
const { contact, sticker } = message;
if (message.isErased) {
if (isTapToView(message)) {
result.viewOnceMessage = await this.toViewOnceMessage({
message,
backupLevel,
});
} else if (message.isErased) {
result.remoteDeletedMessage = {};
} else if (messageHasPaymentEvent(message)) {
const { payment } = message;
@ -2265,7 +2271,7 @@ export class BackupExportStream extends Readable {
serverTimestamp != null
? getSafeLongFromTimestamp(serverTimestamp)
: null,
read: readStatus === ReadStatus.Read,
read: readStatus === ReadStatus.Read || readStatus === ReadStatus.Viewed,
sealedSender: unidentifiedDeliveryReceived === true,
};
}
@ -2443,6 +2449,30 @@ export class BackupExportStream extends Readable {
};
}
private async toViewOnceMessage({
message,
backupLevel,
}: {
message: Pick<
MessageAttributesType,
'attachments' | 'received_at' | 'reactions'
>;
backupLevel: BackupLevel;
}): Promise<Backups.IViewOnceMessage> {
const attachment = message.attachments?.at(0);
return {
attachment:
attachment == null
? null
: await this.processMessageAttachment({
attachment,
backupLevel,
messageReceivedAt: message.received_at,
}),
reactions: this.getMessageReactions(message),
};
}
private async toChatItemRevisions(
parent: Backups.IChatItem,
message: MessageAttributesType,

View file

@ -1303,6 +1303,11 @@ export class BackupImportStream extends Writable {
...attributes,
...(await this.fromStandardMessage(item.standardMessage, chatConvo.id)),
};
} else if (item.viewOnceMessage) {
attributes = {
...attributes,
...(await this.fromViewOnceMessage(item.viewOnceMessage)),
};
} else {
const result = await this.fromNonBubbleChatItem(item, {
aboutMe,
@ -1566,6 +1571,27 @@ export class BackupImportStream extends Writable {
};
}
private async fromViewOnceMessage({
attachment,
reactions,
}: Backups.IViewOnceMessage): Promise<Partial<MessageAttributesType>> {
return {
...(attachment
? {
attachments: [
convertBackupMessageAttachmentToAttachment(attachment),
].filter(isNotNil),
}
: {
attachments: undefined,
readStatus: ReadStatus.Viewed,
isErased: true,
}),
reactions: this.fromReactions(reactions),
isViewOnce: true,
};
}
private async fromRevisions(
mainMessage: MessageAttributesType,
revisions: ReadonlyArray<Backups.IChatItem>