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
.SignalServiceContent
;
10 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceDataMessage
;
11 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
12 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroup
;
13 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupContext
;
14 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceReceiptMessage
;
15 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceTypingMessage
;
16 import org
.whispersystems
.signalservice
.api
.messages
.calls
.AnswerMessage
;
17 import org
.whispersystems
.signalservice
.api
.messages
.calls
.BusyMessage
;
18 import org
.whispersystems
.signalservice
.api
.messages
.calls
.HangupMessage
;
19 import org
.whispersystems
.signalservice
.api
.messages
.calls
.IceUpdateMessage
;
20 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OfferMessage
;
21 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OpaqueMessage
;
22 import org
.whispersystems
.signalservice
.api
.messages
.calls
.SignalServiceCallMessage
;
23 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.BlockedListMessage
;
24 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ContactsMessage
;
25 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.MessageRequestResponseMessage
;
26 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ReadMessage
;
27 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SentTranscriptMessage
;
28 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SignalServiceSyncMessage
;
29 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewOnceOpenMessage
;
30 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewedMessage
;
32 import java
.util
.List
;
33 import java
.util
.Optional
;
35 import java
.util
.stream
.Collectors
;
37 public record MessageEnvelope(
38 Optional
<RecipientAddress
> sourceAddress
,
41 long serverReceivedTimestamp
,
42 long serverDeliveredTimestamp
,
43 boolean isUnidentifiedSender
,
44 Optional
<Receipt
> receipt
,
45 Optional
<Typing
> typing
,
51 public static final record Receipt(long when, Type type
, List
<Long
> timestamps
) {
53 static Receipt
from(final SignalServiceReceiptMessage receiptMessage
) {
54 return new Receipt(receiptMessage
.getWhen(),
55 Type
.from(receiptMessage
.getType()),
56 receiptMessage
.getTimestamps());
65 static Type
from(SignalServiceReceiptMessage
.Type type
) {
66 return switch (type
) {
67 case DELIVERY
-> DELIVERY
;
69 case VIEWED
-> VIEWED
;
70 case UNKNOWN
-> UNKNOWN
;
76 public static final record Typing(long timestamp
, Type type
, Optional
<GroupId
> groupId
) {
78 public static Typing
from(final SignalServiceTypingMessage typingMessage
) {
79 return new Typing(typingMessage
.getTimestamp(),
80 typingMessage
.isTypingStarted() ? Type
.STARTED
: Type
.STOPPED
,
81 Optional
.ofNullable(typingMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()));
90 public static final record Data(
92 Optional
<GroupContext
> groupContext
,
93 Optional
<GroupCallUpdate
> groupCallUpdate
,
94 Optional
<String
> body
,
96 boolean isExpirationUpdate
,
99 boolean hasProfileKey
,
100 Optional
<Reaction
> reaction
,
101 Optional
<Quote
> quote
,
102 List
<Attachment
> attachments
,
103 Optional
<Long
> remoteDeleteId
,
104 Optional
<Sticker
> sticker
,
105 List
<SharedContact
> sharedContacts
,
106 List
<Mention
> mentions
,
107 List
<Preview
> previews
111 final SignalServiceDataMessage dataMessage
,
112 RecipientResolver recipientResolver
,
113 RecipientAddressResolver addressResolver
115 return new Data(dataMessage
.getTimestamp(),
116 Optional
.ofNullable(dataMessage
.getGroupContext().transform(GroupContext
::from
).orNull()),
117 Optional
.ofNullable(dataMessage
.getGroupCallUpdate().transform(GroupCallUpdate
::from
).orNull()),
118 Optional
.ofNullable(dataMessage
.getBody().orNull()),
119 dataMessage
.getExpiresInSeconds(),
120 dataMessage
.isExpirationUpdate(),
121 dataMessage
.isViewOnce(),
122 dataMessage
.isEndSession(),
123 dataMessage
.getProfileKey().isPresent(),
124 Optional
.ofNullable(dataMessage
.getReaction()
125 .transform(r
-> Reaction
.from(r
, recipientResolver
, addressResolver
))
127 Optional
.ofNullable(dataMessage
.getQuote()
128 .transform(q
-> Quote
.from(q
, recipientResolver
, addressResolver
))
130 dataMessage
.getAttachments()
131 .transform(a
-> a
.stream().map(Attachment
::from
).collect(Collectors
.toList()))
133 Optional
.ofNullable(dataMessage
.getRemoteDelete()
134 .transform(SignalServiceDataMessage
.RemoteDelete
::getTargetSentTimestamp
)
136 Optional
.ofNullable(dataMessage
.getSticker().transform(Sticker
::from
).orNull()),
137 dataMessage
.getSharedContacts()
138 .transform(a
-> a
.stream().map(SharedContact
::from
).collect(Collectors
.toList()))
140 dataMessage
.getMentions()
141 .transform(a
-> a
.stream()
142 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
143 .collect(Collectors
.toList()))
145 dataMessage
.getPreviews()
146 .transform(a
-> a
.stream().map(Preview
::from
).collect(Collectors
.toList()))
150 public record GroupContext(GroupId groupId
, boolean isGroupUpdate
, int revision
) {
152 static GroupContext
from(SignalServiceGroupContext groupContext
) {
153 if (groupContext
.getGroupV1().isPresent()) {
154 return new GroupContext(GroupId
.v1(groupContext
.getGroupV1().get().getGroupId()),
155 groupContext
.getGroupV1Type() == SignalServiceGroup
.Type
.UPDATE
,
157 } else if (groupContext
.getGroupV2().isPresent()) {
158 final var groupV2
= groupContext
.getGroupV2().get();
159 return new GroupContext(GroupUtils
.getGroupIdV2(groupV2
.getMasterKey()),
160 groupV2
.hasSignedGroupChange(),
161 groupV2
.getRevision());
163 throw new RuntimeException("Invalid group context state");
168 public record GroupCallUpdate(String eraId
) {
170 static GroupCallUpdate
from(SignalServiceDataMessage
.GroupCallUpdate groupCallUpdate
) {
171 return new GroupCallUpdate(groupCallUpdate
.getEraId());
175 public record Reaction(
176 long targetSentTimestamp
, RecipientAddress targetAuthor
, String emoji
, boolean isRemove
179 static Reaction
from(
180 SignalServiceDataMessage
.Reaction reaction
,
181 RecipientResolver recipientResolver
,
182 RecipientAddressResolver addressResolver
184 return new Reaction(reaction
.getTargetSentTimestamp(),
185 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(reaction
.getTargetAuthor())),
187 reaction
.isRemove());
193 RecipientAddress author
,
194 Optional
<String
> text
,
195 List
<Mention
> mentions
,
196 List
<Attachment
> attachments
200 SignalServiceDataMessage
.Quote quote
,
201 RecipientResolver recipientResolver
,
202 RecipientAddressResolver addressResolver
204 return new Quote(quote
.getId(),
205 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(quote
.getAuthor())),
206 Optional
.ofNullable(quote
.getText()),
209 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
210 .collect(Collectors
.toList()),
211 quote
.getAttachments().stream().map(Attachment
::from
).collect(Collectors
.toList()));
215 public record Mention(RecipientAddress recipient
, int start
, int length
) {
218 SignalServiceDataMessage
.Mention mention
,
219 RecipientResolver recipientResolver
,
220 RecipientAddressResolver addressResolver
222 return new Mention(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(mention
.getUuid())),
224 mention
.getLength());
228 public record Attachment(
230 Optional
<String
> fileName
,
232 Optional
<Long
> uploadTimestamp
,
234 Optional
<byte[]> preview
,
235 Optional
<Attachment
> thumbnail
,
236 Optional
<String
> caption
,
237 Optional
<Integer
> width
,
238 Optional
<Integer
> height
,
244 static Attachment
from(SignalServiceAttachment attachment
) {
245 if (attachment
.isPointer()) {
246 final var a
= attachment
.asPointer();
247 return new Attachment(Optional
.of(a
.getRemoteId().toString()),
248 Optional
.ofNullable(a
.getFileName().orNull()),
250 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
251 Optional
.ofNullable(a
.getSize().transform(Integer
::longValue
).orNull()),
252 Optional
.ofNullable(a
.getPreview().orNull()),
254 Optional
.ofNullable(a
.getCaption().orNull()),
255 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
256 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
261 final var a
= attachment
.asStream();
262 return new Attachment(Optional
.empty(),
263 Optional
.ofNullable(a
.getFileName().orNull()),
265 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
266 Optional
.of(a
.getLength()),
267 Optional
.ofNullable(a
.getPreview().orNull()),
269 Optional
.ofNullable(a
.getCaption().orNull()),
270 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
271 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
278 static Attachment
from(SignalServiceDataMessage
.Quote
.QuotedAttachment a
) {
279 return new Attachment(Optional
.empty(),
280 Optional
.ofNullable(a
.getFileName()),
285 a
.getThumbnail() == null ? Optional
.empty() : Optional
.of(Attachment
.from(a
.getThumbnail())),
295 public record Sticker(byte[] packId
, byte[] packKey
, int stickerId
) {
297 static Sticker
from(SignalServiceDataMessage
.Sticker sticker
) {
298 return new Sticker(sticker
.getPackId(), sticker
.getPackKey(), sticker
.getStickerId());
302 public record SharedContact(
304 Optional
<Avatar
> avatar
,
307 List
<Address
> address
,
308 Optional
<String
> organization
311 static SharedContact
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact sharedContact
) {
312 return new SharedContact(Name
.from(sharedContact
.getName()),
313 Optional
.ofNullable(sharedContact
.getAvatar().transform(Avatar
::from
).orNull()),
314 sharedContact
.getPhone()
315 .transform(p
-> p
.stream().map(Phone
::from
).collect(Collectors
.toList()))
317 sharedContact
.getEmail()
318 .transform(p
-> p
.stream().map(Email
::from
).collect(Collectors
.toList()))
320 sharedContact
.getAddress()
321 .transform(p
-> p
.stream().map(Address
::from
).collect(Collectors
.toList()))
323 Optional
.ofNullable(sharedContact
.getOrganization().orNull()));
327 Optional
<String
> display
,
328 Optional
<String
> given
,
329 Optional
<String
> family
,
330 Optional
<String
> prefix
,
331 Optional
<String
> suffix
,
332 Optional
<String
> middle
335 static Name
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Name name
) {
336 return new Name(Optional
.ofNullable(name
.getDisplay().orNull()),
337 Optional
.ofNullable(name
.getGiven().orNull()),
338 Optional
.ofNullable(name
.getFamily().orNull()),
339 Optional
.ofNullable(name
.getPrefix().orNull()),
340 Optional
.ofNullable(name
.getSuffix().orNull()),
341 Optional
.ofNullable(name
.getMiddle().orNull()));
345 public record Avatar(Attachment attachment
, boolean isProfile
) {
347 static Avatar
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Avatar avatar
) {
348 return new Avatar(Attachment
.from(avatar
.getAttachment()), avatar
.isProfile());
353 String value
, Type type
, Optional
<String
> label
356 static Phone
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone phone
) {
357 return new Phone(phone
.getValue(),
358 Type
.from(phone
.getType()),
359 Optional
.ofNullable(phone
.getLabel().orNull()));
368 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone
.Type type
) {
369 return switch (type
) {
372 case MOBILE
-> MOBILE
;
373 case CUSTOM
-> CUSTOM
;
380 String value
, Type type
, Optional
<String
> label
383 static Email
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email email
) {
384 return new Email(email
.getValue(),
385 Type
.from(email
.getType()),
386 Optional
.ofNullable(email
.getLabel().orNull()));
395 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email
.Type type
) {
396 return switch (type
) {
399 case MOBILE
-> MOBILE
;
400 case CUSTOM
-> CUSTOM
;
406 public record Address(
408 Optional
<String
> label
,
409 Optional
<String
> street
,
410 Optional
<String
> pobox
,
411 Optional
<String
> neighborhood
,
412 Optional
<String
> city
,
413 Optional
<String
> region
,
414 Optional
<String
> postcode
,
415 Optional
<String
> country
418 static Address
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress address
) {
419 return new Address(Address
.Type
.from(address
.getType()),
420 Optional
.ofNullable(address
.getLabel().orNull()),
421 Optional
.ofNullable(address
.getLabel().orNull()),
422 Optional
.ofNullable(address
.getLabel().orNull()),
423 Optional
.ofNullable(address
.getLabel().orNull()),
424 Optional
.ofNullable(address
.getLabel().orNull()),
425 Optional
.ofNullable(address
.getLabel().orNull()),
426 Optional
.ofNullable(address
.getLabel().orNull()),
427 Optional
.ofNullable(address
.getLabel().orNull()));
435 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress
.Type type
) {
436 return switch (type
) {
439 case CUSTOM
-> CUSTOM
;
446 public record Preview(String title
, String description
, long date
, String url
, Optional
<Attachment
> image
) {
448 static Preview
from(SignalServiceDataMessage
.Preview preview
) {
449 return new Preview(preview
.getTitle(),
450 preview
.getDescription(),
453 Optional
.ofNullable(preview
.getImage().transform(Attachment
::from
).orNull()));
458 public static final record Sync(
460 Optional
<Blocked
> blocked
,
463 Optional
<ViewOnceOpen
> viewOnceOpen
,
464 Optional
<Contacts
> contacts
,
465 Optional
<Groups
> groups
,
466 Optional
<MessageRequestResponse
> messageRequestResponse
469 public static Sync
from(
470 final SignalServiceSyncMessage syncMessage
,
471 RecipientResolver recipientResolver
,
472 RecipientAddressResolver addressResolver
474 return new Sync(Optional
.ofNullable(syncMessage
.getSent()
475 .transform(s
-> Sent
.from(s
, recipientResolver
, addressResolver
))
477 Optional
.ofNullable(syncMessage
.getBlockedList()
478 .transform(b
-> Blocked
.from(b
, recipientResolver
, addressResolver
))
480 syncMessage
.getRead()
481 .transform(r
-> r
.stream()
482 .map(rm
-> Read
.from(rm
, recipientResolver
, addressResolver
))
483 .collect(Collectors
.toList()))
485 syncMessage
.getViewed()
486 .transform(r
-> r
.stream()
487 .map(rm
-> Viewed
.from(rm
, recipientResolver
, addressResolver
))
488 .collect(Collectors
.toList()))
490 Optional
.ofNullable(syncMessage
.getViewOnceOpen()
491 .transform(rm
-> ViewOnceOpen
.from(rm
, recipientResolver
, addressResolver
))
493 Optional
.ofNullable(syncMessage
.getContacts().transform(Contacts
::from
).orNull()),
494 Optional
.ofNullable(syncMessage
.getGroups().transform(Groups
::from
).orNull()),
495 Optional
.ofNullable(syncMessage
.getMessageRequestResponse()
496 .transform(m
-> MessageRequestResponse
.from(m
, recipientResolver
, addressResolver
))
502 long expirationStartTimestamp
,
503 Optional
<RecipientAddress
> destination
,
504 Set
<RecipientAddress
> recipients
,
509 SentTranscriptMessage sentMessage
,
510 RecipientResolver recipientResolver
,
511 RecipientAddressResolver addressResolver
513 return new Sent(sentMessage
.getTimestamp(),
514 sentMessage
.getExpirationStartTimestamp(),
515 Optional
.ofNullable(sentMessage
.getDestination()
516 .transform(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
519 sentMessage
.getRecipients()
521 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
522 .collect(Collectors
.toSet()),
523 Data
.from(sentMessage
.getMessage(), recipientResolver
, addressResolver
));
527 public record Blocked(List
<RecipientAddress
> recipients
, List
<GroupId
> groupIds
) {
530 BlockedListMessage blockedListMessage
,
531 RecipientResolver recipientResolver
,
532 RecipientAddressResolver addressResolver
534 return new Blocked(blockedListMessage
.getAddresses()
536 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
537 .collect(Collectors
.toList()),
538 blockedListMessage
.getGroupIds()
540 .map(GroupId
::unknownVersion
)
541 .collect(Collectors
.toList()));
545 public record Read(RecipientAddress sender
, long timestamp
) {
548 ReadMessage readMessage
,
549 RecipientResolver recipientResolver
,
550 RecipientAddressResolver addressResolver
552 return new Read(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
553 readMessage
.getTimestamp());
557 public record Viewed(RecipientAddress sender
, long timestamp
) {
560 ViewedMessage readMessage
,
561 RecipientResolver recipientResolver
,
562 RecipientAddressResolver addressResolver
564 return new Viewed(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
565 readMessage
.getTimestamp());
569 public record ViewOnceOpen(RecipientAddress sender
, long timestamp
) {
571 static ViewOnceOpen
from(
572 ViewOnceOpenMessage readMessage
,
573 RecipientResolver recipientResolver
,
574 RecipientAddressResolver addressResolver
576 return new ViewOnceOpen(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
577 readMessage
.getSender())), readMessage
.getTimestamp());
581 public record Contacts(boolean isComplete
) {
583 static Contacts
from(ContactsMessage contactsMessage
) {
584 return new Contacts(contactsMessage
.isComplete());
588 public record Groups() {
590 static Groups
from(SignalServiceAttachment groupsMessage
) {
595 public record MessageRequestResponse(Type type
, Optional
<GroupId
> groupId
, Optional
<RecipientAddress
> person
) {
597 static MessageRequestResponse
from(
598 MessageRequestResponseMessage messageRequestResponse
,
599 RecipientResolver recipientResolver
,
600 RecipientAddressResolver addressResolver
602 return new MessageRequestResponse(Type
.from(messageRequestResponse
.getType()),
603 Optional
.ofNullable(messageRequestResponse
.getGroupId()
604 .transform(GroupId
::unknownVersion
)
606 Optional
.ofNullable(messageRequestResponse
.getPerson()
607 .transform(p
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
620 static Type
from(MessageRequestResponseMessage
.Type type
) {
621 return switch (type
) {
622 case UNKNOWN
-> UNKNOWN
;
623 case ACCEPT
-> ACCEPT
;
624 case DELETE
-> DELETE
;
626 case BLOCK_AND_DELETE
-> BLOCK_AND_DELETE
;
627 case UNBLOCK_AND_ACCEPT
-> UNBLOCK_AND_ACCEPT
;
634 public static final record Call(
635 Optional
<Integer
> destinationDeviceId
,
636 Optional
<GroupId
> groupId
,
637 Optional
<Long
> timestamp
,
638 Optional
<Offer
> offer
,
639 Optional
<Answer
> answer
,
640 Optional
<Hangup
> hangup
,
642 List
<IceUpdate
> iceUpdate
,
643 Optional
<Opaque
> opaque
646 public static Call
from(final SignalServiceCallMessage callMessage
) {
647 return new Call(Optional
.ofNullable(callMessage
.getDestinationDeviceId().orNull()),
648 Optional
.ofNullable(callMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()),
649 Optional
.ofNullable(callMessage
.getTimestamp().orNull()),
650 Optional
.ofNullable(callMessage
.getOfferMessage().transform(Offer
::from
).orNull()),
651 Optional
.ofNullable(callMessage
.getAnswerMessage().transform(Answer
::from
).orNull()),
652 Optional
.ofNullable(callMessage
.getHangupMessage().transform(Hangup
::from
).orNull()),
653 Optional
.ofNullable(callMessage
.getBusyMessage().transform(Busy
::from
).orNull()),
654 callMessage
.getIceUpdateMessages()
655 .transform(m
-> m
.stream().map(IceUpdate
::from
).collect(Collectors
.toList()))
657 Optional
.ofNullable(callMessage
.getOpaqueMessage().transform(Opaque
::from
).orNull()));
660 public record Offer(long id
, String sdp
, Type type
, byte[] opaque
) {
662 static Offer
from(OfferMessage offerMessage
) {
663 return new Offer(offerMessage
.getId(),
664 offerMessage
.getSdp(),
665 Type
.from(offerMessage
.getType()),
666 offerMessage
.getOpaque());
673 static Type
from(OfferMessage
.Type type
) {
674 return switch (type
) {
675 case AUDIO_CALL
-> AUDIO_CALL
;
676 case VIDEO_CALL
-> VIDEO_CALL
;
682 public record Answer(long id
, String sdp
, byte[] opaque
) {
684 static Answer
from(AnswerMessage answerMessage
) {
685 return new Answer(answerMessage
.getId(), answerMessage
.getSdp(), answerMessage
.getOpaque());
689 public record Busy(long id
) {
691 static Offer
from(OfferMessage offerMessage
) {
692 return new Offer(offerMessage
.getId(),
693 offerMessage
.getSdp(),
694 Offer
.Type
.from(offerMessage
.getType()),
695 offerMessage
.getOpaque());
698 static Busy
from(BusyMessage busyMessage
) {
699 return new Busy(busyMessage
.getId());
703 public record Hangup(long id
, Type type
, int deviceId
, boolean isLegacy
) {
705 static Hangup
from(HangupMessage hangupMessage
) {
706 return new Hangup(hangupMessage
.getId(),
707 Type
.from(hangupMessage
.getType()),
708 hangupMessage
.getDeviceId(),
709 hangupMessage
.isLegacy());
719 static Type
from(HangupMessage
.Type type
) {
720 return switch (type
) {
721 case NORMAL
-> NORMAL
;
722 case ACCEPTED
-> ACCEPTED
;
723 case DECLINED
-> DECLINED
;
725 case NEED_PERMISSION
-> NEED_PERMISSION
;
731 public record IceUpdate(long id
, String sdp
, byte[] opaque
) {
733 static IceUpdate
from(IceUpdateMessage iceUpdateMessage
) {
734 return new IceUpdate(iceUpdateMessage
.getId(), iceUpdateMessage
.getSdp(), iceUpdateMessage
.getOpaque());
738 public record Opaque(byte[] opaque
, Urgency urgency
) {
740 static Opaque
from(OpaqueMessage opaqueMessage
) {
741 return new Opaque(opaqueMessage
.getOpaque(), Urgency
.from(opaqueMessage
.getUrgency()));
744 public enum Urgency
{
748 static Urgency
from(OpaqueMessage
.Urgency urgency
) {
749 return switch (urgency
) {
750 case DROPPABLE
-> DROPPABLE
;
751 case HANDLE_IMMEDIATELY
-> HANDLE_IMMEDIATELY
;
758 public static MessageEnvelope
from(
759 SignalServiceEnvelope envelope
,
760 SignalServiceContent content
,
761 RecipientResolver recipientResolver
,
762 RecipientAddressResolver addressResolver
764 final var source
= !envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()
765 ? recipientResolver
.resolveRecipient(envelope
.getSourceAddress())
766 : envelope
.isUnidentifiedSender() && content
!= null
767 ? recipientResolver
.resolveRecipient(content
.getSender())
769 final var sourceDevice
= envelope
.hasSourceDevice()
770 ? envelope
.getSourceDevice()
771 : content
!= null ? content
.getSenderDevice() : 0;
773 Optional
<Receipt
> receipt
;
774 Optional
<Typing
> typing
;
778 if (content
!= null) {
779 receipt
= Optional
.ofNullable(content
.getReceiptMessage().transform(Receipt
::from
).orNull());
780 typing
= Optional
.ofNullable(content
.getTypingMessage().transform(Typing
::from
).orNull());
781 data
= Optional
.ofNullable(content
.getDataMessage()
782 .transform(dataMessage
-> Data
.from(dataMessage
, recipientResolver
, addressResolver
))
784 sync
= Optional
.ofNullable(content
.getSyncMessage()
785 .transform(s
-> Sync
.from(s
, recipientResolver
, addressResolver
))
787 call
= Optional
.ofNullable(content
.getCallMessage().transform(Call
::from
).orNull());
789 receipt
= Optional
.empty();
790 typing
= Optional
.empty();
791 data
= Optional
.empty();
792 sync
= Optional
.empty();
793 call
= Optional
.empty();
796 return new MessageEnvelope(source
== null
798 : Optional
.of(addressResolver
.resolveRecipientAddress(source
)),
800 envelope
.getTimestamp(),
801 envelope
.getServerReceivedTimestamp(),
802 envelope
.getServerDeliveredTimestamp(),
803 envelope
.isUnidentifiedSender(),