]> nmode's Git Repositories - signal-cli/commitdiff
Implement receive handling for story messages
authorAsamK <asamk@gmx.de>
Mon, 8 Aug 2022 16:12:30 +0000 (18:12 +0200)
committerAsamK <asamk@gmx.de>
Tue, 9 Aug 2022 21:20:12 +0000 (23:20 +0200)
12 files changed:
graalvm-config-dir/reflect-config.json
lib/src/main/java/org/asamk/signal/manager/api/Color.java [new file with mode: 0644]
lib/src/main/java/org/asamk/signal/manager/api/MessageEnvelope.java
lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
src/main/java/org/asamk/signal/ReceiveMessageHandler.java
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java
src/main/java/org/asamk/signal/json/JsonDataMessage.java
src/main/java/org/asamk/signal/json/JsonMessageEnvelope.java
src/main/java/org/asamk/signal/json/JsonStoryContext.java [new file with mode: 0644]
src/main/java/org/asamk/signal/json/JsonStoryMessage.java [new file with mode: 0644]
src/main/java/org/asamk/signal/json/JsonSyncMessage.java
src/main/java/org/asamk/signal/json/JsonSyncStoryMessage.java [new file with mode: 0644]

index c223851bef52c381c75ee68b3546aae42be799ff..389c947c5bac4a29ecf374c4db225b07f76a9009 100644 (file)
   "allDeclaredMethods":true,
   "allDeclaredConstructors":true
 },
   "allDeclaredMethods":true,
   "allDeclaredConstructors":true
 },
+{
+  "name":"org.asamk.signal.json.JsonStoryContext",
+  "allDeclaredFields":true,
+  "queryAllDeclaredMethods":true,
+  "queryAllDeclaredConstructors":true,
+  "methods":[
+    {"name":"authorNumber","parameterTypes":[] }, 
+    {"name":"authorUuid","parameterTypes":[] }, 
+    {"name":"sentTimestamp","parameterTypes":[] }
+  ]
+},
+{
+  "name":"org.asamk.signal.json.JsonStoryMessage",
+  "allDeclaredFields":true,
+  "queryAllDeclaredMethods":true,
+  "queryAllDeclaredConstructors":true,
+  "methods":[
+    {"name":"allowsReplies","parameterTypes":[] }, 
+    {"name":"fileAttachment","parameterTypes":[] }, 
+    {"name":"groupId","parameterTypes":[] }, 
+    {"name":"textAttachment","parameterTypes":[] }
+  ]
+},
+{
+  "name":"org.asamk.signal.json.JsonStoryMessage$TextAttachment",
+  "allDeclaredFields":true,
+  "queryAllDeclaredMethods":true,
+  "queryAllDeclaredConstructors":true,
+  "methods":[
+    {"name":"backgroundColor","parameterTypes":[] }, 
+    {"name":"backgroundGradient","parameterTypes":[] }, 
+    {"name":"preview","parameterTypes":[] }, 
+    {"name":"style","parameterTypes":[] }, 
+    {"name":"text","parameterTypes":[] }, 
+    {"name":"textBackgroundColor","parameterTypes":[] }, 
+    {"name":"textForegroundColor","parameterTypes":[] }
+  ]
+},
+{
+  "name":"org.asamk.signal.json.JsonStoryMessage$TextAttachment$Gradient",
+  "allDeclaredFields":true,
+  "queryAllDeclaredMethods":true,
+  "queryAllDeclaredConstructors":true,
+  "methods":[
+    {"name":"angle","parameterTypes":[] }, 
+    {"name":"endColor","parameterTypes":[] }, 
+    {"name":"startColor","parameterTypes":[] }
+  ]
+},
 {
   "name":"org.asamk.signal.json.JsonSyncDataMessage",
   "allDeclaredFields":true,
 {
   "name":"org.asamk.signal.json.JsonSyncDataMessage",
   "allDeclaredFields":true,
   "allDeclaredMethods":true,
   "allDeclaredConstructors":true
 },
   "allDeclaredMethods":true,
   "allDeclaredConstructors":true
 },
+{
+  "name":"org.asamk.signal.json.JsonSyncStoryMessage",
+  "allDeclaredFields":true,
+  "queryAllDeclaredMethods":true,
+  "queryAllDeclaredConstructors":true,
+  "methods":[
+    {"name":"dataMessage","parameterTypes":[] }, 
+    {"name":"destinationNumber","parameterTypes":[] }, 
+    {"name":"destinationUuid","parameterTypes":[] }
+  ]
+},
 {
   "name":"org.asamk.signal.json.JsonTypingMessage",
   "allDeclaredFields":true,
 {
   "name":"org.asamk.signal.json.JsonTypingMessage",
   "allDeclaredFields":true,
     {"name":"timestamp_"}
   ]
 },
     {"name":"timestamp_"}
   ]
 },
+{
+  "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TextAttachment",
+  "fields":[
+    {"name":"backgroundCase_"}, 
+    {"name":"background_"}, 
+    {"name":"bitField0_"}, 
+    {"name":"preview_"}, 
+    {"name":"textBackgroundColor_"}, 
+    {"name":"textForegroundColor_"}, 
+    {"name":"textStyle_"}, 
+    {"name":"text_"}
+  ]
+},
+{
+  "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TextAttachment$Gradient",
+  "fields":[
+    {"name":"angle_"}, 
+    {"name":"bitField0_"}, 
+    {"name":"endColor_"}, 
+    {"name":"startColor_"}
+  ]
+},
 {
   "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage",
   "fields":[
 {
   "name":"org.whispersystems.signalservice.internal.push.SignalServiceProtos$TypingMessage",
   "fields":[
diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Color.java b/lib/src/main/java/org/asamk/signal/manager/api/Color.java
new file mode 100644 (file)
index 0000000..11f7deb
--- /dev/null
@@ -0,0 +1,24 @@
+package org.asamk.signal.manager.api;
+
+public record Color(int color) {
+
+    public int alpha() {
+        return color >>> 24;
+    }
+
+    public int red() {
+        return (color >> 16) & 0xFF;
+    }
+
+    public int green() {
+        return (color >> 8) & 0xFF;
+    }
+
+    public int blue() {
+        return color & 0xFF;
+    }
+
+    public String toHexColor() {
+        return String.format("#%08x", color);
+    }
+}
index d2164e91a96960db35f3ba333122bccb6e2e8c94..a3546a4b31e14a3595433261fa96588599ab6ae1 100644 (file)
@@ -15,6 +15,8 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
 import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
 import org.whispersystems.signalservice.api.messages.SignalServicePreview;
 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
 import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
 import org.whispersystems.signalservice.api.messages.SignalServicePreview;
 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
+import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
+import org.whispersystems.signalservice.api.messages.SignalServiceTextAttachment;
 import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
 import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
 import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
 import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
 import org.whispersystems.signalservice.api.messages.calls.AnswerMessage;
 import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
@@ -49,7 +51,8 @@ public record MessageEnvelope(
         Optional<Typing> typing,
         Optional<Data> data,
         Optional<Sync> sync,
         Optional<Typing> typing,
         Optional<Data> data,
         Optional<Sync> sync,
-        Optional<Call> call
+        Optional<Call> call,
+        Optional<Story> story
 ) {
 
     public record Receipt(long when, Type type, List<Long> timestamps) {
 ) {
 
     public record Receipt(long when, Type type, List<Long> timestamps) {
@@ -94,6 +97,7 @@ public record MessageEnvelope(
     public record Data(
             long timestamp,
             Optional<GroupContext> groupContext,
     public record Data(
             long timestamp,
             Optional<GroupContext> groupContext,
+            Optional<StoryContext> storyContext,
             Optional<GroupCallUpdate> groupCallUpdate,
             Optional<String> body,
             int expiresInSeconds,
             Optional<GroupCallUpdate> groupCallUpdate,
             Optional<String> body,
             int expiresInSeconds,
@@ -121,6 +125,10 @@ public record MessageEnvelope(
         ) {
             return new Data(dataMessage.getTimestamp(),
                     dataMessage.getGroupContext().map(GroupContext::from),
         ) {
             return new Data(dataMessage.getTimestamp(),
                     dataMessage.getGroupContext().map(GroupContext::from),
+                    dataMessage.getStoryContext()
+                            .map((SignalServiceDataMessage.StoryContext storyContext) -> StoryContext.from(storyContext,
+                                    recipientResolver,
+                                    addressResolver)),
                     dataMessage.getGroupCallUpdate().map(GroupCallUpdate::from),
                     dataMessage.getBody(),
                     dataMessage.getExpiresInSeconds(),
                     dataMessage.getGroupCallUpdate().map(GroupCallUpdate::from),
                     dataMessage.getBody(),
                     dataMessage.getExpiresInSeconds(),
@@ -168,6 +176,18 @@ public record MessageEnvelope(
             }
         }
 
             }
         }
 
+        public record StoryContext(RecipientAddress author, long sentTimestamp) {
+
+            static StoryContext from(
+                    SignalServiceDataMessage.StoryContext storyContext,
+                    RecipientResolver recipientResolver,
+                    RecipientAddressResolver addressResolver
+            ) {
+                return new StoryContext(addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(
+                        storyContext.getAuthorServiceId())), storyContext.getSentTimestamp());
+            }
+        }
+
         public record GroupCallUpdate(String eraId) {
 
             static GroupCallUpdate from(SignalServiceDataMessage.GroupCallUpdate groupCallUpdate) {
         public record GroupCallUpdate(String eraId) {
 
             static GroupCallUpdate from(SignalServiceDataMessage.GroupCallUpdate groupCallUpdate) {
@@ -519,7 +539,8 @@ public record MessageEnvelope(
                 long expirationStartTimestamp,
                 Optional<RecipientAddress> destination,
                 Set<RecipientAddress> recipients,
                 long expirationStartTimestamp,
                 Optional<RecipientAddress> destination,
                 Set<RecipientAddress> recipients,
-                Optional<Data> message
+                Optional<Data> message,
+                Optional<Story> story
         ) {
 
             static Sent from(
         ) {
 
             static Sent from(
@@ -537,7 +558,8 @@ public record MessageEnvelope(
                                 .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d)))
                                 .collect(Collectors.toSet()),
                         sentMessage.getDataMessage()
                                 .map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d)))
                                 .collect(Collectors.toSet()),
                         sentMessage.getDataMessage()
-                                .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)));
+                                .map(message -> Data.from(message, recipientResolver, addressResolver, fileProvider)),
+                        sentMessage.getStoryMessage().map(s -> Story.from(s, fileProvider)));
             }
         }
 
             }
         }
 
@@ -757,6 +779,75 @@ public record MessageEnvelope(
         }
     }
 
         }
     }
 
+    public record Story(
+            boolean allowsReplies,
+            Optional<GroupId> groupId,
+            Optional<Data.Attachment> fileAttachment,
+            Optional<TextAttachment> textAttachment
+    ) {
+
+        public static Story from(
+                SignalServiceStoryMessage storyMessage, final AttachmentFileProvider fileProvider
+        ) {
+            return new Story(storyMessage.getAllowsReplies().orElse(false),
+                    storyMessage.getGroupContext().map(c -> GroupUtils.getGroupIdV2(c.getMasterKey())),
+                    storyMessage.getFileAttachment().map(f -> Data.Attachment.from(f, fileProvider)),
+                    storyMessage.getTextAttachment().map(t -> TextAttachment.from(t, fileProvider)));
+        }
+
+        public record TextAttachment(
+                Optional<String> text,
+                Optional<Style> style,
+                Optional<Color> textForegroundColor,
+                Optional<Color> textBackgroundColor,
+                Optional<Data.Preview> preview,
+                Optional<Gradient> backgroundGradient,
+                Optional<Color> backgroundColor
+        ) {
+
+            static TextAttachment from(
+                    SignalServiceTextAttachment textAttachment, final AttachmentFileProvider fileProvider
+            ) {
+                return new TextAttachment(textAttachment.getText(),
+                        textAttachment.getStyle().map(Style::from),
+                        textAttachment.getTextForegroundColor().map(Color::new),
+                        textAttachment.getTextBackgroundColor().map(Color::new),
+                        textAttachment.getPreview().map(p -> Data.Preview.from(p, fileProvider)),
+                        textAttachment.getBackgroundGradient().map(Gradient::from),
+                        textAttachment.getBackgroundColor().map(Color::new));
+            }
+
+            public enum Style {
+                DEFAULT,
+                REGULAR,
+                BOLD,
+                SERIF,
+                SCRIPT,
+                CONDENSED;
+
+                static Style from(SignalServiceTextAttachment.Style style) {
+                    return switch (style) {
+                        case DEFAULT -> DEFAULT;
+                        case REGULAR -> REGULAR;
+                        case BOLD -> BOLD;
+                        case SERIF -> SERIF;
+                        case SCRIPT -> SCRIPT;
+                        case CONDENSED -> CONDENSED;
+                    };
+                }
+            }
+
+            public record Gradient(Optional<Color> startColor, Optional<Color> endColor, Optional<Integer> angle) {
+
+                static Gradient from(SignalServiceTextAttachment.Gradient gradient) {
+                    return new Gradient(gradient.getStartColor().map(Color::new),
+                            gradient.getEndColor().map(Color::new),
+                            gradient.getAngle());
+                }
+            }
+        }
+    }
+
     public static MessageEnvelope from(
             SignalServiceEnvelope envelope,
             SignalServiceContent content,
     public static MessageEnvelope from(
             SignalServiceEnvelope envelope,
             SignalServiceContent content,
@@ -783,6 +874,7 @@ public record MessageEnvelope(
         Optional<Data> data;
         Optional<Sync> sync;
         Optional<Call> call;
         Optional<Data> data;
         Optional<Sync> sync;
         Optional<Call> call;
+        Optional<Story> story;
         if (content != null) {
             receipt = content.getReceiptMessage().map(Receipt::from);
             typing = content.getTypingMessage().map(Typing::from);
         if (content != null) {
             receipt = content.getReceiptMessage().map(Receipt::from);
             typing = content.getTypingMessage().map(Typing::from);
@@ -790,6 +882,7 @@ public record MessageEnvelope(
                     .map(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver, fileProvider));
             sync = content.getSyncMessage().map(s -> Sync.from(s, recipientResolver, addressResolver, fileProvider));
             call = content.getCallMessage().map(Call::from);
                     .map(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver, fileProvider));
             sync = content.getSyncMessage().map(s -> Sync.from(s, recipientResolver, addressResolver, fileProvider));
             call = content.getCallMessage().map(Call::from);
+            story = content.getStoryMessage().map(s -> Story.from(s, fileProvider));
         } else {
             receipt = envelope.isReceipt() ? Optional.of(new Receipt(envelope.getServerReceivedTimestamp(),
                     Receipt.Type.DELIVERY,
         } else {
             receipt = envelope.isReceipt() ? Optional.of(new Receipt(envelope.getServerReceivedTimestamp(),
                     Receipt.Type.DELIVERY,
@@ -798,6 +891,7 @@ public record MessageEnvelope(
             data = Optional.empty();
             sync = Optional.empty();
             call = Optional.empty();
             data = Optional.empty();
             sync = Optional.empty();
             call = Optional.empty();
+            story = Optional.empty();
         }
 
         return new MessageEnvelope(source == null
         }
 
         return new MessageEnvelope(source == null
@@ -812,7 +906,8 @@ public record MessageEnvelope(
                 typing,
                 data,
                 sync,
                 typing,
                 data,
                 sync,
-                call);
+                call,
+                story);
     }
 
     public interface AttachmentFileProvider {
     }
 
     public interface AttachmentFileProvider {
index d18cc0c0aff4c3f9ca882d473d3c0d1705358187..1d4678fa1d936016d6d14a82962fc0134c71268c 100644 (file)
@@ -52,7 +52,9 @@ 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.SignalServiceGroup;
 import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
 import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
 import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
+import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
+import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@@ -276,6 +278,11 @@ public final class IncomingMessageHandler {
                     receiveConfig.ignoreAttachments()));
         }
 
                     receiveConfig.ignoreAttachments()));
         }
 
+        if (content.getStoryMessage().isPresent()) {
+            final var message = content.getStoryMessage().get();
+            actions.addAll(handleSignalServiceStoryMessage(message, sender, receiveConfig.ignoreAttachments()));
+        }
+
         if (content.getSyncMessage().isPresent()) {
             var syncMessage = content.getSyncMessage().get();
             actions.addAll(handleSyncMessage(syncMessage, sender, receiveConfig.ignoreAttachments()));
         if (content.getSyncMessage().isPresent()) {
             var syncMessage = content.getSyncMessage().get();
             actions.addAll(handleSyncMessage(syncMessage, sender, receiveConfig.ignoreAttachments()));
@@ -347,6 +354,11 @@ public final class IncomingMessageHandler {
                         destination == null ? null : context.getRecipientHelper().resolveRecipient(destination),
                         ignoreAttachments));
             }
                         destination == null ? null : context.getRecipientHelper().resolveRecipient(destination),
                         ignoreAttachments));
             }
+            if (message.getStoryMessage().isPresent()) {
+                actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(),
+                        sender,
+                        ignoreAttachments));
+            }
         }
         if (syncMessage.getRequest().isPresent() && account.isPrimaryDevice()) {
             var rm = syncMessage.getRequest().get();
         }
         if (syncMessage.getRequest().isPresent() && account.isPrimaryDevice()) {
             var rm = syncMessage.getRequest().get();
@@ -566,8 +578,9 @@ public final class IncomingMessageHandler {
     ) {
         var actions = new ArrayList<HandleAction>();
         if (message.getGroupContext().isPresent()) {
     ) {
         var actions = new ArrayList<HandleAction>();
         if (message.getGroupContext().isPresent()) {
-            if (message.getGroupContext().get().getGroupV1().isPresent()) {
-                var groupInfo = message.getGroupContext().get().getGroupV1().get();
+            final var groupContext = message.getGroupContext().get();
+            if (groupContext.getGroupV1().isPresent()) {
+                var groupInfo = groupContext.getGroupV1().get();
                 var groupId = GroupId.v1(groupInfo.getGroupId());
                 var group = context.getGroupHelper().getGroup(groupId);
                 if (group == null || group instanceof GroupInfoV1) {
                 var groupId = GroupId.v1(groupInfo.getGroupId());
                 var group = context.getGroupHelper().getGroup(groupId);
                 if (group == null || group instanceof GroupInfoV1) {
@@ -620,14 +633,8 @@ public final class IncomingMessageHandler {
                     // Received a group v1 message for a v2 group
                 }
             }
                     // Received a group v1 message for a v2 group
                 }
             }
-            if (message.getGroupContext().get().getGroupV2().isPresent()) {
-                final var groupContext = message.getGroupContext().get().getGroupV2().get();
-                final var groupMasterKey = groupContext.getMasterKey();
-
-                context.getGroupHelper()
-                        .getOrMigrateGroup(groupMasterKey,
-                                groupContext.getRevision(),
-                                groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
+            if (groupContext.getGroupV2().isPresent()) {
+                handleGroupV2Context(groupContext.getGroupV2().get());
             }
         }
 
             }
         }
 
@@ -637,8 +644,9 @@ public final class IncomingMessageHandler {
         }
         if (message.isExpirationUpdate() || message.getBody().isPresent()) {
             if (message.getGroupContext().isPresent()) {
         }
         if (message.isExpirationUpdate() || message.getBody().isPresent()) {
             if (message.getGroupContext().isPresent()) {
-                if (message.getGroupContext().get().getGroupV1().isPresent()) {
-                    var groupInfo = message.getGroupContext().get().getGroupV1().get();
+                final var groupContext = message.getGroupContext().get();
+                if (groupContext.getGroupV1().isPresent()) {
+                    var groupInfo = groupContext.getGroupV1().get();
                     var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
                     if (group != null) {
                         if (group.messageExpirationTime != message.getExpiresInSeconds()) {
                     var group = account.getGroupStore().getOrCreateGroupV1(GroupId.v1(groupInfo.getGroupId()));
                     if (group != null) {
                         if (group.messageExpirationTime != message.getExpiresInSeconds()) {
@@ -646,7 +654,7 @@ public final class IncomingMessageHandler {
                             account.getGroupStore().updateGroup(group);
                         }
                     }
                             account.getGroupStore().updateGroup(group);
                         }
                     }
-                } else if (message.getGroupContext().get().getGroupV2().isPresent()) {
+                } else if (groupContext.getGroupV2().isPresent()) {
                     // disappearing message timer already stored in the DecryptedGroup
                 }
             } else if (conversationPartnerAddress != null) {
                     // disappearing message timer already stored in the DecryptedGroup
                 }
             } else if (conversationPartnerAddress != null) {
@@ -686,17 +694,8 @@ public final class IncomingMessageHandler {
                 }
             }
         }
                 }
             }
         }
-        if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
-            final ProfileKey profileKey;
-            try {
-                profileKey = new ProfileKey(message.getProfileKey().get());
-            } catch (InvalidInputException e) {
-                throw new AssertionError(e);
-            }
-            if (account.getSelfRecipientId().equals(source)) {
-                this.account.setProfileKey(profileKey);
-            }
-            this.account.getProfileStore().storeProfileKey(source, profileKey);
+        if (message.getProfileKey().isPresent()) {
+            handleIncomingProfileKey(message.getProfileKey().get(), source);
         }
         if (message.getSticker().isPresent()) {
             final var messageSticker = message.getSticker().get();
         }
         if (message.getSticker().isPresent()) {
             final var messageSticker = message.getSticker().get();
@@ -711,6 +710,62 @@ public final class IncomingMessageHandler {
         return actions;
     }
 
         return actions;
     }
 
+    private List<HandleAction> handleSignalServiceStoryMessage(
+            SignalServiceStoryMessage message, RecipientId source, boolean ignoreAttachments
+    ) {
+        var actions = new ArrayList<HandleAction>();
+        if (message.getGroupContext().isPresent()) {
+            handleGroupV2Context(message.getGroupContext().get());
+        }
+
+        if (!ignoreAttachments) {
+            if (message.getFileAttachment().isPresent()) {
+                context.getAttachmentHelper().downloadAttachment(message.getFileAttachment().get());
+            }
+            if (message.getTextAttachment().isPresent()) {
+                final var textAttachment = message.getTextAttachment().get();
+                if (textAttachment.getPreview().isPresent()) {
+                    final var preview = textAttachment.getPreview().get();
+                    if (preview.getImage().isPresent()) {
+                        context.getAttachmentHelper().downloadAttachment(preview.getImage().get());
+                    }
+                }
+            }
+        }
+
+        if (message.getProfileKey().isPresent()) {
+            handleIncomingProfileKey(message.getProfileKey().get(), source);
+        }
+
+        return actions;
+    }
+
+    private void handleGroupV2Context(final SignalServiceGroupV2 groupContext) {
+        final var groupMasterKey = groupContext.getMasterKey();
+
+        context.getGroupHelper()
+                .getOrMigrateGroup(groupMasterKey,
+                        groupContext.getRevision(),
+                        groupContext.hasSignedGroupChange() ? groupContext.getSignedGroupChange() : null);
+    }
+
+    private void handleIncomingProfileKey(final byte[] profileKeyBytes, final RecipientId source) {
+        if (profileKeyBytes.length != 32) {
+            logger.debug("Received invalid profile key of length {}", profileKeyBytes.length);
+            return;
+        }
+        final ProfileKey profileKey;
+        try {
+            profileKey = new ProfileKey(profileKeyBytes);
+        } catch (InvalidInputException e) {
+            throw new AssertionError(e);
+        }
+        if (account.getSelfRecipientId().equals(source)) {
+            this.account.setProfileKey(profileKey);
+        }
+        this.account.getProfileStore().storeProfileKey(source, profileKey);
+    }
+
     private Pair<RecipientId, Integer> getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
         if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
             return new Pair<>(context.getRecipientHelper().resolveRecipient(envelope.getSourceAddress()),
     private Pair<RecipientId, Integer> getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
         if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
             return new Pair<>(context.getRecipientHelper().resolveRecipient(envelope.getSourceAddress()),
index 390087f9d37b4254fc88d136da71549a2508d174..10f7370675b57d228358f01bc52150a4456f9761 100644 (file)
@@ -68,6 +68,10 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
             var message = envelope.data().get();
             printDataMessage(writer, message);
         }
             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();
         if (envelope.sync().isPresent()) {
             writer.println("Received a sync message");
             var syncMessage = envelope.sync().get();
@@ -107,6 +111,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
             final var groupContext = message.groupContext().get();
             printGroupContext(writer.indentedWriter(), groupContext);
         }
             final var groupContext = message.groupContext().get();
             printGroupContext(writer.indentedWriter(), groupContext);
         }
+        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.groupCallUpdate().get();
         if (message.groupCallUpdate().isPresent()) {
             writer.println("Group call update:");
             final var groupCallUpdate = message.groupCallUpdate().get();
@@ -176,6 +185,28 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
         }
     }
 
         }
     }
 
+    private void printStoryMessage(
+            PlainTextWriter writer, MessageEnvelope.Story message
+    ) {
+        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 printTypingMessage(
             final PlainTextWriter writer, final MessageEnvelope.Typing typingMessage
     ) {
     private void printTypingMessage(
             final PlainTextWriter writer, final MessageEnvelope.Typing typingMessage
     ) {
@@ -305,6 +336,10 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
                 var message = sentTranscriptMessage.message().get();
                 printDataMessage(writer.indentedWriter(), message);
             }
                 var message = sentTranscriptMessage.message().get();
                 printDataMessage(writer.indentedWriter(), message);
             }
+            if (sentTranscriptMessage.story().isPresent()) {
+                var message = sentTranscriptMessage.story().get();
+                printStoryMessage(writer.indentedWriter(), message);
+            }
         }
         if (syncMessage.blocked().isPresent()) {
             writer.println("Received sync message with block list");
         }
         if (syncMessage.blocked().isPresent()) {
             writer.println("Received sync message with block list");
@@ -495,6 +530,13 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
         writer.println("Type: {}", groupContext.isGroupUpdate() ? "UPDATE" : "DELIVER");
     }
 
         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) {
         writer.println("Id: {}", groupId.toBase64());
 
     private void printGroupInfo(final PlainTextWriter writer, final GroupId groupId) {
         writer.println("Id: {}", groupId.toBase64());
 
index 901531474d085c0c7980f02dd93e1151b44c38cf..e13aee4dd652e36810195d68726edc91a78ca0de 100644 (file)
@@ -751,6 +751,7 @@ public class DbusManagerImpl implements Manager {
                                         messageReceived.getGroupId()), false, 0))
                                         : Optional.empty(),
                                 Optional.empty(),
                                         messageReceived.getGroupId()), false, 0))
                                         : Optional.empty(),
                                 Optional.empty(),
+                                Optional.empty(),
                                 Optional.of(messageReceived.getMessage()),
                                 0,
                                 false,
                                 Optional.of(messageReceived.getMessage()),
                                 0,
                                 false,
@@ -768,6 +769,7 @@ public class DbusManagerImpl implements Manager {
                                 List.of(),
                                 List.of())),
                         Optional.empty(),
                                 List.of(),
                                 List.of())),
                         Optional.empty(),
+                        Optional.empty(),
                         Optional.empty());
                 notifyMessageHandlers(envelope);
             };
                         Optional.empty());
                 notifyMessageHandlers(envelope);
             };
@@ -793,6 +795,7 @@ public class DbusManagerImpl implements Manager {
                         Optional.empty(),
                         Optional.empty(),
                         Optional.empty(),
                         Optional.empty(),
                         Optional.empty(),
                         Optional.empty(),
+                        Optional.empty(),
                         Optional.empty());
                 notifyMessageHandlers(envelope);
             };
                         Optional.empty());
                 notifyMessageHandlers(envelope);
             };
@@ -822,6 +825,7 @@ public class DbusManagerImpl implements Manager {
                                                 syncReceived.getGroupId()), false, 0))
                                                 : Optional.empty(),
                                         Optional.empty(),
                                                 syncReceived.getGroupId()), false, 0))
                                                 : Optional.empty(),
                                         Optional.empty(),
+                                        Optional.empty(),
                                         Optional.of(syncReceived.getMessage()),
                                         0,
                                         false,
                                         Optional.of(syncReceived.getMessage()),
                                         0,
                                         false,
@@ -837,7 +841,8 @@ public class DbusManagerImpl implements Manager {
                                         Optional.empty(),
                                         List.of(),
                                         List.of(),
                                         Optional.empty(),
                                         List.of(),
                                         List.of(),
-                                        List.of())))),
+                                        List.of())),
+                                Optional.empty())),
                                 Optional.empty(),
                                 List.of(),
                                 List.of(),
                                 Optional.empty(),
                                 List.of(),
                                 List.of(),
@@ -845,6 +850,7 @@ public class DbusManagerImpl implements Manager {
                                 Optional.empty(),
                                 Optional.empty(),
                                 Optional.empty())),
                                 Optional.empty(),
                                 Optional.empty(),
                                 Optional.empty())),
+                        Optional.empty(),
                         Optional.empty());
                 notifyMessageHandlers(envelope);
             };
                         Optional.empty());
                 notifyMessageHandlers(envelope);
             };
index 74c07cd707d63d3a451d24a2e64e1cc53885459b..5511f53193bfeb1a23704452c76c6120288ec4b7 100644 (file)
@@ -20,13 +20,17 @@ record JsonDataMessage(
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSharedContact> contacts,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSharedContact> contacts,
-        @JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo
+        @JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo,
+        @JsonInclude(JsonInclude.Include.NON_NULL) JsonStoryContext storyContext
 ) {
 
     static JsonDataMessage from(MessageEnvelope.Data dataMessage) {
         final var timestamp = dataMessage.timestamp();
         final var groupInfo = dataMessage.groupContext().isPresent() ? JsonGroupInfo.from(dataMessage.groupContext()
                 .get()) : null;
 ) {
 
     static JsonDataMessage from(MessageEnvelope.Data dataMessage) {
         final var timestamp = dataMessage.timestamp();
         final var groupInfo = dataMessage.groupContext().isPresent() ? JsonGroupInfo.from(dataMessage.groupContext()
                 .get()) : null;
+        final var storyContext = dataMessage.storyContext().isPresent()
+                ? JsonStoryContext.from(dataMessage.storyContext().get())
+                : null;
         final var message = dataMessage.body().orElse(null);
         final var expiresInSeconds = dataMessage.expiresInSeconds();
         final var viewOnce = dataMessage.isViewOnce();
         final var message = dataMessage.body().orElse(null);
         final var expiresInSeconds = dataMessage.expiresInSeconds();
         final var viewOnce = dataMessage.isViewOnce();
@@ -67,6 +71,7 @@ record JsonDataMessage(
                 sticker,
                 remoteDelete,
                 contacts,
                 sticker,
                 remoteDelete,
                 contacts,
-                groupInfo);
+                groupInfo,
+                storyContext);
     }
 }
     }
 }
index 18207747c05c4e2ba2c6cdcbb5b2f5e6cc722645..adc7a251032cb878b26a7fadcc12e3c78dccaa94 100644 (file)
@@ -18,6 +18,7 @@ public record JsonMessageEnvelope(
         Integer sourceDevice,
         long timestamp,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonDataMessage dataMessage,
         Integer sourceDevice,
         long timestamp,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonDataMessage dataMessage,
+        @JsonInclude(JsonInclude.Include.NON_NULL) JsonStoryMessage storyMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessage syncMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonCallMessage callMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonReceiptMessage receiptMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessage syncMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonCallMessage callMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonReceiptMessage receiptMessage,
@@ -60,6 +61,7 @@ public record JsonMessageEnvelope(
         final var typingMessage = envelope.typing().map(JsonTypingMessage::from).orElse(null);
 
         final var dataMessage = envelope.data().map(JsonDataMessage::from).orElse(null);
         final var typingMessage = envelope.typing().map(JsonTypingMessage::from).orElse(null);
 
         final var dataMessage = envelope.data().map(JsonDataMessage::from).orElse(null);
+        final var storyMessage = envelope.story().map(JsonStoryMessage::from).orElse(null);
         final var syncMessage = envelope.sync().map(JsonSyncMessage::from).orElse(null);
         final var callMessage = envelope.call().map(JsonCallMessage::from).orElse(null);
 
         final var syncMessage = envelope.sync().map(JsonSyncMessage::from).orElse(null);
         final var callMessage = envelope.call().map(JsonCallMessage::from).orElse(null);
 
@@ -70,6 +72,7 @@ public record JsonMessageEnvelope(
                 sourceDevice,
                 timestamp,
                 dataMessage,
                 sourceDevice,
                 timestamp,
                 dataMessage,
+                storyMessage,
                 syncMessage,
                 callMessage,
                 receiptMessage,
                 syncMessage,
                 callMessage,
                 receiptMessage,
diff --git a/src/main/java/org/asamk/signal/json/JsonStoryContext.java b/src/main/java/org/asamk/signal/json/JsonStoryContext.java
new file mode 100644 (file)
index 0000000..e2cc0c5
--- /dev/null
@@ -0,0 +1,16 @@
+package org.asamk.signal.json;
+
+import org.asamk.signal.manager.api.MessageEnvelope;
+
+import java.util.UUID;
+
+record JsonStoryContext(
+        String authorNumber, String authorUuid, long sentTimestamp
+) {
+
+    static JsonStoryContext from(MessageEnvelope.Data.StoryContext storyContext) {
+        return new JsonStoryContext(storyContext.author().number().orElse(null),
+                storyContext.author().uuid().map(UUID::toString).orElse(null),
+                storyContext.sentTimestamp());
+    }
+}
diff --git a/src/main/java/org/asamk/signal/json/JsonStoryMessage.java b/src/main/java/org/asamk/signal/json/JsonStoryMessage.java
new file mode 100644 (file)
index 0000000..9fa3ea2
--- /dev/null
@@ -0,0 +1,52 @@
+package org.asamk.signal.json;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import org.asamk.signal.manager.api.Color;
+import org.asamk.signal.manager.api.MessageEnvelope;
+import org.asamk.signal.manager.groups.GroupId;
+
+record JsonStoryMessage(
+        boolean allowsReplies,
+        @JsonInclude(JsonInclude.Include.NON_NULL) String groupId,
+        @JsonInclude(JsonInclude.Include.NON_NULL) JsonAttachment fileAttachment,
+        @JsonInclude(JsonInclude.Include.NON_NULL) TextAttachment textAttachment
+) {
+
+    static JsonStoryMessage from(MessageEnvelope.Story storyMessage) {
+        return new JsonStoryMessage(storyMessage.allowsReplies(),
+                storyMessage.groupId().map(GroupId::toBase64).orElse(null),
+                storyMessage.fileAttachment().map(JsonAttachment::from).orElse(null),
+                storyMessage.textAttachment().map(TextAttachment::from).orElse(null));
+    }
+
+    public record TextAttachment(
+            String text,
+            @JsonInclude(JsonInclude.Include.NON_NULL) String style,
+            @JsonInclude(JsonInclude.Include.NON_NULL) String textForegroundColor,
+            @JsonInclude(JsonInclude.Include.NON_NULL) String textBackgroundColor,
+            @JsonInclude(JsonInclude.Include.NON_NULL) JsonPreview preview,
+            @JsonInclude(JsonInclude.Include.NON_NULL) Gradient backgroundGradient,
+            @JsonInclude(JsonInclude.Include.NON_NULL) String backgroundColor
+    ) {
+
+        static TextAttachment from(MessageEnvelope.Story.TextAttachment textAttachment) {
+            return new TextAttachment(textAttachment.text().orElse(null),
+                    textAttachment.style().map(MessageEnvelope.Story.TextAttachment.Style::name).orElse(null),
+                    textAttachment.textForegroundColor().map(Color::toHexColor).orElse(null),
+                    textAttachment.textBackgroundColor().map(Color::toHexColor).orElse(null),
+                    textAttachment.preview().map(JsonPreview::from).orElse(null),
+                    textAttachment.backgroundGradient().map(Gradient::from).orElse(null),
+                    textAttachment.backgroundColor().map(Color::toHexColor).orElse(null));
+        }
+
+        public record Gradient(String startColor, String endColor, Integer angle) {
+
+            static Gradient from(MessageEnvelope.Story.TextAttachment.Gradient gradient) {
+                return new Gradient(gradient.startColor().map(Color::toHexColor).orElse(null),
+                        gradient.endColor().map(Color::toHexColor).orElse(null),
+                        gradient.angle().orElse(null));
+            }
+        }
+    }
+}
index 6f512016ee0b0126446c679abc2edb6bad4a5cb8..a251d0078045f8d1ca50aeb1fbbe6e5aaba33e2a 100644 (file)
@@ -16,30 +16,20 @@ enum JsonSyncMessageType {
 
 record JsonSyncMessage(
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncDataMessage sentMessage,
 
 record JsonSyncMessage(
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncDataMessage sentMessage,
+        @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncStoryMessage sentStoryMessage,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedNumbers,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedGroupIds,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSyncReadMessage> readMessages,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessageType type
 ) {
 
         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedNumbers,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedGroupIds,
         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSyncReadMessage> readMessages,
         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessageType type
 ) {
 
-    JsonSyncMessage(
-            final JsonSyncDataMessage sentMessage,
-            final List<String> blockedNumbers,
-            final List<String> blockedGroupIds,
-            final List<JsonSyncReadMessage> readMessages,
-            final JsonSyncMessageType type
-    ) {
-        this.sentMessage = sentMessage;
-        this.blockedNumbers = blockedNumbers;
-        this.blockedGroupIds = blockedGroupIds;
-        this.readMessages = readMessages;
-        this.type = type;
-    }
-
     static JsonSyncMessage from(MessageEnvelope.Sync syncMessage) {
     static JsonSyncMessage from(MessageEnvelope.Sync syncMessage) {
-        final var sentMessage = syncMessage.sent().isPresent()
+        final var sentMessage = syncMessage.sent().isPresent() && syncMessage.sent().get().story().isEmpty()
                 ? JsonSyncDataMessage.from(syncMessage.sent().get())
                 : null;
                 ? JsonSyncDataMessage.from(syncMessage.sent().get())
                 : null;
+        final var sentStoryMessage = syncMessage.sent().isPresent() && syncMessage.sent().get().story().isPresent()
+                ? JsonSyncStoryMessage.from(syncMessage.sent().get())
+                : null;
         final List<String> blockedNumbers;
         final List<String> blockedGroupIds;
         if (syncMessage.blocked().isPresent()) {
         final List<String> blockedNumbers;
         final List<String> blockedGroupIds;
         if (syncMessage.blocked().isPresent()) {
@@ -68,6 +58,6 @@ record JsonSyncMessage(
         } else {
             type = null;
         }
         } else {
             type = null;
         }
-        return new JsonSyncMessage(sentMessage, blockedNumbers, blockedGroupIds, readMessages, type);
+        return new JsonSyncMessage(sentMessage, sentStoryMessage, blockedNumbers, blockedGroupIds, readMessages, type);
     }
 }
     }
 }
diff --git a/src/main/java/org/asamk/signal/json/JsonSyncStoryMessage.java b/src/main/java/org/asamk/signal/json/JsonSyncStoryMessage.java
new file mode 100644 (file)
index 0000000..6b43522
--- /dev/null
@@ -0,0 +1,26 @@
+package org.asamk.signal.json;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+
+import org.asamk.signal.manager.api.MessageEnvelope;
+
+import java.util.UUID;
+
+record JsonSyncStoryMessage(
+        String destinationNumber, String destinationUuid, @JsonUnwrapped JsonStoryMessage dataMessage
+) {
+
+    static JsonSyncStoryMessage from(MessageEnvelope.Sync.Sent transcriptMessage) {
+        if (transcriptMessage.destination().isPresent()) {
+            final var address = transcriptMessage.destination().get();
+            return new JsonSyncStoryMessage(address.number().orElse(null),
+                    address.uuid().map(UUID::toString).orElse(null),
+                    transcriptMessage.story().map(JsonStoryMessage::from).orElse(null));
+
+        } else {
+            return new JsonSyncStoryMessage(null,
+                    null,
+                    transcriptMessage.story().map(JsonStoryMessage::from).orElse(null));
+        }
+    }
+}