1 package org
.asamk
.signal
.manager
.api
;
3 import org
.asamk
.signal
.manager
.groups
.GroupId
;
4 import org
.asamk
.signal
.manager
.groups
.GroupUtils
;
5 import org
.asamk
.signal
.manager
.helper
.RecipientAddressResolver
;
6 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientAddress
;
7 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientResolver
;
8 import org
.signal
.libsignal
.metadata
.ProtocolException
;
9 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachment
;
10 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachmentRemoteId
;
11 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceContent
;
12 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceDataMessage
;
13 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
14 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroup
;
15 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupContext
;
16 import org
.whispersystems
.signalservice
.api
.messages
.SignalServicePreview
;
17 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceReceiptMessage
;
18 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceTypingMessage
;
19 import org
.whispersystems
.signalservice
.api
.messages
.calls
.AnswerMessage
;
20 import org
.whispersystems
.signalservice
.api
.messages
.calls
.BusyMessage
;
21 import org
.whispersystems
.signalservice
.api
.messages
.calls
.HangupMessage
;
22 import org
.whispersystems
.signalservice
.api
.messages
.calls
.IceUpdateMessage
;
23 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OfferMessage
;
24 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OpaqueMessage
;
25 import org
.whispersystems
.signalservice
.api
.messages
.calls
.SignalServiceCallMessage
;
26 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.BlockedListMessage
;
27 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ContactsMessage
;
28 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.MessageRequestResponseMessage
;
29 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ReadMessage
;
30 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SentTranscriptMessage
;
31 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SignalServiceSyncMessage
;
32 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewOnceOpenMessage
;
33 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewedMessage
;
36 import java
.util
.List
;
37 import java
.util
.Optional
;
39 import java
.util
.stream
.Collectors
;
41 public record MessageEnvelope(
42 Optional
<RecipientAddress
> sourceAddress
,
45 long serverReceivedTimestamp
,
46 long serverDeliveredTimestamp
,
47 boolean isUnidentifiedSender
,
48 Optional
<Receipt
> receipt
,
49 Optional
<Typing
> typing
,
55 public record Receipt(long when, Type type
, List
<Long
> timestamps
) {
57 static Receipt
from(final SignalServiceReceiptMessage receiptMessage
) {
58 return new Receipt(receiptMessage
.getWhen(),
59 Type
.from(receiptMessage
.getType()),
60 receiptMessage
.getTimestamps());
69 static Type
from(SignalServiceReceiptMessage
.Type type
) {
70 return switch (type
) {
71 case DELIVERY
-> DELIVERY
;
73 case VIEWED
-> VIEWED
;
74 case UNKNOWN
-> UNKNOWN
;
80 public record Typing(long timestamp
, Type type
, Optional
<GroupId
> groupId
) {
82 public static Typing
from(final SignalServiceTypingMessage typingMessage
) {
83 return new Typing(typingMessage
.getTimestamp(),
84 typingMessage
.isTypingStarted() ? Type
.STARTED
: Type
.STOPPED
,
85 typingMessage
.getGroupId().map(GroupId
::unknownVersion
));
96 Optional
<GroupContext
> groupContext
,
97 Optional
<GroupCallUpdate
> groupCallUpdate
,
98 Optional
<String
> body
,
100 boolean isExpirationUpdate
,
102 boolean isEndSession
,
103 boolean isProfileKeyUpdate
,
104 boolean hasProfileKey
,
105 Optional
<Reaction
> reaction
,
106 Optional
<Quote
> quote
,
107 Optional
<Payment
> payment
,
108 List
<Attachment
> attachments
,
109 Optional
<Long
> remoteDeleteId
,
110 Optional
<Sticker
> sticker
,
111 List
<SharedContact
> sharedContacts
,
112 List
<Mention
> mentions
,
113 List
<Preview
> previews
117 final SignalServiceDataMessage dataMessage
,
118 RecipientResolver recipientResolver
,
119 RecipientAddressResolver addressResolver
,
120 final AttachmentFileProvider fileProvider
122 return new Data(dataMessage
.getTimestamp(),
123 dataMessage
.getGroupContext().map(GroupContext
::from
),
124 dataMessage
.getGroupCallUpdate().map(GroupCallUpdate
::from
),
125 dataMessage
.getBody(),
126 dataMessage
.getExpiresInSeconds(),
127 dataMessage
.isExpirationUpdate(),
128 dataMessage
.isViewOnce(),
129 dataMessage
.isEndSession(),
130 dataMessage
.isProfileKeyUpdate(),
131 dataMessage
.getProfileKey().isPresent(),
132 dataMessage
.getReaction().map(r
-> Reaction
.from(r
, recipientResolver
, addressResolver
)),
133 dataMessage
.getQuote().map(q
-> Quote
.from(q
, recipientResolver
, addressResolver
, fileProvider
)),
134 dataMessage
.getPayment().map(p
-> p
.getPaymentNotification().isPresent() ? Payment
.from(p
) : null),
135 dataMessage
.getAttachments()
136 .map(a
-> a
.stream().map(as -> Attachment
.from(as, fileProvider
)).toList())
138 dataMessage
.getRemoteDelete().map(SignalServiceDataMessage
.RemoteDelete
::getTargetSentTimestamp
),
139 dataMessage
.getSticker().map(Sticker
::from
),
140 dataMessage
.getSharedContacts()
142 .map(sharedContact
-> SharedContact
.from(sharedContact
, fileProvider
))
145 dataMessage
.getMentions()
146 .map(a
-> a
.stream().map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
)).toList())
148 dataMessage
.getPreviews()
149 .map(a
-> a
.stream().map(preview
-> Preview
.from(preview
, fileProvider
)).toList())
153 public record GroupContext(GroupId groupId
, boolean isGroupUpdate
, int revision
) {
155 static GroupContext
from(SignalServiceGroupContext groupContext
) {
156 if (groupContext
.getGroupV1().isPresent()) {
157 return new GroupContext(GroupId
.v1(groupContext
.getGroupV1().get().getGroupId()),
158 groupContext
.getGroupV1Type() == SignalServiceGroup
.Type
.UPDATE
,
160 } else if (groupContext
.getGroupV2().isPresent()) {
161 final var groupV2
= groupContext
.getGroupV2().get();
162 return new GroupContext(GroupUtils
.getGroupIdV2(groupV2
.getMasterKey()),
163 groupV2
.hasSignedGroupChange(),
164 groupV2
.getRevision());
166 throw new RuntimeException("Invalid group context state");
171 public record GroupCallUpdate(String eraId
) {
173 static GroupCallUpdate
from(SignalServiceDataMessage
.GroupCallUpdate groupCallUpdate
) {
174 return new GroupCallUpdate(groupCallUpdate
.getEraId());
178 public record Reaction(
179 long targetSentTimestamp
, RecipientAddress targetAuthor
, String emoji
, boolean isRemove
182 static Reaction
from(
183 SignalServiceDataMessage
.Reaction reaction
,
184 RecipientResolver recipientResolver
,
185 RecipientAddressResolver addressResolver
187 return new Reaction(reaction
.getTargetSentTimestamp(),
188 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(reaction
.getTargetAuthor())),
190 reaction
.isRemove());
196 RecipientAddress author
,
197 Optional
<String
> text
,
198 List
<Mention
> mentions
,
199 List
<Attachment
> attachments
203 SignalServiceDataMessage
.Quote quote
,
204 RecipientResolver recipientResolver
,
205 RecipientAddressResolver addressResolver
,
206 final AttachmentFileProvider fileProvider
208 return new Quote(quote
.getId(),
209 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(quote
.getAuthor())),
210 Optional
.ofNullable(quote
.getText()),
211 quote
.getMentions() == null
213 : quote
.getMentions()
215 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
217 quote
.getAttachments() == null
219 : quote
.getAttachments().stream().map(a
-> Attachment
.from(a
, fileProvider
)).toList());
223 public record Payment(String note
, byte[] receipt
) {
225 static Payment
from(SignalServiceDataMessage
.Payment payment
) {
226 return new Payment(payment
.getPaymentNotification().get().getNote(),
227 payment
.getPaymentNotification().get().getReceipt());
231 public record Mention(RecipientAddress recipient
, int start
, int length
) {
234 SignalServiceDataMessage
.Mention mention
,
235 RecipientResolver recipientResolver
,
236 RecipientAddressResolver addressResolver
238 return new Mention(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(mention
.getServiceId())),
240 mention
.getLength());
244 public record Attachment(
247 Optional
<String
> fileName
,
249 Optional
<Long
> uploadTimestamp
,
251 Optional
<byte[]> preview
,
252 Optional
<Attachment
> thumbnail
,
253 Optional
<String
> caption
,
254 Optional
<Integer
> width
,
255 Optional
<Integer
> height
,
261 static Attachment
from(SignalServiceAttachment attachment
, AttachmentFileProvider fileProvider
) {
262 if (attachment
.isPointer()) {
263 final var a
= attachment
.asPointer();
264 return new Attachment(Optional
.of(a
.getRemoteId().toString()),
265 Optional
.of(fileProvider
.getFile(a
.getRemoteId())),
268 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
269 a
.getSize().map(Integer
::longValue
),
273 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
274 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
279 final var a
= attachment
.asStream();
280 return new Attachment(Optional
.empty(),
284 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
285 Optional
.of(a
.getLength()),
289 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
290 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
297 static Attachment
from(
298 SignalServiceDataMessage
.Quote
.QuotedAttachment a
, final AttachmentFileProvider fileProvider
300 return new Attachment(Optional
.empty(),
302 Optional
.ofNullable(a
.getFileName()),
307 a
.getThumbnail() == null
309 : Optional
.of(Attachment
.from(a
.getThumbnail(), fileProvider
)),
319 public record Sticker(StickerPackId packId
, byte[] packKey
, int stickerId
) {
321 static Sticker
from(SignalServiceDataMessage
.Sticker sticker
) {
322 return new Sticker(StickerPackId
.deserialize(sticker
.getPackId()),
323 sticker
.getPackKey(),
324 sticker
.getStickerId());
328 public record SharedContact(
330 Optional
<Avatar
> avatar
,
333 List
<Address
> address
,
334 Optional
<String
> organization
337 static SharedContact
from(
338 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact sharedContact
,
339 final AttachmentFileProvider fileProvider
341 return new SharedContact(Name
.from(sharedContact
.getName()),
342 sharedContact
.getAvatar().map(avatar1
-> Avatar
.from(avatar1
, fileProvider
)),
343 sharedContact
.getPhone().map(p
-> p
.stream().map(Phone
::from
).toList()).orElse(List
.of()),
344 sharedContact
.getEmail().map(p
-> p
.stream().map(Email
::from
).toList()).orElse(List
.of()),
345 sharedContact
.getAddress().map(p
-> p
.stream().map(Address
::from
).toList()).orElse(List
.of()),
346 sharedContact
.getOrganization());
350 Optional
<String
> display
,
351 Optional
<String
> given
,
352 Optional
<String
> family
,
353 Optional
<String
> prefix
,
354 Optional
<String
> suffix
,
355 Optional
<String
> middle
358 static Name
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Name name
) {
359 return new Name(name
.getDisplay(),
368 public record Avatar(Attachment attachment
, boolean isProfile
) {
371 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Avatar avatar
,
372 final AttachmentFileProvider fileProvider
374 return new Avatar(Attachment
.from(avatar
.getAttachment(), fileProvider
), avatar
.isProfile());
379 String value
, Type type
, Optional
<String
> label
382 static Phone
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone phone
) {
383 return new Phone(phone
.getValue(), Type
.from(phone
.getType()), phone
.getLabel());
392 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone
.Type type
) {
393 return switch (type
) {
396 case MOBILE
-> MOBILE
;
397 case CUSTOM
-> CUSTOM
;
404 String value
, Type type
, Optional
<String
> label
407 static Email
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email email
) {
408 return new Email(email
.getValue(), Type
.from(email
.getType()), email
.getLabel());
417 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email
.Type type
) {
418 return switch (type
) {
421 case MOBILE
-> MOBILE
;
422 case CUSTOM
-> CUSTOM
;
428 public record Address(
430 Optional
<String
> label
,
431 Optional
<String
> street
,
432 Optional
<String
> pobox
,
433 Optional
<String
> neighborhood
,
434 Optional
<String
> city
,
435 Optional
<String
> region
,
436 Optional
<String
> postcode
,
437 Optional
<String
> country
440 static Address
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress address
) {
441 return new Address(Address
.Type
.from(address
.getType()),
457 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress
.Type type
) {
458 return switch (type
) {
461 case CUSTOM
-> CUSTOM
;
468 public record Preview(String title
, String description
, long date
, String url
, Optional
<Attachment
> image
) {
471 SignalServicePreview preview
, final AttachmentFileProvider fileProvider
473 return new Preview(preview
.getTitle(),
474 preview
.getDescription(),
477 preview
.getImage().map(as -> Attachment
.from(as, fileProvider
)));
484 Optional
<Blocked
> blocked
,
487 Optional
<ViewOnceOpen
> viewOnceOpen
,
488 Optional
<Contacts
> contacts
,
489 Optional
<Groups
> groups
,
490 Optional
<MessageRequestResponse
> messageRequestResponse
493 public static Sync
from(
494 final SignalServiceSyncMessage syncMessage
,
495 RecipientResolver recipientResolver
,
496 RecipientAddressResolver addressResolver
,
497 final AttachmentFileProvider fileProvider
499 return new Sync(syncMessage
.getSent()
500 .map(s
-> Sent
.from(s
, recipientResolver
, addressResolver
, fileProvider
)),
501 syncMessage
.getBlockedList().map(b
-> Blocked
.from(b
, recipientResolver
, addressResolver
)),
502 syncMessage
.getRead()
503 .map(r
-> r
.stream().map(rm
-> Read
.from(rm
, recipientResolver
, addressResolver
)).toList())
505 syncMessage
.getViewed()
507 .map(rm
-> Viewed
.from(rm
, recipientResolver
, addressResolver
))
510 syncMessage
.getViewOnceOpen().map(rm
-> ViewOnceOpen
.from(rm
, recipientResolver
, addressResolver
)),
511 syncMessage
.getContacts().map(Contacts
::from
),
512 syncMessage
.getGroups().map(Groups
::from
),
513 syncMessage
.getMessageRequestResponse()
514 .map(m
-> MessageRequestResponse
.from(m
, recipientResolver
, addressResolver
)));
519 long expirationStartTimestamp
,
520 Optional
<RecipientAddress
> destination
,
521 Set
<RecipientAddress
> recipients
,
522 Optional
<Data
> message
526 SentTranscriptMessage sentMessage
,
527 RecipientResolver recipientResolver
,
528 RecipientAddressResolver addressResolver
,
529 final AttachmentFileProvider fileProvider
531 return new Sent(sentMessage
.getTimestamp(),
532 sentMessage
.getExpirationStartTimestamp(),
533 sentMessage
.getDestination()
534 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
))),
535 sentMessage
.getRecipients()
537 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
538 .collect(Collectors
.toSet()),
539 sentMessage
.getDataMessage()
540 .map(message
-> Data
.from(message
, recipientResolver
, addressResolver
, fileProvider
)));
544 public record Blocked(List
<RecipientAddress
> recipients
, List
<GroupId
> groupIds
) {
547 BlockedListMessage blockedListMessage
,
548 RecipientResolver recipientResolver
,
549 RecipientAddressResolver addressResolver
551 return new Blocked(blockedListMessage
.getAddresses()
553 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
554 .toList(), blockedListMessage
.getGroupIds().stream().map(GroupId
::unknownVersion
).toList());
558 public record Read(RecipientAddress sender
, long timestamp
) {
561 ReadMessage readMessage
,
562 RecipientResolver recipientResolver
,
563 RecipientAddressResolver addressResolver
565 return new Read(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
566 readMessage
.getTimestamp());
570 public record Viewed(RecipientAddress sender
, long timestamp
) {
573 ViewedMessage readMessage
,
574 RecipientResolver recipientResolver
,
575 RecipientAddressResolver addressResolver
577 return new Viewed(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
578 readMessage
.getTimestamp());
582 public record ViewOnceOpen(RecipientAddress sender
, long timestamp
) {
584 static ViewOnceOpen
from(
585 ViewOnceOpenMessage readMessage
,
586 RecipientResolver recipientResolver
,
587 RecipientAddressResolver addressResolver
589 return new ViewOnceOpen(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
590 readMessage
.getSender())), readMessage
.getTimestamp());
594 public record Contacts(boolean isComplete
) {
596 static Contacts
from(ContactsMessage contactsMessage
) {
597 return new Contacts(contactsMessage
.isComplete());
601 public record Groups() {
603 static Groups
from(SignalServiceAttachment groupsMessage
) {
608 public record MessageRequestResponse(Type type
, Optional
<GroupId
> groupId
, Optional
<RecipientAddress
> person
) {
610 static MessageRequestResponse
from(
611 MessageRequestResponseMessage messageRequestResponse
,
612 RecipientResolver recipientResolver
,
613 RecipientAddressResolver addressResolver
615 return new MessageRequestResponse(Type
.from(messageRequestResponse
.getType()),
616 messageRequestResponse
.getGroupId().map(GroupId
::unknownVersion
),
617 messageRequestResponse
.getPerson()
618 .map(p
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(p
))));
629 static Type
from(MessageRequestResponseMessage
.Type type
) {
630 return switch (type
) {
631 case UNKNOWN
-> UNKNOWN
;
632 case ACCEPT
-> ACCEPT
;
633 case DELETE
-> DELETE
;
635 case BLOCK_AND_DELETE
-> BLOCK_AND_DELETE
;
636 case UNBLOCK_AND_ACCEPT
-> UNBLOCK_AND_ACCEPT
;
644 Optional
<Integer
> destinationDeviceId
,
645 Optional
<GroupId
> groupId
,
646 Optional
<Long
> timestamp
,
647 Optional
<Offer
> offer
,
648 Optional
<Answer
> answer
,
649 Optional
<Hangup
> hangup
,
651 List
<IceUpdate
> iceUpdate
,
652 Optional
<Opaque
> opaque
655 public static Call
from(final SignalServiceCallMessage callMessage
) {
656 return new Call(callMessage
.getDestinationDeviceId(),
657 callMessage
.getGroupId().map(GroupId
::unknownVersion
),
658 callMessage
.getTimestamp(),
659 callMessage
.getOfferMessage().map(Offer
::from
),
660 callMessage
.getAnswerMessage().map(Answer
::from
),
661 callMessage
.getHangupMessage().map(Hangup
::from
),
662 callMessage
.getBusyMessage().map(Busy
::from
),
663 callMessage
.getIceUpdateMessages()
664 .map(m
-> m
.stream().map(IceUpdate
::from
).toList())
666 callMessage
.getOpaqueMessage().map(Opaque
::from
));
669 public record Offer(long id
, String sdp
, Type type
, byte[] opaque
) {
671 static Offer
from(OfferMessage offerMessage
) {
672 return new Offer(offerMessage
.getId(),
673 offerMessage
.getSdp(),
674 Type
.from(offerMessage
.getType()),
675 offerMessage
.getOpaque());
682 static Type
from(OfferMessage
.Type type
) {
683 return switch (type
) {
684 case AUDIO_CALL
-> AUDIO_CALL
;
685 case VIDEO_CALL
-> VIDEO_CALL
;
691 public record Answer(long id
, String sdp
, byte[] opaque
) {
693 static Answer
from(AnswerMessage answerMessage
) {
694 return new Answer(answerMessage
.getId(), answerMessage
.getSdp(), answerMessage
.getOpaque());
698 public record Busy(long id
) {
700 static Busy
from(BusyMessage busyMessage
) {
701 return new Busy(busyMessage
.getId());
705 public record Hangup(long id
, Type type
, int deviceId
, boolean isLegacy
) {
707 static Hangup
from(HangupMessage hangupMessage
) {
708 return new Hangup(hangupMessage
.getId(),
709 Type
.from(hangupMessage
.getType()),
710 hangupMessage
.getDeviceId(),
711 hangupMessage
.isLegacy());
721 static Type
from(HangupMessage
.Type type
) {
722 return switch (type
) {
723 case NORMAL
-> NORMAL
;
724 case ACCEPTED
-> ACCEPTED
;
725 case DECLINED
-> DECLINED
;
727 case NEED_PERMISSION
-> NEED_PERMISSION
;
733 public record IceUpdate(long id
, String sdp
, byte[] opaque
) {
735 static IceUpdate
from(IceUpdateMessage iceUpdateMessage
) {
736 return new IceUpdate(iceUpdateMessage
.getId(), iceUpdateMessage
.getSdp(), iceUpdateMessage
.getOpaque());
740 public record Opaque(byte[] opaque
, Urgency urgency
) {
742 static Opaque
from(OpaqueMessage opaqueMessage
) {
743 return new Opaque(opaqueMessage
.getOpaque(), Urgency
.from(opaqueMessage
.getUrgency()));
746 public enum Urgency
{
750 static Urgency
from(OpaqueMessage
.Urgency urgency
) {
751 return switch (urgency
) {
752 case DROPPABLE
-> DROPPABLE
;
753 case HANDLE_IMMEDIATELY
-> HANDLE_IMMEDIATELY
;
760 public static MessageEnvelope
from(
761 SignalServiceEnvelope envelope
,
762 SignalServiceContent content
,
763 RecipientResolver recipientResolver
,
764 RecipientAddressResolver addressResolver
,
765 final AttachmentFileProvider fileProvider
,
768 final var source
= !envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()
769 ? recipientResolver
.resolveRecipient(envelope
.getSourceAddress())
770 : envelope
.isUnidentifiedSender() && content
!= null
771 ? recipientResolver
.resolveRecipient(content
.getSender())
772 : exception
instanceof ProtocolException e
773 ? recipientResolver
.resolveRecipient(e
.getSender())
775 final var sourceDevice
= envelope
.hasSourceDevice()
776 ? envelope
.getSourceDevice()
778 ? content
.getSenderDevice()
779 : exception
instanceof ProtocolException e ? e
.getSenderDevice() : 0;
781 Optional
<Receipt
> receipt
;
782 Optional
<Typing
> typing
;
786 if (content
!= null) {
787 receipt
= content
.getReceiptMessage().map(Receipt
::from
);
788 typing
= content
.getTypingMessage().map(Typing
::from
);
789 data
= content
.getDataMessage()
790 .map(dataMessage
-> Data
.from(dataMessage
, recipientResolver
, addressResolver
, fileProvider
));
791 sync
= content
.getSyncMessage().map(s
-> Sync
.from(s
, recipientResolver
, addressResolver
, fileProvider
));
792 call
= content
.getCallMessage().map(Call
::from
);
794 receipt
= envelope
.isReceipt() ? Optional
.of(new Receipt(envelope
.getServerReceivedTimestamp(),
795 Receipt
.Type
.DELIVERY
,
796 List
.of(envelope
.getTimestamp()))) : Optional
.empty();
797 typing
= Optional
.empty();
798 data
= Optional
.empty();
799 sync
= Optional
.empty();
800 call
= Optional
.empty();
803 return new MessageEnvelope(source
== null
805 : Optional
.of(addressResolver
.resolveRecipientAddress(source
)),
807 envelope
.getTimestamp(),
808 envelope
.getServerReceivedTimestamp(),
809 envelope
.getServerDeliveredTimestamp(),
810 envelope
.isUnidentifiedSender(),
818 public interface AttachmentFileProvider
{
820 File
getFile(SignalServiceAttachmentRemoteId attachmentRemoteId
);