Fix roundtripping of voice notes with body

This commit is contained in:
Fedor Indutny 2025-04-16 14:27:47 -07:00 committed by GitHub
parent c9c3d24fd9
commit 0b5f0df1ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 50 additions and 14 deletions

View file

@ -38,7 +38,6 @@ import type {
ConversationAttributesType,
MessageAttributesType,
QuotedAttachmentType,
QuotedMessageType,
} from '../../model-types.d';
import { drop } from '../../util/drop';
import { isNotNil } from '../../util/isNotNil';
@ -2195,14 +2194,13 @@ export class BackupExportStream extends Readable {
}
async #toQuote({
quote,
message,
backupLevel,
messageReceivedAt,
}: {
quote?: QuotedMessageType;
message: Pick<MessageAttributesType, 'quote' | 'received_at' | 'body'>;
backupLevel: BackupLevel;
messageReceivedAt: number;
}): Promise<Backups.IQuote | null> {
const { quote } = message;
if (!quote) {
return null;
}
@ -2259,7 +2257,7 @@ export class BackupExportStream extends Readable {
? await this.#processMessageAttachment({
attachment: attachment.thumbnail,
backupLevel,
messageReceivedAt,
message,
})
: undefined,
};
@ -2287,11 +2285,17 @@ export class BackupExportStream extends Readable {
}
#getMessageAttachmentFlag(
message: Pick<MessageAttributesType, 'body'>,
attachment: AttachmentType
): Backups.MessageAttachment.Flag {
const flag = SignalService.AttachmentPointer.Flags.VOICE_MESSAGE;
// eslint-disable-next-line no-bitwise
if (((attachment.flags || 0) & flag) === flag) {
// Legacy data support for iOS
if (message.body) {
return Backups.MessageAttachment.Flag.NONE;
}
return Backups.MessageAttachment.Flag.VOICE_MESSAGE;
}
if (isGIF([attachment])) {
@ -2311,22 +2315,22 @@ export class BackupExportStream extends Readable {
async #processMessageAttachment({
attachment,
backupLevel,
messageReceivedAt,
message,
}: {
attachment: AttachmentType;
backupLevel: BackupLevel;
messageReceivedAt: number;
message: Pick<MessageAttributesType, 'quote' | 'received_at' | 'body'>;
}): Promise<Backups.MessageAttachment> {
const { clientUuid } = attachment;
const filePointer = await this.#processAttachment({
attachment,
backupLevel,
messageReceivedAt,
messageReceivedAt: message.received_at,
});
return new Backups.MessageAttachment({
pointer: filePointer,
flag: this.#getMessageAttachmentFlag(attachment),
flag: this.#getMessageAttachmentFlag(message, attachment),
wasDownloaded: isDownloaded(attachment),
clientUuid: clientUuid ? uuidToBytes(clientUuid) : undefined,
});
@ -2550,9 +2554,8 @@ export class BackupExportStream extends Readable {
}): Promise<Backups.IStandardMessage> {
return {
quote: await this.#toQuote({
quote: message.quote,
message,
backupLevel,
messageReceivedAt: message.received_at,
}),
attachments: message.attachments?.length
? await Promise.all(
@ -2560,7 +2563,7 @@ export class BackupExportStream extends Readable {
return this.#processMessageAttachment({
attachment,
backupLevel,
messageReceivedAt: message.received_at,
message,
});
})
)
@ -2667,7 +2670,7 @@ export class BackupExportStream extends Readable {
: await this.#processMessageAttachment({
attachment,
backupLevel,
messageReceivedAt: message.received_at,
message,
}),
reactions: this.#getMessageReactions(message),
};

View file

@ -355,6 +355,39 @@ describe('backup/attachments', () => {
{ backupLevel: BackupLevel.Paid }
);
});
it('drops voice message flag when body is present', async () => {
const attachment = composeAttachment(1);
attachment.contentType = AUDIO_MP3;
attachment.flags = SignalService.AttachmentPointer.Flags.VOICE_MESSAGE;
strictAssert(isVoiceMessage(attachment), 'it is a voice attachment');
strictAssert(attachment.digest, 'digest exists');
await asymmetricRoundtripHarness(
[
composeMessage(1, {
body: 'hello',
attachments: [attachment],
}),
],
[
composeMessage(1, {
body: 'hello',
hasAttachments: true,
attachments: [
{
...omit(attachment, NON_ROUNDTRIPPED_BACKUP_LOCATOR_FIELDS),
flags: undefined,
backupLocator: {
mediaName: digestToMediaName(attachment.digest),
},
},
],
}),
],
{ backupLevel: BackupLevel.Paid }
);
});
});
describe('Preview attachments', () => {