]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/ReceiveMessageHandler.java
Implement receive handling for story messages
[signal-cli] / src / main / java / org / asamk / signal / ReceiveMessageHandler.java
index b1580b349b0088d99fdea712caf2a35aff447299..10f7370675b57d228358f01bc52150a4456f9761 100644 (file)
@@ -1,31 +1,19 @@
 package org.asamk.signal;
 
 import org.asamk.signal.manager.Manager;
-import org.asamk.signal.manager.UntrustedIdentityException;
+import org.asamk.signal.manager.api.MessageEnvelope;
 import org.asamk.signal.manager.api.RecipientIdentifier;
+import org.asamk.signal.manager.api.UntrustedIdentityException;
 import org.asamk.signal.manager.groups.GroupId;
-import org.asamk.signal.manager.groups.GroupUtils;
+import org.asamk.signal.manager.storage.recipients.RecipientAddress;
+import org.asamk.signal.output.PlainTextWriter;
 import org.asamk.signal.util.DateUtils;
+import org.asamk.signal.util.Hex;
 import org.slf4j.helpers.MessageFormatter;
-import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
-import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
-import org.whispersystems.signalservice.api.messages.SignalServiceContent;
-import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
-import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
-import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
-import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
-import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
-import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
-import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
-import org.whispersystems.signalservice.api.messages.shared.SharedContact;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 
 import java.util.ArrayList;
-import java.util.Base64;
 import java.util.stream.Collectors;
 
-import static org.asamk.signal.util.Util.getLegacyIdentifier;
-
 public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
 
     final Manager m;
@@ -37,133 +25,119 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
     }
 
     @Override
-    public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
-        if (envelope.hasSourceUuid()) {
-            var source = envelope.getSourceAddress();
-            writer.println("Envelope from: {} (device: {})", formatContact(source), envelope.getSourceDevice());
-        } else {
-            writer.println("Envelope from: unknown source");
+    public void handleMessage(MessageEnvelope envelope, Throwable exception) {
+        synchronized (writer) {
+            handleMessageInternal(envelope, exception);
         }
-        writer.println("Timestamp: {}", DateUtils.formatTimestamp(envelope.getTimestamp()));
+    }
+
+    private void handleMessageInternal(MessageEnvelope envelope, Throwable exception) {
+        var source = envelope.sourceAddress();
+        writer.println("Envelope from: {} (device: {})",
+                source.map(this::formatContact).orElse("unknown source"),
+                envelope.sourceDevice());
+        writer.println("Timestamp: {}", DateUtils.formatTimestamp(envelope.timestamp()));
+        writer.println("Server timestamps: received: {} delivered: {}",
+                DateUtils.formatTimestamp(envelope.serverReceivedTimestamp()),
+                DateUtils.formatTimestamp(envelope.serverDeliveredTimestamp()));
         if (envelope.isUnidentifiedSender()) {
             writer.println("Sent by unidentified/sealed sender");
         }
 
-        if (envelope.isReceipt()) {
-            writer.println("Got receipt.");
-        } else if (envelope.isSignalMessage() || envelope.isPreKeySignalMessage() || envelope.isUnidentifiedSender()) {
-            if (exception != null) {
-                if (exception instanceof UntrustedIdentityException) {
-                    var e = (UntrustedIdentityException) exception;
-                    writer.println(
-                            "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
-                    final var recipientName = getLegacyIdentifier(m.resolveSignalServiceAddress(e.getSender()));
-                    writer.println(
-                            "Use 'signal-cli -u {} listIdentities -n {}', verify the key and run 'signal-cli -u {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
-                            m.getSelfNumber(),
-                            recipientName,
-                            m.getSelfNumber(),
-                            recipientName);
-                    writer.println(
-                            "If you don't care about security, use 'signal-cli -u {} trust -a {}' to trust it without verification",
-                            m.getSelfNumber(),
-                            recipientName);
-                } else {
-                    writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
-                }
-            }
-            if (content == null) {
-                writer.println("No message content");
+        if (exception != null) {
+            if (exception instanceof UntrustedIdentityException e) {
+                writer.println(
+                        "The user’s key is untrusted, either the user has reinstalled Signal or a third party sent this message.");
+                final var recipientName = e.getSender().getLegacyIdentifier();
+                writer.println(
+                        "Use 'signal-cli -a {} listIdentities -n {}', verify the key and run 'signal-cli -a {} trust -v \"FINGER_PRINT\" {}' to mark it as trusted",
+                        m.getSelfNumber(),
+                        recipientName,
+                        m.getSelfNumber(),
+                        recipientName);
+                writer.println(
+                        "If you don't care about security, use 'signal-cli -a {} trust -a {}' to trust it without verification",
+                        m.getSelfNumber(),
+                        recipientName);
             } else {
-                writer.println("Sender: {} (device: {})",
-                        formatContact(content.getSender()),
-                        content.getSenderDevice());
-                writer.println("Server timestamps: received: {} delivered: {}",
-                        DateUtils.formatTimestamp(content.getServerReceivedTimestamp()),
-                        DateUtils.formatTimestamp(content.getServerDeliveredTimestamp()));
-
-                if (content.getSenderKeyDistributionMessage().isPresent()) {
-                    final var message = content.getSenderKeyDistributionMessage().get();
-                    writer.println("Received a sender key distribution message for distributionId {}",
-                            message.getDistributionId());
-                }
-
-                if (content.getDataMessage().isPresent()) {
-                    var message = content.getDataMessage().get();
-                    printDataMessage(writer, message);
-                }
-                if (content.getSyncMessage().isPresent()) {
-                    writer.println("Received a sync message");
-                    var syncMessage = content.getSyncMessage().get();
-                    printSyncMessage(writer, syncMessage);
-                }
-
-                if (content.getCallMessage().isPresent()) {
-                    writer.println("Received a call message");
-                    var callMessage = content.getCallMessage().get();
-                    printCallMessage(writer.indentedWriter(), callMessage);
-                }
-                if (content.getReceiptMessage().isPresent()) {
-                    writer.println("Received a receipt message");
-                    var receiptMessage = content.getReceiptMessage().get();
-                    printReceiptMessage(writer.indentedWriter(), receiptMessage);
-                }
-                if (content.getTypingMessage().isPresent()) {
-                    writer.println("Received a typing message");
-                    var typingMessage = content.getTypingMessage().get();
-                    printTypingMessage(writer.indentedWriter(), typingMessage);
-                }
-                if (content.getDecryptionErrorMessage().isPresent()) {
-                    writer.println("Received a decryption error message (resend request)");
-                    var decryptionErrorMessage = content.getDecryptionErrorMessage().get();
-                    printDecryptionErrorMessage(writer.indentedWriter(), decryptionErrorMessage);
-                }
+                writer.println("Exception: {} ({})", exception.getMessage(), exception.getClass().getSimpleName());
             }
-        } else {
-            writer.println("Unknown message received.");
+        }
+
+        if (envelope.data().isPresent()) {
+            var message = envelope.data().get();
+            printDataMessage(writer, message);
+        }
+        if (envelope.story().isPresent()) {
+            var message = envelope.story().get();
+            printStoryMessage(writer.indentedWriter(), message);
+        }
+        if (envelope.sync().isPresent()) {
+            writer.println("Received a sync message");
+            var syncMessage = envelope.sync().get();
+            printSyncMessage(writer, syncMessage);
+        }
+        if (envelope.call().isPresent()) {
+            writer.println("Received a call message");
+            var callMessage = envelope.call().get();
+            printCallMessage(writer.indentedWriter(), callMessage);
+        }
+        if (envelope.receipt().isPresent()) {
+            writer.println("Received a receipt message");
+            var receiptMessage = envelope.receipt().get();
+            printReceiptMessage(writer.indentedWriter(), receiptMessage);
+        }
+        if (envelope.typing().isPresent()) {
+            writer.println("Received a typing message");
+            var typingMessage = envelope.typing().get();
+            printTypingMessage(writer.indentedWriter(), typingMessage);
         }
         writer.println();
     }
 
     private void printDataMessage(
-            PlainTextWriter writer, SignalServiceDataMessage message
+            PlainTextWriter writer, MessageEnvelope.Data message
     ) {
-        writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.getTimestamp()));
+        writer.println("Message timestamp: {}", DateUtils.formatTimestamp(message.timestamp()));
         if (message.isViewOnce()) {
             writer.println("=VIEW ONCE=");
         }
 
-        if (message.getBody().isPresent()) {
-            writer.println("Body: {}", message.getBody().get());
+        if (message.body().isPresent()) {
+            writer.println("Body: {}", message.body().get());
         }
-        if (message.getGroupContext().isPresent()) {
+        if (message.groupContext().isPresent()) {
             writer.println("Group info:");
-            final var groupContext = message.getGroupContext().get();
+            final var groupContext = message.groupContext().get();
             printGroupContext(writer.indentedWriter(), groupContext);
         }
-        if (message.getGroupCallUpdate().isPresent()) {
+        if (message.storyContext().isPresent()) {
+            writer.println("Story reply:");
+            final var storyContext = message.storyContext().get();
+            printStoryContext(writer.indentedWriter(), storyContext);
+        }
+        if (message.groupCallUpdate().isPresent()) {
             writer.println("Group call update:");
-            final var groupCallUpdate = message.getGroupCallUpdate().get();
-            writer.indentedWriter().println("Era id: {}", groupCallUpdate.getEraId());
+            final var groupCallUpdate = message.groupCallUpdate().get();
+            writer.indentedWriter().println("Era id: {}", groupCallUpdate.eraId());
         }
-        if (message.getPreviews().isPresent()) {
+        if (message.previews().size() > 0) {
             writer.println("Previews:");
-            final var previews = message.getPreviews().get();
+            final var previews = message.previews();
             for (var preview : previews) {
                 writer.println("- Preview");
                 printPreview(writer.indentedWriter(), preview);
             }
         }
-        if (message.getSharedContacts().isPresent()) {
-            final var sharedContacts = message.getSharedContacts().get();
+        if (message.sharedContacts().size() > 0) {
             writer.println("Contacts:");
-            for (var contact : sharedContacts) {
+            for (var contact : message.sharedContacts()) {
                 writer.println("- Contact:");
                 printSharedContact(writer.indentedWriter(), contact);
             }
         }
-        if (message.getSticker().isPresent()) {
-            final var sticker = message.getSticker().get();
+        if (message.sticker().isPresent()) {
+            final var sticker = message.sticker().get();
             writer.println("Sticker:");
             printSticker(writer.indentedWriter(), sticker);
         }
@@ -171,182 +145,179 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
             writer.println("Is end session");
         }
         if (message.isExpirationUpdate()) {
-            writer.println("Is Expiration update: {}", message.isExpirationUpdate());
+            writer.println("Is Expiration update: true");
+        }
+        if (message.expiresInSeconds() > 0) {
+            writer.println("Expires in: {} seconds", message.expiresInSeconds());
         }
-        if (message.getExpiresInSeconds() > 0) {
-            writer.println("Expires in: {} seconds", message.getExpiresInSeconds());
+        if (message.isProfileKeyUpdate()) {
+            writer.println("Profile key update");
         }
-        if (message.getProfileKey().isPresent()) {
-            writer.println("Profile key update, key length: {}", message.getProfileKey().get().length);
+        if (message.hasProfileKey()) {
+            writer.println("With profile key");
         }
-        if (message.getReaction().isPresent()) {
+        if (message.reaction().isPresent()) {
             writer.println("Reaction:");
-            final var reaction = message.getReaction().get();
+            final var reaction = message.reaction().get();
             printReaction(writer.indentedWriter(), reaction);
         }
-        if (message.getQuote().isPresent()) {
+        if (message.quote().isPresent()) {
             writer.println("Quote:");
-            var quote = message.getQuote().get();
+            var quote = message.quote().get();
             printQuote(writer.indentedWriter(), quote);
         }
-        if (message.getRemoteDelete().isPresent()) {
-            final var remoteDelete = message.getRemoteDelete().get();
-            writer.println("Remote delete message: timestamp = {}", remoteDelete.getTargetSentTimestamp());
+        if (message.remoteDeleteId().isPresent()) {
+            final var remoteDelete = message.remoteDeleteId().get();
+            writer.println("Remote delete message: timestamp = {}", remoteDelete);
         }
-        if (message.getMentions().isPresent()) {
+        if (message.mentions().size() > 0) {
             writer.println("Mentions:");
-            for (var mention : message.getMentions().get()) {
+            for (var mention : message.mentions()) {
                 printMention(writer, mention);
             }
         }
-        if (message.getAttachments().isPresent()) {
+        if (message.attachments().size() > 0) {
             writer.println("Attachments:");
-            for (var attachment : message.getAttachments().get()) {
+            for (var attachment : message.attachments()) {
                 writer.println("- Attachment:");
                 printAttachment(writer.indentedWriter(), attachment);
             }
         }
     }
 
-    private void printTypingMessage(
-            final PlainTextWriter writer, final SignalServiceTypingMessage typingMessage
+    private void printStoryMessage(
+            PlainTextWriter writer, MessageEnvelope.Story message
     ) {
-        writer.println("Action: {}", typingMessage.getAction());
-        writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.getTimestamp()));
-        if (typingMessage.getGroupId().isPresent()) {
-            writer.println("Group Info:");
-            final var groupId = GroupId.unknownVersion(typingMessage.getGroupId().get());
-            printGroupInfo(writer.indentedWriter(), groupId);
+        writer.println("Story: with replies: {}", message.allowsReplies());
+        if (message.groupId().isPresent()) {
+            writer.println("Group info:");
+            printGroupInfo(writer.indentedWriter(), message.groupId().get());
+        }
+        if (message.textAttachment().isPresent()) {
+            writer.println("Body: {}", message.textAttachment().get().text().orElse(""));
+
+            if (message.textAttachment().get().preview().isPresent()) {
+                writer.println("Preview:");
+                printPreview(writer.indentedWriter(), message.textAttachment().get().preview().get());
+            }
+        }
+        if (message.fileAttachment().isPresent()) {
+            writer.println("Attachments:");
+            printAttachment(writer.indentedWriter(), message.fileAttachment().get());
         }
     }
 
-    private void printDecryptionErrorMessage(
-            final PlainTextWriter writer, final DecryptionErrorMessage decryptionErrorMessage
+    private void printTypingMessage(
+            final PlainTextWriter writer, final MessageEnvelope.Typing typingMessage
     ) {
-        writer.println("Device id: {}", decryptionErrorMessage.getDeviceId());
-        writer.println("Timestamp: {}", DateUtils.formatTimestamp(decryptionErrorMessage.getTimestamp()));
-        writer.println("Ratchet key: {}",
-                decryptionErrorMessage.getRatchetKey().isPresent() ? "is present" : "not present");
+        writer.println("Action: {}", typingMessage.type());
+        writer.println("Timestamp: {}", DateUtils.formatTimestamp(typingMessage.timestamp()));
+        if (typingMessage.groupId().isPresent()) {
+            writer.println("Group Info:");
+            final var groupId = typingMessage.groupId().get();
+            printGroupInfo(writer.indentedWriter(), groupId);
+        }
     }
 
     private void printReceiptMessage(
-            final PlainTextWriter writer, final SignalServiceReceiptMessage receiptMessage
+            final PlainTextWriter writer, final MessageEnvelope.Receipt receiptMessage
     ) {
-        writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.getWhen()));
-        if (receiptMessage.isDeliveryReceipt()) {
+        writer.println("When: {}", DateUtils.formatTimestamp(receiptMessage.when()));
+        if (receiptMessage.type() == MessageEnvelope.Receipt.Type.DELIVERY) {
             writer.println("Is delivery receipt");
         }
-        if (receiptMessage.isReadReceipt()) {
+        if (receiptMessage.type() == MessageEnvelope.Receipt.Type.READ) {
             writer.println("Is read receipt");
         }
-        if (receiptMessage.isViewedReceipt()) {
+        if (receiptMessage.type() == MessageEnvelope.Receipt.Type.VIEWED) {
             writer.println("Is viewed receipt");
         }
         writer.println("Timestamps:");
-        for (long timestamp : receiptMessage.getTimestamps()) {
+        for (long timestamp : receiptMessage.timestamps()) {
             writer.println("- {}", DateUtils.formatTimestamp(timestamp));
         }
     }
 
     private void printCallMessage(
-            final PlainTextWriter writer, final SignalServiceCallMessage callMessage
+            final PlainTextWriter writer, final MessageEnvelope.Call callMessage
     ) {
-        if (callMessage.getDestinationDeviceId().isPresent()) {
-            final var deviceId = callMessage.getDestinationDeviceId().get();
+        if (callMessage.destinationDeviceId().isPresent()) {
+            final var deviceId = callMessage.destinationDeviceId().get();
             writer.println("Destination device id: {}", deviceId);
         }
-        if (callMessage.getGroupId().isPresent()) {
-            final var groupId = GroupId.unknownVersion(callMessage.getGroupId().get());
+        if (callMessage.groupId().isPresent()) {
+            final var groupId = callMessage.groupId().get();
             writer.println("Destination group id: {}", groupId);
         }
-        if (callMessage.getTimestamp().isPresent()) {
-            writer.println("Timestamp: {}", DateUtils.formatTimestamp(callMessage.getTimestamp().get()));
+        if (callMessage.timestamp().isPresent()) {
+            writer.println("Timestamp: {}", DateUtils.formatTimestamp(callMessage.timestamp().get()));
         }
-        if (callMessage.getAnswerMessage().isPresent()) {
-            var answerMessage = callMessage.getAnswerMessage().get();
-            writer.println("Answer message: {}, sdp: {})", answerMessage.getId(), answerMessage.getSdp());
+        if (callMessage.answer().isPresent()) {
+            var answerMessage = callMessage.answer().get();
+            writer.println("Answer message: {}, sdp: {})", answerMessage.id(), answerMessage.sdp());
         }
-        if (callMessage.getBusyMessage().isPresent()) {
-            var busyMessage = callMessage.getBusyMessage().get();
-            writer.println("Busy message: {}", busyMessage.getId());
+        if (callMessage.busy().isPresent()) {
+            var busyMessage = callMessage.busy().get();
+            writer.println("Busy message: {}", busyMessage.id());
         }
-        if (callMessage.getHangupMessage().isPresent()) {
-            var hangupMessage = callMessage.getHangupMessage().get();
-            writer.println("Hangup message: {}", hangupMessage.getId());
+        if (callMessage.hangup().isPresent()) {
+            var hangupMessage = callMessage.hangup().get();
+            writer.println("Hangup message: {}", hangupMessage.id());
         }
-        if (callMessage.getIceUpdateMessages().isPresent()) {
+        if (callMessage.iceUpdate().size() > 0) {
             writer.println("Ice update messages:");
-            var iceUpdateMessages = callMessage.getIceUpdateMessages().get();
+            var iceUpdateMessages = callMessage.iceUpdate();
             for (var iceUpdateMessage : iceUpdateMessages) {
-                writer.println("- {}, sdp: {}", iceUpdateMessage.getId(), iceUpdateMessage.getSdp());
+                writer.println("- {}, sdp: {}", iceUpdateMessage.id(), iceUpdateMessage.sdp());
             }
         }
-        if (callMessage.getOfferMessage().isPresent()) {
-            var offerMessage = callMessage.getOfferMessage().get();
-            writer.println("Offer message: {}, sdp: {}", offerMessage.getId(), offerMessage.getSdp());
+        if (callMessage.offer().isPresent()) {
+            var offerMessage = callMessage.offer().get();
+            writer.println("Offer message: {}, sdp: {}", offerMessage.id(), offerMessage.sdp());
         }
-        if (callMessage.getOpaqueMessage().isPresent()) {
-            final var opaqueMessage = callMessage.getOpaqueMessage().get();
+        if (callMessage.opaque().isPresent()) {
+            final var opaqueMessage = callMessage.opaque().get();
             writer.println("Opaque message: size {}, urgency: {}",
-                    opaqueMessage.getOpaque().length,
-                    opaqueMessage.getUrgency().name());
+                    opaqueMessage.opaque().length,
+                    opaqueMessage.urgency().name());
         }
     }
 
     private void printSyncMessage(
-            final PlainTextWriter writer, final SignalServiceSyncMessage syncMessage
+            final PlainTextWriter writer, final MessageEnvelope.Sync syncMessage
     ) {
-        if (syncMessage.getContacts().isPresent()) {
-            final var contactsMessage = syncMessage.getContacts().get();
+        if (syncMessage.contacts().isPresent()) {
+            final var contactsMessage = syncMessage.contacts().get();
             var type = contactsMessage.isComplete() ? "complete" : "partial";
             writer.println("Received {} sync contacts:", type);
-            printAttachment(writer.indentedWriter(), contactsMessage.getContactsStream());
         }
-        if (syncMessage.getGroups().isPresent()) {
-            writer.println("Received sync groups:");
-            printAttachment(writer.indentedWriter(), syncMessage.getGroups().get());
+        if (syncMessage.groups().isPresent()) {
+            writer.println("Received sync groups.");
         }
-        if (syncMessage.getRead().isPresent()) {
+        if (syncMessage.read().size() > 0) {
             writer.println("Received sync read messages list");
-            for (var rm : syncMessage.getRead().get()) {
+            for (var rm : syncMessage.read()) {
                 writer.println("- From: {} Message timestamp: {}",
-                        formatContact(rm.getSender()),
-                        DateUtils.formatTimestamp(rm.getTimestamp()));
+                        formatContact(rm.sender()),
+                        DateUtils.formatTimestamp(rm.timestamp()));
             }
         }
-        if (syncMessage.getViewed().isPresent()) {
+        if (syncMessage.viewed().size() > 0) {
             writer.println("Received sync viewed messages list");
-            for (var vm : syncMessage.getViewed().get()) {
+            for (var vm : syncMessage.viewed()) {
                 writer.println("- From: {} Message timestamp: {}",
-                        formatContact(vm.getSender()),
-                        DateUtils.formatTimestamp(vm.getTimestamp()));
+                        formatContact(vm.sender()),
+                        DateUtils.formatTimestamp(vm.timestamp()));
             }
         }
-        if (syncMessage.getRequest().isPresent()) {
-            String type;
-            if (syncMessage.getRequest().get().isContactsRequest()) {
-                type = "contacts";
-            } else if (syncMessage.getRequest().get().isGroupsRequest()) {
-                type = "groups";
-            } else if (syncMessage.getRequest().get().isBlockedListRequest()) {
-                type = "blocked list";
-            } else if (syncMessage.getRequest().get().isConfigurationRequest()) {
-                type = "configuration";
-            } else if (syncMessage.getRequest().get().isKeysRequest()) {
-                type = "keys";
-            } else {
-                type = "<unknown>";
-            }
-            writer.println("Received sync request for: {}", type);
-        }
-        if (syncMessage.getSent().isPresent()) {
+        if (syncMessage.sent().isPresent()) {
             writer.println("Received sync sent message");
-            final var sentTranscriptMessage = syncMessage.getSent().get();
+            final var sentTranscriptMessage = syncMessage.sent().get();
             String to;
-            if (sentTranscriptMessage.getDestination().isPresent()) {
-                to = formatContact(sentTranscriptMessage.getDestination().get());
-            } else if (sentTranscriptMessage.getRecipients().size() > 0) {
-                to = sentTranscriptMessage.getRecipients()
+            if (sentTranscriptMessage.destination().isPresent()) {
+                to = formatContact(sentTranscriptMessage.destination().get());
+            } else if (sentTranscriptMessage.recipients().size() > 0) {
+                to = sentTranscriptMessage.recipients()
                         .stream()
                         .map(this::formatContact)
                         .collect(Collectors.joining(", "));
@@ -355,257 +326,196 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
             }
             writer.indentedWriter().println("To: {}", to);
             writer.indentedWriter()
-                    .println("Timestamp: {}", DateUtils.formatTimestamp(sentTranscriptMessage.getTimestamp()));
-            if (sentTranscriptMessage.getExpirationStartTimestamp() > 0) {
+                    .println("Timestamp: {}", DateUtils.formatTimestamp(sentTranscriptMessage.timestamp()));
+            if (sentTranscriptMessage.expirationStartTimestamp() > 0) {
                 writer.indentedWriter()
                         .println("Expiration started at: {}",
-                                DateUtils.formatTimestamp(sentTranscriptMessage.getExpirationStartTimestamp()));
+                                DateUtils.formatTimestamp(sentTranscriptMessage.expirationStartTimestamp()));
             }
-            var message = sentTranscriptMessage.getMessage();
-            printDataMessage(writer.indentedWriter(), message);
-        }
-        if (syncMessage.getBlockedList().isPresent()) {
-            writer.println("Received sync message with block list");
-            writer.println("Blocked numbers:");
-            final var blockedList = syncMessage.getBlockedList().get();
-            for (var address : blockedList.getAddresses()) {
-                writer.println("- {}", getLegacyIdentifier(address));
-            }
-        }
-        if (syncMessage.getVerified().isPresent()) {
-            writer.println("Received sync message with verified identities:");
-            final var verifiedMessage = syncMessage.getVerified().get();
-            writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified());
-        }
-        if (syncMessage.getConfiguration().isPresent()) {
-            writer.println("Received sync message with configuration:");
-            final var configurationMessage = syncMessage.getConfiguration().get();
-            if (configurationMessage.getReadReceipts().isPresent()) {
-                writer.println("- Read receipts: {}",
-                        configurationMessage.getReadReceipts().get() ? "enabled" : "disabled");
+            if (sentTranscriptMessage.message().isPresent()) {
+                var message = sentTranscriptMessage.message().get();
+                printDataMessage(writer.indentedWriter(), message);
             }
-            if (configurationMessage.getLinkPreviews().isPresent()) {
-                writer.println("- Link previews: {}",
-                        configurationMessage.getLinkPreviews().get() ? "enabled" : "disabled");
+            if (sentTranscriptMessage.story().isPresent()) {
+                var message = sentTranscriptMessage.story().get();
+                printStoryMessage(writer.indentedWriter(), message);
             }
-            if (configurationMessage.getTypingIndicators().isPresent()) {
-                writer.println("- Typing indicators: {}",
-                        configurationMessage.getTypingIndicators().get() ? "enabled" : "disabled");
+        }
+        if (syncMessage.blocked().isPresent()) {
+            writer.println("Received sync message with block list");
+            writer.println("Blocked:");
+            final var blockedList = syncMessage.blocked().get();
+            for (var address : blockedList.recipients()) {
+                writer.println("- {}", address.getLegacyIdentifier());
             }
-            if (configurationMessage.getUnidentifiedDeliveryIndicators().isPresent()) {
-                writer.println("- Unidentified Delivery Indicators: {}",
-                        configurationMessage.getUnidentifiedDeliveryIndicators().get() ? "enabled" : "disabled");
+            for (var groupId : blockedList.groupIds()) {
+                writer.println("- {}", groupId.toBase64());
             }
         }
-        if (syncMessage.getFetchType().isPresent()) {
-            final var fetchType = syncMessage.getFetchType().get();
-            writer.println("Received sync message with fetch type: {}", fetchType);
-        }
-        if (syncMessage.getViewOnceOpen().isPresent()) {
-            final var viewOnceOpenMessage = syncMessage.getViewOnceOpen().get();
+        if (syncMessage.viewOnceOpen().isPresent()) {
+            final var viewOnceOpenMessage = syncMessage.viewOnceOpen().get();
             writer.println("Received sync message with view once open message:");
-            writer.indentedWriter().println("Sender: {}", formatContact(viewOnceOpenMessage.getSender()));
+            writer.indentedWriter().println("Sender: {}", formatContact(viewOnceOpenMessage.sender()));
             writer.indentedWriter()
-                    .println("Timestamp: {}", DateUtils.formatTimestamp(viewOnceOpenMessage.getTimestamp()));
-        }
-        if (syncMessage.getStickerPackOperations().isPresent()) {
-            final var stickerPackOperationMessages = syncMessage.getStickerPackOperations().get();
-            writer.println("Received sync message with sticker pack operations:");
-            for (var m : stickerPackOperationMessages) {
-                writer.println("- {}", m.getType().isPresent() ? m.getType().get() : "<unknown>");
-                if (m.getPackId().isPresent()) {
-                    writer.indentedWriter()
-                            .println("packId: {}", Base64.getEncoder().encodeToString(m.getPackId().get()));
-                }
-                if (m.getPackKey().isPresent()) {
-                    writer.indentedWriter()
-                            .println("packKey: {}", Base64.getEncoder().encodeToString(m.getPackKey().get()));
-                }
-            }
+                    .println("Timestamp: {}", DateUtils.formatTimestamp(viewOnceOpenMessage.timestamp()));
         }
-        if (syncMessage.getMessageRequestResponse().isPresent()) {
-            final var requestResponseMessage = syncMessage.getMessageRequestResponse().get();
+        if (syncMessage.messageRequestResponse().isPresent()) {
+            final var requestResponseMessage = syncMessage.messageRequestResponse().get();
             writer.println("Received message request response:");
-            writer.indentedWriter().println("Type: {}", requestResponseMessage.getType());
-            if (requestResponseMessage.getGroupId().isPresent()) {
+            writer.indentedWriter().println("Type: {}", requestResponseMessage.type());
+            if (requestResponseMessage.groupId().isPresent()) {
                 writer.println("For group:");
-                printGroupInfo(writer.indentedWriter(),
-                        GroupId.unknownVersion(requestResponseMessage.getGroupId().get()));
+                printGroupInfo(writer.indentedWriter(), requestResponseMessage.groupId().get());
             }
-            if (requestResponseMessage.getPerson().isPresent()) {
-                writer.indentedWriter()
-                        .println("For Person: {}", formatContact(requestResponseMessage.getPerson().get()));
-            }
-        }
-        if (syncMessage.getKeys().isPresent()) {
-            final var keysMessage = syncMessage.getKeys().get();
-            writer.println("Received sync message with keys:");
-            if (keysMessage.getStorageService().isPresent()) {
-                writer.println("-  storage key: length: {}", keysMessage.getStorageService().get().serialize().length);
+            if (requestResponseMessage.person().isPresent()) {
+                writer.indentedWriter().println("For Person: {}", formatContact(requestResponseMessage.person().get()));
             }
         }
     }
 
     private void printPreview(
-            final PlainTextWriter writer, final SignalServiceDataMessage.Preview preview
+            final PlainTextWriter writer, final MessageEnvelope.Data.Preview preview
     ) {
-        writer.println("Title: {}", preview.getTitle());
-        writer.println("Description: {}", preview.getDescription());
-        writer.println("Date: {}", DateUtils.formatTimestamp(preview.getDate()));
-        writer.println("Url: {}", preview.getUrl());
-        if (preview.getImage().isPresent()) {
+        writer.println("Title: {}", preview.title());
+        writer.println("Description: {}", preview.description());
+        writer.println("Date: {}", DateUtils.formatTimestamp(preview.date()));
+        writer.println("Url: {}", preview.url());
+        if (preview.image().isPresent()) {
             writer.println("Image:");
-            printAttachment(writer.indentedWriter(), preview.getImage().get());
+            printAttachment(writer.indentedWriter(), preview.image().get());
         }
     }
 
     private void printSticker(
-            final PlainTextWriter writer, final SignalServiceDataMessage.Sticker sticker
+            final PlainTextWriter writer, final MessageEnvelope.Data.Sticker sticker
     ) {
-        writer.println("Pack id: {}", Base64.getEncoder().encodeToString(sticker.getPackId()));
-        writer.println("Pack key: {}", Base64.getEncoder().encodeToString(sticker.getPackKey()));
-        writer.println("Sticker id: {}", sticker.getStickerId());
-        writer.println("Image:");
-        printAttachment(writer.indentedWriter(), sticker.getAttachment());
+        writer.println("Pack id: {}", Hex.toStringCondensed(sticker.packId().serialize()));
+        writer.println("Sticker id: {}", sticker.stickerId());
     }
 
     private void printReaction(
-            final PlainTextWriter writer, final SignalServiceDataMessage.Reaction reaction
+            final PlainTextWriter writer, final MessageEnvelope.Data.Reaction reaction
     ) {
-        writer.println("Emoji: {}", reaction.getEmoji());
-        writer.println("Target author: {}", formatContact(reaction.getTargetAuthor()));
-        writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.getTargetSentTimestamp()));
+        writer.println("Emoji: {}", reaction.emoji());
+        writer.println("Target author: {}", formatContact(reaction.targetAuthor()));
+        writer.println("Target timestamp: {}", DateUtils.formatTimestamp(reaction.targetSentTimestamp()));
         writer.println("Is remove: {}", reaction.isRemove());
     }
 
     private void printQuote(
-            final PlainTextWriter writer, final SignalServiceDataMessage.Quote quote
+            final PlainTextWriter writer, final MessageEnvelope.Data.Quote quote
     ) {
-        writer.println("Id: {}", quote.getId());
-        writer.println("Author: {}", formatContact(quote.getAuthor()));
-        writer.println("Text: {}", quote.getText());
-        if (quote.getMentions() != null && quote.getMentions().size() > 0) {
+        writer.println("Id: {}", quote.id());
+        writer.println("Author: {}", formatContact(quote.author()));
+        if (quote.text().isPresent()) {
+            writer.println("Text: {}", quote.text().get());
+        }
+        if (quote.mentions() != null && quote.mentions().size() > 0) {
             writer.println("Mentions:");
-            for (var mention : quote.getMentions()) {
+            for (var mention : quote.mentions()) {
                 printMention(writer, mention);
             }
         }
-        if (quote.getAttachments().size() > 0) {
+        if (quote.attachments().size() > 0) {
             writer.println("Attachments:");
-            for (var attachment : quote.getAttachments()) {
-                writer.println("- Filename: {}", attachment.getFileName());
-                writer.indent(w -> {
-                    w.println("Type: {}", attachment.getContentType());
-                    w.println("Thumbnail:");
-                    if (attachment.getThumbnail() != null) {
-                        printAttachment(w, attachment.getThumbnail());
-                    }
-                });
+            for (var attachment : quote.attachments()) {
+                writer.println("- Attachment:");
+                printAttachment(writer.indentedWriter(), attachment);
             }
         }
     }
 
-    private void printSharedContact(final PlainTextWriter writer, final SharedContact contact) {
+    private void printSharedContact(final PlainTextWriter writer, final MessageEnvelope.Data.SharedContact contact) {
         writer.println("Name:");
-        var name = contact.getName();
+        var name = contact.name();
         writer.indent(w -> {
-            if (name.getDisplay().isPresent() && !name.getDisplay().get().isBlank()) {
-                w.println("Display name: {}", name.getDisplay().get());
+            if (name.display().isPresent() && !name.display().get().isBlank()) {
+                w.println("Display name: {}", name.display().get());
             }
-            if (name.getGiven().isPresent() && !name.getGiven().get().isBlank()) {
-                w.println("First name: {}", name.getGiven().get());
+            if (name.given().isPresent() && !name.given().get().isBlank()) {
+                w.println("First name: {}", name.given().get());
             }
-            if (name.getMiddle().isPresent() && !name.getMiddle().get().isBlank()) {
-                w.println("Middle name: {}", name.getMiddle().get());
+            if (name.middle().isPresent() && !name.middle().get().isBlank()) {
+                w.println("Middle name: {}", name.middle().get());
             }
-            if (name.getFamily().isPresent() && !name.getFamily().get().isBlank()) {
-                w.println("Family name: {}", name.getFamily().get());
+            if (name.family().isPresent() && !name.family().get().isBlank()) {
+                w.println("Family name: {}", name.family().get());
             }
-            if (name.getPrefix().isPresent() && !name.getPrefix().get().isBlank()) {
-                w.println("Prefix name: {}", name.getPrefix().get());
+            if (name.prefix().isPresent() && !name.prefix().get().isBlank()) {
+                w.println("Prefix name: {}", name.prefix().get());
             }
-            if (name.getSuffix().isPresent() && !name.getSuffix().get().isBlank()) {
-                w.println("Suffix name: {}", name.getSuffix().get());
+            if (name.suffix().isPresent() && !name.suffix().get().isBlank()) {
+                w.println("Suffix name: {}", name.suffix().get());
             }
         });
 
-        if (contact.getAvatar().isPresent()) {
-            var avatar = contact.getAvatar().get();
+        if (contact.avatar().isPresent()) {
+            var avatar = contact.avatar().get();
             writer.println("Avatar: (profile: {})", avatar.isProfile());
-            printAttachment(writer.indentedWriter(), avatar.getAttachment());
+            printAttachment(writer.indentedWriter(), avatar.attachment());
         }
 
-        if (contact.getOrganization().isPresent()) {
-            writer.println("Organisation: {}", contact.getOrganization().get());
+        if (contact.organization().isPresent()) {
+            writer.println("Organisation: {}", contact.organization().get());
         }
 
-        if (contact.getPhone().isPresent()) {
+        if (contact.phone().size() > 0) {
             writer.println("Phone details:");
-            for (var phone : contact.getPhone().get()) {
+            for (var phone : contact.phone()) {
                 writer.println("- Phone:");
                 writer.indent(w -> {
-                    if (phone.getValue() != null) {
-                        w.println("Number: {}", phone.getValue());
-                    }
-                    if (phone.getType() != null) {
-                        w.println("Type: {}", phone.getType());
-                    }
-                    if (phone.getLabel().isPresent() && !phone.getLabel().get().isBlank()) {
-                        w.println("Label: {}", phone.getLabel().get());
+                    w.println("Number: {}", phone.value());
+                    w.println("Type: {}", phone.type());
+                    if (phone.label().isPresent() && !phone.label().get().isBlank()) {
+                        w.println("Label: {}", phone.label().get());
                     }
                 });
             }
         }
 
-        if (contact.getEmail().isPresent()) {
+        if (contact.email().size() > 0) {
             writer.println("Email details:");
-            for (var email : contact.getEmail().get()) {
+            for (var email : contact.email()) {
                 writer.println("- Email:");
                 writer.indent(w -> {
-                    if (email.getValue() != null) {
-                        w.println("Address: {}", email.getValue());
-                    }
-                    if (email.getType() != null) {
-                        w.println("Type: {}", email.getType());
-                    }
-                    if (email.getLabel().isPresent() && !email.getLabel().get().isBlank()) {
-                        w.println("Label: {}", email.getLabel().get());
+                    w.println("Address: {}", email.value());
+                    w.println("Type: {}", email.type());
+                    if (email.label().isPresent() && !email.label().get().isBlank()) {
+                        w.println("Label: {}", email.label().get());
                     }
                 });
             }
         }
 
-        if (contact.getAddress().isPresent()) {
+        if (contact.address().size() > 0) {
             writer.println("Address details:");
-            for (var address : contact.getAddress().get()) {
+            for (var address : contact.address()) {
                 writer.println("- Address:");
                 writer.indent(w -> {
-                    if (address.getType() != null) {
-                        w.println("Type: {}", address.getType());
+                    w.println("Type: {}", address.type());
+                    if (address.label().isPresent() && !address.label().get().isBlank()) {
+                        w.println("Label: {}", address.label().get());
                     }
-                    if (address.getLabel().isPresent() && !address.getLabel().get().isBlank()) {
-                        w.println("Label: {}", address.getLabel().get());
+                    if (address.street().isPresent() && !address.street().get().isBlank()) {
+                        w.println("Street: {}", address.street().get());
                     }
-                    if (address.getStreet().isPresent() && !address.getStreet().get().isBlank()) {
-                        w.println("Street: {}", address.getStreet().get());
+                    if (address.pobox().isPresent() && !address.pobox().get().isBlank()) {
+                        w.println("Pobox: {}", address.pobox().get());
                     }
-                    if (address.getPobox().isPresent() && !address.getPobox().get().isBlank()) {
-                        w.println("Pobox: {}", address.getPobox().get());
+                    if (address.neighborhood().isPresent() && !address.neighborhood().get().isBlank()) {
+                        w.println("Neighbourhood: {}", address.neighborhood().get());
                     }
-                    if (address.getNeighborhood().isPresent() && !address.getNeighborhood().get().isBlank()) {
-                        w.println("Neighbourhood: {}", address.getNeighborhood().get());
+                    if (address.city().isPresent() && !address.city().get().isBlank()) {
+                        w.println("City: {}", address.city().get());
                     }
-                    if (address.getCity().isPresent() && !address.getCity().get().isBlank()) {
-                        w.println("City: {}", address.getCity().get());
+                    if (address.region().isPresent() && !address.region().get().isBlank()) {
+                        w.println("Region: {}", address.region().get());
                     }
-                    if (address.getRegion().isPresent() && !address.getRegion().get().isBlank()) {
-                        w.println("Region: {}", address.getRegion().get());
+                    if (address.postcode().isPresent() && !address.postcode().get().isBlank()) {
+                        w.println("Postcode: {}", address.postcode().get());
                     }
-                    if (address.getPostcode().isPresent() && !address.getPostcode().get().isBlank()) {
-                        w.println("Postcode: {}", address.getPostcode().get());
-                    }
-                    if (address.getCountry().isPresent() && !address.getCountry().get().isBlank()) {
-                        w.println("Country: {}", address.getCountry().get());
+                    if (address.country().isPresent() && !address.country().get().isBlank()) {
+                        w.println("Country: {}", address.country().get());
                     }
                 });
             }
@@ -613,30 +523,18 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
     }
 
     private void printGroupContext(
-            final PlainTextWriter writer, final SignalServiceGroupContext groupContext
+            final PlainTextWriter writer, final MessageEnvelope.Data.GroupContext groupContext
     ) {
-        final var groupId = GroupUtils.getGroupId(groupContext);
-        if (groupContext.getGroupV1().isPresent()) {
-            var groupInfo = groupContext.getGroupV1().get();
-            printGroupInfo(writer, groupId);
-            writer.println("Type: {}", groupInfo.getType());
-            if (groupInfo.getMembers().isPresent()) {
-                writer.println("Members:");
-                for (var member : groupInfo.getMembers().get()) {
-                    writer.println("- {}", formatContact(member));
-                }
-            }
-            if (groupInfo.getAvatar().isPresent()) {
-                writer.println("Avatar:");
-                printAttachment(writer.indentedWriter(), groupInfo.getAvatar().get());
-            }
-        } else if (groupContext.getGroupV2().isPresent()) {
-            final var groupInfo = groupContext.getGroupV2().get();
-            printGroupInfo(writer, groupId);
-            writer.println("Revision: {}", groupInfo.getRevision());
-            writer.println("Master key length: {}", groupInfo.getMasterKey().serialize().length);
-            writer.println("Has signed group change: {}", groupInfo.hasSignedGroupChange());
-        }
+        printGroupInfo(writer, groupContext.groupId());
+        writer.println("Revision: {}", groupContext.revision());
+        writer.println("Type: {}", groupContext.isGroupUpdate() ? "UPDATE" : "DELIVER");
+    }
+
+    private void printStoryContext(
+            final PlainTextWriter writer, final MessageEnvelope.Data.StoryContext storyContext
+    ) {
+        writer.println("Sender: {}", formatContact(storyContext.author()));
+        writer.println("Sent timestamp: {}", storyContext.sentTimestamp());
     }
 
     private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) {
@@ -644,65 +542,70 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
 
         var group = m.getGroup(groupId);
         if (group != null) {
-            writer.println("Name: {}", group.getTitle());
+            writer.println("Name: {}", group.title());
         } else {
             writer.println("Name: <Unknown group>");
         }
     }
 
     private void printMention(
-            PlainTextWriter writer, SignalServiceDataMessage.Mention mention
+            PlainTextWriter writer, MessageEnvelope.Data.Mention mention
     ) {
-        final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid()));
-        writer.println("- {}: {} (length: {})", formatContact(address), mention.getStart(), mention.getLength());
+        writer.println("- {}: {} (length: {})", formatContact(mention.recipient()), mention.start(), mention.length());
     }
 
-    private void printAttachment(PlainTextWriter writer, SignalServiceAttachment attachment) {
-        writer.println("Content-Type: {}", attachment.getContentType());
-        writer.println("Type: {}", attachment.isPointer() ? "Pointer" : attachment.isStream() ? "Stream" : "<unknown>");
-        if (attachment.isPointer()) {
-            final var pointer = attachment.asPointer();
-            writer.println("Id: {} Key length: {}", pointer.getRemoteId(), pointer.getKey().length);
-            if (pointer.getUploadTimestamp() > 0) {
-                writer.println("Upload timestamp: {}", DateUtils.formatTimestamp(pointer.getUploadTimestamp()));
-            }
-            if (pointer.getCaption().isPresent()) {
-                writer.println("Caption: {}", pointer.getCaption().get());
-            }
-            if (pointer.getFileName().isPresent()) {
-                writer.println("Filename: {}", pointer.getFileName().get());
-            }
+    private void printAttachment(PlainTextWriter writer, MessageEnvelope.Data.Attachment attachment) {
+        writer.println("Content-Type: {}", attachment.contentType());
+        writer.println("Type: {}", attachment.id().isPresent() ? "Pointer" : "Stream");
+        if (attachment.id().isPresent()) {
+            writer.println("Id: {}", attachment.id().get());
+        }
+        if (attachment.uploadTimestamp().isPresent()) {
+            writer.println("Upload timestamp: {}", DateUtils.formatTimestamp(attachment.uploadTimestamp().get()));
+        }
+        if (attachment.caption().isPresent()) {
+            writer.println("Caption: {}", attachment.caption().get());
+        }
+        if (attachment.fileName().isPresent()) {
+            writer.println("Filename: {}", attachment.fileName().get());
+        }
+        if (attachment.size().isPresent() || attachment.preview().isPresent()) {
             writer.println("Size: {}{}",
-                    pointer.getSize().isPresent() ? pointer.getSize().get() + " bytes" : "<unavailable>",
-                    pointer.getPreview().isPresent() ? " (Preview is available: "
-                            + pointer.getPreview().get().length
+                    attachment.size().isPresent() ? attachment.size().get() + " bytes" : "<unavailable>",
+                    attachment.preview().isPresent() ? " (Preview is available: "
+                            + attachment.preview().get().length
                             + " bytes)" : "");
-            final var flags = new ArrayList<String>();
-            if (pointer.getVoiceNote()) {
-                flags.add("voice note");
-            }
-            if (pointer.isBorderless()) {
-                flags.add("borderless");
-            }
-            if (pointer.isGif()) {
-                flags.add("video gif");
-            }
-            if (flags.size() > 0) {
-                writer.println("Flags: {}", String.join(", ", flags));
-            }
-            if (pointer.getWidth() > 0 || pointer.getHeight() > 0) {
-                writer.println("Dimensions: {}x{}", pointer.getWidth(), pointer.getHeight());
-            }
-            var file = m.getAttachmentFile(pointer.getRemoteId());
+        }
+        if (attachment.thumbnail().isPresent()) {
+            writer.println("Thumbnail:");
+            printAttachment(writer.indentedWriter(), attachment.thumbnail().get());
+        }
+        final var flags = new ArrayList<String>();
+        if (attachment.isVoiceNote()) {
+            flags.add("voice note");
+        }
+        if (attachment.isBorderless()) {
+            flags.add("borderless");
+        }
+        if (attachment.isGif()) {
+            flags.add("video gif");
+        }
+        if (flags.size() > 0) {
+            writer.println("Flags: {}", String.join(", ", flags));
+        }
+        if (attachment.width().isPresent() || attachment.height().isPresent()) {
+            writer.println("Dimensions: {}x{}", attachment.width().orElse(0), attachment.height().orElse(0));
+        }
+        if (attachment.file().isPresent()) {
+            var file = attachment.file().get();
             if (file.exists()) {
                 writer.println("Stored plaintext in: {}", file);
             }
         }
     }
 
-    private String formatContact(SignalServiceAddress address) {
-        address = m.resolveSignalServiceAddress(address);
-        final var number = getLegacyIdentifier(address);
+    private String formatContact(RecipientAddress address) {
+        final var number = address.getLegacyIdentifier();
         final var name = m.getContactOrProfileName(RecipientIdentifier.Single.fromAddress(address));
         if (name == null || name.isEmpty()) {
             return number;