From: AsamK Date: Wed, 25 May 2022 21:23:33 +0000 (+0200) Subject: Implement sending link previews X-Git-Tag: v0.10.7~8 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/b178c7c67aea7bf334cbf0d54a4666af0a65b5d9 Implement sending link previews Fixes #276 --- diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 503f17f5..62612d44 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -755,6 +755,19 @@ {"name":"receipt","parameterTypes":[] } ] }, +{ + "name":"org.asamk.signal.json.JsonPreview", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"date","parameterTypes":[] }, + {"name":"description","parameterTypes":[] }, + {"name":"image","parameterTypes":[] }, + {"name":"title","parameterTypes":[] }, + {"name":"url","parameterTypes":[] } + ] +}, { "name":"org.asamk.signal.json.JsonQuote", "allDeclaredFields":true, @@ -2721,7 +2734,8 @@ {"name":"bitField0_"}, {"name":"bodyRanges_"}, {"name":"id_"}, - {"name":"text_"} + {"name":"text_"}, + {"name":"type_"} ] }, { diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 40bd7240..b5dcb5e0 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -65,6 +65,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; +import org.whispersystems.signalservice.api.messages.SignalServicePreview; import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; import org.whispersystems.signalservice.api.push.ACI; @@ -551,9 +552,8 @@ class ManagerImpl implements Manager { final SignalServiceDataMessage.Builder messageBuilder, final Message message ) throws AttachmentInvalidException, IOException, UnregisteredRecipientException, InvalidStickerException { messageBuilder.withBody(message.messageText()); - final var attachments = message.attachments(); - if (attachments != null) { - messageBuilder.withAttachments(context.getAttachmentHelper().uploadAttachments(attachments)); + if (message.attachments().size() > 0) { + messageBuilder.withAttachments(context.getAttachmentHelper().uploadAttachments(message.attachments())); } if (message.mentions().size() > 0) { messageBuilder.withMentions(resolveMentions(message.mentions())); @@ -592,6 +592,19 @@ class ManagerImpl implements Manager { manifestSticker.emoji(), AttachmentUtils.createAttachmentStream(streamDetails, Optional.empty()))); } + if (message.previews().size() > 0) { + final var previews = new ArrayList(message.previews().size()); + for (final var p : message.previews()) { + final var image = p.image().isPresent() ? context.getAttachmentHelper() + .uploadAttachment(p.image().get()) : null; + previews.add(new SignalServicePreview(p.url(), + p.title(), + p.description(), + 0, + Optional.ofNullable(image))); + } + messageBuilder.withPreviews(previews); + } } private ArrayList resolveMentions(final List mentionList) throws UnregisteredRecipientException { diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Message.java b/lib/src/main/java/org/asamk/signal/manager/api/Message.java index 3df110cd..1e76faea 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Message.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Message.java @@ -8,7 +8,8 @@ public record Message( List attachments, List mentions, Optional quote, - Optional sticker + Optional sticker, + List previews ) { public record Mention(RecipientIdentifier.Single recipient, int start, int length) {} @@ -16,4 +17,6 @@ public record Message( public record Quote(long timestamp, RecipientIdentifier.Single author, String message, List mentions) {} public record Sticker(byte[] packId, int stickerId) {} + + public record Preview(String url, String title, String description, Optional image) {} } diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 9b54635e..1ccb067c 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -253,6 +253,20 @@ Specify the message of the original message. *--quote-mention*:: Specify the mentions of the original message (same format as `--mention`). +*--preview-url*:: +Specify the url for the link preview. +The same url must also appear in the message body, otherwise the preview won't be +displayed by the apps. + +*--preview-title*:: +Specify the title for the link preview (mandatory). + +*--preview-description*:: +Specify the description for the link preview (optional). + +*--preview-image*:: +Specify the image file for the link preview (optional). + *-e*, *--end-session*:: Clear session state and send end session message. diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 5e81047a..4ad75e95 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -72,6 +72,11 @@ public class SendCommand implements JsonRpcLocalCommand { .nargs("*") .help("Quote with mention of another group member (syntax: start:length:recipientNumber)"); subparser.addArgument("--sticker").help("Send a sticker (syntax: stickerPackId:stickerId)"); + subparser.addArgument("--preview-url") + .help("Specify the url for the link preview (the same url must also appear in the message body)."); + subparser.addArgument("--preview-title").help("Specify the title for the link preview (mandatory)."); + subparser.addArgument("--preview-description").help("Specify the description for the link preview (optional)."); + subparser.addArgument("--preview-image").help("Specify the image file for the link preview (optional)."); } @Override @@ -146,12 +151,27 @@ public class SendCommand implements JsonRpcLocalCommand { quote = null; } + final List previews; + String previewUrl = ns.getString("preview-url"); + if (previewUrl != null) { + String previewTitle = ns.getString("preview-title"); + String previewDescription = ns.getString("preview-description"); + String previewImage = ns.getString("preview-image"); + previews = List.of(new Message.Preview(previewUrl, + Optional.ofNullable(previewTitle).orElse(""), + Optional.ofNullable(previewDescription).orElse(""), + Optional.ofNullable(previewImage))); + } else { + previews = List.of(); + } + try { var results = m.sendMessage(new Message(messageText == null ? "" : messageText, attachments, mentions, Optional.ofNullable(quote), - Optional.ofNullable(sticker)), recipientIdentifiers); + Optional.ofNullable(sticker), + previews), recipientIdentifiers); outputResult(outputWriter, results); } catch (AttachmentInvalidException | IOException e) { throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 18b53eea..ce35ef14 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -216,7 +216,8 @@ public class DbusSignalImpl implements Signal { attachments, List.of(), Optional.empty(), - Optional.empty()), + Optional.empty(), + List.of()), getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() .map(RecipientIdentifier.class::cast) .collect(Collectors.toSet())); @@ -367,7 +368,8 @@ public class DbusSignalImpl implements Signal { attachments, List.of(), Optional.empty(), - Optional.empty()), Set.of(RecipientIdentifier.NoteToSelf.INSTANCE)); + Optional.empty(), + List.of()), Set.of(RecipientIdentifier.NoteToSelf.INSTANCE)); checkSendMessageResults(results); return results.timestamp(); } catch (AttachmentInvalidException e) { @@ -408,7 +410,8 @@ public class DbusSignalImpl implements Signal { attachments, List.of(), Optional.empty(), - Optional.empty()), Set.of(getGroupRecipientIdentifier(groupId))); + Optional.empty(), + List.of()), Set.of(getGroupRecipientIdentifier(groupId))); checkSendMessageResults(results); return results.timestamp(); } catch (IOException | InvalidStickerException e) { diff --git a/src/main/java/org/asamk/signal/json/JsonDataMessage.java b/src/main/java/org/asamk/signal/json/JsonDataMessage.java index 46328466..74c07cd7 100644 --- a/src/main/java/org/asamk/signal/json/JsonDataMessage.java +++ b/src/main/java/org/asamk/signal/json/JsonDataMessage.java @@ -15,6 +15,7 @@ record JsonDataMessage( @JsonInclude(JsonInclude.Include.NON_NULL) JsonQuote quote, @JsonInclude(JsonInclude.Include.NON_NULL) JsonPayment payment, @JsonInclude(JsonInclude.Include.NON_NULL) List mentions, + @JsonInclude(JsonInclude.Include.NON_NULL) List previews, @JsonInclude(JsonInclude.Include.NON_NULL) List attachments, @JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker, @JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete, @@ -36,6 +37,10 @@ record JsonDataMessage( .stream() .map(JsonMention::from) .toList() : null; + final var previews = dataMessage.previews().size() > 0 ? dataMessage.previews() + .stream() + .map(JsonPreview::from) + .toList() : null; final var remoteDelete = dataMessage.remoteDeleteId().isPresent() ? new JsonRemoteDelete(dataMessage.remoteDeleteId().get()) : null; @@ -57,6 +62,7 @@ record JsonDataMessage( quote, payment, mentions, + previews, attachments, sticker, remoteDelete, diff --git a/src/main/java/org/asamk/signal/json/JsonPreview.java b/src/main/java/org/asamk/signal/json/JsonPreview.java new file mode 100644 index 00000000..c5e0d49e --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonPreview.java @@ -0,0 +1,13 @@ +package org.asamk.signal.json; + +import org.asamk.signal.manager.api.MessageEnvelope; + +public record JsonPreview(String url, String title, String description, JsonAttachment image) { + + static JsonPreview from(MessageEnvelope.Data.Preview preview) { + return new JsonPreview(preview.url(), + preview.title(), + preview.description(), + preview.image().map(JsonAttachment::from).orElse(null)); + } +}