From e867c57af8f7582ed7307ea118b76f92a483f465 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 20 Aug 2023 22:27:50 +0200 Subject: [PATCH] Add addStickerPack command --- CHANGELOG.md | 4 ++ .../org/asamk/signal/manager/Manager.java | 2 + .../signal/manager/api/StickerPackUrl.java | 2 +- .../helper/IncomingMessageHandler.java | 18 ++---- .../signal/manager/helper/SendHelper.java | 4 ++ .../signal/manager/helper/StickerHelper.java | 35 +++++++++-- .../signal/manager/helper/SyncHelper.java | 20 ++++++ .../signal/manager/internal/ManagerImpl.java | 16 +++++ .../manager/jobs/RetrieveStickerPackJob.java | 4 -- .../storage/stickers/StickerStore.java | 3 +- man/signal-cli.1.adoc | 8 +++ .../commands/AddStickerPackCommand.java | 62 +++++++++++++++++++ .../org/asamk/signal/commands/Commands.java | 1 + .../asamk/signal/dbus/DbusManagerImpl.java | 5 ++ 14 files changed, 159 insertions(+), 25 deletions(-) create mode 100644 src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 55300aab..e86df17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 38d9e12c..e527c1a0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -211,6 +211,8 @@ public interface Manager extends Closeable { */ StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException; + void installStickerPack(StickerPackUrl url) throws IOException; + List getStickerPacks(); void requestAllSyncData() throws IOException; diff --git a/lib/src/main/java/org/asamk/signal/manager/api/StickerPackUrl.java b/lib/src/main/java/org/asamk/signal/manager/api/StickerPackUrl.java index d018ac39..c571a424 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/StickerPackUrl.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/StickerPackUrl.java @@ -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); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java index 6ffa08fb..9ffee5c8 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java @@ -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())); } } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index c1ba113a..8cd37aa9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -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) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java index 915f3d71..daa5aeb6 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StickerHelper.java @@ -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); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index f015cb1f..47923051 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -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 installStickers, List 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()), diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index 36051333..3c7649e9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -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 getStickerPacks() { final var stickerPackStore = context.getStickerPackStore(); diff --git a/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java b/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java index 54c0390b..9d90a546 100644 --- a/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java +++ b/lib/src/main/java/org/asamk/signal/manager/jobs/RetrieveStickerPackJob.java @@ -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) { diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java index 6667d978..aacd18bd 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/stickers/StickerStore.java @@ -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 getStickerPacks() { + public List getStickerPacks() { final var sql = ( """ SELECT s._id, s.pack_id, s.pack_key, s.installed diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 5a8d287d..56449d19 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -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 index 00000000..33de6e30 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/AddStickerPackCommand.java @@ -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.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); + } + } + } +} diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 51820c9a..2783ca37 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -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()); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index f87eded6..9b7908b7 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -481,6 +481,11 @@ public class DbusManagerImpl implements Manager { } } + @Override + public void installStickerPack(final StickerPackUrl url) throws IOException { + throw new UnsupportedOperationException(); + } + @Override public List getStickerPacks() { throw new UnsupportedOperationException(); -- 2.50.1