diff --git a/client/src/cli.rs b/client/src/cli.rs index 6a8dd3613d..0b513268ea 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -164,6 +164,9 @@ pub enum CliCommands { #[arg(long)] quote_mention: Vec, + #[arg(long)] + quote_attachment: Vec, + #[arg(long)] sticker: Option, diff --git a/client/src/jsonrpc.rs b/client/src/jsonrpc.rs index cd25730532..e3031bf39a 100644 --- a/client/src/jsonrpc.rs +++ b/client/src/jsonrpc.rs @@ -143,6 +143,7 @@ pub trait Rpc { #[allow(non_snake_case)] quoteAuthor: Option, #[allow(non_snake_case)] quoteMessage: Option, #[allow(non_snake_case)] quoteMention: Vec, + #[allow(non_snake_case)] quoteAttachment: Vec, sticker: Option, #[allow(non_snake_case)] storyTimestamp: Option, #[allow(non_snake_case)] storyAuthor: Option, diff --git a/client/src/main.rs b/client/src/main.rs index c840fd5ea9..958d9d9fd0 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -140,6 +140,7 @@ async fn handle_command( quote_author, quote_message, quote_mention, + quote_attachment, sticker, story_timestamp, story_author, @@ -158,6 +159,7 @@ async fn handle_command( quote_author, quote_message, quote_mention, + quote_attachment, sticker, story_timestamp, story_author, diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 75d452539c..6aec194b1a 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -544,7 +544,8 @@ { "name":"org.asamk.Signal", "allDeclaredMethods":true, - "allDeclaredClasses":true + "allDeclaredClasses":true, + "methods":[{"name":"getSelfNumber","parameterTypes":[] }, {"name":"sendGroupMessageReaction","parameterTypes":["java.lang.String","boolean","java.lang.String","long","byte[]"] }, {"name":"sendMessage","parameterTypes":["java.lang.String","java.util.List","java.lang.String"] }, {"name":"sendMessageReaction","parameterTypes":["java.lang.String","boolean","java.lang.String","long","java.util.List"] }] }, { "name":"org.asamk.Signal$Configuration", @@ -1747,7 +1748,8 @@ { "name":"org.freedesktop.dbus.interfaces.Introspectable", "allDeclaredMethods":true, - "allDeclaredClasses":true + "allDeclaredClasses":true, + "methods":[{"name":"Introspect","parameterTypes":[] }] }, { "name":"org.freedesktop.dbus.interfaces.Peer", 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 ac9e4999b6..48277e6dc9 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 @@ -21,8 +21,12 @@ public record Quote( RecipientIdentifier.Single author, String message, List mentions, - List textStyles - ) {} + List textStyles, + List attachments + ) { + + public record Attachment(String contentType, String filename, String preview) {} + } public record Sticker(byte[] packId, int stickerId) {} diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index d1e96026a4..bfb52ebfd1 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -627,12 +627,19 @@ private void applyMessage( } if (message.quote().isPresent()) { final var quote = message.quote().get(); + final var quotedAttachments = new ArrayList(); + for (final var a : quote.attachments()) { + final var quotedAttachment = new SignalServiceDataMessage.Quote.QuotedAttachment(a.contentType(), + a.filename(), + a.preview() == null ? null : context.getAttachmentHelper().uploadAttachment(a.preview())); + quotedAttachments.add(quotedAttachment); + } messageBuilder.withQuote(new SignalServiceDataMessage.Quote(quote.timestamp(), context.getRecipientHelper() .resolveSignalServiceAddress(context.getRecipientHelper().resolveRecipient(quote.author())) .getServiceId(), quote.message(), - List.of(), + quotedAttachments, resolveMentions(quote.mentions()), SignalServiceDataMessage.Quote.Type.NORMAL, quote.textStyles().stream().map(TextStyle::toBodyRange).toList())); diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 56a2625308..1c3d91baa3 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -78,6 +78,9 @@ public void attachToSubparser(final Subparser subparser) { subparser.addArgument("--quote-mention") .nargs("*") .help("Quote with mention of another group member (syntax: start:length:recipientNumber)"); + subparser.addArgument("--quote-attachment") + .nargs("*") + .help("Specify the attachments of the original message (syntax: contentType[:filename[:previewFile]], e.g. 'audio/aac' or 'image/png:test.png:/tmp/preview.jpg'."); subparser.addArgument("--quote-text-style") .nargs("*") .help("Quote with style parts of the message text (syntax: start:length:STYLE)"); @@ -145,19 +148,19 @@ public void handleCommand( messageText = ""; } - List attachments = ns.getList("attachment"); + var attachments = ns.getList("attachment"); if (attachments == null) { attachments = List.of(); } final var selfNumber = m.getSelfNumber(); - List mentionStrings = ns.getList("mention"); + final var mentionStrings = ns.getList("mention"); final var mentions = mentionStrings == null ? List.of() : parseMentions(selfNumber, mentionStrings); - List textStyleStrings = ns.getList("text-style"); + final var textStyleStrings = ns.getList("text-style"); final var textStyles = textStyleStrings == null ? List.of() : parseTextStyles(textStyleStrings); final Message.Quote quote; @@ -165,29 +168,34 @@ public void handleCommand( if (quoteTimestamp != null) { final var quoteAuthor = ns.getString("quote-author"); final var quoteMessage = ns.getString("quote-message"); - List quoteMentionStrings = ns.getList("quote-mention"); + final var quoteMentionStrings = ns.getList("quote-mention"); final var quoteMentions = quoteMentionStrings == null ? List.of() : parseMentions(selfNumber, quoteMentionStrings); - List quoteTextStyleStrings = ns.getList("quote-text-style"); + final var quoteTextStyleStrings = ns.getList("quote-text-style"); + final var quoteAttachmentStrings = ns.getList("quote-attachment"); final var quoteTextStyles = quoteTextStyleStrings == null ? List.of() : parseTextStyles(quoteTextStyleStrings); + final var quoteAttachments = quoteAttachmentStrings == null + ? List.of() + : parseQuoteAttachments(quoteAttachmentStrings); quote = new Message.Quote(quoteTimestamp, CommandUtil.getSingleRecipientIdentifier(quoteAuthor, selfNumber), quoteMessage == null ? "" : quoteMessage, quoteMentions, - quoteTextStyles); + quoteTextStyles, + quoteAttachments); } else { quote = null; } final List previews; - String previewUrl = ns.getString("preview-url"); + final var 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"); + final var previewTitle = ns.getString("preview-title"); + final var previewDescription = ns.getString("preview-description"); + final var previewImage = ns.getString("preview-image"); previews = List.of(new Message.Preview(previewUrl, Optional.ofNullable(previewTitle).orElse(""), Optional.ofNullable(previewDescription).orElse(""), @@ -241,9 +249,8 @@ public void handleCommand( private List parseMentions( final String selfNumber, final List mentionStrings ) throws UserErrorException { - List mentions; - final Pattern mentionPattern = Pattern.compile("(\\d+):(\\d+):(.+)"); - mentions = new ArrayList<>(); + final var mentionPattern = Pattern.compile("(\\d+):(\\d+):(.+)"); + final var mentions = new ArrayList(); for (final var mention : mentionStrings) { final var matcher = mentionPattern.matcher(mention); if (!matcher.matches()) { @@ -261,9 +268,8 @@ private List parseMentions( private List parseTextStyles( final List textStylesStrings ) throws UserErrorException { - List textStyles; - final Pattern textStylePattern = Pattern.compile("(\\d+):(\\d+):(.+)"); - textStyles = new ArrayList<>(); + final var textStylePattern = Pattern.compile("(\\d+):(\\d+):(.+)"); + final var textStyles = new ArrayList(); for (final var textStyle : textStylesStrings) { final var matcher = textStylePattern.matcher(textStyle); if (!matcher.matches()) { @@ -283,7 +289,7 @@ private List parseTextStyles( } private Message.Sticker parseSticker(final String stickerString) throws UserErrorException { - final Pattern stickerPattern = Pattern.compile("([\\da-f]+):(\\d+)"); + final var stickerPattern = Pattern.compile("([\\da-f]+):(\\d+)"); final var matcher = stickerPattern.matcher(stickerString); if (!matcher.matches() || matcher.group(1).length() % 2 != 0) { throw new UserErrorException("Invalid sticker syntax (" @@ -292,4 +298,21 @@ private Message.Sticker parseSticker(final String stickerString) throws UserErro } return new Message.Sticker(Hex.toByteArray(matcher.group(1)), Integer.parseInt(matcher.group(2))); } + + private List parseQuoteAttachments( + final List attachmentStrings + ) throws UserErrorException { + final var attachmentPattern = Pattern.compile("([^:]+)(:([^:]+)(:(.+))?)?"); + final var attachments = new ArrayList(); + for (final var attachment : attachmentStrings) { + final var matcher = attachmentPattern.matcher(attachment); + if (!matcher.matches()) { + throw new UserErrorException("Invalid attachment syntax (" + + attachment + + ") expected 'contentType[:filename[:previewFile]]'"); + } + attachments.add(new Message.Quote.Attachment(matcher.group(1), matcher.group(3), matcher.group(5))); + } + return attachments; + } }