Add addStickerPack command

This commit is contained in:
AsamK 2023-08-20 22:27:50 +02:00
parent 133e2cc222
commit e867c57af8
14 changed files with 159 additions and 25 deletions

View 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

View 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;

View 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);

View 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()));
}
}
}

View 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) {

View 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,16 +29,34 @@ 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");
}
}
final JsonStickerPack manifest;
try {
manifest = context.getStickerPackStore().retrieveManifest(packId);
@ -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);

View 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()),

View 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();

View 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) {

View 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

View 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.

View file

@ -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);
}
}
}
}

View 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());

View 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();