X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/e5a67d6ce1312fe118e99b8bc8fb2f55ed1dbcf2..f2005593ecefd37c7e1666c2dc0c71b259271af0:/src/main/java/org/asamk/signal/commands/SendCommand.java diff --git a/src/main/java/org/asamk/signal/commands/SendCommand.java b/src/main/java/org/asamk/signal/commands/SendCommand.java index 56a26253..9784171a 100644 --- a/src/main/java/org/asamk/signal/commands/SendCommand.java +++ b/src/main/java/org/asamk/signal/commands/SendCommand.java @@ -35,7 +35,7 @@ import static org.asamk.signal.util.SendMessageResultUtils.outputResult; public class SendCommand implements JsonRpcLocalCommand { - private final static Logger logger = LoggerFactory.getLogger(SendCommand.class); + private static final Logger logger = LoggerFactory.getLogger(SendCommand.class); @Override public String getName() { @@ -47,15 +47,20 @@ public class SendCommand implements JsonRpcLocalCommand { subparser.help("Send a message to another user or group."); subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*"); subparser.addArgument("-g", "--group-id", "--group").help("Specify the recipient group ID.").nargs("*"); + subparser.addArgument("-u", "--username").help("Specify the recipient username or username link.").nargs("*"); subparser.addArgument("--note-to-self") .help("Send the message to self without notification.") .action(Arguments.storeTrue()); + subparser.addArgument("--notify-self") + .help("If self is part of recipients/groups send a normal message, not a sync message.") + .action(Arguments.storeTrue()); var mut = subparser.addMutuallyExclusiveGroup(); mut.addArgument("-m", "--message").help("Specify the message to be sent."); mut.addArgument("--message-from-stdin") .action(Arguments.storeTrue()) .help("Read the message from standard input."); + subparser.addArgument("-a", "--attachment") .nargs("*") .help("Add an attachment. " @@ -66,10 +71,12 @@ public class SendCommand implements JsonRpcLocalCommand { .action(Arguments.storeTrue()); subparser.addArgument("--mention") .nargs("*") - .help("Mention another group member (syntax: start:length:recipientNumber)"); + .help("Mention another group member (syntax: start:length:recipientNumber). " + + "Unit of start and length is UTF-16 code units, NOT Unicode code points."); subparser.addArgument("--text-style") .nargs("*") - .help("Style parts of the message text (syntax: start:length:STYLE)"); + .help("Style parts of the message text (syntax: start:length:STYLE). " + + "Unit of start and length is UTF-16 code units, NOT Unicode code points."); subparser.addArgument("--quote-timestamp") .type(long.class) .help("Specify the timestamp of a previous message with the recipient or group to add a quote to the new message."); @@ -78,6 +85,9 @@ public class SendCommand implements JsonRpcLocalCommand { 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)"); @@ -98,16 +108,21 @@ public class SendCommand implements JsonRpcLocalCommand { @Override public void handleCommand( - final Namespace ns, final Manager m, final OutputWriter outputWriter + final Namespace ns, + final Manager m, + final OutputWriter outputWriter ) throws CommandException { + final var notifySelf = Boolean.TRUE.equals(ns.getBoolean("notify-self")); final var isNoteToSelf = Boolean.TRUE.equals(ns.getBoolean("note-to-self")); final var recipientStrings = ns.getList("recipient"); final var groupIdStrings = ns.getList("group-id"); + final var usernameStrings = ns.getList("username"); final var recipientIdentifiers = CommandUtil.getRecipientIdentifiers(m, isNoteToSelf, recipientStrings, - groupIdStrings); + groupIdStrings, + usernameStrings); final var isEndSession = Boolean.TRUE.equals(ns.getBoolean("end-session")); if (isEndSession) { @@ -145,19 +160,19 @@ public class SendCommand implements JsonRpcLocalCommand { 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 +180,34 @@ public class SendCommand implements JsonRpcLocalCommand { 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(""), @@ -224,7 +244,7 @@ public class SendCommand implements JsonRpcLocalCommand { textStyles); var results = editTimestamp != null ? m.sendEditMessage(message, recipientIdentifiers, editTimestamp) - : m.sendMessage(message, recipientIdentifiers); + : m.sendMessage(message, recipientIdentifiers, notifySelf); outputResult(outputWriter, results); } catch (AttachmentInvalidException | IOException e) { throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() @@ -239,11 +259,11 @@ public class SendCommand implements JsonRpcLocalCommand { } private List parseMentions( - final String selfNumber, final List mentionStrings + 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 +281,8 @@ public class SendCommand implements JsonRpcLocalCommand { 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 +302,7 @@ public class SendCommand implements JsonRpcLocalCommand { } 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 +311,21 @@ public class SendCommand implements JsonRpcLocalCommand { } 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; + } }