Always download long text attachments and use them as message body

Fixes #1901
This commit is contained in:
AsamK 2026-03-01 09:23:04 +01:00
parent 775236efc3
commit 8fcd953ece
2 changed files with 104 additions and 22 deletions

View File

@ -3,6 +3,7 @@ package org.asamk.signal.manager.api;
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.manager.helper.RecipientAddressResolver;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.asamk.signal.manager.util.MimeUtils;
import org.signal.core.models.ServiceId;
import org.signal.libsignal.metadata.ProtocolException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -37,6 +38,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.ViewedMessage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -128,10 +130,22 @@ public record MessageEnvelope(
static Data from(
final SignalServiceDataMessage dataMessage,
Map<String, String> longTexts,
RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider
) {
var body = dataMessage.getBody();
if (dataMessage.getAttachments().isPresent()) {
for (final var attachment : dataMessage.getAttachments().get()) {
if (MimeUtils.LONG_TEXT.equals(attachment.getContentType()) && attachment.isPointer()) {
final var longBody = longTexts.get(attachment.asPointer().getRemoteId().toString());
if (longBody != null) {
body = Optional.of(longBody);
}
}
}
}
return new Data(dataMessage.getTimestamp(),
dataMessage.getGroupContext().map(GroupContext::from),
dataMessage.getStoryContext()
@ -139,7 +153,7 @@ public record MessageEnvelope(
recipientResolver,
addressResolver)),
dataMessage.getGroupCallUpdate().map(GroupCallUpdate::from),
dataMessage.getBody(),
body,
dataMessage.getExpiresInSeconds(),
dataMessage.isExpirationUpdate(),
dataMessage.isViewOnce(),
@ -621,12 +635,17 @@ public record MessageEnvelope(
public static Edit from(
final SignalServiceEditMessage editMessage,
Map<String, String> longTexts,
RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider
) {
return new Edit(editMessage.getTargetSentTimestamp(),
Data.from(editMessage.getDataMessage(), recipientResolver, addressResolver, fileProvider));
Data.from(editMessage.getDataMessage(),
longTexts,
recipientResolver,
addressResolver,
fileProvider));
}
}
@ -643,12 +662,13 @@ public record MessageEnvelope(
public static Sync from(
final SignalServiceSyncMessage syncMessage,
Map<String, String> longTexts,
RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider
) {
return new Sync(syncMessage.getSent()
.map(s -> Sent.from(s, recipientResolver, addressResolver, fileProvider)),
.map(s -> Sent.from(s, longTexts, recipientResolver, addressResolver, fileProvider)),
syncMessage.getBlockedList().map(b -> Blocked.from(b, recipientResolver, addressResolver)),
syncMessage.getRead()
.map(r -> r.stream().map(rm -> Read.from(rm, recipientResolver, addressResolver)).toList())
@ -677,6 +697,7 @@ public record MessageEnvelope(
static Sent from(
SentTranscriptMessage sentMessage,
Map<String, String> longTexts,
RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider
@ -692,9 +713,17 @@ public record MessageEnvelope(
.toApiRecipientAddress())
.collect(Collectors.toSet()),
sentMessage.getDataMessage()
.map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)),
.map(message -> Data.from(message,
longTexts,
recipientResolver,
addressResolver,
fileProvider)),
sentMessage.getEditMessage()
.map(message -> Edit.from(message, recipientResolver, addressResolver, fileProvider)),
.map(message -> Edit.from(message,
longTexts,
recipientResolver,
addressResolver,
fileProvider)),
sentMessage.getStoryMessage().map(s -> Story.from(s, fileProvider)));
}
}
@ -993,6 +1022,7 @@ public record MessageEnvelope(
public static MessageEnvelope from(
SignalServiceEnvelope envelope,
SignalServiceContent content,
Map<String, String> longTexts,
RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider,
@ -1023,9 +1053,15 @@ public record MessageEnvelope(
receipt = content.getReceiptMessage().map(Receipt::from);
typing = content.getTypingMessage().map(Typing::from);
data = content.getDataMessage()
.map(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver, fileProvider));
edit = content.getEditMessage().map(s -> Edit.from(s, recipientResolver, addressResolver, fileProvider));
sync = content.getSyncMessage().map(s -> Sync.from(s, recipientResolver, addressResolver, fileProvider));
.map(dataMessage -> Data.from(dataMessage,
longTexts,
recipientResolver,
addressResolver,
fileProvider));
edit = content.getEditMessage()
.map(s -> Edit.from(s, longTexts, recipientResolver, addressResolver, fileProvider));
sync = content.getSyncMessage()
.map(s -> Sync.from(s, longTexts, recipientResolver, addressResolver, fileProvider));
call = content.getCallMessage().map(Call::from);
story = content.getStoryMessage().map(s -> Story.from(s, fileProvider));
} else {

View File

@ -35,6 +35,7 @@ import org.asamk.signal.manager.storage.groups.GroupInfoV1;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.stickers.StickerPack;
import org.asamk.signal.manager.util.MimeUtils;
import org.signal.core.models.ServiceId;
import org.signal.core.models.ServiceId.ACI;
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
@ -70,8 +71,13 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.Envelope;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@ -273,13 +279,18 @@ public final class IncomingMessageHandler {
return List.of();
} else {
List<HandleAction> actions;
Map<String, String> longTexts;
if (content != null) {
actions = handleMessage(envelope, content, receiveConfig);
final var results = handleMessage(envelope, content, receiveConfig);
actions = results.first();
longTexts = results.second();
} else {
actions = List.of();
longTexts = Map.of();
}
handler.handleMessage(MessageEnvelope.from(envelope,
content,
longTexts,
account.getRecipientResolver(),
account.getRecipientAddressResolver(),
context.getAttachmentHelper()::getAttachmentFile,
@ -288,12 +299,13 @@ public final class IncomingMessageHandler {
}
}
public List<HandleAction> handleMessage(
public Pair<List<HandleAction>, Map<String, String>> handleMessage(
SignalServiceEnvelope envelope,
SignalServiceContent content,
ReceiveConfig receiveConfig
) {
var actions = new ArrayList<HandleAction>();
final var actions = new ArrayList<HandleAction>();
final var longTexts = new HashMap<String, String>();
final var senderDeviceAddress = getSender(envelope, content);
final var sender = senderDeviceAddress.recipientId();
final var senderServiceId = senderDeviceAddress.serviceId();
@ -368,11 +380,13 @@ public final class IncomingMessageHandler {
message.getTimestamp()));
}
actions.addAll(handleSignalServiceDataMessage(message,
final var dataResults = handleSignalServiceDataMessage(message,
false,
senderDeviceAddress,
destination,
receiveConfig));
receiveConfig);
actions.addAll(dataResults.first());
longTexts.putAll(dataResults.second());
}
if (content.getStoryMessage().isPresent()) {
@ -382,10 +396,12 @@ public final class IncomingMessageHandler {
if (content.getSyncMessage().isPresent()) {
var syncMessage = content.getSyncMessage().get();
actions.addAll(handleSyncMessage(envelope, syncMessage, senderDeviceAddress, receiveConfig));
final var syncResults = handleSyncMessage(envelope, syncMessage, senderDeviceAddress, receiveConfig);
actions.addAll(syncResults.first());
longTexts.putAll(syncResults.second());
}
return actions;
return new Pair<>(actions, longTexts);
}
private boolean handlePniSignatureMessage(
@ -471,19 +487,20 @@ public final class IncomingMessageHandler {
}
}
private List<HandleAction> handleSyncMessage(
private Pair<List<HandleAction>, Map<String, String>> handleSyncMessage(
final SignalServiceEnvelope envelope,
final SignalServiceSyncMessage syncMessage,
final DeviceAddress sender,
final ReceiveConfig receiveConfig
) {
var actions = new ArrayList<HandleAction>();
final var actions = new ArrayList<HandleAction>();
final var longTexts = new HashMap<String, String>();
account.setMultiDevice(true);
if (syncMessage.getSent().isPresent()) {
var message = syncMessage.getSent().get();
final var destination = message.getDestination().orElse(null);
if (message.getDataMessage().isPresent()) {
actions.addAll(handleSignalServiceDataMessage(message.getDataMessage().get(),
final var dataResults = handleSignalServiceDataMessage(message.getDataMessage().get(),
true,
sender,
destination == null
@ -491,7 +508,9 @@ public final class IncomingMessageHandler {
: new DeviceAddress(account.getRecipientResolver().resolveRecipient(destination),
destination.getServiceId(),
0),
receiveConfig));
receiveConfig);
actions.addAll(dataResults.first());
longTexts.putAll(dataResults.second());
}
if (message.getStoryMessage().isPresent()) {
actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(),
@ -643,7 +662,7 @@ public final class IncomingMessageHandler {
actions.add(RetrieveDeviceNameAction.create());
}
}
return actions;
return new Pair<>(actions, longTexts);
}
private SignalServiceGroupContext getGroupContext(SignalServiceContent content) {
@ -742,13 +761,14 @@ public final class IncomingMessageHandler {
return false;
}
private List<HandleAction> handleSignalServiceDataMessage(
private Pair<List<HandleAction>, Map<String, String>> handleSignalServiceDataMessage(
SignalServiceDataMessage message,
boolean isSync,
DeviceAddress source,
DeviceAddress destination,
ReceiveConfig receiveConfig
) {
final var longTexts = new HashMap<String, String>();
var actions = new ArrayList<HandleAction>();
if (message.getGroupContext().isPresent()) {
final var groupContext = message.getGroupContext().get();
@ -843,6 +863,17 @@ public final class IncomingMessageHandler {
if (message.getAttachments().isPresent()) {
for (var attachment : message.getAttachments().get()) {
context.getAttachmentHelper().downloadAttachment(attachment);
if (attachment.isPointer()) {
final var file = context.getAttachmentHelper().getAttachmentFile(attachment.asPointer());
if (MimeUtils.LONG_TEXT.equals(attachment.getContentType()) && attachment.isPointer()) {
try {
final var longText = Files.readString(file.toPath());
longTexts.put(attachment.asPointer().getRemoteId().toString(), longText);
} catch (IOException e) {
logger.warn("Failed to read long text attachment, ignoring", e);
}
}
}
}
}
if (message.getSharedContacts().isPresent()) {
@ -872,6 +903,21 @@ public final class IncomingMessageHandler {
}
}
}
} else {
if (message.getAttachments().isPresent()) {
for (var attachment : message.getAttachments().get()) {
if (MimeUtils.LONG_TEXT.equals(attachment.getContentType()) && attachment.isPointer()) {
try {
context.getAttachmentHelper().retrieveAttachment(attachment, in -> {
final var longText = new String(in.readAllBytes(), StandardCharsets.UTF_8);
longTexts.put(attachment.asPointer().getRemoteId().toString(), longText);
});
} catch (IOException e) {
logger.warn("Failed to download long text attachment, ignoring", e);
}
}
}
}
}
if (message.getGiftBadge().isPresent()) {
handleIncomingGiftBadge(message.getGiftBadge().get());
@ -892,7 +938,7 @@ public final class IncomingMessageHandler {
.enqueueJob(new RetrieveStickerPackJob(stickerPackId, messageSticker.getPackKey()));
}
}
return actions;
return new Pair<>(actions, longTexts);
}
private void handleIncomingGiftBadge(final SignalServiceDataMessage.GiftBadge giftBadge) {