mirror of
https://github.com/AsamK/signal-cli
synced 2025-09-02 12:30:39 +00:00
it works!
This commit is contained in:
parent
3cbb8de656
commit
43a2b4674f
6 changed files with 243 additions and 19 deletions
|
@ -222,7 +222,7 @@ public class Main {
|
|||
ArgumentParser parser = ArgumentParsers.newFor("signal-cli")
|
||||
.build()
|
||||
.defaultHelp(true)
|
||||
.description("Commandline interface for Signal.")
|
||||
.description("Commandline interface for Signal, patched to support sending messages from stdin and outputing reactions.")
|
||||
.version(BaseConfig.PROJECT_NAME + " " + BaseConfig.PROJECT_VERSION);
|
||||
|
||||
parser.addArgument("-v", "--version")
|
||||
|
|
|
@ -6,17 +6,71 @@ import net.sourceforge.argparse4j.inf.Subparser;
|
|||
|
||||
import org.asamk.signal.DbusReceiveMessageHandler;
|
||||
import org.asamk.signal.JsonDbusReceiveMessageHandler;
|
||||
import org.asamk.signal.ReceiveMessageHandler;
|
||||
import org.asamk.signal.JsonReceiveMessageHandler;
|
||||
import org.asamk.signal.dbus.DbusSignalImpl;
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.manager.AttachmentInvalidException;
|
||||
import org.freedesktop.dbus.connections.impl.DBusConnection;
|
||||
import org.freedesktop.dbus.exceptions.DBusException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static org.asamk.signal.DbusConfig.SIGNAL_BUSNAME;
|
||||
import static org.asamk.signal.DbusConfig.SIGNAL_OBJECTPATH;
|
||||
import static org.asamk.signal.util.ErrorUtils.handleAssertionError;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.EncapsulatedExceptions;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
|
||||
class InputReader implements Runnable {
|
||||
private volatile boolean alive = true;
|
||||
private Manager m;
|
||||
|
||||
public void terminate() {
|
||||
this.alive = false;
|
||||
}
|
||||
InputReader (final Manager m) {
|
||||
this.m = m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
||||
while (alive) {
|
||||
try {
|
||||
String in = br.readLine();
|
||||
String args[] = in.split(":", 2);
|
||||
// it should really read only one line though
|
||||
if (args.length == 2) {
|
||||
System.out.println(Arrays.toString(args));
|
||||
System.out.println(args.length);
|
||||
String message = args[1];
|
||||
List<String> recipients = new ArrayList<String>();
|
||||
recipients.add(args[0]);
|
||||
List<String> attachments = new ArrayList<>();
|
||||
try {
|
||||
System.out.println("sent " + in);
|
||||
this.m.sendMessage(message, attachments, recipients);
|
||||
} catch (AssertionError | EncapsulatedExceptions| AttachmentInvalidException | InvalidNumberException e) {
|
||||
System.err.println("aaaaaa (Manager.java ~L1457)");
|
||||
e.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
// getMessageSender().sendTyping(signalServiceAddress?, ....)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DaemonCommand implements LocalCommand {
|
||||
|
||||
|
@ -39,7 +93,7 @@ public class DaemonCommand implements LocalCommand {
|
|||
System.err.println("User is not registered.");
|
||||
return 1;
|
||||
}
|
||||
DBusConnection conn = null;
|
||||
/* DBusConnection conn = null;
|
||||
try {
|
||||
try {
|
||||
DBusConnection.DBusBusType busType;
|
||||
|
@ -48,9 +102,9 @@ public class DaemonCommand implements LocalCommand {
|
|||
} else {
|
||||
busType = DBusConnection.DBusBusType.SESSION;
|
||||
}
|
||||
conn = DBusConnection.getConnection(busType);
|
||||
conn.exportObject(SIGNAL_OBJECTPATH, new DbusSignalImpl(m));
|
||||
conn.requestBusName(SIGNAL_BUSNAME);
|
||||
// conn = DBusConnection.getConnection(busType);
|
||||
// conn.exportObject(SIGNAL_OBJECTPATH, new DbusSignalImpl(m));
|
||||
// conn.requestBusName(SIGNAL_BUSNAME);
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
System.err.println("Missing native library dependency for dbus service: " + e.getMessage());
|
||||
return 1;
|
||||
|
@ -58,9 +112,17 @@ public class DaemonCommand implements LocalCommand {
|
|||
e.printStackTrace();
|
||||
return 2;
|
||||
}
|
||||
*/
|
||||
boolean ignoreAttachments = ns.getBoolean("ignore_attachments");
|
||||
InputReader reader = new InputReader(m);
|
||||
Thread readerThread = new Thread(reader);
|
||||
readerThread.start();
|
||||
try {
|
||||
m.receiveMessages(1, TimeUnit.HOURS, false, ignoreAttachments, ns.getBoolean("json") ? new JsonDbusReceiveMessageHandler(m, conn, SIGNAL_OBJECTPATH) : new DbusReceiveMessageHandler(m, conn, SIGNAL_OBJECTPATH));
|
||||
m.receiveMessagesAndReadStdin(1, TimeUnit.HOURS, false, ignoreAttachments,
|
||||
ns.getBoolean("json")
|
||||
? new JsonReceiveMessageHandler(m)
|
||||
: new ReceiveMessageHandler(m)
|
||||
/*true*/);
|
||||
return 0;
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error while receiving messages: " + e.getMessage());
|
||||
|
@ -68,11 +130,9 @@ public class DaemonCommand implements LocalCommand {
|
|||
} catch (AssertionError e) {
|
||||
handleAssertionError(e);
|
||||
return 1;
|
||||
} finally {
|
||||
reader.terminate();
|
||||
}
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import java.io.IOException;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Arrays;
|
||||
import static org.asamk.signal.util.ErrorUtils.handleAssertionError;
|
||||
import static org.asamk.signal.util.ErrorUtils.handleGroupIdFormatException;
|
||||
|
||||
|
@ -102,6 +102,7 @@ public class SendCommand implements DbusCommand {
|
|||
}
|
||||
|
||||
try {
|
||||
System.out.println(Arrays.toString(ns.getList("recipient").toArray()));
|
||||
long timestamp = signal.sendMessage(messageText, attachments, ns.getList("recipient"));
|
||||
System.out.println(timestamp);
|
||||
return 0;
|
||||
|
|
|
@ -4,11 +4,29 @@ import org.asamk.Signal;
|
|||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||
|
||||
//import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
// i think this is what you have to do to get another dict in json
|
||||
// but i'm not sure
|
||||
class JsonReaction {
|
||||
String emoji; // unicode??
|
||||
String targetAuthor;
|
||||
long targetTimestamp;
|
||||
boolean isRemove;
|
||||
JsonReaction (SignalServiceDataMessage.Reaction reaction) {
|
||||
this.emoji = reaction.getEmoji();
|
||||
// comment on this line from ReceiveMessageHandler: todo resolve
|
||||
this.targetAuthor = reaction.getTargetAuthor().getLegacyIdentifier();
|
||||
this.targetTimestamp = reaction.getTargetSentTimestamp();
|
||||
this.isRemove = reaction.isRemove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class JsonDataMessage {
|
||||
|
||||
long timestamp;
|
||||
|
@ -16,6 +34,8 @@ class JsonDataMessage {
|
|||
int expiresInSeconds;
|
||||
List<JsonAttachment> attachments;
|
||||
JsonGroupInfo groupInfo;
|
||||
JsonReaction reaction;
|
||||
SignalServiceDataMessage.Quote quote;
|
||||
|
||||
JsonDataMessage(SignalServiceDataMessage dataMessage) {
|
||||
this.timestamp = dataMessage.getTimestamp();
|
||||
|
@ -35,8 +55,38 @@ class JsonDataMessage {
|
|||
} else {
|
||||
this.attachments = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
if (dataMessage.getReaction().isPresent()) {
|
||||
final SignalServiceDataMessage.Reaction reaction = dataMessage.getReaction().get();
|
||||
this.reaction = new JsonReaction(reaction);
|
||||
/* this.emoji = reaction.getEmoji();
|
||||
// comment on this line from ReceiveMessageHandler: todo resolve
|
||||
this.targetAuthor = reaction.getTargetAuthor().getLegacyIdentifier();
|
||||
this.targetTimestamp = reaction.getTargetSentTimestamp();
|
||||
*/ } /*else {
|
||||
this.reaction = null;
|
||||
/*
|
||||
this.emoji = "";
|
||||
this.targetAuthor = "";
|
||||
this.targetTimestamp = 0;
|
||||
|
||||
*/ // }
|
||||
/*
|
||||
|
||||
if (message.getQuote().isPresent()) {
|
||||
SignalServiceDataMessage.Quote quote = message.getQuote().get();
|
||||
System.out.println("Quote: (" + quote.getId() + ")");
|
||||
// there doesn't seem to be any fucking way to find a message's id?
|
||||
System.out.println(" Author: " + quote.getAuthor().getLegacyIdentifier());
|
||||
System.out.println(" Text: " + quote.getText());
|
||||
}
|
||||
if (message.isExpirationUpdate()) {
|
||||
System.out.println("Is Expiration update: " + message.isExpirationUpdate());
|
||||
}
|
||||
*/
|
||||
}
|
||||
// very confusingly MessageReceived seems to be only made in JsonDbusReceiveMessageHandler
|
||||
// and only when *sending* to dbus, so to my current understanding this never gets called
|
||||
// which would suggest i'm not understanding something
|
||||
public JsonDataMessage(Signal.MessageReceived messageReceived) {
|
||||
timestamp = messageReceived.getTimestamp();
|
||||
message = messageReceived.getMessage();
|
||||
|
@ -46,7 +96,8 @@ class JsonDataMessage {
|
|||
.map(JsonAttachment::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// i don't understand what SyncMessages are so i'm gonna ignore them
|
||||
// i think it only matters if you have multiple devices on your end
|
||||
public JsonDataMessage(Signal.SyncMessageReceived messageReceived) {
|
||||
timestamp = messageReceived.getTimestamp();
|
||||
message = messageReceived.getMessage();
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package org.asamk.signal.json;
|
||||
|
||||
import org.asamk.Signal;
|
||||
//import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
|
||||
public class JsonMessageEnvelope {
|
||||
|
||||
// gotta do something so that it actually emits valid json instead of null
|
||||
// or just fix it on the python side i guess
|
||||
String source;
|
||||
int sourceDevice;
|
||||
String relay;
|
||||
|
@ -16,7 +18,7 @@ public class JsonMessageEnvelope {
|
|||
JsonSyncMessage syncMessage;
|
||||
JsonCallMessage callMessage;
|
||||
JsonReceiptMessage receiptMessage;
|
||||
|
||||
// String typingAction;
|
||||
public JsonMessageEnvelope(SignalServiceEnvelope envelope, SignalServiceContent content) {
|
||||
if (!envelope.isUnidentifiedSender() && envelope.hasSource()) {
|
||||
SignalServiceAddress source = envelope.getSourceAddress();
|
||||
|
@ -43,7 +45,11 @@ public class JsonMessageEnvelope {
|
|||
if (content.getReceiptMessage().isPresent()) {
|
||||
this.receiptMessage = new JsonReceiptMessage(content.getReceiptMessage().get());
|
||||
}
|
||||
}
|
||||
/* if (content.getTypingMessage().isPresent()) {
|
||||
SignalServiceTypingMessage typingMessage = content.getTypingMessage().get();
|
||||
this.typingAction = content.getTypingMessage().get();
|
||||
}
|
||||
*/ }
|
||||
}
|
||||
|
||||
public JsonMessageEnvelope(Signal.MessageReceived messageReceived) {
|
||||
|
|
|
@ -113,6 +113,7 @@ import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider
|
|||
import org.whispersystems.signalservice.internal.util.Hex;
|
||||
import org.whispersystems.util.Base64;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -120,10 +121,12 @@ import java.io.FileNotFoundException;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
@ -148,6 +151,7 @@ import java.util.zip.ZipEntry;
|
|||
import java.util.zip.ZipFile;
|
||||
|
||||
import static org.asamk.signal.manager.ServiceConfig.capabilities;
|
||||
import static org.asamk.signal.util.ErrorUtils.handleAssertionError;
|
||||
|
||||
public class Manager implements Closeable {
|
||||
|
||||
|
@ -1419,6 +1423,108 @@ public class Manager implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
public void receiveMessagesAndReadStdin(long timeout, TimeUnit unit, boolean returnOnTimeout, boolean ignoreAttachments, ReceiveMessageHandler handler) throws IOException {
|
||||
retryFailedReceivedMessages(handler, ignoreAttachments);
|
||||
final SignalServiceMessageReceiver messageReceiver = getMessageReceiver();
|
||||
|
||||
Set<HandleAction> queuedActions = null;
|
||||
|
||||
if (messagePipe == null) {
|
||||
messagePipe = messageReceiver.createMessagePipe();
|
||||
}
|
||||
|
||||
boolean hasCaughtUpWithOldMessages = false;
|
||||
|
||||
while (true) {
|
||||
SignalServiceEnvelope envelope;
|
||||
SignalServiceContent content = null;
|
||||
Exception exception = null;
|
||||
final long now = new Date().getTime();
|
||||
try {
|
||||
Optional<SignalServiceEnvelope> result = messagePipe.readOrEmpty(timeout, unit, envelope1 -> {
|
||||
// store message on disk, before acknowledging receipt to the server
|
||||
try {
|
||||
String source = envelope1.getSourceE164().isPresent() ? envelope1.getSourceE164().get() : "";
|
||||
File cacheFile = getMessageCacheFile(source, now, envelope1.getTimestamp());
|
||||
Utils.storeEnvelope(envelope1, cacheFile);
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to store encrypted message in disk cache, ignoring: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
if (result.isPresent()) {
|
||||
envelope = result.get();
|
||||
} else {
|
||||
// Received indicator that server queue is empty
|
||||
hasCaughtUpWithOldMessages = true;
|
||||
|
||||
if (queuedActions != null) {
|
||||
for (HandleAction action : queuedActions) {
|
||||
try {
|
||||
action.execute(this);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
queuedActions.clear();
|
||||
queuedActions = null;
|
||||
}
|
||||
|
||||
// Continue to wait another timeout for new messages
|
||||
continue;
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
if (returnOnTimeout)
|
||||
return;
|
||||
continue;
|
||||
} catch (InvalidVersionException e) {
|
||||
System.err.println("Ignoring error: " + e.getMessage());
|
||||
continue;
|
||||
}
|
||||
if (envelope.hasSource()) {
|
||||
// Store uuid if we don't have it already
|
||||
SignalServiceAddress source = envelope.getSourceAddress();
|
||||
resolveSignalServiceAddress(source);
|
||||
}
|
||||
if (!envelope.isReceipt()) {
|
||||
try {
|
||||
content = decryptMessage(envelope);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
List<HandleAction> actions = handleMessage(envelope, content, ignoreAttachments);
|
||||
if (hasCaughtUpWithOldMessages) {
|
||||
for (HandleAction action : actions) {
|
||||
try {
|
||||
action.execute(this);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (queuedActions == null) {
|
||||
queuedActions = new HashSet<>();
|
||||
}
|
||||
queuedActions.addAll(actions);
|
||||
}
|
||||
}
|
||||
account.save();
|
||||
if (!isMessageBlocked(envelope, content)) {
|
||||
handler.handleMessage(envelope, content, exception);
|
||||
}
|
||||
if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
|
||||
File cacheFile = null;
|
||||
try {
|
||||
String source = envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : "";
|
||||
cacheFile = getMessageCacheFile(source, now, envelope.getTimestamp());
|
||||
Files.delete(cacheFile.toPath());
|
||||
// Try to delete directory if empty
|
||||
new File(getMessageCachePath()).delete();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to delete cached message file “" + cacheFile + "”: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void receiveMessages(long timeout, TimeUnit unit, boolean returnOnTimeout, boolean ignoreAttachments, ReceiveMessageHandler handler) throws IOException {
|
||||
retryFailedReceivedMessages(handler, ignoreAttachments);
|
||||
final SignalServiceMessageReceiver messageReceiver = getMessageReceiver();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue