## [Unreleased]
### Added
- `--verbose` flag to increase log level
+- `--note-to-self` flag for `send` command to send a note to linked devices
+
+### Changed
+- Messages sent to self number will be sent as normal Signal messages again, to
+ send a sync message, use the new `--note-to-self` flag
### Fixed
- Disable registration lock before removing the PIN
}
dependencies {
- implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_15'
+ implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_16'
implementation 'org.bouncycastle:bcprov-jdk15on:1.68'
implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1'
implementation 'com.github.hypfvieh:dbus-java:3.2.4'
*-a* [ATTACHMENT [ATTACHMENT ...]], *--attachment* [ATTACHMENT [ATTACHMENT ...]]::
Add one or more files as attachment.
+*--note-to-self*::
+Send the message to self without notification.
+
*-e*, *--endsession*::
Clear session state and send end session message.
String message, List<String> attachments, List<String> recipients
) throws Error.AttachmentInvalid, Error.Failure, Error.InvalidNumber, Error.UnregisteredUser, Error.UntrustedIdentity;
+ long sendNoteToSelfMessage(
+ String message, List<String> attachments
+ ) throws Error.AttachmentInvalid, Error.Failure, Error.UnregisteredUser, Error.UntrustedIdentity;
+
void sendEndSessionMessage(List<String> recipients) throws Error.Failure, Error.InvalidNumber, Error.UnregisteredUser, Error.UntrustedIdentity;
long sendGroupMessage(
package org.asamk.signal.commands;
import net.sourceforge.argparse4j.impl.Arguments;
+import net.sourceforge.argparse4j.inf.MutuallyExclusiveGroup;
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
import java.io.IOException;
import java.nio.charset.Charset;
-import java.util.ArrayList;
import java.util.List;
import static org.asamk.signal.util.ErrorUtils.handleAssertionError;
@Override
public void attachToSubparser(final Subparser subparser) {
- subparser.addArgument("-g", "--group").help("Specify the recipient group ID.");
subparser.addArgument("recipient").help("Specify the recipients' phone number.").nargs("*");
+ final MutuallyExclusiveGroup mutuallyExclusiveGroup = subparser.addMutuallyExclusiveGroup();
+ mutuallyExclusiveGroup.addArgument("-g", "--group").help("Specify the recipient group ID.");
+ mutuallyExclusiveGroup.addArgument("--note-to-self")
+ .help("Send the message to self without notification.")
+ .action(Arguments.storeTrue());
+
subparser.addArgument("-m", "--message").help("Specify the message, if missing standard input is used.");
subparser.addArgument("-a", "--attachment").nargs("*").help("Add file as attachment");
subparser.addArgument("-e", "--endsession")
final List<String> recipients = ns.getList("recipient");
final Boolean isEndSession = ns.getBoolean("endsession");
final String groupIdString = ns.getString("group");
+ final Boolean isNoteToSelf = ns.getBoolean("note_to_self");
final boolean noRecipients = recipients == null || recipients.isEmpty();
- if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null)) {
+ if ((noRecipients && isEndSession) || (noRecipients && groupIdString == null && !isNoteToSelf)) {
System.err.println("No recipients given");
System.err.println("Aborting sending.");
return 1;
System.err.println("You cannot specify recipients by phone number and groups at the same time");
return 1;
}
+ if (!noRecipients && isNoteToSelf) {
+ System.err.println("You cannot specify recipients by phone number and not to self at the same time");
+ return 1;
+ }
if (isEndSession) {
try {
List<String> attachments = ns.getList("attachment");
if (attachments == null) {
- attachments = new ArrayList<>();
+ attachments = List.of();
}
- try {
- if (groupIdString != null) {
+ if (groupIdString != null) {
+ try {
byte[] groupId;
try {
groupId = Util.decodeGroupId(groupIdString).serialize();
long timestamp = signal.sendGroupMessage(messageText, attachments, groupId);
System.out.println(timestamp);
return 0;
+ } catch (AssertionError e) {
+ handleAssertionError(e);
+ return 1;
+ } catch (DBusExecutionException e) {
+ System.err.println("Failed to send group message: " + e.getMessage());
+ return 2;
+ }
+ }
+
+ if (isNoteToSelf) {
+ try {
+ long timestamp = signal.sendNoteToSelfMessage(messageText, attachments);
+ System.out.println(timestamp);
+ return 0;
+ } catch (AssertionError e) {
+ handleAssertionError(e);
+ return 1;
+ } catch (DBusExecutionException e) {
+ System.err.println("Failed to send note to self message: " + e.getMessage());
+ return 2;
}
- } catch (AssertionError e) {
- handleAssertionError(e);
- return 1;
- } catch (DBusExecutionException e) {
- System.err.println("Failed to send message: " + e.getMessage());
- return 2;
}
try {
}
}
+ @Override
+ public long sendNoteToSelfMessage(
+ final String message, final List<String> attachments
+ ) throws Error.AttachmentInvalid, Error.Failure, Error.UnregisteredUser, Error.UntrustedIdentity {
+ try {
+ final Pair<Long, List<SendMessageResult>> results = m.sendSelfMessage(message, attachments);
+ checkSendMessageResults(results.first(), results.second());
+ return results.first();
+ } catch (AttachmentInvalidException e) {
+ throw new Error.AttachmentInvalid(e.getMessage());
+ } catch (IOException e) {
+ throw new Error.Failure(e.getMessage());
+ }
+ }
+
@Override
public void sendEndSessionMessage(final List<String> recipients) {
try {
return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
}
+ public Pair<Long, List<SendMessageResult>> sendSelfMessage(
+ String messageText, List<String> attachments
+ ) throws IOException, AttachmentInvalidException {
+ final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
+ .withBody(messageText);
+ if (attachments != null) {
+ messageBuilder.withAttachments(AttachmentUtils.getSignalServiceAttachments(attachments));
+ }
+ return sendSelfMessage(messageBuilder);
+ }
+
public Pair<Long, List<SendMessageResult>> sendMessageReaction(
String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List<String> recipients
) throws IOException, InvalidNumberException {
final int expirationTime = contact != null ? contact.messageExpirationTime : 0;
messageBuilder.withExpiration(expirationTime);
message = messageBuilder.build();
- if (address.matches(account.getSelfAddress())) {
- results.add(sendSelfMessage(message));
- } else {
- results.add(sendMessage(address, message));
- }
+ results.add(sendMessage(address, message));
}
return new Pair<>(timestamp, results);
}
}
}
+ private Pair<Long, List<SendMessageResult>> sendSelfMessage(
+ SignalServiceDataMessage.Builder messageBuilder
+ ) throws IOException {
+ final long timestamp = System.currentTimeMillis();
+ messageBuilder.withTimestamp(timestamp);
+ getOrCreateMessagePipe();
+ getOrCreateUnidentifiedMessagePipe();
+ try {
+ final SignalServiceAddress address = getSelfAddress();
+
+ final ContactInfo contact = account.getContactStore().getContact(address);
+ final int expirationTime = contact != null ? contact.messageExpirationTime : 0;
+ messageBuilder.withExpiration(expirationTime);
+
+ SignalServiceDataMessage message = messageBuilder.build();
+ final SendMessageResult result = sendSelfMessage(message);
+ return new Pair<>(timestamp, List.of(result));
+ } finally {
+ account.save();
+ }
+ }
+
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
SignalServiceMessageSender messageSender = createMessageSender();