Implement editing of previous messages

This commit is contained in:
AsamK 2023-05-11 19:10:29 +02:00
parent 72390e595d
commit 8a31b7f2c1
13 changed files with 179 additions and 42 deletions

View file

@ -142,6 +142,10 @@ public interface Manager extends Closeable {
Message message, Set<RecipientIdentifier> recipients
) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException;
SendMessageResults sendEditMessage(
Message message, Set<RecipientIdentifier> recipients, long editTargetTimestamp
) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException;
SendMessageResults sendRemoteDeleteMessage(
long targetSentTimestamp, Set<RecipientIdentifier> recipients
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException;

View file

@ -466,6 +466,14 @@ class ManagerImpl implements Manager {
private SendMessageResults sendMessage(
SignalServiceDataMessage.Builder messageBuilder, Set<RecipientIdentifier> recipients
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
return sendMessage(messageBuilder, recipients, Optional.empty());
}
private SendMessageResults sendMessage(
SignalServiceDataMessage.Builder messageBuilder,
Set<RecipientIdentifier> recipients,
Optional<Long> editTargetTimestamp
) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
var results = new HashMap<RecipientIdentifier, List<SendMessageResult>>();
long timestamp = System.currentTimeMillis();
@ -474,17 +482,19 @@ class ManagerImpl implements Manager {
if (recipient instanceof RecipientIdentifier.Single single) {
try {
final var recipientId = context.getRecipientHelper().resolveRecipient(single);
final var result = context.getSendHelper().sendMessage(messageBuilder, recipientId);
final var result = context.getSendHelper()
.sendMessage(messageBuilder, recipientId, editTargetTimestamp);
results.put(recipient, List.of(toSendMessageResult(result)));
} catch (UnregisteredRecipientException e) {
results.put(recipient,
List.of(SendMessageResult.unregisteredFailure(single.toPartialRecipientAddress())));
}
} else if (recipient instanceof RecipientIdentifier.NoteToSelf) {
final var result = context.getSendHelper().sendSelfMessage(messageBuilder);
final var result = context.getSendHelper().sendSelfMessage(messageBuilder, editTargetTimestamp);
results.put(recipient, List.of(toSendMessageResult(result)));
} else if (recipient instanceof RecipientIdentifier.Group group) {
final var result = context.getSendHelper().sendAsGroupMessage(messageBuilder, group.groupId());
final var result = context.getSendHelper()
.sendAsGroupMessage(messageBuilder, group.groupId(), editTargetTimestamp);
results.put(recipient, result.stream().map(this::toSendMessageResult).toList());
}
}
@ -581,6 +591,15 @@ class ManagerImpl implements Manager {
return sendMessage(messageBuilder, recipients);
}
@Override
public SendMessageResults sendEditMessage(
Message message, Set<RecipientIdentifier> recipients, long editTargetTimestamp
) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException, UnregisteredRecipientException, InvalidStickerException {
final var messageBuilder = SignalServiceDataMessage.newBuilder();
applyMessage(messageBuilder, message);
return sendMessage(messageBuilder, recipients, Optional.of(editTargetTimestamp));
}
private void applyMessage(
final SignalServiceDataMessage.Builder messageBuilder, final Message message
) throws AttachmentInvalidException, IOException, UnregisteredRecipientException, InvalidStickerException {

View file

@ -9,6 +9,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
@ -51,6 +52,7 @@ public record MessageEnvelope(
Optional<Receipt> receipt,
Optional<Typing> typing,
Optional<Data> data,
Optional<Edit> edit,
Optional<Sync> sync,
Optional<Call> call,
Optional<Story> story
@ -541,6 +543,19 @@ public record MessageEnvelope(
}
}
public record Edit(long targetSentTimestamp, Data dataMessage) {
public static Edit from(
final SignalServiceEditMessage editMessage,
RecipientResolver recipientResolver,
RecipientAddressResolver addressResolver,
final AttachmentFileProvider fileProvider
) {
return new Edit(editMessage.getTargetSentTimestamp(),
Data.from(editMessage.getDataMessage(), recipientResolver, addressResolver, fileProvider));
}
}
public record Sync(
Optional<Sent> sent,
Optional<Blocked> blocked,
@ -582,6 +597,7 @@ public record MessageEnvelope(
Optional<RecipientAddress> destination,
Set<RecipientAddress> recipients,
Optional<Data> message,
Optional<Edit> editMessage,
Optional<Story> story
) {
@ -603,6 +619,8 @@ public record MessageEnvelope(
.collect(Collectors.toSet()),
sentMessage.getDataMessage()
.map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)),
sentMessage.getEditMessage()
.map(message -> Edit.from(message, recipientResolver, addressResolver, fileProvider)),
sentMessage.getStoryMessage().map(s -> Story.from(s, fileProvider)));
}
}
@ -920,6 +938,7 @@ public record MessageEnvelope(
Optional<Receipt> receipt;
Optional<Typing> typing;
Optional<Data> data;
Optional<Edit> edit;
Optional<Sync> sync;
Optional<Call> call;
Optional<Story> story;
@ -928,6 +947,7 @@ public record MessageEnvelope(
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));
call = content.getCallMessage().map(Call::from);
story = content.getStoryMessage().map(s -> Story.from(s, fileProvider));
@ -937,6 +957,7 @@ public record MessageEnvelope(
List.of(envelope.getTimestamp()))) : Optional.empty();
typing = Optional.empty();
data = Optional.empty();
edit = Optional.empty();
sync = Optional.empty();
call = Optional.empty();
story = Optional.empty();
@ -953,6 +974,7 @@ public record MessageEnvelope(
receipt,
typing,
data,
edit,
sync,
call,
story);

View file

@ -563,7 +563,7 @@ public class GroupHelper {
private void sendExpirationTimerUpdate(GroupIdV1 groupId) throws IOException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException {
final var messageBuilder = SignalServiceDataMessage.newBuilder().asExpirationUpdate();
context.getSendHelper().sendAsGroupMessage(messageBuilder, groupId);
context.getSendHelper().sendAsGroupMessage(messageBuilder, groupId, Optional.empty());
}
private SendGroupMessageResults updateGroupV2(

View file

@ -29,6 +29,7 @@ import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEditMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
@ -71,8 +72,10 @@ public class SendHelper {
* The message is extended with the current expiration timer.
*/
public SendMessageResult sendMessage(
final SignalServiceDataMessage.Builder messageBuilder, final RecipientId recipientId
) throws IOException {
final SignalServiceDataMessage.Builder messageBuilder,
final RecipientId recipientId,
Optional<Long> editTargetTimestamp
) {
var contact = account.getContactStore().getContact(recipientId);
if (contact == null || !contact.isProfileSharingEnabled()) {
final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
@ -89,7 +92,7 @@ public class SendHelper {
}
final var message = messageBuilder.build();
return sendMessage(message, recipientId);
return sendMessage(message, recipientId, editTargetTimestamp);
}
/**
@ -97,10 +100,10 @@ public class SendHelper {
* The message is extended with the current expiration timer for the group and the group context.
*/
public List<SendMessageResult> sendAsGroupMessage(
SignalServiceDataMessage.Builder messageBuilder, GroupId groupId
SignalServiceDataMessage.Builder messageBuilder, GroupId groupId, Optional<Long> editTargetTimestamp
) throws IOException, GroupNotFoundException, NotAGroupMemberException, GroupSendingNotAllowedException {
final var g = getGroupForSending(groupId);
return sendAsGroupMessage(messageBuilder, g);
return sendAsGroupMessage(messageBuilder, g, editTargetTimestamp);
}
/**
@ -112,7 +115,7 @@ public class SendHelper {
final Set<RecipientId> recipientIds,
final DistributionId distributionId
) throws IOException {
return sendGroupMessage(message, recipientIds, distributionId, ContentHint.IMPLICIT);
return sendGroupMessage(message, recipientIds, distributionId, ContentHint.IMPLICIT, Optional.empty());
}
public SendMessageResult sendReceiptMessage(
@ -169,7 +172,7 @@ public class SendHelper {
}
public SendMessageResult sendSelfMessage(
SignalServiceDataMessage.Builder messageBuilder
SignalServiceDataMessage.Builder messageBuilder, Optional<Long> editTargetTimestamp
) {
final var recipientId = account.getSelfRecipientId();
final var contact = account.getContactStore().getContact(recipientId);
@ -177,7 +180,7 @@ public class SendHelper {
messageBuilder.withExpiration(expirationTime);
var message = messageBuilder.build();
return sendSelfMessage(message);
return sendSelfMessage(message, editTargetTimestamp);
}
public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) {
@ -289,7 +292,7 @@ public class SendHelper {
}
private List<SendMessageResult> sendAsGroupMessage(
final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo g
final SignalServiceDataMessage.Builder messageBuilder, final GroupInfo g, Optional<Long> editTargetTimestamp
) throws IOException, GroupSendingNotAllowedException {
GroupUtils.setGroupContext(messageBuilder, g);
messageBuilder.withExpiration(g.getMessageExpirationTimer());
@ -308,14 +311,19 @@ public class SendHelper {
}
}
return sendGroupMessage(message, recipients, g.getDistributionId(), ContentHint.RESENDABLE);
return sendGroupMessage(message,
recipients,
g.getDistributionId(),
ContentHint.RESENDABLE,
editTargetTimestamp);
}
private List<SendMessageResult> sendGroupMessage(
final SignalServiceDataMessage message,
final Set<RecipientId> recipientIds,
final DistributionId distributionId,
final ContentHint contentHint
final ContentHint contentHint,
final Optional<Long> editTargetTimestamp
) throws IOException {
final var messageSender = dependencies.getMessageSender();
final var messageSendLogStore = account.getMessageSendLogStore();
@ -355,7 +363,7 @@ public class SendHelper {
SignalServiceMessageSender.SenderKeyGroupEvents.EMPTY,
urgent,
false,
null,
editTargetTimestamp.map(timestamp -> new SignalServiceEditMessage(timestamp, message)).orElse(null),
sendResult -> {
logger.trace("Partial message send results: {}", sendResult.size());
synchronized (entryId) {
@ -613,18 +621,27 @@ public class SendHelper {
}
private SendMessageResult sendMessage(
SignalServiceDataMessage message, RecipientId recipientId
SignalServiceDataMessage message, RecipientId recipientId, Optional<Long> editTargetTimestamp
) {
final var messageSendLogStore = account.getMessageSendLogStore();
final var urgent = true;
final var includePniSignature = false;
final var result = handleSendMessage(recipientId,
(messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address,
editTargetTimestamp.isEmpty()
? (messageSender, address, unidentifiedAccess) -> messageSender.sendDataMessage(address,
unidentifiedAccess,
ContentHint.RESENDABLE,
message,
SignalServiceMessageSender.IndividualSendEvents.EMPTY,
urgent,
false));
includePniSignature)
: (messageSender, address, unidentifiedAccess) -> messageSender.sendEditMessage(address,
unidentifiedAccess,
ContentHint.RESENDABLE,
message,
SignalServiceMessageSender.IndividualSendEvents.EMPTY,
urgent,
editTargetTimestamp.get()));
messageSendLogStore.insertIfPossible(message.getTimestamp(), result, ContentHint.RESENDABLE, urgent);
handleSendMessageResult(result);
return result;
@ -665,17 +682,17 @@ public class SendHelper {
}
}
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) {
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message, Optional<Long> editTargetTimestamp) {
var address = account.getSelfAddress();
var transcript = new SentTranscriptMessage(Optional.of(address),
message.getTimestamp(),
Optional.of(message),
editTargetTimestamp.isEmpty() ? Optional.of(message) : Optional.empty(),
message.getExpiresInSeconds(),
Map.of(address.getServiceId(), true),
false,
Optional.empty(),
Set.of(),
Optional.empty());
editTargetTimestamp.map((timestamp) -> new SignalServiceEditMessage(timestamp, message)));
var syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);
return sendSyncMessage(syncMessage);