diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index fa2db7c3..9d7d97ee 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -130,7 +130,7 @@ Uses a list of phone numbers to determine the statuses of those users. Shows if [NUMBER [NUMBER ...]]:: One or more numbers to check. -*--json*:: +*--j*, *--json*:: Output the statuses as an array of json objects. === send @@ -184,7 +184,7 @@ 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*:: +*--j*, *--json*:: Output received messages in json format, one object per line. === joinGroup @@ -222,10 +222,13 @@ Specify the recipient group ID in base64 encoding. === listGroups -Show a list of known groups. +Show a list of known groups and related information. *-d*, *--detailed*:: -Include the list of members of each group. +Include the list of members of each group and the group invite link. + +*--j*, *--json*:: +Output group information as a list of json objects. === listIdentities diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 882f4a5c..dcd41cfa 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -21,7 +21,7 @@ public class GetUserStatusCommand implements LocalCommand { 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") + subparser.addArgument("-j", "--json") .help("Output received messages in json format, one json object per line.") .action(Arguments.storeTrue()); } diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index b4be4ad0..5fb1bee6 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -9,32 +9,61 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl; 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.Collections; 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) { + private enum MembersType { + MEMBERS, + PENDING_MEMBERS, + REQUESTING_MEMBERS, + } + + private static Set getMembersSet(Manager m, GroupInfo group, MembersType type) { + Set members; + switch (type) { + case MEMBERS: + members = group.getMembers(); + break; + case PENDING_MEMBERS: + members = group.getPendingMembers(); + break; + case REQUESTING_MEMBERS: + members = group.getRequestingMembers(); + break; + default: + return Collections.emptySet(); + } + + return members.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) { - Set members = group.getMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toSet()); - - Set pendingMembers = group.getPendingMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toSet()); - - Set requestingMembers = group.getRequestingMembers() - .stream() - .map(m::resolveSignalServiceAddress) - .map(SignalServiceAddress::getLegacyIdentifier) - .collect(Collectors.toSet()); - final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); System.out.println(String.format( @@ -43,9 +72,9 @@ public class ListGroupsCommand implements LocalCommand { group.getTitle(), group.isMember(m.getSelfAddress()), group.isBlocked(), - members, - pendingMembers, - requestingMembers, + getMembersSet(m, group, MembersType.MEMBERS), + getMembersSet(m, group, MembersType.PENDING_MEMBERS), + getMembersSet(m, group, MembersType.REQUESTING_MEMBERS), groupInviteLink == null ? '-' : groupInviteLink.getUrl())); } else { System.out.println(String.format("Id: %s Name: %s Active: %s Blocked: %b", @@ -60,6 +89,9 @@ public class ListGroupsCommand implements LocalCommand { 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("-j", "--json") + .help("Output received messages in json format, one json object per line.") + .action(Arguments.storeTrue()); } @Override @@ -72,9 +104,86 @@ public class ListGroupsCommand implements LocalCommand { List groups = m.getGroups(); boolean detailed = ns.getBoolean("detailed"); - for (GroupInfo group : groups) { - printGroup(m, group, detailed); + if (ns.getBoolean("json")) { + final ObjectMapper jsonProcessor = new ObjectMapper(); + jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + + if (detailed) { + List objects = new ArrayList<>(); + for (GroupInfo group : groups) { + final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); + + objects.add(new JsonListGroupDetailed(group.getGroupId().toBase64(), + group.getTitle(), + group.isMember(m.getSelfAddress()), + group.isBlocked(), + getMembersSet(m, group, MembersType.MEMBERS), + getMembersSet(m, group, MembersType.PENDING_MEMBERS), + getMembersSet(m, group, MembersType.REQUESTING_MEMBERS), + groupInviteLink == null ? null : groupInviteLink.getUrl())); + } + + return printGroupsJson(jsonProcessor, objects); + } else { + List objects = groups.stream().map( + group -> new JsonListGroup(group.getGroupId().toBase64(), + group.getTitle(), + group.isMember(m.getSelfAddress()), + group.isBlocked())) + .collect(Collectors.toList()); + + return printGroupsJson(jsonProcessor, objects); + } + } else { + for (GroupInfo group : groups) { + printGroupPlainText(m, group, detailed); + } } + return 0; } + + private static final class JsonListGroup { + + public String id; + public String name; + public boolean isMember; + public boolean isBlocked; + + public JsonListGroup(String id, String name, boolean isMember, boolean isBlocked) { + this.id = id; + this.name = name; + this.isMember = isMember; + this.isBlocked = isBlocked; + } + } + + private static final class JsonListGroupDetailed { + + public String id; + public String name; + public boolean isMember; + public boolean isBlocked; + + public Set members; + public Set pendingMembers; + public Set requestingMembers; + public String groupInviteLink; + + public JsonListGroupDetailed(String id, String name, boolean isMember, boolean isBlocked, + Set members, Set pendingMembers, + Set 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; + } + } } diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index 84a918fb..9df55dfb 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -35,7 +35,7 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { subparser.addArgument("--ignore-attachments") .help("Don’t download attachments of received messages.") .action(Arguments.storeTrue()); - subparser.addArgument("--json") + subparser.addArgument("-j", "--json") .help("Output received messages in json format, one json object per line.") .action(Arguments.storeTrue()); }