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
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachment
;
9 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachmentRemoteId
;
10 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceContent
;
11 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceDataMessage
;
12 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
13 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroup
;
14 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupContext
;
15 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceReceiptMessage
;
16 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceTypingMessage
;
17 import org
.whispersystems
.signalservice
.api
.messages
.calls
.AnswerMessage
;
18 import org
.whispersystems
.signalservice
.api
.messages
.calls
.BusyMessage
;
19 import org
.whispersystems
.signalservice
.api
.messages
.calls
.HangupMessage
;
20 import org
.whispersystems
.signalservice
.api
.messages
.calls
.IceUpdateMessage
;
21 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OfferMessage
;
22 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OpaqueMessage
;
23 import org
.whispersystems
.signalservice
.api
.messages
.calls
.SignalServiceCallMessage
;
24 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.BlockedListMessage
;
25 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ContactsMessage
;
26 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.MessageRequestResponseMessage
;
27 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ReadMessage
;
28 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SentTranscriptMessage
;
29 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SignalServiceSyncMessage
;
30 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewOnceOpenMessage
;
31 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewedMessage
;
34 import java
.util
.List
;
35 import java
.util
.Optional
;
37 import java
.util
.stream
.Collectors
;
39 public record MessageEnvelope(
40 Optional
<RecipientAddress
> sourceAddress
,
43 long serverReceivedTimestamp
,
44 long serverDeliveredTimestamp
,
45 boolean isUnidentifiedSender
,
46 Optional
<Receipt
> receipt
,
47 Optional
<Typing
> typing
,
53 public record Receipt(long when, Type type
, List
<Long
> timestamps
) {
55 static Receipt
from(final SignalServiceReceiptMessage receiptMessage
) {
56 return new Receipt(receiptMessage
.getWhen(),
57 Type
.from(receiptMessage
.getType()),
58 receiptMessage
.getTimestamps());
67 static Type
from(SignalServiceReceiptMessage
.Type type
) {
68 return switch (type
) {
69 case DELIVERY
-> DELIVERY
;
71 case VIEWED
-> VIEWED
;
72 case UNKNOWN
-> UNKNOWN
;
78 public record Typing(long timestamp
, Type type
, Optional
<GroupId
> groupId
) {
80 public static Typing
from(final SignalServiceTypingMessage typingMessage
) {
81 return new Typing(typingMessage
.getTimestamp(),
82 typingMessage
.isTypingStarted() ? Type
.STARTED
: Type
.STOPPED
,
83 Optional
.ofNullable(typingMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()));
94 Optional
<GroupContext
> groupContext
,
95 Optional
<GroupCallUpdate
> groupCallUpdate
,
96 Optional
<String
> body
,
98 boolean isExpirationUpdate
,
100 boolean isEndSession
,
101 boolean hasProfileKey
,
102 Optional
<Reaction
> reaction
,
103 Optional
<Quote
> quote
,
104 Optional
<Payment
> payment
,
105 List
<Attachment
> attachments
,
106 Optional
<Long
> remoteDeleteId
,
107 Optional
<Sticker
> sticker
,
108 List
<SharedContact
> sharedContacts
,
109 List
<Mention
> mentions
,
110 List
<Preview
> previews
114 final SignalServiceDataMessage dataMessage
,
115 RecipientResolver recipientResolver
,
116 RecipientAddressResolver addressResolver
,
117 final AttachmentFileProvider fileProvider
119 return new Data(dataMessage
.getTimestamp(),
120 Optional
.ofNullable(dataMessage
.getGroupContext().transform(GroupContext
::from
).orNull()),
121 Optional
.ofNullable(dataMessage
.getGroupCallUpdate().transform(GroupCallUpdate
::from
).orNull()),
122 Optional
.ofNullable(dataMessage
.getBody().orNull()),
123 dataMessage
.getExpiresInSeconds(),
124 dataMessage
.isExpirationUpdate(),
125 dataMessage
.isViewOnce(),
126 dataMessage
.isEndSession(),
127 dataMessage
.getProfileKey().isPresent(),
128 Optional
.ofNullable(dataMessage
.getReaction()
129 .transform(r
-> Reaction
.from(r
, recipientResolver
, addressResolver
))
131 Optional
.ofNullable(dataMessage
.getQuote()
132 .transform(q
-> Quote
.from(q
, recipientResolver
, addressResolver
, fileProvider
))
134 Optional
.ofNullable(dataMessage
.getPayment()
135 .transform(p
-> p
.getPaymentNotification().isPresent() ? Payment
.from(p
) : null)
137 dataMessage
.getAttachments()
138 .transform(a
-> a
.stream().map(as -> Attachment
.from(as, fileProvider
)).toList())
140 Optional
.ofNullable(dataMessage
.getRemoteDelete()
141 .transform(SignalServiceDataMessage
.RemoteDelete
::getTargetSentTimestamp
)
143 Optional
.ofNullable(dataMessage
.getSticker().transform(Sticker
::from
).orNull()),
144 dataMessage
.getSharedContacts()
145 .transform(a
-> a
.stream()
146 .map(sharedContact
-> SharedContact
.from(sharedContact
, fileProvider
))
149 dataMessage
.getMentions()
150 .transform(a
-> a
.stream()
151 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
154 dataMessage
.getPreviews()
155 .transform(a
-> a
.stream().map(preview
-> Preview
.from(preview
, fileProvider
)).toList())
159 public record GroupContext(GroupId groupId
, boolean isGroupUpdate
, int revision
) {
161 static GroupContext
from(SignalServiceGroupContext groupContext
) {
162 if (groupContext
.getGroupV1().isPresent()) {
163 return new GroupContext(GroupId
.v1(groupContext
.getGroupV1().get().getGroupId()),
164 groupContext
.getGroupV1Type() == SignalServiceGroup
.Type
.UPDATE
,
166 } else if (groupContext
.getGroupV2().isPresent()) {
167 final var groupV2
= groupContext
.getGroupV2().get();
168 return new GroupContext(GroupUtils
.getGroupIdV2(groupV2
.getMasterKey()),
169 groupV2
.hasSignedGroupChange(),
170 groupV2
.getRevision());
172 throw new RuntimeException("Invalid group context state");
177 public record GroupCallUpdate(String eraId
) {
179 static GroupCallUpdate
from(SignalServiceDataMessage
.GroupCallUpdate groupCallUpdate
) {
180 return new GroupCallUpdate(groupCallUpdate
.getEraId());
184 public record Reaction(
185 long targetSentTimestamp
, RecipientAddress targetAuthor
, String emoji
, boolean isRemove
188 static Reaction
from(
189 SignalServiceDataMessage
.Reaction reaction
,
190 RecipientResolver recipientResolver
,
191 RecipientAddressResolver addressResolver
193 return new Reaction(reaction
.getTargetSentTimestamp(),
194 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(reaction
.getTargetAuthor())),
196 reaction
.isRemove());
202 RecipientAddress author
,
203 Optional
<String
> text
,
204 List
<Mention
> mentions
,
205 List
<Attachment
> attachments
209 SignalServiceDataMessage
.Quote quote
,
210 RecipientResolver recipientResolver
,
211 RecipientAddressResolver addressResolver
,
212 final AttachmentFileProvider fileProvider
214 return new Quote(quote
.getId(),
215 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(quote
.getAuthor())),
216 Optional
.ofNullable(quote
.getText()),
217 quote
.getMentions() == null
219 : quote
.getMentions()
221 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
223 quote
.getAttachments() == null
225 : quote
.getAttachments().stream().map(a
-> Attachment
.from(a
, fileProvider
)).toList());
229 public record Payment(String note
, byte[] receipt
) {
231 static Payment
from(SignalServiceDataMessage
.Payment payment
) {
232 return new Payment(payment
.getPaymentNotification().get().getNote(),
233 payment
.getPaymentNotification().get().getReceipt());
237 public record Mention(RecipientAddress recipient
, int start
, int length
) {
240 SignalServiceDataMessage
.Mention mention
,
241 RecipientResolver recipientResolver
,
242 RecipientAddressResolver addressResolver
244 return new Mention(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(mention
.getAci())),
246 mention
.getLength());
250 public record Attachment(
253 Optional
<String
> fileName
,
255 Optional
<Long
> uploadTimestamp
,
257 Optional
<byte[]> preview
,
258 Optional
<Attachment
> thumbnail
,
259 Optional
<String
> caption
,
260 Optional
<Integer
> width
,
261 Optional
<Integer
> height
,
267 static Attachment
from(SignalServiceAttachment attachment
, AttachmentFileProvider fileProvider
) {
268 if (attachment
.isPointer()) {
269 final var a
= attachment
.asPointer();
270 return new Attachment(Optional
.of(a
.getRemoteId().toString()),
271 Optional
.of(fileProvider
.getFile(a
.getRemoteId())),
272 Optional
.ofNullable(a
.getFileName().orNull()),
274 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
275 Optional
.ofNullable(a
.getSize().transform(Integer
::longValue
).orNull()),
276 Optional
.ofNullable(a
.getPreview().orNull()),
278 Optional
.ofNullable(a
.getCaption().orNull()),
279 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
280 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
285 final var a
= attachment
.asStream();
286 return new Attachment(Optional
.empty(),
288 Optional
.ofNullable(a
.getFileName().orNull()),
290 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
291 Optional
.of(a
.getLength()),
292 Optional
.ofNullable(a
.getPreview().orNull()),
294 Optional
.ofNullable(a
.getCaption().orNull()),
295 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
296 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
303 static Attachment
from(
304 SignalServiceDataMessage
.Quote
.QuotedAttachment a
, final AttachmentFileProvider fileProvider
306 return new Attachment(Optional
.empty(),
308 Optional
.ofNullable(a
.getFileName()),
313 a
.getThumbnail() == null
315 : Optional
.of(Attachment
.from(a
.getThumbnail(), fileProvider
)),
325 public record Sticker(StickerPackId packId
, byte[] packKey
, int stickerId
) {
327 static Sticker
from(SignalServiceDataMessage
.Sticker sticker
) {
328 return new Sticker(StickerPackId
.deserialize(sticker
.getPackId()),
329 sticker
.getPackKey(),
330 sticker
.getStickerId());
334 public record SharedContact(
336 Optional
<Avatar
> avatar
,
339 List
<Address
> address
,
340 Optional
<String
> organization
343 static SharedContact
from(
344 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact sharedContact
,
345 final AttachmentFileProvider fileProvider
347 return new SharedContact(Name
.from(sharedContact
.getName()),
348 Optional
.ofNullable(sharedContact
.getAvatar()
349 .transform(avatar1
-> Avatar
.from(avatar1
, fileProvider
))
351 sharedContact
.getPhone().transform(p
-> p
.stream().map(Phone
::from
).toList()).or(List
.of()),
352 sharedContact
.getEmail().transform(p
-> p
.stream().map(Email
::from
).toList()).or(List
.of()),
353 sharedContact
.getAddress().transform(p
-> p
.stream().map(Address
::from
).toList()).or(List
.of()),
354 Optional
.ofNullable(sharedContact
.getOrganization().orNull()));
358 Optional
<String
> display
,
359 Optional
<String
> given
,
360 Optional
<String
> family
,
361 Optional
<String
> prefix
,
362 Optional
<String
> suffix
,
363 Optional
<String
> middle
366 static Name
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Name name
) {
367 return new Name(Optional
.ofNullable(name
.getDisplay().orNull()),
368 Optional
.ofNullable(name
.getGiven().orNull()),
369 Optional
.ofNullable(name
.getFamily().orNull()),
370 Optional
.ofNullable(name
.getPrefix().orNull()),
371 Optional
.ofNullable(name
.getSuffix().orNull()),
372 Optional
.ofNullable(name
.getMiddle().orNull()));
376 public record Avatar(Attachment attachment
, boolean isProfile
) {
379 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Avatar avatar
,
380 final AttachmentFileProvider fileProvider
382 return new Avatar(Attachment
.from(avatar
.getAttachment(), fileProvider
), avatar
.isProfile());
387 String value
, Type type
, Optional
<String
> label
390 static Phone
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone phone
) {
391 return new Phone(phone
.getValue(),
392 Type
.from(phone
.getType()),
393 Optional
.ofNullable(phone
.getLabel().orNull()));
402 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone
.Type type
) {
403 return switch (type
) {
406 case MOBILE
-> MOBILE
;
407 case CUSTOM
-> CUSTOM
;
414 String value
, Type type
, Optional
<String
> label
417 static Email
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email email
) {
418 return new Email(email
.getValue(),
419 Type
.from(email
.getType()),
420 Optional
.ofNullable(email
.getLabel().orNull()));
429 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email
.Type type
) {
430 return switch (type
) {
433 case MOBILE
-> MOBILE
;
434 case CUSTOM
-> CUSTOM
;
440 public record Address(
442 Optional
<String
> label
,
443 Optional
<String
> street
,
444 Optional
<String
> pobox
,
445 Optional
<String
> neighborhood
,
446 Optional
<String
> city
,
447 Optional
<String
> region
,
448 Optional
<String
> postcode
,
449 Optional
<String
> country
452 static Address
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress address
) {
453 return new Address(Address
.Type
.from(address
.getType()),
454 Optional
.ofNullable(address
.getLabel().orNull()),
455 Optional
.ofNullable(address
.getLabel().orNull()),
456 Optional
.ofNullable(address
.getLabel().orNull()),
457 Optional
.ofNullable(address
.getLabel().orNull()),
458 Optional
.ofNullable(address
.getLabel().orNull()),
459 Optional
.ofNullable(address
.getLabel().orNull()),
460 Optional
.ofNullable(address
.getLabel().orNull()),
461 Optional
.ofNullable(address
.getLabel().orNull()));
469 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress
.Type type
) {
470 return switch (type
) {
473 case CUSTOM
-> CUSTOM
;
480 public record Preview(String title
, String description
, long date
, String url
, Optional
<Attachment
> image
) {
483 SignalServiceDataMessage
.Preview preview
, final AttachmentFileProvider fileProvider
485 return new Preview(preview
.getTitle(),
486 preview
.getDescription(),
489 Optional
.ofNullable(preview
.getImage()
490 .transform(as -> Attachment
.from(as, fileProvider
))
498 Optional
<Blocked
> blocked
,
501 Optional
<ViewOnceOpen
> viewOnceOpen
,
502 Optional
<Contacts
> contacts
,
503 Optional
<Groups
> groups
,
504 Optional
<MessageRequestResponse
> messageRequestResponse
507 public static Sync
from(
508 final SignalServiceSyncMessage syncMessage
,
509 RecipientResolver recipientResolver
,
510 RecipientAddressResolver addressResolver
,
511 final AttachmentFileProvider fileProvider
513 return new Sync(Optional
.ofNullable(syncMessage
.getSent()
514 .transform(s
-> Sent
.from(s
, recipientResolver
, addressResolver
, fileProvider
))
516 Optional
.ofNullable(syncMessage
.getBlockedList()
517 .transform(b
-> Blocked
.from(b
, recipientResolver
, addressResolver
))
519 syncMessage
.getRead()
520 .transform(r
-> r
.stream()
521 .map(rm
-> Read
.from(rm
, recipientResolver
, addressResolver
))
524 syncMessage
.getViewed()
525 .transform(r
-> r
.stream()
526 .map(rm
-> Viewed
.from(rm
, recipientResolver
, addressResolver
))
529 Optional
.ofNullable(syncMessage
.getViewOnceOpen()
530 .transform(rm
-> ViewOnceOpen
.from(rm
, recipientResolver
, addressResolver
))
532 Optional
.ofNullable(syncMessage
.getContacts().transform(Contacts
::from
).orNull()),
533 Optional
.ofNullable(syncMessage
.getGroups().transform(Groups
::from
).orNull()),
534 Optional
.ofNullable(syncMessage
.getMessageRequestResponse()
535 .transform(m
-> MessageRequestResponse
.from(m
, recipientResolver
, addressResolver
))
541 long expirationStartTimestamp
,
542 Optional
<RecipientAddress
> destination
,
543 Set
<RecipientAddress
> recipients
,
548 SentTranscriptMessage sentMessage
,
549 RecipientResolver recipientResolver
,
550 RecipientAddressResolver addressResolver
,
551 final AttachmentFileProvider fileProvider
553 return new Sent(sentMessage
.getTimestamp(),
554 sentMessage
.getExpirationStartTimestamp(),
555 Optional
.ofNullable(sentMessage
.getDestination()
556 .transform(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
559 sentMessage
.getRecipients()
561 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
562 .collect(Collectors
.toSet()),
563 Data
.from(sentMessage
.getMessage(), recipientResolver
, addressResolver
, fileProvider
));
567 public record Blocked(List
<RecipientAddress
> recipients
, List
<GroupId
> groupIds
) {
570 BlockedListMessage blockedListMessage
,
571 RecipientResolver recipientResolver
,
572 RecipientAddressResolver addressResolver
574 return new Blocked(blockedListMessage
.getAddresses()
576 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
577 .toList(), blockedListMessage
.getGroupIds().stream().map(GroupId
::unknownVersion
).toList());
581 public record Read(RecipientAddress sender
, long timestamp
) {
584 ReadMessage readMessage
,
585 RecipientResolver recipientResolver
,
586 RecipientAddressResolver addressResolver
588 return new Read(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
589 readMessage
.getTimestamp());
593 public record Viewed(RecipientAddress sender
, long timestamp
) {
596 ViewedMessage readMessage
,
597 RecipientResolver recipientResolver
,
598 RecipientAddressResolver addressResolver
600 return new Viewed(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
601 readMessage
.getTimestamp());
605 public record ViewOnceOpen(RecipientAddress sender
, long timestamp
) {
607 static ViewOnceOpen
from(
608 ViewOnceOpenMessage readMessage
,
609 RecipientResolver recipientResolver
,
610 RecipientAddressResolver addressResolver
612 return new ViewOnceOpen(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
613 readMessage
.getSender())), readMessage
.getTimestamp());
617 public record Contacts(boolean isComplete
) {
619 static Contacts
from(ContactsMessage contactsMessage
) {
620 return new Contacts(contactsMessage
.isComplete());
624 public record Groups() {
626 static Groups
from(SignalServiceAttachment groupsMessage
) {
631 public record MessageRequestResponse(Type type
, Optional
<GroupId
> groupId
, Optional
<RecipientAddress
> person
) {
633 static MessageRequestResponse
from(
634 MessageRequestResponseMessage messageRequestResponse
,
635 RecipientResolver recipientResolver
,
636 RecipientAddressResolver addressResolver
638 return new MessageRequestResponse(Type
.from(messageRequestResponse
.getType()),
639 Optional
.ofNullable(messageRequestResponse
.getGroupId()
640 .transform(GroupId
::unknownVersion
)
642 Optional
.ofNullable(messageRequestResponse
.getPerson()
643 .transform(p
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
656 static Type
from(MessageRequestResponseMessage
.Type type
) {
657 return switch (type
) {
658 case UNKNOWN
-> UNKNOWN
;
659 case ACCEPT
-> ACCEPT
;
660 case DELETE
-> DELETE
;
662 case BLOCK_AND_DELETE
-> BLOCK_AND_DELETE
;
663 case UNBLOCK_AND_ACCEPT
-> UNBLOCK_AND_ACCEPT
;
671 Optional
<Integer
> destinationDeviceId
,
672 Optional
<GroupId
> groupId
,
673 Optional
<Long
> timestamp
,
674 Optional
<Offer
> offer
,
675 Optional
<Answer
> answer
,
676 Optional
<Hangup
> hangup
,
678 List
<IceUpdate
> iceUpdate
,
679 Optional
<Opaque
> opaque
682 public static Call
from(final SignalServiceCallMessage callMessage
) {
683 return new Call(Optional
.ofNullable(callMessage
.getDestinationDeviceId().orNull()),
684 Optional
.ofNullable(callMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()),
685 Optional
.ofNullable(callMessage
.getTimestamp().orNull()),
686 Optional
.ofNullable(callMessage
.getOfferMessage().transform(Offer
::from
).orNull()),
687 Optional
.ofNullable(callMessage
.getAnswerMessage().transform(Answer
::from
).orNull()),
688 Optional
.ofNullable(callMessage
.getHangupMessage().transform(Hangup
::from
).orNull()),
689 Optional
.ofNullable(callMessage
.getBusyMessage().transform(Busy
::from
).orNull()),
690 callMessage
.getIceUpdateMessages()
691 .transform(m
-> m
.stream().map(IceUpdate
::from
).toList())
693 Optional
.ofNullable(callMessage
.getOpaqueMessage().transform(Opaque
::from
).orNull()));
696 public record Offer(long id
, String sdp
, Type type
, byte[] opaque
) {
698 static Offer
from(OfferMessage offerMessage
) {
699 return new Offer(offerMessage
.getId(),
700 offerMessage
.getSdp(),
701 Type
.from(offerMessage
.getType()),
702 offerMessage
.getOpaque());
709 static Type
from(OfferMessage
.Type type
) {
710 return switch (type
) {
711 case AUDIO_CALL
-> AUDIO_CALL
;
712 case VIDEO_CALL
-> VIDEO_CALL
;
718 public record Answer(long id
, String sdp
, byte[] opaque
) {
720 static Answer
from(AnswerMessage answerMessage
) {
721 return new Answer(answerMessage
.getId(), answerMessage
.getSdp(), answerMessage
.getOpaque());
725 public record Busy(long id
) {
727 static Busy
from(BusyMessage busyMessage
) {
728 return new Busy(busyMessage
.getId());
732 public record Hangup(long id
, Type type
, int deviceId
, boolean isLegacy
) {
734 static Hangup
from(HangupMessage hangupMessage
) {
735 return new Hangup(hangupMessage
.getId(),
736 Type
.from(hangupMessage
.getType()),
737 hangupMessage
.getDeviceId(),
738 hangupMessage
.isLegacy());
748 static Type
from(HangupMessage
.Type type
) {
749 return switch (type
) {
750 case NORMAL
-> NORMAL
;
751 case ACCEPTED
-> ACCEPTED
;
752 case DECLINED
-> DECLINED
;
754 case NEED_PERMISSION
-> NEED_PERMISSION
;
760 public record IceUpdate(long id
, String sdp
, byte[] opaque
) {
762 static IceUpdate
from(IceUpdateMessage iceUpdateMessage
) {
763 return new IceUpdate(iceUpdateMessage
.getId(), iceUpdateMessage
.getSdp(), iceUpdateMessage
.getOpaque());
767 public record Opaque(byte[] opaque
, Urgency urgency
) {
769 static Opaque
from(OpaqueMessage opaqueMessage
) {
770 return new Opaque(opaqueMessage
.getOpaque(), Urgency
.from(opaqueMessage
.getUrgency()));
773 public enum Urgency
{
777 static Urgency
from(OpaqueMessage
.Urgency urgency
) {
778 return switch (urgency
) {
779 case DROPPABLE
-> DROPPABLE
;
780 case HANDLE_IMMEDIATELY
-> HANDLE_IMMEDIATELY
;
787 public static MessageEnvelope
from(
788 SignalServiceEnvelope envelope
,
789 SignalServiceContent content
,
790 RecipientResolver recipientResolver
,
791 RecipientAddressResolver addressResolver
,
792 final AttachmentFileProvider fileProvider
794 final var source
= !envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()
795 ? recipientResolver
.resolveRecipient(envelope
.getSourceAddress())
796 : envelope
.isUnidentifiedSender() && content
!= null
797 ? recipientResolver
.resolveRecipient(content
.getSender())
799 final var sourceDevice
= envelope
.hasSourceDevice()
800 ? envelope
.getSourceDevice()
801 : content
!= null ? content
.getSenderDevice() : 0;
803 Optional
<Receipt
> receipt
;
804 Optional
<Typing
> typing
;
808 if (content
!= null) {
809 receipt
= Optional
.ofNullable(content
.getReceiptMessage().transform(Receipt
::from
).orNull());
810 typing
= Optional
.ofNullable(content
.getTypingMessage().transform(Typing
::from
).orNull());
811 data
= Optional
.ofNullable(content
.getDataMessage()
812 .transform(dataMessage
-> Data
.from(dataMessage
, recipientResolver
, addressResolver
, fileProvider
))
814 sync
= Optional
.ofNullable(content
.getSyncMessage()
815 .transform(s
-> Sync
.from(s
, recipientResolver
, addressResolver
, fileProvider
))
817 call
= Optional
.ofNullable(content
.getCallMessage().transform(Call
::from
).orNull());
819 receipt
= envelope
.isReceipt() ? Optional
.of(new Receipt(envelope
.getServerReceivedTimestamp(),
820 Receipt
.Type
.DELIVERY
,
821 List
.of(envelope
.getTimestamp()))) : Optional
.empty();
822 typing
= Optional
.empty();
823 data
= Optional
.empty();
824 sync
= Optional
.empty();
825 call
= Optional
.empty();
828 return new MessageEnvelope(source
== null
830 : Optional
.of(addressResolver
.resolveRecipientAddress(source
)),
832 envelope
.getTimestamp(),
833 envelope
.getServerReceivedTimestamp(),
834 envelope
.getServerDeliveredTimestamp(),
835 envelope
.isUnidentifiedSender(),
843 public interface AttachmentFileProvider
{
845 File
getFile(SignalServiceAttachmentRemoteId attachmentRemoteId
);