package org.asamk.signal.manager.api;
-import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.manager.helper.RecipientAddressResolver;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
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;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ViewedMessage;
+import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.File;
+import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Optional<Receipt> receipt,
Optional<Typing> typing,
Optional<Data> data,
+ Optional<Edit> edit,
Optional<Sync> sync,
Optional<Call> call,
Optional<Story> story
Optional<Sticker> sticker,
List<SharedContact> sharedContacts,
List<Mention> mentions,
- List<Preview> previews
+ List<Preview> previews,
+ List<TextStyle> textStyles
) {
static Data from(
dataMessage.isProfileKeyUpdate(),
dataMessage.getProfileKey().isPresent(),
dataMessage.getReaction().map(r -> Reaction.from(r, recipientResolver, addressResolver)),
- dataMessage.getQuote().map(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)),
+ dataMessage.getQuote()
+ .filter(q -> q.getAuthor() != null && q.getAuthor().isValid())
+ .map(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider)),
dataMessage.getPayment().map(p -> p.getPaymentNotification().isPresent() ? Payment.from(p) : null),
dataMessage.getAttachments()
.map(a -> a.stream().map(as -> Attachment.from(as, fileProvider)).toList())
.orElse(List.of()),
dataMessage.getPreviews()
.map(a -> a.stream().map(preview -> Preview.from(preview, fileProvider)).toList())
+ .orElse(List.of()),
+ dataMessage.getBodyRanges()
+ .map(a -> a.stream().filter(r -> r.style != null).map(TextStyle::from).toList())
.orElse(List.of()));
}
RecipientAddress author,
Optional<String> text,
List<Mention> mentions,
- List<Attachment> attachments
+ List<Attachment> attachments,
+ List<TextStyle> textStyles
) {
static Quote from(
return new Quote(quote.getId(),
addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor()))
.toApiRecipientAddress(),
- Optional.ofNullable(quote.getText()),
+ Optional.of(quote.getText()),
quote.getMentions() == null
? List.of()
: quote.getMentions()
.toList(),
quote.getAttachments() == null
? List.of()
- : quote.getAttachments().stream().map(a -> Attachment.from(a, fileProvider)).toList());
+ : quote.getAttachments().stream().map(a -> Attachment.from(a, fileProvider)).toList(),
+ quote.getBodyRanges() == null
+ ? List.of()
+ : quote.getBodyRanges()
+ .stream()
+ .filter(r -> r.style != null)
+ .map(TextStyle::from)
+ .toList());
}
}
boolean isBorderless
) {
- static Attachment from(SignalServiceAttachment attachment, AttachmentFileProvider fileProvider) {
- if (attachment.isPointer()) {
- final var a = attachment.asPointer();
+ static Attachment from(SignalServiceAttachment signalAttachment, AttachmentFileProvider fileProvider) {
+ if (signalAttachment.isPointer()) {
+ final var a = signalAttachment.asPointer();
final var attachmentFile = fileProvider.getFile(a);
return new Attachment(Optional.of(attachmentFile.getName()),
Optional.of(attachmentFile),
a.isGif(),
a.isBorderless());
} else {
- final var a = attachment.asStream();
- return new Attachment(Optional.empty(),
- Optional.empty(),
- a.getFileName(),
- a.getContentType(),
- a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()),
- Optional.of(a.getLength()),
- a.getPreview(),
- Optional.empty(),
- a.getCaption(),
- a.getWidth() == 0 ? Optional.empty() : Optional.of(a.getWidth()),
- a.getHeight() == 0 ? Optional.empty() : Optional.of(a.getHeight()),
- a.getVoiceNote(),
- a.isGif(),
- a.isBorderless());
+ Attachment attachment = null;
+ try (final var a = signalAttachment.asStream()) {
+ attachment = new Attachment(Optional.empty(),
+ Optional.empty(),
+ a.getFileName(),
+ a.getContentType(),
+ a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()),
+ Optional.of(a.getLength()),
+ a.getPreview(),
+ Optional.empty(),
+ a.getCaption(),
+ a.getWidth() == 0 ? Optional.empty() : Optional.of(a.getWidth()),
+ a.getHeight() == 0 ? Optional.empty() : Optional.of(a.getHeight()),
+ a.getVoiceNote(),
+ a.isGif(),
+ a.isBorderless());
+ return attachment;
+ } catch (IOException e) {
+ return attachment;
+ }
}
}
static Attachment from(
- SignalServiceDataMessage.Quote.QuotedAttachment a, final AttachmentFileProvider fileProvider
+ SignalServiceDataMessage.Quote.QuotedAttachment a,
+ final AttachmentFileProvider fileProvider
) {
return new Attachment(Optional.empty(),
Optional.empty(),
}
public record Name(
- Optional<String> display,
+ Optional<String> nickname,
Optional<String> given,
Optional<String> family,
Optional<String> prefix,
) {
static Name from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Name name) {
- return new Name(name.getDisplay(),
+ return new Name(name.getNickname(),
name.getGiven(),
name.getFamily(),
name.getPrefix(),
) {
static Address from(org.whispersystems.signalservice.api.messages.shared.SharedContact.PostalAddress address) {
- return new Address(Address.Type.from(address.getType()),
- address.getLabel(),
+ return new Address(Type.from(address.getType()),
address.getLabel(),
- address.getLabel(),
- address.getLabel(),
- address.getLabel(),
- address.getLabel(),
- address.getLabel(),
- address.getLabel());
+ address.getStreet(),
+ address.getPobox(),
+ address.getNeighborhood(),
+ address.getCity(),
+ address.getRegion(),
+ address.getPostcode(),
+ address.getCountry());
}
public enum Type {
public record Preview(String title, String description, long date, String url, Optional<Attachment> image) {
- static Preview from(
- SignalServicePreview preview, final AttachmentFileProvider fileProvider
- ) {
+ static Preview from(SignalServicePreview preview, final AttachmentFileProvider fileProvider) {
return new Preview(preview.getTitle(),
preview.getDescription(),
preview.getDate(),
preview.getImage().map(as -> Attachment.from(as, fileProvider)));
}
}
+
+ }
+
+ 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<RecipientAddress> destination,
Set<RecipientAddress> recipients,
Optional<Data> message,
+ Optional<Edit> editMessage,
Optional<Story> story
) {
.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)));
}
}
DELETE,
BLOCK,
BLOCK_AND_DELETE,
- UNBLOCK_AND_ACCEPT;
+ UNBLOCK_AND_ACCEPT,
+ SPAM,
+ BLOCK_AND_SPAM;
static Type from(MessageRequestResponseMessage.Type type) {
return switch (type) {
case BLOCK -> BLOCK;
case BLOCK_AND_DELETE -> BLOCK_AND_DELETE;
case UNBLOCK_AND_ACCEPT -> UNBLOCK_AND_ACCEPT;
+ case SPAM -> SPAM;
+ case BLOCK_AND_SPAM -> BLOCK_AND_SPAM;
};
}
}
Optional<Hangup> hangup,
Optional<Busy> busy,
List<IceUpdate> iceUpdate,
- Optional<Opaque> opaque
+ Optional<Opaque> opaque,
+ boolean isUrgent
) {
public static Call from(final SignalServiceCallMessage callMessage) {
callMessage.getIceUpdateMessages()
.map(m -> m.stream().map(IceUpdate::from).toList())
.orElse(List.of()),
- callMessage.getOpaqueMessage().map(Opaque::from));
+ callMessage.getOpaqueMessage().map(Opaque::from),
+ callMessage.isUrgent());
}
- public record Offer(long id, String sdp, Type type, byte[] opaque) {
+ public record Offer(long id, Type type, byte[] opaque) {
static Offer from(OfferMessage offerMessage) {
- return new Offer(offerMessage.getId(),
- offerMessage.getSdp(),
- Type.from(offerMessage.getType()),
- offerMessage.getOpaque());
+ return new Offer(offerMessage.getId(), Type.from(offerMessage.getType()), offerMessage.getOpaque());
}
public enum Type {
}
}
- public record Answer(long id, String sdp, byte[] opaque) {
+ public record Answer(long id, byte[] opaque) {
static Answer from(AnswerMessage answerMessage) {
- return new Answer(answerMessage.getId(), answerMessage.getSdp(), answerMessage.getOpaque());
+ return new Answer(answerMessage.getId(), answerMessage.getOpaque());
}
}
}
}
- public record Hangup(long id, Type type, int deviceId, boolean isLegacy) {
+ public record Hangup(long id, Type type, int deviceId) {
static Hangup from(HangupMessage hangupMessage) {
return new Hangup(hangupMessage.getId(),
Type.from(hangupMessage.getType()),
- hangupMessage.getDeviceId(),
- hangupMessage.isLegacy());
+ hangupMessage.getDeviceId());
}
public enum Type {
}
}
- public record IceUpdate(long id, String sdp, byte[] opaque) {
+ public record IceUpdate(long id, byte[] opaque) {
static IceUpdate from(IceUpdateMessage iceUpdateMessage) {
- return new IceUpdate(iceUpdateMessage.getId(), iceUpdateMessage.getSdp(), iceUpdateMessage.getOpaque());
+ return new IceUpdate(iceUpdateMessage.getId(), iceUpdateMessage.getOpaque());
}
}
Optional<TextAttachment> textAttachment
) {
- public static Story from(
- SignalServiceStoryMessage storyMessage, final AttachmentFileProvider fileProvider
- ) {
+ public static Story from(SignalServiceStoryMessage storyMessage, final AttachmentFileProvider fileProvider) {
return new Story(storyMessage.getAllowsReplies().orElse(false),
storyMessage.getGroupContext().map(c -> GroupUtils.getGroupIdV2(c.getMasterKey())),
storyMessage.getFileAttachment().map(f -> Data.Attachment.from(f, fileProvider)),
) {
static TextAttachment from(
- SignalServiceTextAttachment textAttachment, final AttachmentFileProvider fileProvider
+ SignalServiceTextAttachment textAttachment,
+ final AttachmentFileProvider fileProvider
) {
return new TextAttachment(textAttachment.getText(),
textAttachment.getStyle().map(Style::from),
final AttachmentFileProvider fileProvider,
Exception exception
) {
- final var source = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid()
- ? recipientResolver.resolveRecipient(envelope.getSourceAddress())
+ final var serviceId = envelope.getSourceServiceId().map(ServiceId::parseOrNull).orElse(null);
+ final var source = !envelope.isUnidentifiedSender() && serviceId != null
+ ? recipientResolver.resolveRecipient(serviceId)
: envelope.isUnidentifiedSender() && content != null
? recipientResolver.resolveRecipient(content.getSender())
: exception instanceof ProtocolException e
Optional<Receipt> receipt;
Optional<Typing> typing;
Optional<Data> data;
+ Optional<Edit> edit;
Optional<Sync> sync;
Optional<Call> call;
Optional<Story> story;
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));
List.of(envelope.getTimestamp()))) : Optional.empty();
typing = Optional.empty();
data = Optional.empty();
+ edit = Optional.empty();
sync = Optional.empty();
call = Optional.empty();
story = Optional.empty();
receipt,
typing,
data,
+ edit,
sync,
call,
story);