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 List
<Attachment
> attachments
,
105 Optional
<Long
> remoteDeleteId
,
106 Optional
<Sticker
> sticker
,
107 List
<SharedContact
> sharedContacts
,
108 List
<Mention
> mentions
,
109 List
<Preview
> previews
113 final SignalServiceDataMessage dataMessage
,
114 RecipientResolver recipientResolver
,
115 RecipientAddressResolver addressResolver
,
116 final AttachmentFileProvider fileProvider
118 return new Data(dataMessage
.getTimestamp(),
119 Optional
.ofNullable(dataMessage
.getGroupContext().transform(GroupContext
::from
).orNull()),
120 Optional
.ofNullable(dataMessage
.getGroupCallUpdate().transform(GroupCallUpdate
::from
).orNull()),
121 Optional
.ofNullable(dataMessage
.getBody().orNull()),
122 dataMessage
.getExpiresInSeconds(),
123 dataMessage
.isExpirationUpdate(),
124 dataMessage
.isViewOnce(),
125 dataMessage
.isEndSession(),
126 dataMessage
.getProfileKey().isPresent(),
127 Optional
.ofNullable(dataMessage
.getReaction()
128 .transform(r
-> Reaction
.from(r
, recipientResolver
, addressResolver
))
130 Optional
.ofNullable(dataMessage
.getQuote()
131 .transform(q
-> Quote
.from(q
, recipientResolver
, addressResolver
, fileProvider
))
133 dataMessage
.getAttachments()
134 .transform(a
-> a
.stream()
135 .map(as -> Attachment
.from(as, fileProvider
))
136 .collect(Collectors
.toList()))
138 Optional
.ofNullable(dataMessage
.getRemoteDelete()
139 .transform(SignalServiceDataMessage
.RemoteDelete
::getTargetSentTimestamp
)
141 Optional
.ofNullable(dataMessage
.getSticker().transform(Sticker
::from
).orNull()),
142 dataMessage
.getSharedContacts()
143 .transform(a
-> a
.stream()
144 .map(sharedContact
-> SharedContact
.from(sharedContact
, fileProvider
))
145 .collect(Collectors
.toList()))
147 dataMessage
.getMentions()
148 .transform(a
-> a
.stream()
149 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
150 .collect(Collectors
.toList()))
152 dataMessage
.getPreviews()
153 .transform(a
-> a
.stream()
154 .map(preview
-> Preview
.from(preview
, fileProvider
))
155 .collect(Collectors
.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
))
222 .collect(Collectors
.toList()),
223 quote
.getAttachments() == null
225 : quote
.getAttachments()
227 .map(a
-> Attachment
.from(a
, fileProvider
))
228 .collect(Collectors
.toList()));
232 public record Mention(RecipientAddress recipient
, int start
, int length
) {
235 SignalServiceDataMessage
.Mention mention
,
236 RecipientResolver recipientResolver
,
237 RecipientAddressResolver addressResolver
239 return new Mention(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(mention
.getAci())),
241 mention
.getLength());
245 public record Attachment(
248 Optional
<String
> fileName
,
250 Optional
<Long
> uploadTimestamp
,
252 Optional
<byte[]> preview
,
253 Optional
<Attachment
> thumbnail
,
254 Optional
<String
> caption
,
255 Optional
<Integer
> width
,
256 Optional
<Integer
> height
,
262 static Attachment
from(SignalServiceAttachment attachment
, AttachmentFileProvider fileProvider
) {
263 if (attachment
.isPointer()) {
264 final var a
= attachment
.asPointer();
265 return new Attachment(Optional
.of(a
.getRemoteId().toString()),
266 Optional
.of(fileProvider
.getFile(a
.getRemoteId())),
267 Optional
.ofNullable(a
.getFileName().orNull()),
269 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
270 Optional
.ofNullable(a
.getSize().transform(Integer
::longValue
).orNull()),
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()),
280 final var a
= attachment
.asStream();
281 return new Attachment(Optional
.empty(),
283 Optional
.ofNullable(a
.getFileName().orNull()),
285 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
286 Optional
.of(a
.getLength()),
287 Optional
.ofNullable(a
.getPreview().orNull()),
289 Optional
.ofNullable(a
.getCaption().orNull()),
290 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
291 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
298 static Attachment
from(
299 SignalServiceDataMessage
.Quote
.QuotedAttachment a
, final AttachmentFileProvider fileProvider
301 return new Attachment(Optional
.empty(),
303 Optional
.ofNullable(a
.getFileName()),
308 a
.getThumbnail() == null
310 : Optional
.of(Attachment
.from(a
.getThumbnail(), fileProvider
)),
320 public record Sticker(byte[] packId
, byte[] packKey
, int stickerId
) {
322 static Sticker
from(SignalServiceDataMessage
.Sticker sticker
) {
323 return new Sticker(sticker
.getPackId(), sticker
.getPackKey(), sticker
.getStickerId());
327 public record SharedContact(
329 Optional
<Avatar
> avatar
,
332 List
<Address
> address
,
333 Optional
<String
> organization
336 static SharedContact
from(
337 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact sharedContact
,
338 final AttachmentFileProvider fileProvider
340 return new SharedContact(Name
.from(sharedContact
.getName()),
341 Optional
.ofNullable(sharedContact
.getAvatar()
342 .transform(avatar1
-> Avatar
.from(avatar1
, fileProvider
))
344 sharedContact
.getPhone()
345 .transform(p
-> p
.stream().map(Phone
::from
).collect(Collectors
.toList()))
347 sharedContact
.getEmail()
348 .transform(p
-> p
.stream().map(Email
::from
).collect(Collectors
.toList()))
350 sharedContact
.getAddress()
351 .transform(p
-> p
.stream().map(Address
::from
).collect(Collectors
.toList()))
353 Optional
.ofNullable(sharedContact
.getOrganization().orNull()));
357 Optional
<String
> display
,
358 Optional
<String
> given
,
359 Optional
<String
> family
,
360 Optional
<String
> prefix
,
361 Optional
<String
> suffix
,
362 Optional
<String
> middle
365 static Name
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Name name
) {
366 return new Name(Optional
.ofNullable(name
.getDisplay().orNull()),
367 Optional
.ofNullable(name
.getGiven().orNull()),
368 Optional
.ofNullable(name
.getFamily().orNull()),
369 Optional
.ofNullable(name
.getPrefix().orNull()),
370 Optional
.ofNullable(name
.getSuffix().orNull()),
371 Optional
.ofNullable(name
.getMiddle().orNull()));
375 public record Avatar(Attachment attachment
, boolean isProfile
) {
378 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Avatar avatar
,
379 final AttachmentFileProvider fileProvider
381 return new Avatar(Attachment
.from(avatar
.getAttachment(), fileProvider
), avatar
.isProfile());
386 String value
, Type type
, Optional
<String
> label
389 static Phone
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone phone
) {
390 return new Phone(phone
.getValue(),
391 Type
.from(phone
.getType()),
392 Optional
.ofNullable(phone
.getLabel().orNull()));
401 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone
.Type type
) {
402 return switch (type
) {
405 case MOBILE
-> MOBILE
;
406 case CUSTOM
-> CUSTOM
;
413 String value
, Type type
, Optional
<String
> label
416 static Email
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email email
) {
417 return new Email(email
.getValue(),
418 Type
.from(email
.getType()),
419 Optional
.ofNullable(email
.getLabel().orNull()));
428 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email
.Type type
) {
429 return switch (type
) {
432 case MOBILE
-> MOBILE
;
433 case CUSTOM
-> CUSTOM
;
439 public record Address(
441 Optional
<String
> label
,
442 Optional
<String
> street
,
443 Optional
<String
> pobox
,
444 Optional
<String
> neighborhood
,
445 Optional
<String
> city
,
446 Optional
<String
> region
,
447 Optional
<String
> postcode
,
448 Optional
<String
> country
451 static Address
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress address
) {
452 return new Address(Address
.Type
.from(address
.getType()),
453 Optional
.ofNullable(address
.getLabel().orNull()),
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()));
468 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress
.Type type
) {
469 return switch (type
) {
472 case CUSTOM
-> CUSTOM
;
479 public record Preview(String title
, String description
, long date
, String url
, Optional
<Attachment
> image
) {
482 SignalServiceDataMessage
.Preview preview
, final AttachmentFileProvider fileProvider
484 return new Preview(preview
.getTitle(),
485 preview
.getDescription(),
488 Optional
.ofNullable(preview
.getImage()
489 .transform(as -> Attachment
.from(as, fileProvider
))
497 Optional
<Blocked
> blocked
,
500 Optional
<ViewOnceOpen
> viewOnceOpen
,
501 Optional
<Contacts
> contacts
,
502 Optional
<Groups
> groups
,
503 Optional
<MessageRequestResponse
> messageRequestResponse
506 public static Sync
from(
507 final SignalServiceSyncMessage syncMessage
,
508 RecipientResolver recipientResolver
,
509 RecipientAddressResolver addressResolver
,
510 final AttachmentFileProvider fileProvider
512 return new Sync(Optional
.ofNullable(syncMessage
.getSent()
513 .transform(s
-> Sent
.from(s
, recipientResolver
, addressResolver
, fileProvider
))
515 Optional
.ofNullable(syncMessage
.getBlockedList()
516 .transform(b
-> Blocked
.from(b
, recipientResolver
, addressResolver
))
518 syncMessage
.getRead()
519 .transform(r
-> r
.stream()
520 .map(rm
-> Read
.from(rm
, recipientResolver
, addressResolver
))
521 .collect(Collectors
.toList()))
523 syncMessage
.getViewed()
524 .transform(r
-> r
.stream()
525 .map(rm
-> Viewed
.from(rm
, recipientResolver
, addressResolver
))
526 .collect(Collectors
.toList()))
528 Optional
.ofNullable(syncMessage
.getViewOnceOpen()
529 .transform(rm
-> ViewOnceOpen
.from(rm
, recipientResolver
, addressResolver
))
531 Optional
.ofNullable(syncMessage
.getContacts().transform(Contacts
::from
).orNull()),
532 Optional
.ofNullable(syncMessage
.getGroups().transform(Groups
::from
).orNull()),
533 Optional
.ofNullable(syncMessage
.getMessageRequestResponse()
534 .transform(m
-> MessageRequestResponse
.from(m
, recipientResolver
, addressResolver
))
540 long expirationStartTimestamp
,
541 Optional
<RecipientAddress
> destination
,
542 Set
<RecipientAddress
> recipients
,
547 SentTranscriptMessage sentMessage
,
548 RecipientResolver recipientResolver
,
549 RecipientAddressResolver addressResolver
,
550 final AttachmentFileProvider fileProvider
552 return new Sent(sentMessage
.getTimestamp(),
553 sentMessage
.getExpirationStartTimestamp(),
554 Optional
.ofNullable(sentMessage
.getDestination()
555 .transform(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
558 sentMessage
.getRecipients()
560 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
561 .collect(Collectors
.toSet()),
562 Data
.from(sentMessage
.getMessage(), recipientResolver
, addressResolver
, fileProvider
));
566 public record Blocked(List
<RecipientAddress
> recipients
, List
<GroupId
> groupIds
) {
569 BlockedListMessage blockedListMessage
,
570 RecipientResolver recipientResolver
,
571 RecipientAddressResolver addressResolver
573 return new Blocked(blockedListMessage
.getAddresses()
575 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
576 .collect(Collectors
.toList()),
577 blockedListMessage
.getGroupIds()
579 .map(GroupId
::unknownVersion
)
580 .collect(Collectors
.toList()));
584 public record Read(RecipientAddress sender
, long timestamp
) {
587 ReadMessage readMessage
,
588 RecipientResolver recipientResolver
,
589 RecipientAddressResolver addressResolver
591 return new Read(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
592 readMessage
.getTimestamp());
596 public record Viewed(RecipientAddress sender
, long timestamp
) {
599 ViewedMessage readMessage
,
600 RecipientResolver recipientResolver
,
601 RecipientAddressResolver addressResolver
603 return new Viewed(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
604 readMessage
.getTimestamp());
608 public record ViewOnceOpen(RecipientAddress sender
, long timestamp
) {
610 static ViewOnceOpen
from(
611 ViewOnceOpenMessage readMessage
,
612 RecipientResolver recipientResolver
,
613 RecipientAddressResolver addressResolver
615 return new ViewOnceOpen(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
616 readMessage
.getSender())), readMessage
.getTimestamp());
620 public record Contacts(boolean isComplete
) {
622 static Contacts
from(ContactsMessage contactsMessage
) {
623 return new Contacts(contactsMessage
.isComplete());
627 public record Groups() {
629 static Groups
from(SignalServiceAttachment groupsMessage
) {
634 public record MessageRequestResponse(Type type
, Optional
<GroupId
> groupId
, Optional
<RecipientAddress
> person
) {
636 static MessageRequestResponse
from(
637 MessageRequestResponseMessage messageRequestResponse
,
638 RecipientResolver recipientResolver
,
639 RecipientAddressResolver addressResolver
641 return new MessageRequestResponse(Type
.from(messageRequestResponse
.getType()),
642 Optional
.ofNullable(messageRequestResponse
.getGroupId()
643 .transform(GroupId
::unknownVersion
)
645 Optional
.ofNullable(messageRequestResponse
.getPerson()
646 .transform(p
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
659 static Type
from(MessageRequestResponseMessage
.Type type
) {
660 return switch (type
) {
661 case UNKNOWN
-> UNKNOWN
;
662 case ACCEPT
-> ACCEPT
;
663 case DELETE
-> DELETE
;
665 case BLOCK_AND_DELETE
-> BLOCK_AND_DELETE
;
666 case UNBLOCK_AND_ACCEPT
-> UNBLOCK_AND_ACCEPT
;
674 Optional
<Integer
> destinationDeviceId
,
675 Optional
<GroupId
> groupId
,
676 Optional
<Long
> timestamp
,
677 Optional
<Offer
> offer
,
678 Optional
<Answer
> answer
,
679 Optional
<Hangup
> hangup
,
681 List
<IceUpdate
> iceUpdate
,
682 Optional
<Opaque
> opaque
685 public static Call
from(final SignalServiceCallMessage callMessage
) {
686 return new Call(Optional
.ofNullable(callMessage
.getDestinationDeviceId().orNull()),
687 Optional
.ofNullable(callMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()),
688 Optional
.ofNullable(callMessage
.getTimestamp().orNull()),
689 Optional
.ofNullable(callMessage
.getOfferMessage().transform(Offer
::from
).orNull()),
690 Optional
.ofNullable(callMessage
.getAnswerMessage().transform(Answer
::from
).orNull()),
691 Optional
.ofNullable(callMessage
.getHangupMessage().transform(Hangup
::from
).orNull()),
692 Optional
.ofNullable(callMessage
.getBusyMessage().transform(Busy
::from
).orNull()),
693 callMessage
.getIceUpdateMessages()
694 .transform(m
-> m
.stream().map(IceUpdate
::from
).collect(Collectors
.toList()))
696 Optional
.ofNullable(callMessage
.getOpaqueMessage().transform(Opaque
::from
).orNull()));
699 public record Offer(long id
, String sdp
, Type type
, byte[] opaque
) {
701 static Offer
from(OfferMessage offerMessage
) {
702 return new Offer(offerMessage
.getId(),
703 offerMessage
.getSdp(),
704 Type
.from(offerMessage
.getType()),
705 offerMessage
.getOpaque());
712 static Type
from(OfferMessage
.Type type
) {
713 return switch (type
) {
714 case AUDIO_CALL
-> AUDIO_CALL
;
715 case VIDEO_CALL
-> VIDEO_CALL
;
721 public record Answer(long id
, String sdp
, byte[] opaque
) {
723 static Answer
from(AnswerMessage answerMessage
) {
724 return new Answer(answerMessage
.getId(), answerMessage
.getSdp(), answerMessage
.getOpaque());
728 public record Busy(long id
) {
730 static Busy
from(BusyMessage busyMessage
) {
731 return new Busy(busyMessage
.getId());
735 public record Hangup(long id
, Type type
, int deviceId
, boolean isLegacy
) {
737 static Hangup
from(HangupMessage hangupMessage
) {
738 return new Hangup(hangupMessage
.getId(),
739 Type
.from(hangupMessage
.getType()),
740 hangupMessage
.getDeviceId(),
741 hangupMessage
.isLegacy());
751 static Type
from(HangupMessage
.Type type
) {
752 return switch (type
) {
753 case NORMAL
-> NORMAL
;
754 case ACCEPTED
-> ACCEPTED
;
755 case DECLINED
-> DECLINED
;
757 case NEED_PERMISSION
-> NEED_PERMISSION
;
763 public record IceUpdate(long id
, String sdp
, byte[] opaque
) {
765 static IceUpdate
from(IceUpdateMessage iceUpdateMessage
) {
766 return new IceUpdate(iceUpdateMessage
.getId(), iceUpdateMessage
.getSdp(), iceUpdateMessage
.getOpaque());
770 public record Opaque(byte[] opaque
, Urgency urgency
) {
772 static Opaque
from(OpaqueMessage opaqueMessage
) {
773 return new Opaque(opaqueMessage
.getOpaque(), Urgency
.from(opaqueMessage
.getUrgency()));
776 public enum Urgency
{
780 static Urgency
from(OpaqueMessage
.Urgency urgency
) {
781 return switch (urgency
) {
782 case DROPPABLE
-> DROPPABLE
;
783 case HANDLE_IMMEDIATELY
-> HANDLE_IMMEDIATELY
;
790 public static MessageEnvelope
from(
791 SignalServiceEnvelope envelope
,
792 SignalServiceContent content
,
793 RecipientResolver recipientResolver
,
794 RecipientAddressResolver addressResolver
,
795 final AttachmentFileProvider fileProvider
797 final var source
= !envelope
.isUnidentifiedSender() && envelope
.hasSourceUuid()
798 ? recipientResolver
.resolveRecipient(envelope
.getSourceAddress())
799 : envelope
.isUnidentifiedSender() && content
!= null
800 ? recipientResolver
.resolveRecipient(content
.getSender())
802 final var sourceDevice
= envelope
.hasSourceDevice()
803 ? envelope
.getSourceDevice()
804 : content
!= null ? content
.getSenderDevice() : 0;
806 Optional
<Receipt
> receipt
;
807 Optional
<Typing
> typing
;
811 if (content
!= null) {
812 receipt
= Optional
.ofNullable(content
.getReceiptMessage().transform(Receipt
::from
).orNull());
813 typing
= Optional
.ofNullable(content
.getTypingMessage().transform(Typing
::from
).orNull());
814 data
= Optional
.ofNullable(content
.getDataMessage()
815 .transform(dataMessage
-> Data
.from(dataMessage
, recipientResolver
, addressResolver
, fileProvider
))
817 sync
= Optional
.ofNullable(content
.getSyncMessage()
818 .transform(s
-> Sync
.from(s
, recipientResolver
, addressResolver
, fileProvider
))
820 call
= Optional
.ofNullable(content
.getCallMessage().transform(Call
::from
).orNull());
822 receipt
= Optional
.empty();
823 typing
= Optional
.empty();
824 data
= Optional
.empty();
825 sync
= Optional
.empty();
826 call
= Optional
.empty();
829 return new MessageEnvelope(source
== null
831 : Optional
.of(addressResolver
.resolveRecipientAddress(source
)),
833 envelope
.getTimestamp(),
834 envelope
.getServerReceivedTimestamp(),
835 envelope
.getServerDeliveredTimestamp(),
836 envelope
.isUnidentifiedSender(),
844 public interface AttachmentFileProvider
{
846 File
getFile(SignalServiceAttachmentRemoteId attachmentRemoteId
);