*--dbus-system*::
Make request via system dbus.
+*-o* OUTPUT-MODE, *--output* OUTPUT-MODE::
+Specify if you want commands to output in either "plain-text" mode or in "json". Defaults to "plain-text"
+
== Commands
=== register
=== getUserStatus
-Uses a list of phone numbers to determine the statuses of those users. Shows if they are registered on the Signal Servers or not.
+Uses a list of phone numbers to determine the statuses of those users. Shows if they are registered on the Signal Servers or not. In json mode this is outputted as a list of objects.
[NUMBER [NUMBER ...]]::
One or more numbers to check.
-*--json*::
-Output the statuses as an array of json objects.
=== send
=== receive
Query the server for new messages.
-New messages are printed on standardoutput and attachments are downloaded to the config directory.
+New messages are printed on standard output and attachments are downloaded to the config directory. In json mode this is outputted as one json object per line.
*-t* TIMEOUT, *--timeout* TIMEOUT::
Number of seconds to wait for new messages (negative values disable timeout).
Default is 5 seconds.
*--ignore-attachments*::
Don’t download attachments of received messages.
-*--json*::
-Output received messages in json format, one object per line.
=== joinGroup
=== listGroups
-Show a list of known groups.
+Show a list of known groups and related information. In json mode this is outputted as an list of objects and is always in detailed mode.
*-d*, *--detailed*::
-Include the list of members of each group.
+Include the list of members of each group and the group invite link.
=== listIdentities
mut.addArgument("--dbus").help("Make request via user dbus.").action(Arguments.storeTrue());
mut.addArgument("--dbus-system").help("Make request via system dbus.").action(Arguments.storeTrue());
+ parser.addArgument("-o", "--output").help("Choose to output in plain text or JSON")
+ .choices("plain-text", "json").setDefault("plain-text");
+
Subparsers subparsers = parser.addSubparsers()
.title("subcommands")
.dest("command")
import net.sourceforge.argparse4j.inf.Subparser;
import org.asamk.signal.manager.Manager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashSet;
public class GetUserStatusCommand implements LocalCommand {
+ // TODO delete later when "json" variable is removed
+ final static Logger logger = LoggerFactory.getLogger(GetUserStatusCommand.class);
+
@Override
public void attachToSubparser(final Subparser subparser) {
subparser.addArgument("number").help("Phone number").nargs("+");
subparser.help("Check if the specified phone number/s have been registered");
subparser.addArgument("--json")
- .help("Output received messages in json format, one json object per line.")
+ .help("WARNING: This parameter is now deprecated! Please use the \"output\" option instead.\n\nOutput received messages in json format, one json object per line.")
.action(Arguments.storeTrue());
}
ObjectMapper jsonProcessor = new ObjectMapper();
jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
+ boolean inJson = ns.getString("output").equals("json");
+
+ // TODO delete later when "json" variable is removed
+ if (ns.getBoolean("json")) {
+ logger.warn("\"--json\" option has been deprecated, please use \"output\" instead.");
+ }
+
// Get a map of registration statuses
Map<String, Boolean> registered;
try {
}
// Output
- if (ns.getBoolean("json")) {
+ if (inJson) {
List<JsonIsRegistered> objects = registered.entrySet()
.stream()
.map(entry -> new JsonIsRegistered(entry.getKey(), entry.getValue()))
import org.asamk.signal.manager.storage.groups.GroupInfo;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class ListGroupsCommand implements LocalCommand {
- private static void printGroup(Manager m, GroupInfo group, boolean detailed) {
- if (detailed) {
- Set<String> members = group.getMembers()
- .stream()
- .map(m::resolveSignalServiceAddress)
- .map(SignalServiceAddress::getLegacyIdentifier)
- .collect(Collectors.toSet());
-
- Set<String> pendingMembers = group.getPendingMembers()
- .stream()
- .map(m::resolveSignalServiceAddress)
- .map(SignalServiceAddress::getLegacyIdentifier)
- .collect(Collectors.toSet());
-
- Set<String> requestingMembers = group.getRequestingMembers()
- .stream()
- .map(m::resolveSignalServiceAddress)
- .map(SignalServiceAddress::getLegacyIdentifier)
- .collect(Collectors.toSet());
+ private static Set<String> resolveMembers(Manager m, Set<SignalServiceAddress> addresses) {
+ return addresses.stream().map(m::resolveSignalServiceAddress)
+ .map(SignalServiceAddress::getLegacyIdentifier)
+ .collect(Collectors.toSet());
+ }
+
+ private static int printGroupsJson(ObjectMapper jsonProcessor, List<?> objects) {
+ try {
+ jsonProcessor.writeValue(System.out, objects);
+ System.out.println();
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ return 1;
+ }
+ return 0;
+ }
+
+ private static void printGroupPlainText(Manager m, GroupInfo group, boolean detailed) {
+ if (detailed) {
final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink();
System.out.println(String.format(
group.getTitle(),
group.isMember(m.getSelfAddress()),
group.isBlocked(),
- members,
- pendingMembers,
- requestingMembers,
+ resolveMembers(m, group.getMembers()),
+ resolveMembers(m, group.getPendingMembers()),
+ resolveMembers(m, group.getRequestingMembers()),
groupInviteLink == null ? '-' : groupInviteLink.getUrl()));
} else {
System.out.println(String.format("Id: %s Name: %s Active: %s Blocked: %b",
@Override
public void attachToSubparser(final Subparser subparser) {
- subparser.addArgument("-d", "--detailed").action(Arguments.storeTrue()).help("List members of each group");
- subparser.help("List group name and ids");
+ subparser.addArgument("-d", "--detailed").action(Arguments.storeTrue())
+ .help("List the members and group invite links of each group. If output=json, then this is always set");
+
+ subparser.help("List group information including names, ids, active status, blocked status and members");
}
@Override
public int handleCommand(final Namespace ns, final Manager m) {
- List<GroupInfo> groups = m.getGroups();
- boolean detailed = ns.getBoolean("detailed");
+ if (ns.getString("output").equals("json")) {
+ final ObjectMapper jsonProcessor = new ObjectMapper();
+ jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+ jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
- for (GroupInfo group : groups) {
- printGroup(m, group, detailed);
+ List<JsonGroup> objects = new ArrayList<>();
+ for (GroupInfo group : m.getGroups()) {
+ final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink();
+
+ objects.add(new JsonGroup(group.getGroupId().toBase64(),
+ group.getTitle(),
+ group.isMember(m.getSelfAddress()),
+ group.isBlocked(),
+ resolveMembers(m, group.getMembers()),
+ resolveMembers(m, group.getPendingMembers()),
+ resolveMembers(m, group.getRequestingMembers()),
+ groupInviteLink == null ? null : groupInviteLink.getUrl()));
+ }
+ return printGroupsJson(jsonProcessor, objects);
+ } else {
+ boolean detailed = ns.getBoolean("detailed");
+ for (GroupInfo group : m.getGroups()) {
+ printGroupPlainText(m, group, detailed);
+ }
}
+
return 0;
}
+
+ private static final class JsonGroup {
+
+ public String id;
+ public String name;
+ public boolean isMember;
+ public boolean isBlocked;
+
+ public Set<String> members;
+ public Set<String> pendingMembers;
+ public Set<String> requestingMembers;
+ public String groupInviteLink;
+
+ public JsonGroup(String id, String name, boolean isMember, boolean isBlocked,
+ Set<String> members, Set<String> pendingMembers,
+ Set<String> requestingMembers, String groupInviteLink)
+ {
+ this.id = id;
+ this.name = name;
+ this.isMember = isMember;
+ this.isBlocked = isBlocked;
+
+ this.members = members;
+ this.pendingMembers = pendingMembers;
+ this.requestingMembers = requestingMembers;
+ this.groupInviteLink = groupInviteLink;
+ }
+ }
}
import org.freedesktop.dbus.exceptions.DBusException;
import org.whispersystems.util.Base64;
+// TODO delete later when "json" variable is removed
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand {
+ // TODO delete later when "json" variable is removed
+ final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class);
+
@Override
public void attachToSubparser(final Subparser subparser) {
subparser.addArgument("-t", "--timeout")
.help("Don’t download attachments of received messages.")
.action(Arguments.storeTrue());
subparser.addArgument("--json")
- .help("Output received messages in json format, one json object per line.")
+ .help("WARNING: This parameter is now deprecated! Please use the \"output\" option instead.\n\nOutput received messages in json format, one json object per line.")
.action(Arguments.storeTrue());
}
public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) {
final ObjectMapper jsonProcessor;
+
+ boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json");
+
+ // TODO delete later when "json" variable is removed
if (ns.getBoolean("json")) {
+ logger.warn("\"--json\" option has been deprecated, please use \"output\" instead.");
+ }
+
+ if (inJson) {
jsonProcessor = new ObjectMapper();
jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
@Override
public int handleCommand(final Namespace ns, final Manager m) {
+ boolean inJson = ns.getString("output").equals("json") || ns.getBoolean("json");
+
+ // TODO delete later when "json" variable is removed
+ if (ns.getBoolean("json")) {
+ logger.warn("\"--json\" option has been deprecated, please use \"output\" instead.");
+ }
+
double timeout = 5;
if (ns.getDouble("timeout") != null) {
timeout = ns.getDouble("timeout");
}
boolean ignoreAttachments = ns.getBoolean("ignore_attachments");
try {
- final Manager.ReceiveMessageHandler handler = ns.getBoolean("json")
+ final Manager.ReceiveMessageHandler handler = inJson
? new JsonReceiveMessageHandler(m)
: new ReceiveMessageHandler(m);
m.receiveMessages((long) (timeout * 1000),
*
* @param numbers The set of phone number in question
* @return A map of numbers to booleans. True if registered, false otherwise. Should never be null
- * @throws IOException if its unable to check if the users are registered
+ * @throws IOException if its unable to get the contacts to check if they're registered
*/
public Map<String, Boolean> areUsersRegistered(Set<String> numbers) throws IOException {
// Note "contactDetails" has no optionals. It only gives us info on users who are registered