1 package org
.asamk
.signal
.manager
.api
;
3 import org
.asamk
.signal
.manager
.groups
.GroupId
;
4 import org
.asamk
.signal
.manager
.groups
.GroupUtils
;
5 import org
.asamk
.signal
.manager
.helper
.RecipientAddressResolver
;
6 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientAddress
;
7 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientResolver
;
8 import org
.signal
.libsignal
.metadata
.ProtocolException
;
9 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachment
;
10 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachmentRemoteId
;
11 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceContent
;
12 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceDataMessage
;
13 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
14 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroup
;
15 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceGroupContext
;
16 import org
.whispersystems
.signalservice
.api
.messages
.SignalServicePreview
;
17 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceReceiptMessage
;
18 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceTypingMessage
;
19 import org
.whispersystems
.signalservice
.api
.messages
.calls
.AnswerMessage
;
20 import org
.whispersystems
.signalservice
.api
.messages
.calls
.BusyMessage
;
21 import org
.whispersystems
.signalservice
.api
.messages
.calls
.HangupMessage
;
22 import org
.whispersystems
.signalservice
.api
.messages
.calls
.IceUpdateMessage
;
23 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OfferMessage
;
24 import org
.whispersystems
.signalservice
.api
.messages
.calls
.OpaqueMessage
;
25 import org
.whispersystems
.signalservice
.api
.messages
.calls
.SignalServiceCallMessage
;
26 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.BlockedListMessage
;
27 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ContactsMessage
;
28 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.MessageRequestResponseMessage
;
29 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ReadMessage
;
30 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SentTranscriptMessage
;
31 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.SignalServiceSyncMessage
;
32 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewOnceOpenMessage
;
33 import org
.whispersystems
.signalservice
.api
.messages
.multidevice
.ViewedMessage
;
36 import java
.util
.List
;
37 import java
.util
.Optional
;
39 import java
.util
.stream
.Collectors
;
41 public record MessageEnvelope(
42 Optional
<RecipientAddress
> sourceAddress
,
45 long serverReceivedTimestamp
,
46 long serverDeliveredTimestamp
,
47 boolean isUnidentifiedSender
,
48 Optional
<Receipt
> receipt
,
49 Optional
<Typing
> typing
,
55 public record Receipt(long when, Type type
, List
<Long
> timestamps
) {
57 static Receipt
from(final SignalServiceReceiptMessage receiptMessage
) {
58 return new Receipt(receiptMessage
.getWhen(),
59 Type
.from(receiptMessage
.getType()),
60 receiptMessage
.getTimestamps());
69 static Type
from(SignalServiceReceiptMessage
.Type type
) {
70 return switch (type
) {
71 case DELIVERY
-> DELIVERY
;
73 case VIEWED
-> VIEWED
;
74 case UNKNOWN
-> UNKNOWN
;
80 public record Typing(long timestamp
, Type type
, Optional
<GroupId
> groupId
) {
82 public static Typing
from(final SignalServiceTypingMessage typingMessage
) {
83 return new Typing(typingMessage
.getTimestamp(),
84 typingMessage
.isTypingStarted() ? Type
.STARTED
: Type
.STOPPED
,
85 Optional
.ofNullable(typingMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()));
96 Optional
<GroupContext
> groupContext
,
97 Optional
<GroupCallUpdate
> groupCallUpdate
,
98 Optional
<String
> body
,
100 boolean isExpirationUpdate
,
102 boolean isEndSession
,
103 boolean hasProfileKey
,
104 Optional
<Reaction
> reaction
,
105 Optional
<Quote
> quote
,
106 Optional
<Payment
> payment
,
107 List
<Attachment
> attachments
,
108 Optional
<Long
> remoteDeleteId
,
109 Optional
<Sticker
> sticker
,
110 List
<SharedContact
> sharedContacts
,
111 List
<Mention
> mentions
,
112 List
<Preview
> previews
116 final SignalServiceDataMessage dataMessage
,
117 RecipientResolver recipientResolver
,
118 RecipientAddressResolver addressResolver
,
119 final AttachmentFileProvider fileProvider
121 return new Data(dataMessage
.getTimestamp(),
122 Optional
.ofNullable(dataMessage
.getGroupContext().transform(GroupContext
::from
).orNull()),
123 Optional
.ofNullable(dataMessage
.getGroupCallUpdate().transform(GroupCallUpdate
::from
).orNull()),
124 Optional
.ofNullable(dataMessage
.getBody().orNull()),
125 dataMessage
.getExpiresInSeconds(),
126 dataMessage
.isExpirationUpdate(),
127 dataMessage
.isViewOnce(),
128 dataMessage
.isEndSession(),
129 dataMessage
.getProfileKey().isPresent(),
130 Optional
.ofNullable(dataMessage
.getReaction()
131 .transform(r
-> Reaction
.from(r
, recipientResolver
, addressResolver
))
133 Optional
.ofNullable(dataMessage
.getQuote()
134 .transform(q
-> Quote
.from(q
, recipientResolver
, addressResolver
, fileProvider
))
136 Optional
.ofNullable(dataMessage
.getPayment()
137 .transform(p
-> p
.getPaymentNotification().isPresent() ? Payment
.from(p
) : null)
139 dataMessage
.getAttachments()
140 .transform(a
-> a
.stream().map(as -> Attachment
.from(as, fileProvider
)).toList())
142 Optional
.ofNullable(dataMessage
.getRemoteDelete()
143 .transform(SignalServiceDataMessage
.RemoteDelete
::getTargetSentTimestamp
)
145 Optional
.ofNullable(dataMessage
.getSticker().transform(Sticker
::from
).orNull()),
146 dataMessage
.getSharedContacts()
147 .transform(a
-> a
.stream()
148 .map(sharedContact
-> SharedContact
.from(sharedContact
, fileProvider
))
151 dataMessage
.getMentions()
152 .transform(a
-> a
.stream()
153 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
156 dataMessage
.getPreviews()
157 .transform(a
-> a
.stream().map(preview
-> Preview
.from(preview
, fileProvider
)).toList())
161 public record GroupContext(GroupId groupId
, boolean isGroupUpdate
, int revision
) {
163 static GroupContext
from(SignalServiceGroupContext groupContext
) {
164 if (groupContext
.getGroupV1().isPresent()) {
165 return new GroupContext(GroupId
.v1(groupContext
.getGroupV1().get().getGroupId()),
166 groupContext
.getGroupV1Type() == SignalServiceGroup
.Type
.UPDATE
,
168 } else if (groupContext
.getGroupV2().isPresent()) {
169 final var groupV2
= groupContext
.getGroupV2().get();
170 return new GroupContext(GroupUtils
.getGroupIdV2(groupV2
.getMasterKey()),
171 groupV2
.hasSignedGroupChange(),
172 groupV2
.getRevision());
174 throw new RuntimeException("Invalid group context state");
179 public record GroupCallUpdate(String eraId
) {
181 static GroupCallUpdate
from(SignalServiceDataMessage
.GroupCallUpdate groupCallUpdate
) {
182 return new GroupCallUpdate(groupCallUpdate
.getEraId());
186 public record Reaction(
187 long targetSentTimestamp
, RecipientAddress targetAuthor
, String emoji
, boolean isRemove
190 static Reaction
from(
191 SignalServiceDataMessage
.Reaction reaction
,
192 RecipientResolver recipientResolver
,
193 RecipientAddressResolver addressResolver
195 return new Reaction(reaction
.getTargetSentTimestamp(),
196 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(reaction
.getTargetAuthor())),
198 reaction
.isRemove());
204 RecipientAddress author
,
205 Optional
<String
> text
,
206 List
<Mention
> mentions
,
207 List
<Attachment
> attachments
211 SignalServiceDataMessage
.Quote quote
,
212 RecipientResolver recipientResolver
,
213 RecipientAddressResolver addressResolver
,
214 final AttachmentFileProvider fileProvider
216 return new Quote(quote
.getId(),
217 addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(quote
.getAuthor())),
218 Optional
.ofNullable(quote
.getText()),
219 quote
.getMentions() == null
221 : quote
.getMentions()
223 .map(m
-> Mention
.from(m
, recipientResolver
, addressResolver
))
225 quote
.getAttachments() == null
227 : quote
.getAttachments().stream().map(a
-> Attachment
.from(a
, fileProvider
)).toList());
231 public record Payment(String note
, byte[] receipt
) {
233 static Payment
from(SignalServiceDataMessage
.Payment payment
) {
234 return new Payment(payment
.getPaymentNotification().get().getNote(),
235 payment
.getPaymentNotification().get().getReceipt());
239 public record Mention(RecipientAddress recipient
, int start
, int length
) {
242 SignalServiceDataMessage
.Mention mention
,
243 RecipientResolver recipientResolver
,
244 RecipientAddressResolver addressResolver
246 return new Mention(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(mention
.getServiceId())),
248 mention
.getLength());
252 public record Attachment(
255 Optional
<String
> fileName
,
257 Optional
<Long
> uploadTimestamp
,
259 Optional
<byte[]> preview
,
260 Optional
<Attachment
> thumbnail
,
261 Optional
<String
> caption
,
262 Optional
<Integer
> width
,
263 Optional
<Integer
> height
,
269 static Attachment
from(SignalServiceAttachment attachment
, AttachmentFileProvider fileProvider
) {
270 if (attachment
.isPointer()) {
271 final var a
= attachment
.asPointer();
272 return new Attachment(Optional
.of(a
.getRemoteId().toString()),
273 Optional
.of(fileProvider
.getFile(a
.getRemoteId())),
274 Optional
.ofNullable(a
.getFileName().orNull()),
276 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
277 Optional
.ofNullable(a
.getSize().transform(Integer
::longValue
).orNull()),
278 Optional
.ofNullable(a
.getPreview().orNull()),
280 Optional
.ofNullable(a
.getCaption().orNull()),
281 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
282 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
287 final var a
= attachment
.asStream();
288 return new Attachment(Optional
.empty(),
290 Optional
.ofNullable(a
.getFileName().orNull()),
292 a
.getUploadTimestamp() == 0 ? Optional
.empty() : Optional
.of(a
.getUploadTimestamp()),
293 Optional
.of(a
.getLength()),
294 Optional
.ofNullable(a
.getPreview().orNull()),
296 Optional
.ofNullable(a
.getCaption().orNull()),
297 a
.getWidth() == 0 ? Optional
.empty() : Optional
.of(a
.getWidth()),
298 a
.getHeight() == 0 ? Optional
.empty() : Optional
.of(a
.getHeight()),
305 static Attachment
from(
306 SignalServiceDataMessage
.Quote
.QuotedAttachment a
, final AttachmentFileProvider fileProvider
308 return new Attachment(Optional
.empty(),
310 Optional
.ofNullable(a
.getFileName()),
315 a
.getThumbnail() == null
317 : Optional
.of(Attachment
.from(a
.getThumbnail(), fileProvider
)),
327 public record Sticker(StickerPackId packId
, byte[] packKey
, int stickerId
) {
329 static Sticker
from(SignalServiceDataMessage
.Sticker sticker
) {
330 return new Sticker(StickerPackId
.deserialize(sticker
.getPackId()),
331 sticker
.getPackKey(),
332 sticker
.getStickerId());
336 public record SharedContact(
338 Optional
<Avatar
> avatar
,
341 List
<Address
> address
,
342 Optional
<String
> organization
345 static SharedContact
from(
346 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact sharedContact
,
347 final AttachmentFileProvider fileProvider
349 return new SharedContact(Name
.from(sharedContact
.getName()),
350 Optional
.ofNullable(sharedContact
.getAvatar()
351 .transform(avatar1
-> Avatar
.from(avatar1
, fileProvider
))
353 sharedContact
.getPhone().transform(p
-> p
.stream().map(Phone
::from
).toList()).or(List
.of()),
354 sharedContact
.getEmail().transform(p
-> p
.stream().map(Email
::from
).toList()).or(List
.of()),
355 sharedContact
.getAddress().transform(p
-> p
.stream().map(Address
::from
).toList()).or(List
.of()),
356 Optional
.ofNullable(sharedContact
.getOrganization().orNull()));
360 Optional
<String
> display
,
361 Optional
<String
> given
,
362 Optional
<String
> family
,
363 Optional
<String
> prefix
,
364 Optional
<String
> suffix
,
365 Optional
<String
> middle
368 static Name
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Name name
) {
369 return new Name(Optional
.ofNullable(name
.getDisplay().orNull()),
370 Optional
.ofNullable(name
.getGiven().orNull()),
371 Optional
.ofNullable(name
.getFamily().orNull()),
372 Optional
.ofNullable(name
.getPrefix().orNull()),
373 Optional
.ofNullable(name
.getSuffix().orNull()),
374 Optional
.ofNullable(name
.getMiddle().orNull()));
378 public record Avatar(Attachment attachment
, boolean isProfile
) {
381 org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Avatar avatar
,
382 final AttachmentFileProvider fileProvider
384 return new Avatar(Attachment
.from(avatar
.getAttachment(), fileProvider
), avatar
.isProfile());
389 String value
, Type type
, Optional
<String
> label
392 static Phone
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone phone
) {
393 return new Phone(phone
.getValue(),
394 Type
.from(phone
.getType()),
395 Optional
.ofNullable(phone
.getLabel().orNull()));
404 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Phone
.Type type
) {
405 return switch (type
) {
408 case MOBILE
-> MOBILE
;
409 case CUSTOM
-> CUSTOM
;
416 String value
, Type type
, Optional
<String
> label
419 static Email
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email email
) {
420 return new Email(email
.getValue(),
421 Type
.from(email
.getType()),
422 Optional
.ofNullable(email
.getLabel().orNull()));
431 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.Email
.Type type
) {
432 return switch (type
) {
435 case MOBILE
-> MOBILE
;
436 case CUSTOM
-> CUSTOM
;
442 public record Address(
444 Optional
<String
> label
,
445 Optional
<String
> street
,
446 Optional
<String
> pobox
,
447 Optional
<String
> neighborhood
,
448 Optional
<String
> city
,
449 Optional
<String
> region
,
450 Optional
<String
> postcode
,
451 Optional
<String
> country
454 static Address
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress address
) {
455 return new Address(Address
.Type
.from(address
.getType()),
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()),
462 Optional
.ofNullable(address
.getLabel().orNull()),
463 Optional
.ofNullable(address
.getLabel().orNull()));
471 static Type
from(org
.whispersystems
.signalservice
.api
.messages
.shared
.SharedContact
.PostalAddress
.Type type
) {
472 return switch (type
) {
475 case CUSTOM
-> CUSTOM
;
482 public record Preview(String title
, String description
, long date
, String url
, Optional
<Attachment
> image
) {
485 SignalServicePreview preview
, final AttachmentFileProvider fileProvider
487 return new Preview(preview
.getTitle(),
488 preview
.getDescription(),
491 Optional
.ofNullable(preview
.getImage()
492 .transform(as -> Attachment
.from(as, fileProvider
))
500 Optional
<Blocked
> blocked
,
503 Optional
<ViewOnceOpen
> viewOnceOpen
,
504 Optional
<Contacts
> contacts
,
505 Optional
<Groups
> groups
,
506 Optional
<MessageRequestResponse
> messageRequestResponse
509 public static Sync
from(
510 final SignalServiceSyncMessage syncMessage
,
511 RecipientResolver recipientResolver
,
512 RecipientAddressResolver addressResolver
,
513 final AttachmentFileProvider fileProvider
515 return new Sync(Optional
.ofNullable(syncMessage
.getSent()
516 .transform(s
-> Sent
.from(s
, recipientResolver
, addressResolver
, fileProvider
))
518 Optional
.ofNullable(syncMessage
.getBlockedList()
519 .transform(b
-> Blocked
.from(b
, recipientResolver
, addressResolver
))
521 syncMessage
.getRead()
522 .transform(r
-> r
.stream()
523 .map(rm
-> Read
.from(rm
, recipientResolver
, addressResolver
))
526 syncMessage
.getViewed()
527 .transform(r
-> r
.stream()
528 .map(rm
-> Viewed
.from(rm
, recipientResolver
, addressResolver
))
531 Optional
.ofNullable(syncMessage
.getViewOnceOpen()
532 .transform(rm
-> ViewOnceOpen
.from(rm
, recipientResolver
, addressResolver
))
534 Optional
.ofNullable(syncMessage
.getContacts().transform(Contacts
::from
).orNull()),
535 Optional
.ofNullable(syncMessage
.getGroups().transform(Groups
::from
).orNull()),
536 Optional
.ofNullable(syncMessage
.getMessageRequestResponse()
537 .transform(m
-> MessageRequestResponse
.from(m
, recipientResolver
, addressResolver
))
543 long expirationStartTimestamp
,
544 Optional
<RecipientAddress
> destination
,
545 Set
<RecipientAddress
> recipients
,
550 SentTranscriptMessage sentMessage
,
551 RecipientResolver recipientResolver
,
552 RecipientAddressResolver addressResolver
,
553 final AttachmentFileProvider fileProvider
555 return new Sent(sentMessage
.getTimestamp(),
556 sentMessage
.getExpirationStartTimestamp(),
557 Optional
.ofNullable(sentMessage
.getDestination()
558 .transform(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
561 sentMessage
.getRecipients()
563 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
564 .collect(Collectors
.toSet()),
565 Data
.from(sentMessage
.getMessage(), recipientResolver
, addressResolver
, fileProvider
));
569 public record Blocked(List
<RecipientAddress
> recipients
, List
<GroupId
> groupIds
) {
572 BlockedListMessage blockedListMessage
,
573 RecipientResolver recipientResolver
,
574 RecipientAddressResolver addressResolver
576 return new Blocked(blockedListMessage
.getAddresses()
578 .map(d
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(d
)))
579 .toList(), blockedListMessage
.getGroupIds().stream().map(GroupId
::unknownVersion
).toList());
583 public record Read(RecipientAddress sender
, long timestamp
) {
586 ReadMessage readMessage
,
587 RecipientResolver recipientResolver
,
588 RecipientAddressResolver addressResolver
590 return new Read(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
591 readMessage
.getTimestamp());
595 public record Viewed(RecipientAddress sender
, long timestamp
) {
598 ViewedMessage readMessage
,
599 RecipientResolver recipientResolver
,
600 RecipientAddressResolver addressResolver
602 return new Viewed(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(readMessage
.getSender())),
603 readMessage
.getTimestamp());
607 public record ViewOnceOpen(RecipientAddress sender
, long timestamp
) {
609 static ViewOnceOpen
from(
610 ViewOnceOpenMessage readMessage
,
611 RecipientResolver recipientResolver
,
612 RecipientAddressResolver addressResolver
614 return new ViewOnceOpen(addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
615 readMessage
.getSender())), readMessage
.getTimestamp());
619 public record Contacts(boolean isComplete
) {
621 static Contacts
from(ContactsMessage contactsMessage
) {
622 return new Contacts(contactsMessage
.isComplete());
626 public record Groups() {
628 static Groups
from(SignalServiceAttachment groupsMessage
) {
633 public record MessageRequestResponse(Type type
, Optional
<GroupId
> groupId
, Optional
<RecipientAddress
> person
) {
635 static MessageRequestResponse
from(
636 MessageRequestResponseMessage messageRequestResponse
,
637 RecipientResolver recipientResolver
,
638 RecipientAddressResolver addressResolver
640 return new MessageRequestResponse(Type
.from(messageRequestResponse
.getType()),
641 Optional
.ofNullable(messageRequestResponse
.getGroupId()
642 .transform(GroupId
::unknownVersion
)
644 Optional
.ofNullable(messageRequestResponse
.getPerson()
645 .transform(p
-> addressResolver
.resolveRecipientAddress(recipientResolver
.resolveRecipient(
658 static Type
from(MessageRequestResponseMessage
.Type type
) {
659 return switch (type
) {
660 case UNKNOWN
-> UNKNOWN
;
661 case ACCEPT
-> ACCEPT
;
662 case DELETE
-> DELETE
;
664 case BLOCK_AND_DELETE
-> BLOCK_AND_DELETE
;
665 case UNBLOCK_AND_ACCEPT
-> UNBLOCK_AND_ACCEPT
;
673 Optional
<Integer
> destinationDeviceId
,
674 Optional
<GroupId
> groupId
,
675 Optional
<Long
> timestamp
,
676 Optional
<Offer
> offer
,
677 Optional
<Answer
> answer
,
678 Optional
<Hangup
> hangup
,
680 List
<IceUpdate
> iceUpdate
,
681 Optional
<Opaque
> opaque
684 public static Call
from(final SignalServiceCallMessage callMessage
) {
685 return new Call(Optional
.ofNullable(callMessage
.getDestinationDeviceId().orNull()),
686 Optional
.ofNullable(callMessage
.getGroupId().transform(GroupId
::unknownVersion
).orNull()),
687 Optional
.ofNullable(callMessage
.getTimestamp().orNull()),
688 Optional
.ofNullable(callMessage
.getOfferMessage().transform(Offer
::from
).orNull()),
689 Optional
.ofNullable(callMessage
.getAnswerMessage().transform(Answer
::from
).orNull()),
690 Optional
.ofNullable(callMessage
.getHangupMessage().transform(Hangup
::from
).orNull()),
691 Optional
.ofNullable(callMessage
.getBusyMessage().transform(Busy
::from
).orNull()),
692 callMessage
.getIceUpdateMessages()
693 .transform(m
-> m
.stream().map(IceUpdate
::from
).toList())
695 Optional
.ofNullable(callMessage
.getOpaqueMessage().transform(Opaque
::from
).orNull()));
698 public record Offer(long id
, String sdp
, Type type
, byte[] opaque
) {
700 static Offer
from(OfferMessage offerMessage
) {
701 return new Offer(offerMessage
.getId(),
702 offerMessage
.getSdp(),
703 Type
.from(offerMessage
.getType()),
704 offerMessage
.getOpaque());
711 static Type
from(OfferMessage
.Type type
) {
712 return switch (type
) {
713 case AUDIO_CALL
-> AUDIO_CALL
;
714 case VIDEO_CALL
-> VIDEO_CALL
;
720 public record Answer(long id
, String sdp
, byte[] opaque
) {
722 static Answer
from(AnswerMessage answerMessage
) {
723 return new Answer(answerMessage
.getId(), answerMessage
.getSdp(), answerMessage
.getOpaque());
727 public record Busy(long id
) {
729 static Busy
from(BusyMessage busyMessage
) {
730 return new Busy(busyMessage
.getId());
734 public record Hangup(long id
, Type type
, int deviceId
, boolean isLegacy
) {
736 static Hangup
from(HangupMessage hangupMessage
) {
737 return new Hangup(hangupMessage
.getId(),
738 Type
.from(hangupMessage
.getType()),
739 hangupMessage
.getDeviceId(),
740 hangupMessage
.isLegacy());
750 static Type
from(HangupMessage
.Type type
) {
751 return switch (type
) {
752 case NORMAL
-> NORMAL
;
753 case ACCEPTED
-> ACCEPTED
;
754 case DECLINED
-> DECLINED
;
756 case NEED_PERMISSION
-> NEED_PERMISSION
;
762 public record IceUpdate(long id
, String sdp
, byte[] opaque
) {
764 static IceUpdate
from(IceUpdateMessage iceUpdateMessage
) {
765 return new IceUpdate(iceUpdateMessage
.getId(), iceUpdateMessage
.getSdp(), iceUpdateMessage
.getOpaque());
769 public record Opaque(byte[] opaque
, Urgency urgency
) {
771 static Opaque
from(OpaqueMessage opaqueMessage
) {
772 return new Opaque(opaqueMessage
.getOpaque(), Urgency
.from(opaqueMessage
.getUrgency()));
775 public enum Urgency
{
779 static Urgency
from(OpaqueMessage
.Urgency urgency
) {
780 return switch (urgency
) {
781 case DROPPABLE
-> DROPPABLE
;
782 case HANDLE_IMMEDIATELY
-> HANDLE_IMMEDIATELY
;
789 public static MessageEnvelope
from(
790 SignalServiceEnvelope envelope
,
791 SignalServiceContent content
,
792 RecipientResolver recipientResolver
,
793 RecipientAddressResolver addressResolver
,
794 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())
801 : exception
instanceof ProtocolException e
802 ? recipientResolver
.resolveRecipient(e
.getSender())
804 final var sourceDevice
= envelope
.hasSourceDevice()
805 ? envelope
.getSourceDevice()
807 ? content
.getSenderDevice()
808 : exception
instanceof ProtocolException e ? e
.getSenderDevice() : 0;
810 Optional
<Receipt
> receipt
;
811 Optional
<Typing
> typing
;
815 if (content
!= null) {
816 receipt
= Optional
.ofNullable(content
.getReceiptMessage().transform(Receipt
::from
).orNull());
817 typing
= Optional
.ofNullable(content
.getTypingMessage().transform(Typing
::from
).orNull());
818 data
= Optional
.ofNullable(content
.getDataMessage()
819 .transform(dataMessage
-> Data
.from(dataMessage
, recipientResolver
, addressResolver
, fileProvider
))
821 sync
= Optional
.ofNullable(content
.getSyncMessage()
822 .transform(s
-> Sync
.from(s
, recipientResolver
, addressResolver
, fileProvider
))
824 call
= Optional
.ofNullable(content
.getCallMessage().transform(Call
::from
).orNull());
826 receipt
= envelope
.isReceipt() ? Optional
.of(new Receipt(envelope
.getServerReceivedTimestamp(),
827 Receipt
.Type
.DELIVERY
,
828 List
.of(envelope
.getTimestamp()))) : Optional
.empty();
829 typing
= Optional
.empty();
830 data
= Optional
.empty();
831 sync
= Optional
.empty();
832 call
= Optional
.empty();
835 return new MessageEnvelope(source
== null
837 : Optional
.of(addressResolver
.resolveRecipientAddress(source
)),
839 envelope
.getTimestamp(),
840 envelope
.getServerReceivedTimestamp(),
841 envelope
.getServerDeliveredTimestamp(),
842 envelope
.isUnidentifiedSender(),
850 public interface AttachmentFileProvider
{
852 File
getFile(SignalServiceAttachmentRemoteId attachmentRemoteId
);