mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Add addStickerPack command
This commit is contained in:
parent
133e2cc222
commit
e867c57af8
14 changed files with 159 additions and 25 deletions
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- New `addStickerPack` command
|
||||||
|
|
||||||
## [0.12.0] - 2023-08-11
|
## [0.12.0] - 2023-08-11
|
||||||
**Attention**: Now requires native libsignal-client version 0.30.0
|
**Attention**: Now requires native libsignal-client version 0.30.0
|
||||||
|
|
||||||
|
|
|
@ -211,6 +211,8 @@ public interface Manager extends Closeable {
|
||||||
*/
|
*/
|
||||||
StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException;
|
StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException;
|
||||||
|
|
||||||
|
void installStickerPack(StickerPackUrl url) throws IOException;
|
||||||
|
|
||||||
List<StickerPack> getStickerPacks();
|
List<StickerPack> getStickerPacks();
|
||||||
|
|
||||||
void requestAllSyncData() throws IOException;
|
void requestAllSyncData() throws IOException;
|
||||||
|
|
|
@ -22,7 +22,7 @@ public final class StickerPackUrl {
|
||||||
public static StickerPackUrl fromUri(URI uri) throws InvalidStickerPackLinkException {
|
public static StickerPackUrl fromUri(URI uri) throws InvalidStickerPackLinkException {
|
||||||
final var rawQuery = uri.getRawFragment();
|
final var rawQuery = uri.getRawFragment();
|
||||||
if (isEmpty(rawQuery)) {
|
if (isEmpty(rawQuery)) {
|
||||||
throw new RuntimeException("Invalid sticker pack uri");
|
throw new InvalidStickerPackLinkException("Invalid sticker pack uri");
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = Utils.getQueryMap(rawQuery);
|
var query = Utils.getQueryMap(rawQuery);
|
||||||
|
|
|
@ -585,23 +585,15 @@ public final class IncomingMessageHandler {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
|
final var stickerPackId = StickerPackId.deserialize(m.getPackId().get());
|
||||||
|
final var stickerPackKey = m.getPackKey().orElse(null);
|
||||||
final var installed = m.getType().isEmpty()
|
final var installed = m.getType().isEmpty()
|
||||||
|| m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
|
|| m.getType().get() == StickerPackOperationMessage.Type.INSTALL;
|
||||||
|
|
||||||
var sticker = account.getStickerStore().getStickerPack(stickerPackId);
|
final var sticker = context.getStickerHelper()
|
||||||
if (m.getPackKey().isPresent()) {
|
.addOrUpdateStickerPack(stickerPackId, stickerPackKey, installed);
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sticker != null && sticker.isInstalled() != installed) {
|
if (sticker != null && installed) {
|
||||||
account.getStickerStore().updateStickerPackInstalled(sticker.packId(), installed);
|
context.getJobExecutor().enqueueJob(new RetrieveStickerPackJob(stickerPackId, sticker.packKey()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,10 @@ public class SendHelper {
|
||||||
|
|
||||||
public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) {
|
public SendMessageResult sendSyncMessage(SignalServiceSyncMessage message) {
|
||||||
var messageSender = dependencies.getMessageSender();
|
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 {
|
try {
|
||||||
return messageSender.sendSyncMessage(message, context.getUnidentifiedAccessHelper().getAccessForSync());
|
return messageSender.sendSyncMessage(message, context.getUnidentifiedAccessHelper().getAccessForSync());
|
||||||
} catch (UnregisteredUserException e) {
|
} catch (UnregisteredUserException e) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.asamk.signal.manager.api.StickerPackId;
|
||||||
import org.asamk.signal.manager.internal.SignalDependencies;
|
import org.asamk.signal.manager.internal.SignalDependencies;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
import org.asamk.signal.manager.storage.stickerPacks.JsonStickerPack;
|
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.asamk.signal.manager.util.IOUtils;
|
||||||
import org.signal.libsignal.protocol.InvalidMessageException;
|
import org.signal.libsignal.protocol.InvalidMessageException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -28,15 +29,33 @@ public class StickerHelper {
|
||||||
this.context = context;
|
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(
|
public JsonStickerPack getOrRetrieveStickerPack(
|
||||||
StickerPackId packId, byte[] packKey
|
StickerPackId packId, byte[] packKey
|
||||||
) throws InvalidStickerException {
|
) throws InvalidStickerException {
|
||||||
if (!context.getStickerPackStore().existsStickerPack(packId)) {
|
try {
|
||||||
try {
|
retrieveStickerPack(packId, packKey);
|
||||||
retrieveStickerPack(packId, packKey);
|
} catch (InvalidMessageException | IOException e) {
|
||||||
} catch (InvalidMessageException | IOException e) {
|
throw new InvalidStickerException("Failed to retrieve sticker pack");
|
||||||
throw new InvalidStickerException("Failed to retrieve sticker pack");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
final JsonStickerPack manifest;
|
final JsonStickerPack manifest;
|
||||||
try {
|
try {
|
||||||
|
@ -48,6 +67,10 @@ public class StickerHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void retrieveStickerPack(StickerPackId packId, byte[] packKey) throws InvalidMessageException, IOException {
|
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()));
|
logger.debug("Retrieving sticker pack {}.", Hex.toStringCondensed(packId.serialize()));
|
||||||
final var messageReceiver = dependencies.getMessageReceiver();
|
final var messageReceiver = dependencies.getMessageReceiver();
|
||||||
final var manifest = messageReceiver.retrieveStickerManifest(packId.serialize(), packKey);
|
final var manifest = messageReceiver.retrieveStickerManifest(packId.serialize(), packKey);
|
||||||
|
|
|
@ -6,9 +6,11 @@ import org.asamk.signal.manager.api.TrustLevel;
|
||||||
import org.asamk.signal.manager.storage.SignalAccount;
|
import org.asamk.signal.manager.storage.SignalAccount;
|
||||||
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
|
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
|
||||||
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
|
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.AttachmentUtils;
|
||||||
import org.asamk.signal.manager.util.IOUtils;
|
import org.asamk.signal.manager.util.IOUtils;
|
||||||
import org.asamk.signal.manager.util.MimeUtils;
|
import org.asamk.signal.manager.util.MimeUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.signal.libsignal.protocol.IdentityKey;
|
import org.signal.libsignal.protocol.IdentityKey;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.KeysMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
|
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.messages.multidevice.VerifiedMessage;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||||
|
@ -40,6 +43,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class SyncHelper {
|
public class SyncHelper {
|
||||||
|
|
||||||
|
@ -222,6 +226,22 @@ public class SyncHelper {
|
||||||
context.getSendHelper().sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage));
|
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() {
|
public void sendConfigurationMessage() {
|
||||||
final var config = account.getConfigurationStore();
|
final var config = account.getConfigurationStore();
|
||||||
var configurationMessage = new ConfigurationMessage(Optional.ofNullable(config.getReadReceipts()),
|
var configurationMessage = new ConfigurationMessage(Optional.ofNullable(config.getReadReceipts()),
|
||||||
|
|
|
@ -72,6 +72,7 @@ import org.asamk.signal.manager.util.AttachmentUtils;
|
||||||
import org.asamk.signal.manager.util.KeyUtils;
|
import org.asamk.signal.manager.util.KeyUtils;
|
||||||
import org.asamk.signal.manager.util.MimeUtils;
|
import org.asamk.signal.manager.util.MimeUtils;
|
||||||
import org.asamk.signal.manager.util.StickerUtils;
|
import org.asamk.signal.manager.util.StickerUtils;
|
||||||
|
import org.signal.libsignal.protocol.InvalidMessageException;
|
||||||
import org.signal.libsignal.usernames.BaseUsernameException;
|
import org.signal.libsignal.usernames.BaseUsernameException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -891,10 +892,25 @@ public class ManagerImpl implements Manager {
|
||||||
|
|
||||||
var sticker = new StickerPack(packId, packKey);
|
var sticker = new StickerPack(packId, packKey);
|
||||||
account.getStickerStore().addStickerPack(sticker);
|
account.getStickerStore().addStickerPack(sticker);
|
||||||
|
context.getSyncHelper().sendStickerOperationsMessage(List.of(sticker), List.of());
|
||||||
|
|
||||||
return new StickerPackUrl(packId, packKey);
|
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
|
@Override
|
||||||
public List<org.asamk.signal.manager.api.StickerPack> getStickerPacks() {
|
public List<org.asamk.signal.manager.api.StickerPack> getStickerPacks() {
|
||||||
final var stickerPackStore = context.getStickerPackStore();
|
final var stickerPackStore = context.getStickerPackStore();
|
||||||
|
|
|
@ -23,10 +23,6 @@ public class RetrieveStickerPackJob implements Job {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(Context context) {
|
public void run(Context context) {
|
||||||
if (context.getStickerPackStore().existsStickerPack(packId)) {
|
|
||||||
logger.debug("Sticker pack {} already downloaded.", Hex.toStringCondensed(packId.serialize()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
context.getStickerHelper().retrieveStickerPack(packId, packKey);
|
context.getStickerHelper().retrieveStickerPack(packId, packKey);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class StickerStore {
|
public class StickerStore {
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ public class StickerStore {
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<StickerPack> getStickerPacks() {
|
public List<StickerPack> getStickerPacks() {
|
||||||
final var sql = (
|
final var sql = (
|
||||||
"""
|
"""
|
||||||
SELECT s._id, s.pack_id, s.pack_key, s.installed
|
SELECT s._id, s.pack_id, s.pack_key, s.installed
|
||||||
|
|
|
@ -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.
|
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
|
=== getAttachment
|
||||||
|
|
||||||
Gets the raw data for a specified attachment.
|
Gets the raw data for a specified attachment.
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ public class Commands {
|
||||||
addCommand(new FinishLinkCommand());
|
addCommand(new FinishLinkCommand());
|
||||||
addCommand(new GetAttachmentCommand());
|
addCommand(new GetAttachmentCommand());
|
||||||
addCommand(new GetUserStatusCommand());
|
addCommand(new GetUserStatusCommand());
|
||||||
|
addCommand(new AddStickerPackCommand());
|
||||||
addCommand(new JoinGroupCommand());
|
addCommand(new JoinGroupCommand());
|
||||||
addCommand(new JsonRpcDispatcherCommand());
|
addCommand(new JsonRpcDispatcherCommand());
|
||||||
addCommand(new LinkCommand());
|
addCommand(new LinkCommand());
|
||||||
|
|
|
@ -481,6 +481,11 @@ public class DbusManagerImpl implements Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installStickerPack(final StickerPackUrl url) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<StickerPack> getStickerPacks() {
|
public List<StickerPack> getStickerPacks() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue