From e3752e733adaad6f04c7d525e1b465a4b46474c7 Mon Sep 17 00:00:00 2001 From: AsamK Date: Sun, 22 Aug 2021 14:25:48 +0200 Subject: [PATCH] Implement sendReceipt command Fixes #305 --- .../asamk/signal/manager/HandleAction.java | 3 +- .../org/asamk/signal/manager/Manager.java | 26 ++++++++- man/signal-cli.1.adoc | 13 +++++ .../org/asamk/signal/commands/Commands.java | 1 + .../signal/commands/SendReceiptCommand.java | 56 +++++++++++++++++++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/asamk/signal/commands/SendReceiptCommand.java diff --git a/lib/src/main/java/org/asamk/signal/manager/HandleAction.java b/lib/src/main/java/org/asamk/signal/manager/HandleAction.java index 08f51590..2396df06 100644 --- a/lib/src/main/java/org/asamk/signal/manager/HandleAction.java +++ b/lib/src/main/java/org/asamk/signal/manager/HandleAction.java @@ -4,6 +4,7 @@ import org.asamk.signal.manager.groups.GroupIdV1; import org.asamk.signal.manager.storage.recipients.RecipientId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import java.util.List; import java.util.Objects; interface HandleAction { @@ -23,7 +24,7 @@ class SendReceiptAction implements HandleAction { @Override public void execute(Manager m) throws Throwable { - m.sendReceipt(address, timestamp); + m.sendDeliveryReceipt(address, List.of(timestamp)); } @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 8aa7ed18..60a196dc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -1191,11 +1191,31 @@ public class Manager implements Closeable { return sendHelper.sendGroupMessage(messageBuilder.build(), Set.of(resolveRecipient(recipient))); } - void sendReceipt( - SignalServiceAddress remoteAddress, long messageId + public void sendReadReceipt( + String sender, List messageIds + ) throws IOException, UntrustedIdentityException, InvalidNumberException { + var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, + messageIds, + System.currentTimeMillis()); + + sendHelper.sendReceiptMessage(receiptMessage, canonicalizeAndResolveRecipient(sender)); + } + + public void sendViewedReceipt( + String sender, List messageIds + ) throws IOException, UntrustedIdentityException, InvalidNumberException { + var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.VIEWED, + messageIds, + System.currentTimeMillis()); + + sendHelper.sendReceiptMessage(receiptMessage, canonicalizeAndResolveRecipient(sender)); + } + + void sendDeliveryReceipt( + SignalServiceAddress remoteAddress, List messageIds ) throws IOException, UntrustedIdentityException { var receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.DELIVERY, - List.of(messageId), + messageIds, System.currentTimeMillis()); sendHelper.sendReceiptMessage(receiptMessage, resolveRecipient(remoteAddress)); diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 3df9198b..de554c02 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -199,6 +199,19 @@ Specify the timestamp of the message to which to react. *-r*, *--remove*:: Remove a reaction. +=== sendReceipt + +Send a read or viewed receipt to a previously received message. + +RECIPIENT:: +Specify the sender’s phone number. + +*-t* TIMESTAMP, *--target-timestamp* TIMESTAMP:: +Specify the timestamp of the message to which to react. + +*--type* TYPE:: +Specify the receipt type, either `read` (the default) or `viewed`. + === sendTyping Send typing message to trigger a typing indicator for the recipient. diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index d46d6b8b..90e8e114 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -30,6 +30,7 @@ public class Commands { addCommand(new SendCommand()); addCommand(new SendContactsCommand()); addCommand(new SendReactionCommand()); + addCommand(new SendReceiptCommand()); addCommand(new SendSyncRequestCommand()); addCommand(new SendTypingCommand()); addCommand(new SetPinCommand()); diff --git a/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java new file mode 100644 index 00000000..74f48112 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/SendReceiptCommand.java @@ -0,0 +1,56 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.signal.OutputWriter; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UserErrorException; +import org.asamk.signal.manager.Manager; +import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.util.InvalidNumberException; + +import java.io.IOException; + +public class SendReceiptCommand implements JsonRpcLocalCommand { + + @Override + public String getName() { + return "sendReceipt"; + } + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.help("Send a read or viewed receipt to a previously received message."); + subparser.addArgument("recipient").help("Specify the sender's phone number.").required(true); + subparser.addArgument("-t", "--target-timestamp") + .type(long.class) + .nargs("+") + .help("Specify the timestamp of the messages for which a receipt should be sent."); + subparser.addArgument("--type").help("Specify the receipt type.").choices("read", "viewed").setDefault("read"); + } + + @Override + public void handleCommand( + final Namespace ns, final Manager m, final OutputWriter outputWriter + ) throws CommandException { + final var recipient = ns.getString("recipient"); + + final var targetTimestamps = ns.getList("target-timestamp"); + final var type = ns.getString("type"); + + try { + if ("read".equals(type)) { + m.sendReadReceipt(recipient, targetTimestamps); + } else if ("viewed".equals(type)) { + m.sendViewedReceipt(recipient, targetTimestamps); + } else { + throw new UserErrorException("Unknown receipt type: " + type); + } + } catch (IOException | UntrustedIdentityException e) { + throw new UserErrorException("Failed to send message: " + e.getMessage()); + } catch (InvalidNumberException e) { + throw new UserErrorException("Invalid number: " + e.getMessage()); + } + } +} -- 2.50.1