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 99099e92..cbdf2803 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -992,6 +992,22 @@ public class Manager implements Closeable { return sendSelfMessage(messageBuilder); } + public Pair> remoteDelete( + long targetSentTimestamp, List recipients + ) throws IOException, InvalidNumberException { + var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + return sendMessage(messageBuilder, getSignalServiceAddresses(recipients)); + } + + public Pair> remoteGroupDelete( + long targetSentTimestamp, GroupId groupId + ) throws IOException, NotAGroupMemberException, GroupNotFoundException { + var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); + final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); + return sendGroupMessage(messageBuilder, groupId); + } + public Pair> sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List recipients ) throws IOException, InvalidNumberException { diff --git a/src/main/java/org/asamk/Signal.java b/src/main/java/org/asamk/Signal.java index f8841da9..717d137a 100644 --- a/src/main/java/org/asamk/Signal.java +++ b/src/main/java/org/asamk/Signal.java @@ -21,6 +21,14 @@ public interface Signal extends DBusInterface { String message, List attachments, List recipients ) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UntrustedIdentity; + long remoteDelete( + long targetSentTimestamp, List recipients + ) throws Error.Failure, Error.InvalidNumber; + + long remoteGroupDelete( + long targetSentTimestamp, byte[] groupId + ) throws Error.Failure, Error.GroupNotFound; + long sendMessageReaction( String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, String recipient ) throws Error.InvalidNumber, Error.Failure; diff --git a/src/main/java/org/asamk/signal/commands/Commands.java b/src/main/java/org/asamk/signal/commands/Commands.java index 4bc17930..830049a5 100644 --- a/src/main/java/org/asamk/signal/commands/Commands.java +++ b/src/main/java/org/asamk/signal/commands/Commands.java @@ -22,20 +22,21 @@ public class Commands { addCommand("receive", new ReceiveCommand()); addCommand("register", new RegisterCommand()); addCommand("removeDevice", new RemoveDeviceCommand()); + addCommand("remoteDelete", new RemoteDeleteCommand()); addCommand("removePin", new RemovePinCommand()); addCommand("send", new SendCommand()); - addCommand("sendReaction", new SendReactionCommand()); addCommand("sendContacts", new SendContactsCommand()); - addCommand("updateContact", new UpdateContactCommand()); + addCommand("sendReaction", new SendReactionCommand()); addCommand("setPin", new SetPinCommand()); addCommand("trust", new TrustCommand()); addCommand("unblock", new UnblockCommand()); addCommand("unregister", new UnregisterCommand()); addCommand("updateAccount", new UpdateAccountCommand()); + addCommand("updateContact", new UpdateContactCommand()); addCommand("updateGroup", new UpdateGroupCommand()); addCommand("updateProfile", new UpdateProfileCommand()); - addCommand("verify", new VerifyCommand()); addCommand("uploadStickerPack", new UploadStickerPackCommand()); + addCommand("verify", new VerifyCommand()); } public static Map getCommands() { diff --git a/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java b/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java new file mode 100644 index 00000000..9bf4b556 --- /dev/null +++ b/src/main/java/org/asamk/signal/commands/RemoteDeleteCommand.java @@ -0,0 +1,85 @@ +package org.asamk.signal.commands; + +import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; + +import org.asamk.Signal; +import org.asamk.signal.PlainTextWriterImpl; +import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.commands.exceptions.UnexpectedErrorException; +import org.asamk.signal.commands.exceptions.UserErrorException; +import org.asamk.signal.manager.groups.GroupIdFormatException; +import org.asamk.signal.util.Util; +import org.freedesktop.dbus.errors.UnknownObject; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.util.List; + +import static org.asamk.signal.util.ErrorUtils.handleAssertionError; + +public class RemoteDeleteCommand implements DbusCommand { + + @Override + public void attachToSubparser(final Subparser subparser) { + subparser.help("Remotely delete a previously sent message."); + subparser.addArgument("-t", "--target-timestamp") + .required(true) + .type(long.class) + .help("Specify the timestamp of the message to delete."); + var mut = subparser.addMutuallyExclusiveGroup().required(true); + mut.addArgument("-g", "--group").help("Specify the recipient group ID."); + mut.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*"); + } + + @Override + public void handleCommand(final Namespace ns, final Signal signal) throws CommandException { + final List recipients = ns.getList("recipient"); + final var groupIdString = ns.getString("group"); + + // Possibly unnecessary (see above — recipient(s) or group is required), + // but just for case... + final var noRecipients = recipients == null || recipients.isEmpty(); + if (noRecipients && groupIdString == null) { + throw new UserErrorException("No recipients given"); + } + // Possibly unnecessary (see above — recipient(s) and group are mutually exclusive), + // but just for case... + if (!noRecipients && groupIdString != null) { + throw new UserErrorException("You cannot specify recipients by phone number and groups at the same time"); + } + + final long targetTimestamp = ns.getLong("target_timestamp"); + + final var writer = new PlainTextWriterImpl(System.out); + + byte[] groupId = null; + if (groupIdString != null) { + try { + groupId = Util.decodeGroupId(groupIdString).serialize(); + } catch (GroupIdFormatException e) { + throw new UserErrorException("Invalid group id: " + e.getMessage()); + } + } + + try { + long timestamp; + if (groupId != null) { + timestamp = signal.remoteGroupDelete(targetTimestamp, groupId); + } else { + timestamp = signal.remoteDelete(targetTimestamp, recipients); + } + writer.println("{}", timestamp); + } catch (AssertionError e) { + handleAssertionError(e); + throw e; + } catch (UnknownObject e) { + throw new UserErrorException("Failed to find dbus object, maybe missing the -u flag: " + e.getMessage()); + } catch (Signal.Error.InvalidNumber e) { + throw new UserErrorException("Invalid number: " + e.getMessage()); + } catch (Signal.Error.GroupNotFound e) { + throw new UserErrorException("Failed to send to group: " + e.getMessage()); + } catch (DBusExecutionException e) { + throw new UnexpectedErrorException("Failed to send message: " + e.getMessage()); + } + } +} diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 9bc4b67f..50a84dd3 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -103,6 +103,36 @@ public class DbusSignalImpl implements Signal { } } + @Override + public long remoteDelete( + final long targetSentTimestamp, final List recipients + ) { + try { + final var results = m.remoteDelete(targetSentTimestamp, recipients); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (InvalidNumberException e) { + throw new Error.InvalidNumber(e.getMessage()); + } + } + + @Override + public long remoteGroupDelete( + final long targetSentTimestamp, final byte[] groupId + ) { + try { + final var results = m.remoteGroupDelete(targetSentTimestamp, GroupId.unknownVersion(groupId)); + checkSendMessageResults(results.first(), results.second()); + return results.first(); + } catch (IOException e) { + throw new Error.Failure(e.getMessage()); + } catch (GroupNotFoundException | NotAGroupMemberException e) { + throw new Error.GroupNotFound(e.getMessage()); + } + } + @Override public long sendMessageReaction( final String emoji, final boolean remove, final String targetAuthor, final long targetSentTimestamp, final String recipient