]> nmode's Git Repositories - signal-cli/commitdiff
Add addStickerPack command
authorAsamK <asamk@gmx.de>
Sun, 20 Aug 2023 20:27:50 +0000 (22:27 +0200)
committerAsamK <asamk@gmx.de>
Sun, 20 Aug 2023 20:36:18 +0000 (22:36 +0200)
14 files changed:
CHANGELOG.md
lib/src/main/java/org/asamk/signal/manager/Manager.java
lib/src/main/java/org/asamk/signal/manager/api/StickerPackUrl.java
lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java
lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java
lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java
lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java
lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java
lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java
man/signal-cli.1.adoc
src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java [new file with mode: 0644]
src/main/java/org/asamk/signal/commands/Commands.java
src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java

index 55300aabd952c99144427a1c08d552e14af35aaf..e86df17fa795d0ac45f36040c98f5e41157d7edd 100644 (file)
@@ -2,6 +2,10 @@
 
 ## [Unreleased]
 
+### Added
+
+- New `addStickerPack` command
+
 ## [0.12.0] - 2023-08-11
 **Attention**: Now requires native libsignal-client version 0.30.0
 
index 38d9e12cbcb760a622948295e979d13637234c34..e527c1a0ba69b5a82fb6fa0654de34e99d984d0b 100644 (file)
@@ -211,6 +211,8 @@ public interface Manager extends Closeable {
      */
     StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException;
 
+    void installStickerPack(StickerPackUrl url) throws IOException;
+
     List<StickerPack> getStickerPacks();
 
     void requestAllSyncData() throws IOException;
index d018ac390236426f286f9f33465dcfb1ccbf7be5..c571a424cf6639add2ea1050e1827a807760f710 100644 (file)
@@ -22,7 +22,7 @@ public final class StickerPackUrl {
     public static StickerPackUrl fromUri(URI uri) throws InvalidStickerPackLinkException {
         final var rawQuery = uri.getRawFragment();
         if (isEmpty(rawQuery)) {
-            throw new RuntimeException("Invalid sticker pack uri");
+            throw new InvalidStickerPackLinkException("Invalid sticker pack uri");
         }
 
         var query = Utils.getQueryMap(rawQuery);
index 6ffa08fb663d7cea202f230b7196429b9fc4ee5d..9ffee5c8c2164e3c14213d4623fe907c40450522 100644 (file)
@@ -585,23 +585,15 @@ public final class IncomingMessageHandler {
                     continue;
                 }
                 final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
+                final var stickerPackKey = m.getPackKey().orElse(null);
                 final var installed = m.getType().isEmpty()
                         || m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
 
-                var sticker = account.getStickerStore().getStickerPack(stickerPackId);
-                if (m.getPackKey().isPresent()) {
-                    if (sticker == null) {
-                        sticker = new StickerPack(-1, stickerPackId, m.getPackKey().get(), installed);
-                        account.getStickerStore().addStickerPack(sticker);
-                    }
-                    if (installed) {
-                        context.getJobExecutor()
-                                .enqueueJob(new RetrieveStickerPackJob(stickerPackId, m.getPackKey().get()));
-                    }
-                }
+                final var sticker = context.getStickerHelper()
+                        .addOrUpdateStickerPack(stickerPackId, stickerPackKey, installed);
 
-                if (sticker != null && sticker.isInstalled() != installed) {
-                    account.getStickerStore().updateStickerPackInstalled(sticker.packId(), installed);
+                if (sticker != null && installed) {
+                    context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, sticker.packKey()));
                 }
             }
         }
index c1ba113a66ff20c532e0155938ec156ce9ad2ba7..8cd37aa9c2860397b44c0c54bb2a134b3d197b4e 100644 (file)
@@ -186,6 +186,10 @@ public class SendHelper {
 
     public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) {
         var messageSender = dependencies.getMessageSender();
+        if (!account.isMultiDevice()) {
+            logger.trace("Not sending sync message because there are no linked devices.");
+            return SendMessageResult.success(account.getSelfAddress(), List.of(), false, false, 0, Optional.empty());
+        }
         try {
             return messageSender.sendSyncMessage(message, context.getUnidentifiedAccessHelper().getAccessForSync());
         } catch (UnregisteredUserException e) {
index 915f3d71fe32f537282acf261ed8bc7db607eafb..daa5aeb60820498bef81ee601a79038e49c1ef3a 100644 (file)
@@ -5,6 +5,7 @@ import org.asamk.signal.manager.api.StickerPackId;
 import org.asamk.signal.manager.internal.SignalDependencies;
 import org.asamk.signal.manager.storage.SignalAccount;
 import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack;
+import org.asamk.signal.manager.storage.stickers.StickerPack;
 import org.asamk.signal.manager.util.IOUtils;
 import org.signal.libsignal.protocol.InvalidMessageException;
 import org.slf4j.Logger;
@@ -28,15 +29,33 @@ public class StickerHelper {
         this.context = context;
     }
 
+    public StickerPack addOrUpdateStickerPack(
+            final StickerPackId stickerPackId, final byte[] stickerPackKey, final boolean installed
+    ) {
+        final var sticker = account.getStickerStore().getStickerPack(stickerPackId);
+        if (sticker != null) {
+            if (sticker.isInstalled() != installed) {
+                account.getStickerStore().updateStickerPackInstalled(sticker.packId(), installed);
+            }
+            return sticker;
+        }
+
+        if (stickerPackKey == null) {
+            return null;
+        }
+
+        final var newSticker = new StickerPack(-1, stickerPackId, stickerPackKey, installed);
+        account.getStickerStore().addStickerPack(newSticker);
+        return newSticker;
+    }
+
     public JsonStickerPack getOrRetrieveStickerPack(
             StickerPackId packId, byte[] packKey
     ) throws InvalidStickerException {
-        if (!context.getStickerPackStore().existsStickerPack(packId)) {
-            try {
-                retrieveStickerPack(packId, packKey);
-            } catch (InvalidMessageException | IOException e) {
-                throw new InvalidStickerException("Failed to retrieve sticker pack");
-            }
+        try {
+            retrieveStickerPack(packId, packKey);
+        } catch (InvalidMessageException | IOException e) {
+            throw new InvalidStickerException("Failed to retrieve sticker pack");
         }
         final JsonStickerPack manifest;
         try {
@@ -48,6 +67,10 @@ public class StickerHelper {
     }
 
     public void retrieveStickerPack(StickerPackId packId, byte[] packKey) throws InvalidMessageException, IOException {
+        if (context.getStickerPackStore().existsStickerPack(packId)) {
+            logger.debug("Sticker pack {} already downloaded.", Hex.toStringCondensed(packId.serialize()));
+            return;
+        }
         logger.debug("Retrieving sticker pack {}.", Hex.toStringCondensed(packId.serialize()));
         final var messageReceiver = dependencies.getMessageReceiver();
         final var manifest = messageReceiver.retrieveStickerManifest(packId.serialize(), packKey);
index f015cb1ff776f74a7aa149a5eceb0d92c6d4a938..47923051a3d9bdc4bb0f6e0ee31dc5479b1ac171 100644 (file)
@@ -6,9 +6,11 @@ import org.asamk.signal.manager.api.TrustLevel;
 import org.asamk.signal.manager.storage.SignalAccount;
 import org.asamk.signal.manager.storage.groups.GroupInfoV1;
 import org.asamk.signal.manager.storage.recipients.RecipientAddress;
+import org.asamk.signal.manager.storage.stickers.StickerPack;
 import org.asamk.signal.manager.util.AttachmentUtils;
 import org.asamk.signal.manager.util.IOUtils;
 import org.asamk.signal.manager.util.MimeUtils;
+import org.jetbrains.annotations.NotNull;
 import org.signal.libsignal.protocol.IdentityKey;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -26,6 +28,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOut
 import org.whispersystems.signalservice.api.messages.multidevice.KeysMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
+import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
@@ -40,6 +43,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class SyncHelper {
 
@@ -222,6 +226,22 @@ public class SyncHelper {
         context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage));
     }
 
+    public void sendStickerOperationsMessage(List<StickerPack> installStickers, List<StickerPack> removeStickers) {
+        var installStickerMessages = installStickers.stream().map(s -> getStickerPackOperationMessage(s, true));
+        var removeStickerMessages = removeStickers.stream().map(s -> getStickerPackOperationMessage(s, false));
+        var stickerMessages = Stream.concat(installStickerMessages, removeStickerMessages).toList();
+        context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forStickerPackOperations(stickerMessages));
+    }
+
+    @NotNull
+    private static StickerPackOperationMessage getStickerPackOperationMessage(
+            final StickerPack s, final boolean installed
+    ) {
+        return new StickerPackOperationMessage(s.packId().serialize(),
+                s.packKey(),
+                installed ? StickerPackOperationMessage.Type.INSTALL : StickerPackOperationMessage.Type.REMOVE);
+    }
+
     public void sendConfigurationMessage() {
         final var config = account.getConfigurationStore();
         var configurationMessage = new ConfigurationMessage(Optional.ofNullable(config.getReadReceipts()),
index 36051333a379a888ac853b2a66067629a86bbc20..3c7649e9f1464a8d1034ec2df16713d4831f4fd8 100644 (file)
@@ -72,6 +72,7 @@ import org.asamk.signal.manager.util.AttachmentUtils;
 import org.asamk.signal.manager.util.KeyUtils;
 import org.asamk.signal.manager.util.MimeUtils;
 import org.asamk.signal.manager.util.StickerUtils;
+import org.signal.libsignal.protocol.InvalidMessageException;
 import org.signal.libsignal.usernames.BaseUsernameException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -891,10 +892,25 @@ public class ManagerImpl implements Manager {
 
         var sticker = new StickerPack(packId, packKey);
         account.getStickerStore().addStickerPack(sticker);
+        context.getSyncHelper().sendStickerOperationsMessage(List.of(sticker), List.of());
 
         return new StickerPackUrl(packId, packKey);
     }
 
+    @Override
+    public void installStickerPack(StickerPackUrl url) throws IOException {
+        final var packId = url.getPackId();
+        final var packKey = url.getPackKey();
+        try {
+            context.getStickerHelper().retrieveStickerPack(packId, packKey);
+        } catch (InvalidMessageException e) {
+            throw new IOException(e);
+        }
+
+        final var sticker = context.getStickerHelper().addOrUpdateStickerPack(packId, packKey, true);
+        context.getSyncHelper().sendStickerOperationsMessage(List.of(sticker), List.of());
+    }
+
     @Override
     public List<org.asamk.signal.manager.api.StickerPack> getStickerPacks() {
         final var stickerPackStore = context.getStickerPackStore();
index 54c0390b8ec5b831170715f132d60136fed070c0..9d90a5463e0185be9ca9e250ea6b5baedc1be585 100644 (file)
@@ -23,10 +23,6 @@ public class RetrieveStickerPackJob implements Job {
 
     @Override
     public void run(Context context) {
-        if (context.getStickerPackStore().existsStickerPack(packId)) {
-            logger.debug("Sticker pack {} already downloaded.", Hex.toStringCondensed(packId.serialize()));
-            return;
-        }
         try {
             context.getStickerHelper().retrieveStickerPack(packId, packKey);
         } catch (IOException e) {
index 6667d9789c9f3a00edfb8713a73183800dfd4b45..aacd18bd57ba9270e3c508f5d6afce27efd7744e 100644 (file)
@@ -10,6 +10,7 @@ import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Collection;
+import java.util.List;
 
 public class StickerStore {
 
@@ -36,7 +37,7 @@ public class StickerStore {
         this.database = database;
     }
 
-    public Collection<StickerPack> getStickerPacks() {
+    public List<StickerPack> getStickerPacks() {
         final var sql = (
                 """
                 SELECT s._id, s.pack_id, s.pack_key, s.installed
index 5a8d287dfae68d45adab42ea0b4aeb38a8c23f17..56449d19521907f0902dbef5fc60eb99c46a9fce 100644 (file)
@@ -652,6 +652,14 @@ The path of the manifest.json or a zip file containing the sticker pack you wish
 
 Show a list of known sticker packs.
 
+=== addStickerPack
+
+Install a sticker pack for this account.
+
+*--uri* [URI]::
+Specify the uri of the sticker pack.
+e.g. https://signal.art/addstickers/#pack_id=XXX&pack_key=XXX)"
+
 === getAttachment
 
 Gets the raw data for a specified attachment.
diff --git a/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java b/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java
new file mode 100644 (file)
index 0000000..33de6e3
--- /dev/null
@@ -0,0 +1,62 @@
+package org.asamk.signal.commands;
+
+import net.sourceforge.argparse4j.inf.Namespace;
+import net.sourceforge.argparse4j.inf.Subparser;
+
+import org.asamk.signal.commands.exceptions.CommandException;
+import org.asamk.signal.commands.exceptions.IOErrorException;
+import org.asamk.signal.commands.exceptions.UserErrorException;
+import org.asamk.signal.manager.Manager;
+import org.asamk.signal.manager.api.StickerPackUrl;
+import org.asamk.signal.output.OutputWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class AddStickerPackCommand implements JsonRpcLocalCommand {
+
+    private final static Logger logger = LoggerFactory.getLogger(AddStickerPackCommand.class);
+
+    @Override
+    public String getName() {
+        return "addStickerPack";
+    }
+
+    @Override
+    public void attachToSubparser(final Subparser subparser) {
+        subparser.help("Install a sticker pack for this account.");
+        subparser.addArgument("--uri")
+                .required(true)
+                .nargs("+")
+                .help("Specify the uri of the sticker pack. (e.g. https://signal.art/addstickers/#pack_id=XXX&pack_key=XXX)");
+    }
+
+    @Override
+    public void handleCommand(
+            final Namespace ns, final Manager m, final OutputWriter outputWriter
+    ) throws CommandException {
+        final var uris = ns.<String>getList("uri");
+        for (final var uri : uris) {
+            final URI stickerUri;
+            try {
+                stickerUri = new URI(uri);
+            } catch (URISyntaxException e) {
+                throw new UserErrorException("Sticker pack uri has invalid format: " + e.getMessage());
+            }
+
+            try {
+                var stickerPackUrl = StickerPackUrl.fromUri(stickerUri);
+                m.installStickerPack(stickerPackUrl);
+            } catch (IOException e) {
+                logger.error("Install sticker pack failed", e);
+                throw new IOErrorException("Install sticker pack failed", e);
+            } catch (StickerPackUrl.InvalidStickerPackLinkException e) {
+                logger.error("Invalid sticker pack link", e);
+                throw new UserErrorException("Invalid sticker pack link", e);
+            }
+        }
+    }
+}
index 51820c9a2dc250123832098163eacdf5cd60feae..2783ca3748cde9801d8c5cb7cc4f9a00b6ec57ff 100644 (file)
@@ -17,6 +17,7 @@ public class Commands {
         addCommand(new FinishLinkCommand());
         addCommand(new GetAttachmentCommand());
         addCommand(new GetUserStatusCommand());
+        addCommand(new AddStickerPackCommand());
         addCommand(new JoinGroupCommand());
         addCommand(new JsonRpcDispatcherCommand());
         addCommand(new LinkCommand());
index f87eded6a68a65d7207a4eb37440bb85ca4bef69..9b7908b7db3a798b733429a3534bc3fab99be892 100644 (file)
@@ -481,6 +481,11 @@ public class DbusManagerImpl implements Manager {
         }
     }
 
+    @Override
+    public void installStickerPack(final StickerPackUrl url) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public List<StickerPack> getStickerPacks() {
         throw new UnsupportedOperationException();