Implement sending link previews

Fixes #276
This commit is contained in:
AsamK 2022-05-25 23:23:33 +02:00
parent 0f701df91f
commit b178c7c67a
8 changed files with 95 additions and 9 deletions

View file

@ -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_"}
]
},
{

View file

@ -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<SignalServicePreview>(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<SignalServiceDataMessage.Mention> resolveMentions(final List<Message.Mention> mentionList) throws UnregisteredRecipientException {

View file

@ -8,7 +8,8 @@ public record Message(
List<String> attachments,
List<Mention> mentions,
Optional<Quote> quote,
Optional<Sticker> sticker
Optional<Sticker> sticker,
List<Preview> 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<Mention> mentions) {}
public record Sticker(byte[] packId, int stickerId) {}
public record Preview(String url, String title, String description, Optional<String> image) {}
}

View file

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

View file

@ -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<Message.Preview> 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()

View file

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

View file

@ -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<JsonMention> mentions,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonPreview> previews,
@JsonInclude(JsonInclude.Include.NON_NULL) List<JsonAttachment> 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,

View file

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