Add sticker pack url to list output

This commit is contained in:
AsamK 2022-01-03 18:50:27 +01:00
parent 99eef05084
commit 8a5f98dac6
10 changed files with 133 additions and 44 deletions

View file

@ -444,7 +444,8 @@
{"name":"installed","parameterTypes":[] }, {"name":"installed","parameterTypes":[] },
{"name":"packId","parameterTypes":[] }, {"name":"packId","parameterTypes":[] },
{"name":"stickers","parameterTypes":[] }, {"name":"stickers","parameterTypes":[] },
{"name":"title","parameterTypes":[] } {"name":"title","parameterTypes":[] },
{"name":"url","parameterTypes":[] }
]} ]}
, ,
{ {

View file

@ -1,18 +1,16 @@
package org.asamk.signal.manager; package org.asamk.signal.manager;
import org.asamk.signal.manager.api.InvalidDeviceLinkException; import org.asamk.signal.manager.api.InvalidDeviceLinkException;
import org.asamk.signal.manager.util.Utils;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.Curve; import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.ecc.ECPublicKey;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import static org.whispersystems.signalservice.internal.util.Util.isEmpty; import static org.whispersystems.signalservice.internal.util.Util.isEmpty;
@ -24,7 +22,7 @@ public record DeviceLinkInfo(String deviceIdentifier, ECPublicKey deviceKey) {
throw new RuntimeException("Invalid device link uri"); throw new RuntimeException("Invalid device link uri");
} }
var query = getQueryMap(rawQuery); var query = Utils.getQueryMap(rawQuery);
var deviceIdentifier = query.get("uuid"); var deviceIdentifier = query.get("uuid");
var publicKeyEncoded = query.get("pub_key"); var publicKeyEncoded = query.get("pub_key");
@ -48,18 +46,6 @@ public record DeviceLinkInfo(String deviceIdentifier, ECPublicKey deviceKey) {
return new DeviceLinkInfo(deviceIdentifier, deviceKey); return new DeviceLinkInfo(deviceIdentifier, deviceKey);
} }
private static Map<String, String> getQueryMap(String query) {
var params = query.split("&");
var map = new HashMap<String, String>();
for (var param : params) {
final var paramParts = param.split("=");
var name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8);
var value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8);
map.put(name, value);
}
return map;
}
public URI createDeviceLinkUri() { public URI createDeviceLinkUri() {
final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", "");
try { try {

View file

@ -14,6 +14,7 @@ import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.SendMessageResults;
import org.asamk.signal.manager.api.StickerPack; import org.asamk.signal.manager.api.StickerPack;
import org.asamk.signal.manager.api.StickerPackUrl;
import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UnregisteredRecipientException;
import org.asamk.signal.manager.api.UpdateGroup; import org.asamk.signal.manager.api.UpdateGroup;
@ -220,7 +221,7 @@ public interface Manager extends Closeable {
* @param path Path can be a path to a manifest.json file or to a zip file that contains a manifest.json file * @param path Path can be a path to a manifest.json file or to a zip file that contains a manifest.json file
* @return if successful, returns the URL to install the sticker pack in the signal app * @return if successful, returns the URL to install the sticker pack in the signal app
*/ */
URI uploadStickerPack(File path) throws IOException, StickerPackInvalidException; StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException;
List<StickerPack> getStickerPacks(); List<StickerPack> getStickerPacks();

View file

@ -31,6 +31,7 @@ import org.asamk.signal.manager.api.SendMessageResult;
import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.SendMessageResults;
import org.asamk.signal.manager.api.StickerPack; import org.asamk.signal.manager.api.StickerPack;
import org.asamk.signal.manager.api.StickerPackId; import org.asamk.signal.manager.api.StickerPackId;
import org.asamk.signal.manager.api.StickerPackUrl;
import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.api.UnregisteredRecipientException; import org.asamk.signal.manager.api.UnregisteredRecipientException;
import org.asamk.signal.manager.api.UpdateGroup; import org.asamk.signal.manager.api.UpdateGroup;
@ -70,9 +71,6 @@ import org.whispersystems.signalservice.internal.util.Util;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -658,7 +656,7 @@ public class ManagerImpl implements Manager {
} }
@Override @Override
public URI uploadStickerPack(File path) throws IOException, StickerPackInvalidException { public StickerPackUrl uploadStickerPack(File path) throws IOException, StickerPackInvalidException {
var manifest = StickerUtils.getSignalServiceStickerManifestUpload(path); var manifest = StickerUtils.getSignalServiceStickerManifestUpload(path);
var messageSender = dependencies.getMessageSender(); var messageSender = dependencies.getMessageSender();
@ -670,17 +668,7 @@ public class ManagerImpl implements Manager {
var sticker = new Sticker(packId, packKey); var sticker = new Sticker(packId, packKey);
account.getStickerStore().updateSticker(sticker); account.getStickerStore().updateSticker(sticker);
try { return new StickerPackUrl(packId, packKey);
return new URI("https",
"signal.art",
"/addstickers/",
"pack_id="
+ URLEncoder.encode(Hex.toStringCondensed(packId.serialize()), StandardCharsets.UTF_8)
+ "&pack_key="
+ URLEncoder.encode(Hex.toStringCondensed(packKey), StandardCharsets.UTF_8));
} catch (URISyntaxException e) {
throw new AssertionError(e);
}
} }
@Override @Override
@ -691,7 +679,7 @@ public class ManagerImpl implements Manager {
try { try {
final var manifest = stickerPackStore.retrieveManifest(pack.getPackId()); final var manifest = stickerPackStore.retrieveManifest(pack.getPackId());
return new StickerPack(pack.getPackId(), return new StickerPack(pack.getPackId(),
pack.getPackKey(), new StickerPackUrl(pack.getPackId(), pack.getPackKey()),
pack.isInstalled(), pack.isInstalled(),
manifest.title(), manifest.title(),
manifest.author(), manifest.author(),

View file

@ -5,7 +5,7 @@ import java.util.Optional;
public record StickerPack( public record StickerPack(
StickerPackId packId, StickerPackId packId,
byte[] packKey, StickerPackUrl url,
boolean installed, boolean installed,
String title, String title,
String author, String author,
@ -14,7 +14,7 @@ public record StickerPack(
) { ) {
public StickerPack(final StickerPackId packId, final byte[] packKey, final boolean installed) { public StickerPack(final StickerPackId packId, final byte[] packKey, final boolean installed) {
this(packId, packKey, installed, "", "", Optional.empty(), List.of()); this(packId, new StickerPackUrl(packId, packKey), installed, "", "", Optional.empty(), List.of());
} }
public record Sticker(int id, String emoji, String contentType) {} public record Sticker(int id, String emoji, String contentType) {}

View file

@ -0,0 +1,88 @@
package org.asamk.signal.manager.api;
import org.asamk.signal.manager.util.Utils;
import org.whispersystems.signalservice.internal.util.Hex;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import static org.whispersystems.signalservice.internal.util.Util.isEmpty;
public final class StickerPackUrl {
private final StickerPackId packId;
private final byte[] packKey;
/**
* @throws InvalidStickerPackLinkException If url cannot be parsed.
*/
public static StickerPackUrl fromUri(URI uri) throws InvalidStickerPackLinkException {
final var rawQuery = uri.getRawFragment();
if (isEmpty(rawQuery)) {
throw new RuntimeException("Invalid sticker pack uri");
}
var query = Utils.getQueryMap(rawQuery);
var packIdString = query.get("pack_id");
var packKeyString = query.get("pack_key");
if (isEmpty(packIdString) || isEmpty(packKeyString)) {
throw new InvalidStickerPackLinkException("Incomplete sticker pack uri");
}
StickerPackId packId;
try {
packId = StickerPackId.deserialize(Hex.fromStringCondensed(packIdString));
} catch (IOException e) {
throw new InvalidStickerPackLinkException("Invalid sticker pack", e);
}
final byte[] packKey;
try {
packKey = Hex.fromStringCondensed(packKeyString);
} catch (IOException e) {
throw new InvalidStickerPackLinkException("Invalid sticker pack uri", e);
}
return new StickerPackUrl(packId, packKey);
}
public StickerPackUrl(final StickerPackId packId, final byte[] packKey) {
this.packId = packId;
this.packKey = packKey;
}
public URI getUrl() {
try {
return new URI("https",
"signal.art",
"/addstickers/",
"pack_id="
+ URLEncoder.encode(Hex.toStringCondensed(packId.serialize()), StandardCharsets.UTF_8)
+ "&pack_key="
+ URLEncoder.encode(Hex.toStringCondensed(packKey), StandardCharsets.UTF_8));
} catch (URISyntaxException e) {
throw new AssertionError(e);
}
}
public StickerPackId getPackId() {
return packId;
}
public byte[] getPackKey() {
return packKey;
}
public final static class InvalidStickerPackLinkException extends Exception {
public InvalidStickerPackLinkException(String message) {
super(message);
}
public InvalidStickerPackLinkException(final String message, final Throwable cause) {
super(message, cause);
}
}
}

View file

@ -14,8 +14,12 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URLConnection; import java.net.URLConnection;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -106,4 +110,16 @@ public class Utils {
} }
}, leftStream.isParallel() || rightStream.isParallel()); }, leftStream.isParallel() || rightStream.isParallel());
} }
public static Map<String, String> getQueryMap(String query) {
var params = query.split("&");
var map = new HashMap<String, String>();
for (var param : params) {
final var paramParts = param.split("=");
var name = URLDecoder.decode(paramParts[0], StandardCharsets.UTF_8);
var value = URLDecoder.decode(paramParts[1], StandardCharsets.UTF_8);
map.put(name, value);
}
return map;
}
} }

View file

@ -35,21 +35,29 @@ public class ListStickerPacksCommand implements JsonRpcLocalCommand {
jsonWriter.write(jsonStickerPacks); jsonWriter.write(jsonStickerPacks);
} else if (outputWriter instanceof PlainTextWriter plainTextWriter) { } else if (outputWriter instanceof PlainTextWriter plainTextWriter) {
for (final var sticker : stickerPacks) { for (final var sticker : stickerPacks) {
plainTextWriter.println("Pack {}: “{}” by “{}” has {} stickers", plainTextWriter.println("Pack {}: “{}” by “{}” has {} stickers. {}",
Hex.toStringCondensed(sticker.packId().serialize()), Hex.toStringCondensed(sticker.packId().serialize()),
sticker.title(), sticker.title(),
sticker.author(), sticker.author(),
sticker.stickers().size()); sticker.stickers().size(),
sticker.url().getUrl());
} }
} }
} }
private record JsonStickerPack( private record JsonStickerPack(
String packId, boolean installed, String title, String author, JsonSticker cover, List<JsonSticker> stickers String packId,
String url,
boolean installed,
String title,
String author,
JsonSticker cover,
List<JsonSticker> stickers
) { ) {
JsonStickerPack(StickerPack stickerPack) { JsonStickerPack(StickerPack stickerPack) {
this(Hex.toStringCondensed(stickerPack.packId().serialize()), this(Hex.toStringCondensed(stickerPack.packId().serialize()),
stickerPack.url().getUrl().toString(),
stickerPack.installed(), stickerPack.installed(),
stickerPack.title(), stickerPack.title(),
stickerPack.author(), stickerPack.author(),

View file

@ -43,10 +43,10 @@ public class UploadStickerPackCommand implements JsonRpcLocalCommand {
try { try {
var url = m.uploadStickerPack(path); var url = m.uploadStickerPack(path);
if (outputWriter instanceof PlainTextWriter writer) { if (outputWriter instanceof PlainTextWriter writer) {
writer.println("{}", url); writer.println("{}", url.getUrl());
} else { } else {
final var writer = (JsonWriter) outputWriter; final var writer = (JsonWriter) outputWriter;
writer.write(Map.of("url", url)); writer.write(Map.of("url", url.getUrl()));
} }
} catch (IOException e) { } catch (IOException e) {
throw new IOErrorException("Upload error (maybe image size too large):" + e.getMessage(), e); throw new IOErrorException("Upload error (maybe image size too large):" + e.getMessage(), e);

View file

@ -19,6 +19,7 @@ import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults; import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.api.SendMessageResults; import org.asamk.signal.manager.api.SendMessageResults;
import org.asamk.signal.manager.api.StickerPack; import org.asamk.signal.manager.api.StickerPack;
import org.asamk.signal.manager.api.StickerPackUrl;
import org.asamk.signal.manager.api.TypingAction; import org.asamk.signal.manager.api.TypingAction;
import org.asamk.signal.manager.api.UpdateGroup; import org.asamk.signal.manager.api.UpdateGroup;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
@ -436,10 +437,10 @@ public class DbusManagerImpl implements Manager {
} }
@Override @Override
public URI uploadStickerPack(final File path) throws IOException, StickerPackInvalidException { public StickerPackUrl uploadStickerPack(final File path) throws IOException, StickerPackInvalidException {
try { try {
return new URI(signal.uploadStickerPack(path.getPath())); return StickerPackUrl.fromUri(new URI(signal.uploadStickerPack(path.getPath())));
} catch (URISyntaxException e) { } catch (URISyntaxException | StickerPackUrl.InvalidStickerPackLinkException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }