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 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 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()));
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()),
207 quote
.getMentions() == null
209 : quote
.getMentions()
211 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
212 .collect(Collectors
.toList()),
213 quote
.getAttachments() == null
215 : quote
.getAttachments().stream().map(Attachment
::from
).collect(Collectors
.toList()));
219 public record Mention(RecipientAddress recipient
, int start
, int length
) {
222 SignalServiceDataMessage
.Mention mention
,
223 RecipientResolver recipientResolver
,
224 RecipientAddressResolver addressResolver
226 return new Mention(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(mention
.getUuid())),
228 mention
.getLength());
232 public record Attachment(
234 Optional
<String
> fileName
,
236 Optional
<Long
> uploadTimestamp
,
238 Optional
<byte[]> preview
,
239 Optional
<Attachment
> thumbnail
,
240 Optional
<String
> caption
,
241 Optional
<Integer
> width
,
242 Optional
<Integer
> height
,
248 static Attachment
from(SignalServiceAttachment attachment
) {
249 if (attachment
.isPointer()) {
250 final var a
= attachment
.asPointer();
251 return new Attachment(Optional
.of(a
.getRemoteId().toString()),
252 Optional
.ofNullable(a
.getFileName().orNull()),
254 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
255 Optional
.ofNullable(a
.getSize().transform(Integer
::longValue
).orNull()),
256 Optional
.ofNullable(a
.getPreview().orNull()),
258 Optional
.ofNullable(a
.getCaption().orNull()),
259 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
260 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
265 final var a
= attachment
.asStream();
266 return new Attachment(Optional
.empty(),
267 Optional
.ofNullable(a
.getFileName().orNull()),
269 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
270 Optional
.of(a
.getLength()),
271 Optional
.ofNullable(a
.getPreview().orNull()),
273 Optional
.ofNullable(a
.getCaption().orNull()),
274 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
275 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
282 static Attachment
from(SignalServiceDataMessage
.Quote
.QuotedAttachment a
) {
283 return new Attachment(Optional
.empty(),
284 Optional
.ofNullable(a
.getFileName()),
289 a
.getThumbnail() == null ? Optional
.empty() : Optional
.of(Attachment
.from(a
.getThumbnail())),
299 public record Sticker(byte[] packId
, byte[] packKey
, int stickerId
) {
301 static Sticker
from(SignalServiceDataMessage
.Sticker sticker
) {
302 return new Sticker(sticker
.getPackId(), sticker
.getPackKey(), sticker
.getStickerId());
306 public record SharedContact(
308 Optional
<Avatar
> avatar
,
311 List
<Address
> address
,
312 Optional
<String
> organization
315 static SharedContact
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact sharedContact
) {
316 return new SharedContact(Name
.from(sharedContact
.getName()),
317 Optional
.ofNullable(sharedContact
.getAvatar().transform(Avatar
::from
).orNull()),
318 sharedContact
.getPhone()
319 .transform(p
-> p
.stream().map(Phone
::from
).collect(Collectors
.toList()))
321 sharedContact
.getEmail()
322 .transform(p
-> p
.stream().map(Email
::from
).collect(Collectors
.toList()))
324 sharedContact
.getAddress()
325 .transform(p
-> p
.stream().map(Address
::from
).collect(Collectors
.toList()))
327 Optional
.ofNullable(sharedContact
.getOrganization().orNull()));
331 Optional
<String
> display
,
332 Optional
<String
> given
,
333 Optional
<String
> family
,
334 Optional
<String
> prefix
,
335 Optional
<String
> suffix
,
336 Optional
<String
> middle
339 static Name
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Name name
) {
340 return new Name(Optional
.ofNullable(name
.getDisplay().orNull()),
341 Optional
.ofNullable(name
.getGiven().orNull()),
342 Optional
.ofNullable(name
.getFamily().orNull()),
343 Optional
.ofNullable(name
.getPrefix().orNull()),
344 Optional
.ofNullable(name
.getSuffix().orNull()),
345 Optional
.ofNullable(name
.getMiddle().orNull()));
349 public record Avatar(Attachment attachment
, boolean isProfile
) {
351 static Avatar
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Avatar avatar
) {
352 return new Avatar(Attachment
.from(avatar
.getAttachment()), avatar
.isProfile());
357 String value
, Type type
, Optional
<String
> label
360 static Phone
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone phone
) {
361 return new Phone(phone
.getValue(),
362 Type
.from(phone
.getType()),
363 Optional
.ofNullable(phone
.getLabel().orNull()));
372 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone
.Type type
) {
373 return switch (type
) {
376 case MOBILE
-> MOBILE
;
377 case CUSTOM
-> CUSTOM
;
384 String value
, Type type
, Optional
<String
> label
387 static Email
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email email
) {
388 return new Email(email
.getValue(),
389 Type
.from(email
.getType()),
390 Optional
.ofNullable(email
.getLabel().orNull()));
399 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email
.Type type
) {
400 return switch (type
) {
403 case MOBILE
-> MOBILE
;
404 case CUSTOM
-> CUSTOM
;
410 public record Address(
412 Optional
<String
> label
,
413 Optional
<String
> street
,
414 Optional
<String
> pobox
,
415 Optional
<String
> neighborhood
,
416 Optional
<String
> city
,
417 Optional
<String
> region
,
418 Optional
<String
> postcode
,
419 Optional
<String
> country
422 static Address
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress address
) {
423 return new Address(Address
.Type
.from(address
.getType()),
424 Optional
.ofNullable(address
.getLabel().orNull()),
425 Optional
.ofNullable(address
.getLabel().orNull()),
426 Optional
.ofNullable(address
.getLabel().orNull()),
427 Optional
.ofNullable(address
.getLabel().orNull()),
428 Optional
.ofNullable(address
.getLabel().orNull()),
429 Optional
.ofNullable(address
.getLabel().orNull()),
430 Optional
.ofNullable(address
.getLabel().orNull()),
431 Optional
.ofNullable(address
.getLabel().orNull()));
439 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress
.Type type
) {
440 return switch (type
) {
443 case CUSTOM
-> CUSTOM
;
450 public record Preview(String title
, String description
, long date
, String url
, Optional
<Attachment
> image
) {
452 static Preview
from(SignalServiceDataMessage
.Preview preview
) {
453 return new Preview(preview
.getTitle(),
454 preview
.getDescription(),
457 Optional
.ofNullable(preview
.getImage().transform(Attachment
::from
).orNull()));
464 Optional
<Blocked
> blocked
,
467 Optional
<ViewOnceOpen
> viewOnceOpen
,
468 Optional
<Contacts
> contacts
,
469 Optional
<Groups
> groups
,
470 Optional
<MessageRequestResponse
> messageRequestResponse
473 public static Sync
from(
474 final SignalServiceSyncMessage syncMessage
,
475 RecipientResolver recipientResolver
,
476 RecipientAddressResolver addressResolver
478 return new Sync(Optional
.ofNullable(syncMessage
.getSent()
479 .transform(s
-> Sent
.from(s
, recipientResolver
, addressResolver
))
481 Optional
.ofNullable(syncMessage
.getBlockedList()
482 .transform(b
-> Blocked
.from(b
, recipientResolver
, addressResolver
))
484 syncMessage
.getRead()
485 .transform(r
-> r
.stream()
486 .map(rm
-> Read
.from(rm
, recipientResolver
, addressResolver
))
487 .collect(Collectors
.toList()))
489 syncMessage
.getViewed()
490 .transform(r
-> r
.stream()
491 .map(rm
-> Viewed
.from(rm
, recipientResolver
, addressResolver
))
492 .collect(Collectors
.toList()))
494 Optional
.ofNullable(syncMessage
.getViewOnceOpen()
495 .transform(rm
-> ViewOnceOpen
.from(rm
, recipientResolver
, addressResolver
))
497 Optional
.ofNullable(syncMessage
.getContacts().transform(Contacts
::from
).orNull()),
498 Optional
.ofNullable(syncMessage
.getGroups().transform(Groups
::from
).orNull()),
499 Optional
.ofNullable(syncMessage
.getMessageRequestResponse()
500 .transform(m
-> MessageRequestResponse
.from(m
, recipientResolver
, addressResolver
))
506 long expirationStartTimestamp
,
507 Optional
<RecipientAddress
> destination
,
508 Set
<RecipientAddress
> recipients
,
513 SentTranscriptMessage sentMessage
,
514 RecipientResolver recipientResolver
,
515 RecipientAddressResolver addressResolver
517 return new Sent(sentMessage
.getTimestamp(),
518 sentMessage
.getExpirationStartTimestamp(),
519 Optional
.ofNullable(sentMessage
.getDestination()
520 .transform(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
523 sentMessage
.getRecipients()
525 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
526 .collect(Collectors
.toSet()),
527 Data
.from(sentMessage
.getMessage(), recipientResolver
, addressResolver
));
531 public record Blocked(List
<RecipientAddress
> recipients
, List
<GroupId
> groupIds
) {
534 BlockedListMessage blockedListMessage
,
535 RecipientResolver recipientResolver
,
536 RecipientAddressResolver addressResolver
538 return new Blocked(blockedListMessage
.getAddresses()
540 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
541 .collect(Collectors
.toList()),
542 blockedListMessage
.getGroupIds()
544 .map(GroupId
::unknownVersion
)
545 .collect(Collectors
.toList()));
549 public record Read(RecipientAddress sender
, long timestamp
) {
552 ReadMessage readMessage
,
553 RecipientResolver recipientResolver
,
554 RecipientAddressResolver addressResolver
556 return new Read(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
557 readMessage
.getTimestamp());
561 public record Viewed(RecipientAddress sender
, long timestamp
) {
564 ViewedMessage readMessage
,
565 RecipientResolver recipientResolver
,
566 RecipientAddressResolver addressResolver
568 return new Viewed(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
569 readMessage
.getTimestamp());
573 public record ViewOnceOpen(RecipientAddress sender
, long timestamp
) {
575 static ViewOnceOpen
from(
576 ViewOnceOpenMessage readMessage
,
577 RecipientResolver recipientResolver
,
578 RecipientAddressResolver addressResolver
580 return new ViewOnceOpen(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
581 readMessage
.getSender())), readMessage
.getTimestamp());
585 public record Contacts(boolean isComplete
) {
587 static Contacts
from(ContactsMessage contactsMessage
) {
588 return new Contacts(contactsMessage
.isComplete());
592 public record Groups() {
594 static Groups
from(SignalServiceAttachment groupsMessage
) {
599 public record MessageRequestResponse(Type type
, Optional
<GroupId
> groupId
, Optional
<RecipientAddress
> person
) {
601 static MessageRequestResponse
from(
602 MessageRequestResponseMessage messageRequestResponse
,
603 RecipientResolver recipientResolver
,
604 RecipientAddressResolver addressResolver
606 return new MessageRequestResponse(Type
.from(messageRequestResponse
.getType()),
607 Optional
.ofNullable(messageRequestResponse
.getGroupId()
608 .transform(GroupId
::unknownVersion
)
610 Optional
.ofNullable(messageRequestResponse
.getPerson()
611 .transform(p
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
624 static Type
from(MessageRequestResponseMessage
.Type type
) {
625 return switch (type
) {
626 case UNKNOWN
-> UNKNOWN
;
627 case ACCEPT
-> ACCEPT
;
628 case DELETE
-> DELETE
;
630 case BLOCK_AND_DELETE
-> BLOCK_AND_DELETE
;
631 case UNBLOCK_AND_ACCEPT
-> UNBLOCK_AND_ACCEPT
;
639 Optional
<Integer
> destinationDeviceId
,
640 Optional
<GroupId
> groupId
,
641 Optional
<Long
> timestamp
,
642 Optional
<Offer
> offer
,
643 Optional
<Answer
> answer
,
644 Optional
<Hangup
> hangup
,
646 List
<IceUpdate
> iceUpdate
,
647 Optional
<Opaque
> opaque
650 public static Call
from(final SignalServiceCallMessage callMessage
) {
651 return new Call(Optional
.ofNullable(callMessage
.getDestinationDeviceId().orNull()),
652 Optional
.ofNullable(callMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()),
653 Optional
.ofNullable(callMessage
.getTimestamp().orNull()),
654 Optional
.ofNullable(callMessage
.getOfferMessage().transform(Offer
::from
).orNull()),
655 Optional
.ofNullable(callMessage
.getAnswerMessage().transform(Answer
::from
).orNull()),
656 Optional
.ofNullable(callMessage
.getHangupMessage().transform(Hangup
::from
).orNull()),
657 Optional
.ofNullable(callMessage
.getBusyMessage().transform(Busy
::from
).orNull()),
658 callMessage
.getIceUpdateMessages()
659 .transform(m
-> m
.stream().map(IceUpdate
::from
).collect(Collectors
.toList()))
661 Optional
.ofNullable(callMessage
.getOpaqueMessage().transform(Opaque
::from
).orNull()));
664 public record Offer(long id
, String sdp
, Type type
, byte[] opaque
) {
666 static Offer
from(OfferMessage offerMessage
) {
667 return new Offer(offerMessage
.getId(),
668 offerMessage
.getSdp(),
669 Type
.from(offerMessage
.getType()),
670 offerMessage
.getOpaque());
677 static Type
from(OfferMessage
.Type type
) {
678 return switch (type
) {
679 case AUDIO_CALL
-> AUDIO_CALL
;
680 case VIDEO_CALL
-> VIDEO_CALL
;
686 public record Answer(long id
, String sdp
, byte[] opaque
) {
688 static Answer
from(AnswerMessage answerMessage
) {
689 return new Answer(answerMessage
.getId(), answerMessage
.getSdp(), answerMessage
.getOpaque());
693 public record Busy(long id
) {
695 static Offer
from(OfferMessage offerMessage
) {
696 return new Offer(offerMessage
.getId(),
697 offerMessage
.getSdp(),
698 Offer
.Type
.from(offerMessage
.getType()),
699 offerMessage
.getOpaque());
702 static Busy
from(BusyMessage busyMessage
) {
703 return new Busy(busyMessage
.getId());
707 public record Hangup(long id
, Type type
, int deviceId
, boolean isLegacy
) {
709 static Hangup
from(HangupMessage hangupMessage
) {
710 return new Hangup(hangupMessage
.getId(),
711 Type
.from(hangupMessage
.getType()),
712 hangupMessage
.getDeviceId(),
713 hangupMessage
.isLegacy());
723 static Type
from(HangupMessage
.Type type
) {
724 return switch (type
) {
725 case NORMAL
-> NORMAL
;
726 case ACCEPTED
-> ACCEPTED
;
727 case DECLINED
-> DECLINED
;
729 case NEED_PERMISSION
-> NEED_PERMISSION
;
735 public record IceUpdate(long id
, String sdp
, byte[] opaque
) {
737 static IceUpdate
from(IceUpdateMessage iceUpdateMessage
) {
738 return new IceUpdate(iceUpdateMessage
.getId(), iceUpdateMessage
.getSdp(), iceUpdateMessage
.getOpaque());
742 public record Opaque(byte[] opaque
, Urgency urgency
) {
744 static Opaque
from(OpaqueMessage opaqueMessage
) {
745 return new Opaque(opaqueMessage
.getOpaque(), Urgency
.from(opaqueMessage
.getUrgency()));
748 public enum Urgency
{
752 static Urgency
from(OpaqueMessage
.Urgency urgency
) {
753 return switch (urgency
) {
754 case DROPPABLE
-> DROPPABLE
;
755 case HANDLE_IMMEDIATELY
-> HANDLE_IMMEDIATELY
;
762 public static MessageEnvelope
from(
763 SignalServiceEnvelope envelope
,
764 SignalServiceContent content
,
765 RecipientResolver recipientResolver
,
766 RecipientAddressResolver addressResolver
768 final var source
= !envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()
769 ? recipientResolver
.resolveRecipient(envelope
.getSourceAddress())
770 : envelope
.isUnidentifiedSender() && content
!= null
771 ? recipientResolver
.resolveRecipient(content
.getSender())
773 final var sourceDevice
= envelope
.hasSourceDevice()
774 ? envelope
.getSourceDevice()
775 : content
!= null ? content
.getSenderDevice() : 0;
777 Optional
<Receipt
> receipt
;
778 Optional
<Typing
> typing
;
782 if (content
!= null) {
783 receipt
= Optional
.ofNullable(content
.getReceiptMessage().transform(Receipt
::from
).orNull());
784 typing
= Optional
.ofNullable(content
.getTypingMessage().transform(Typing
::from
).orNull());
785 data
= Optional
.ofNullable(content
.getDataMessage()
786 .transform(dataMessage
-> Data
.from(dataMessage
, recipientResolver
, addressResolver
))
788 sync
= Optional
.ofNullable(content
.getSyncMessage()
789 .transform(s
-> Sync
.from(s
, recipientResolver
, addressResolver
))
791 call
= Optional
.ofNullable(content
.getCallMessage().transform(Call
::from
).orNull());
793 receipt
= Optional
.empty();
794 typing
= Optional
.empty();
795 data
= Optional
.empty();
796 sync
= Optional
.empty();
797 call
= Optional
.empty();
800 return new MessageEnvelope(source
== null
802 : Optional
.of(addressResolver
.resolveRecipientAddress(source
)),
804 envelope
.getTimestamp(),
805 envelope
.getServerReceivedTimestamp(),
806 envelope
.getServerDeliveredTimestamp(),
807 envelope
.isUnidentifiedSender(),