Flag deletes and replies to group stories as stories.

This commit is contained in:
Greyson Parrelli 2022-10-07 18:43:06 -04:00
parent 30b635cca2
commit 437d6c7a52
5 changed files with 59 additions and 15 deletions

View file

@ -296,7 +296,7 @@ public final class PushGroupSendJob extends PushSendJob {
.withExpiration(groupRecipient.getExpiresInSeconds()) .withExpiration(groupRecipient.getExpiresInSeconds())
.asGroupMessage(group) .asGroupMessage(group)
.build(); .build();
return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId, true), groupDataMessage, message.isUrgent()); return GroupSendUtil.sendResendableDataMessage(context, groupRecipient.requireGroupId().requireV2(), null, destinations, isRecipientUpdate, ContentHint.IMPLICIT, new MessageId(messageId, true), groupDataMessage, message.isUrgent(), false);
} else { } else {
throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!"); throw new UndeliverableMessageException("Messages can no longer be sent to V1 groups!");
} }
@ -351,12 +351,14 @@ public final class PushGroupSendJob extends PushSendJob {
return GroupSendUtil.sendResendableDataMessage(context, return GroupSendUtil.sendResendableDataMessage(context,
groupRecipient.getGroupId().map(GroupId::requireV2).orElse(null), groupRecipient.getGroupId().map(GroupId::requireV2).orElse(null),
null,
destinations, destinations,
isRecipientUpdate, isRecipientUpdate,
ContentHint.RESENDABLE, ContentHint.RESENDABLE,
new MessageId(messageId, true), new MessageId(messageId, true),
groupMessageBuilder.build(), groupMessageBuilder.build(),
message.isUrgent()); message.isUrgent(),
message.getStoryType().isStory() || message.getParentStoryId() != null);
} }
} catch (ServerRejectedException e) { } catch (ServerRejectedException e) {
throw new UndeliverableMessageException(e); throw new UndeliverableMessageException(e);

View file

@ -238,12 +238,14 @@ public class ReactionSendJob extends BaseJob {
boolean includesSelf = nonSelfDestinations.size() != destinations.size(); boolean includesSelf = nonSelfDestinations.size() != destinations.size();
List<SendMessageResult> results = GroupSendUtil.sendResendableDataMessage(context, List<SendMessageResult> results = GroupSendUtil.sendResendableDataMessage(context,
conversationRecipient.getGroupId().map(GroupId::requireV2).orElse(null), conversationRecipient.getGroupId().map(GroupId::requireV2).orElse(null),
null,
nonSelfDestinations, nonSelfDestinations,
false, false,
ContentHint.RESENDABLE, ContentHint.RESENDABLE,
messageId, messageId,
dataMessage, dataMessage,
true); true,
false);
if (includesSelf) { if (includesSelf) {
results.add(ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(dataMessage)); results.add(ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(dataMessage));

View file

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.jobs; package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
@ -10,8 +11,11 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.MessageDatabase; import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListId;
import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageId;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.StoryType;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Data;
@ -29,6 +33,7 @@ import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import java.io.IOException; import java.io.IOException;
@ -157,7 +162,10 @@ public class RemoteDeleteSendJob extends BaseJob {
List<Recipient> eligible = RecipientUtil.getEligibleForSending(Stream.of(recipients).map(Recipient::resolved).toList()); List<Recipient> eligible = RecipientUtil.getEligibleForSending(Stream.of(recipients).map(Recipient::resolved).toList());
List<RecipientId> skipped = Stream.of(SetUtil.difference(possible, eligible)).map(Recipient::getId).toList(); List<RecipientId> skipped = Stream.of(SetUtil.difference(possible, eligible)).map(Recipient::getId).toList();
GroupSendJobHelper.SendResult sendResult = deliver(conversationRecipient, eligible, targetSentTimestamp); boolean isForStory = message.isMms() && (((MmsMessageRecord) message).getStoryType().isStory() || ((MmsMessageRecord) message).getParentStoryId() != null);
DistributionListId distributionListId = isForStory ? message.getRecipient().getDistributionListId().orElse(null) : null;
GroupSendJobHelper.SendResult sendResult = deliver(conversationRecipient, eligible, targetSentTimestamp, isForStory, distributionListId);
for (Recipient completion : sendResult.completed) { for (Recipient completion : sendResult.completed) {
recipients.remove(completion.getId()); recipients.remove(completion.getId());
@ -196,7 +204,11 @@ public class RemoteDeleteSendJob extends BaseJob {
Log.w(TAG, "Failed to send remote delete to all recipients! (" + (initialRecipientCount - recipients.size() + "/" + initialRecipientCount + ")") ); Log.w(TAG, "Failed to send remote delete to all recipients! (" + (initialRecipientCount - recipients.size() + "/" + initialRecipientCount + ")") );
} }
private @NonNull GroupSendJobHelper.SendResult deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations, long targetSentTimestamp) private @NonNull GroupSendJobHelper.SendResult deliver(@NonNull Recipient conversationRecipient,
@NonNull List<Recipient> destinations,
long targetSentTimestamp,
boolean isForStory,
@Nullable DistributionListId distributionListId)
throws IOException, UntrustedIdentityException throws IOException, UntrustedIdentityException
{ {
SignalServiceDataMessage.Builder dataMessageBuilder = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage.Builder dataMessageBuilder = SignalServiceDataMessage.newBuilder()
@ -210,12 +222,14 @@ public class RemoteDeleteSendJob extends BaseJob {
SignalServiceDataMessage dataMessage = dataMessageBuilder.build(); SignalServiceDataMessage dataMessage = dataMessageBuilder.build();
List<SendMessageResult> results = GroupSendUtil.sendResendableDataMessage(context, List<SendMessageResult> results = GroupSendUtil.sendResendableDataMessage(context,
conversationRecipient.getGroupId().map(GroupId::requireV2).orElse(null), conversationRecipient.getGroupId().map(GroupId::requireV2).orElse(null),
distributionListId,
destinations, destinations,
false, false,
ContentHint.RESENDABLE, ContentHint.RESENDABLE,
new MessageId(messageId, isMms), new MessageId(messageId, isMms),
dataMessage, dataMessage,
true); true,
isForStory);
return GroupSendJobHelper.getCompletedSends(destinations, results); return GroupSendJobHelper.getCompletedSends(destinations, results);
} }

View file

@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
import org.whispersystems.signalservice.api.util.Preconditions;
import org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException; import org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException;
import org.whispersystems.signalservice.internal.push.http.CancelationSignal; import org.whispersystems.signalservice.internal.push.http.CancelationSignal;
import org.whispersystems.signalservice.internal.push.http.PartialSendCompleteListener; import org.whispersystems.signalservice.internal.push.http.PartialSendCompleteListener;
@ -82,19 +83,41 @@ public final class GroupSendUtil {
* *
* @param groupId The groupId of the group you're sending to, or null if you're sending to a collection of recipients not joined by a group. * @param groupId The groupId of the group you're sending to, or null if you're sending to a collection of recipients not joined by a group.
* @param isRecipientUpdate True if you've already sent this message to some recipients in the past, otherwise false. * @param isRecipientUpdate True if you've already sent this message to some recipients in the past, otherwise false.
* @param isForStory True if the message is related to a story, and should be sent with the story flag on the envelope
*/ */
@WorkerThread @WorkerThread
public static List<SendMessageResult> sendResendableDataMessage(@NonNull Context context, public static List<SendMessageResult> sendResendableDataMessage(@NonNull Context context,
@Nullable GroupId.V2 groupId, @Nullable GroupId.V2 groupId,
@Nullable DistributionListId distributionListId,
@NonNull List<Recipient> allTargets, @NonNull List<Recipient> allTargets,
boolean isRecipientUpdate, boolean isRecipientUpdate,
ContentHint contentHint, ContentHint contentHint,
@NonNull MessageId messageId, @NonNull MessageId messageId,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
boolean urgent) boolean urgent,
boolean isForStory)
throws IOException, UntrustedIdentityException throws IOException, UntrustedIdentityException
{ {
return sendMessage(context, groupId, getDistributionId(groupId), messageId, allTargets, isRecipientUpdate, false, DataSendOperation.resendable(message, contentHint, messageId, urgent), null); Preconditions.checkArgument(groupId == null || distributionListId == null, "Cannot supply both a groupId and a distributionListId!");
DistributionId distributionId = groupId != null ? getDistributionId(groupId) : getDistributionId(distributionListId);
return sendMessage(context, groupId, distributionId, messageId, allTargets, isRecipientUpdate, isForStory, DataSendOperation.resendable(message, contentHint, messageId, urgent, isForStory), null);
}
@WorkerThread
public static List<SendMessageResult> sendResendableStoryRelatedMessage(@NonNull Context context,
@Nullable GroupId.V2 groupId,
@NonNull DistributionListId distributionListId,
@NonNull List<Recipient> allTargets,
boolean isRecipientUpdate,
ContentHint contentHint,
@NonNull MessageId messageId,
@NonNull SignalServiceDataMessage message,
boolean urgent)
throws IOException, UntrustedIdentityException
{
return sendMessage(context, groupId, getDistributionId(distributionListId), messageId, allTargets, isRecipientUpdate, true, DataSendOperation.resendable(message, contentHint, messageId, urgent, true), null);
} }
/** /**
@ -441,21 +464,23 @@ public final class GroupSendUtil {
private final MessageId relatedMessageId; private final MessageId relatedMessageId;
private final boolean resendable; private final boolean resendable;
private final boolean urgent; private final boolean urgent;
private final boolean isForStory;
public static DataSendOperation resendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, @NonNull MessageId relatedMessageId, boolean urgent) { public static DataSendOperation resendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, @NonNull MessageId relatedMessageId, boolean urgent, boolean isForStory) {
return new DataSendOperation(message, contentHint, true, relatedMessageId, urgent); return new DataSendOperation(message, contentHint, true, relatedMessageId, urgent, isForStory);
} }
public static DataSendOperation unresendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean urgent) { public static DataSendOperation unresendable(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean urgent) {
return new DataSendOperation(message, contentHint, false, null, urgent); return new DataSendOperation(message, contentHint, false, null, urgent, false);
} }
private DataSendOperation(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean resendable, @Nullable MessageId relatedMessageId, boolean urgent) { private DataSendOperation(@NonNull SignalServiceDataMessage message, @NonNull ContentHint contentHint, boolean resendable, @Nullable MessageId relatedMessageId, boolean urgent, boolean isForStory) {
this.message = message; this.message = message;
this.contentHint = contentHint; this.contentHint = contentHint;
this.resendable = resendable; this.resendable = resendable;
this.relatedMessageId = relatedMessageId; this.relatedMessageId = relatedMessageId;
this.urgent = urgent; this.urgent = urgent;
this.isForStory = isForStory;
if (resendable && relatedMessageId == null) { if (resendable && relatedMessageId == null) {
throw new IllegalArgumentException("If a message is resendable, it must have a related message ID!"); throw new IllegalArgumentException("If a message is resendable, it must have a related message ID!");
@ -471,7 +496,7 @@ public final class GroupSendUtil {
throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException
{ {
SenderKeyGroupEvents listener = relatedMessageId != null ? new SenderKeyMetricEventListener(relatedMessageId.getId()) : SenderKeyGroupEvents.EMPTY; SenderKeyGroupEvents listener = relatedMessageId != null ? new SenderKeyMetricEventListener(relatedMessageId.getId()) : SenderKeyGroupEvents.EMPTY;
return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message, listener, urgent); return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message, listener, urgent, isForStory);
} }
@Override @Override

View file

@ -475,14 +475,15 @@ public class SignalServiceMessageSender {
ContentHint contentHint, ContentHint contentHint,
SignalServiceDataMessage message, SignalServiceDataMessage message,
SenderKeyGroupEvents sendEvents, SenderKeyGroupEvents sendEvents,
boolean urgent) boolean urgent,
boolean isForStory)
throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException
{ {
Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients using DistributionId " + distributionId); Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients using DistributionId " + distributionId);
Content content = createMessageContent(message); Content content = createMessageContent(message);
Optional<byte[]> groupId = message.getGroupId(); Optional<byte[]> groupId = message.getGroupId();
List<SendMessageResult> results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId, false, sendEvents, urgent, false); List<SendMessageResult> results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId, false, sendEvents, urgent, isForStory);
sendEvents.onMessageSent(); sendEvents.onMessageSent();