From: AsamK Date: Wed, 17 Apr 2024 19:26:16 +0000 (+0200) Subject: Add more details to listContacts command X-Git-Tag: v0.13.3~5 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/e4c5144fbf46cc91a38f5011118e6008db894a80 Add more details to listContacts command Fixes #1502 --- diff --git a/graalvm-config-dir/reflect-config.json b/graalvm-config-dir/reflect-config.json index 252839b2..c6e85760 100644 --- a/graalvm-config-dir/reflect-config.json +++ b/graalvm-config-dir/reflect-config.json @@ -849,6 +849,24 @@ "queryAllDeclaredConstructors":true, "methods":[{"name":"id","parameterTypes":[] }, {"name":"opaque","parameterTypes":[] }, {"name":"sdp","parameterTypes":[] }, {"name":"type","parameterTypes":[] }] }, +{ + "name":"org.asamk.signal.json.JsonContact", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"color","parameterTypes":[] }, {"name":"familyName","parameterTypes":[] }, {"name":"givenName","parameterTypes":[] }, {"name":"internal","parameterTypes":[] }, {"name":"isBlocked","parameterTypes":[] }, {"name":"isHidden","parameterTypes":[] }, {"name":"messageExpirationTime","parameterTypes":[] }, {"name":"name","parameterTypes":[] }, {"name":"nickFamilyName","parameterTypes":[] }, {"name":"nickGivenName","parameterTypes":[] }, {"name":"nickName","parameterTypes":[] }, {"name":"note","parameterTypes":[] }, {"name":"number","parameterTypes":[] }, {"name":"profile","parameterTypes":[] }, {"name":"profileSharing","parameterTypes":[] }, {"name":"unregistered","parameterTypes":[] }, {"name":"username","parameterTypes":[] }, {"name":"uuid","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.json.JsonContact$JsonInternal", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"capabilities","parameterTypes":[] }, {"name":"discoverableByPhonenumber","parameterTypes":[] }, {"name":"sharesPhoneNumber","parameterTypes":[] }, {"name":"unidentifiedAccessMode","parameterTypes":[] }] +}, +{ + "name":"org.asamk.signal.json.JsonContact$JsonProfile", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"about","parameterTypes":[] }, {"name":"aboutEmoji","parameterTypes":[] }, {"name":"familyName","parameterTypes":[] }, {"name":"givenName","parameterTypes":[] }, {"name":"hasAvatar","parameterTypes":[] }, {"name":"lastUpdateTimestamp","parameterTypes":[] }, {"name":"mobileCoinAddress","parameterTypes":[] }] +}, { "name":"org.asamk.signal.json.JsonContactAddress", "allDeclaredFields":true, diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 31bc5499..7335eba6 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -590,6 +590,12 @@ Specify if only blocked or unblocked contacts should be shown (default: all cont *--name*:: Find contacts with the given contact or profile name. +*--detailed*:: +List the contacts with more details. If output=json, then this is always set + +*--internal*:: +Include internal information that's normally not user visible + === listIdentities List all known identity keys and their trust status, fingerprint and safety number. diff --git a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java index 698e6f2b..3865d829 100644 --- a/src/main/java/org/asamk/signal/commands/ListContactsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListContactsCommand.java @@ -5,8 +5,10 @@ import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.signal.commands.exceptions.CommandException; +import org.asamk.signal.json.JsonContact; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.api.Contact; +import org.asamk.signal.manager.api.PhoneNumberSharingMode; import org.asamk.signal.manager.api.Profile; import org.asamk.signal.output.JsonWriter; import org.asamk.signal.output.OutputWriter; @@ -35,6 +37,12 @@ public class ListContactsCommand implements JsonRpcLocalCommand { .type(Boolean.class) .help("Specify if only blocked or unblocked contacts should be shown (default: all contacts)"); subparser.addArgument("--name").help("Find contacts with the given contact or profile name."); + subparser.addArgument("--detailed") + .action(Arguments.storeTrue()) + .help("List the contacts with more details. If output=json, then this is always set"); + subparser.addArgument("--internal") + .action(Arguments.storeTrue()) + .help("Include internal information that's normally not user visible"); } @Override @@ -51,33 +59,94 @@ public class ListContactsCommand implements JsonRpcLocalCommand { recipientIdentifiers, Optional.ofNullable(name)); + final var detailed = Boolean.TRUE.equals(ns.getBoolean("detailed")); + final var internal = Boolean.TRUE.equals(ns.getBoolean("internal")); + switch (outputWriter) { case PlainTextWriter writer -> { for (var r : recipients) { final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact(); final var profile = r.getProfile() == null ? Profile.newBuilder().build() : r.getProfile(); writer.println( - "Number: {} Name: {} Profile name: {} Username: {} Color: {} Blocked: {} Message expiration: {}", - r.getAddress().getLegacyIdentifier(), + "Number: {} ACI: {} Name: {} Profile name: {} Username: {} Color: {} Blocked: {} Message expiration: {}", + r.getAddress().number().orElse(""), + r.getAddress().aci().orElse(""), contact.getName(), profile.getDisplayName(), r.getAddress().username().orElse(""), - contact.color(), + Optional.ofNullable(contact.color()).orElse(""), contact.isBlocked(), contact.messageExpirationTime() == 0 ? "disabled" : contact.messageExpirationTime() + "s"); + if (detailed) { + writer.indentedWriter() + .println( + "PNI: {} Given name: {} Family name: {}, Nick name: {} Nick given name: {} Nick family name {} Note: {} Archived: {} Hidden: {} Profile sharing: {} About: {} About Emoji: {} Unregistered: {}", + r.getAddress().pni().orElse(""), + Optional.ofNullable(r.getContact().givenName()).orElse(""), + Optional.ofNullable(r.getContact().familyName()).orElse(""), + Optional.ofNullable(r.getContact().nickName()).orElse(""), + Optional.ofNullable(r.getContact().nickNameGivenName()).orElse(""), + Optional.ofNullable(r.getContact().nickNameFamilyName()).orElse(""), + Optional.ofNullable(r.getContact().note()).orElse(""), + r.getContact().isArchived(), + r.getContact().isHidden(), + r.getContact().isProfileSharingEnabled(), + Optional.ofNullable(r.getProfile().getAbout()).orElse(""), + Optional.ofNullable(r.getProfile().getAboutEmoji()).orElse(""), + r.getContact().unregisteredTimestamp() != null); + } + if (internal) { + writer.indentedWriter() + .println( + "Capabilities: {} Unidentified access mode: {} Shares number: {} Discoverable by number: {}", + r.getProfile().getCapabilities().stream().map(Enum::name).toList(), + Optional.ofNullable(r.getProfile().getUnidentifiedAccessMode() + == Profile.UnidentifiedAccessMode.UNKNOWN + ? null + : r.getProfile().getUnidentifiedAccessMode().name()).orElse(""), + r.getProfile().getPhoneNumberSharingMode() == null + ? "" + : String.valueOf(r.getProfile().getPhoneNumberSharingMode() + == PhoneNumberSharingMode.EVERYBODY), + r.getDiscoverable() == null ? "" : String.valueOf(r.getDiscoverable())); + } } } case JsonWriter writer -> { final var jsonContacts = recipients.stream().map(r -> { final var address = r.getAddress(); final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact(); + final var jsonInternal = !internal + ? null + : new JsonContact.JsonInternal(r.getProfile() + .getCapabilities() + .stream() + .map(Enum::name) + .toList(), + r.getProfile().getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNKNOWN + ? null + : r.getProfile().getUnidentifiedAccessMode().name(), + r.getProfile().getPhoneNumberSharingMode() == null + ? null + : r.getProfile().getPhoneNumberSharingMode() + == PhoneNumberSharingMode.EVERYBODY, + r.getDiscoverable()); return new JsonContact(address.number().orElse(null), address.uuid().map(UUID::toString).orElse(null), address.username().orElse(null), contact.getName(), + contact.givenName(), + contact.familyName(), + contact.nickName(), + contact.nickNameGivenName(), + contact.nickNameFamilyName(), + contact.note(), contact.color(), contact.isBlocked(), + contact.isHidden(), contact.messageExpirationTime(), + r.getContact().isProfileSharingEnabled(), + r.getContact().unregisteredTimestamp() != null, r.getProfile() == null ? null : new JsonContact.JsonProfile(r.getProfile().getLastUpdateTimestamp(), @@ -85,34 +154,15 @@ public class ListContactsCommand implements JsonRpcLocalCommand { r.getProfile().getFamilyName(), r.getProfile().getAbout(), r.getProfile().getAboutEmoji(), + r.getProfile().getAvatarUrlPath() != null, r.getProfile().getMobileCoinAddress() == null ? null : Base64.getEncoder() - .encodeToString(r.getProfile().getMobileCoinAddress()))); + .encodeToString(r.getProfile().getMobileCoinAddress())), + jsonInternal); }).toList(); writer.write(jsonContacts); } } } - - private record JsonContact( - String number, - String uuid, - String username, - String name, - String color, - boolean isBlocked, - int messageExpirationTime, - JsonProfile profile - ) { - - private record JsonProfile( - long lastUpdateTimestamp, - String givenName, - String familyName, - String about, - String aboutEmoji, - String mobileCoinAddress - ) {} - } } diff --git a/src/main/java/org/asamk/signal/json/JsonContact.java b/src/main/java/org/asamk/signal/json/JsonContact.java new file mode 100644 index 00000000..71f05c8d --- /dev/null +++ b/src/main/java/org/asamk/signal/json/JsonContact.java @@ -0,0 +1,44 @@ +package org.asamk.signal.json; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; + +public record JsonContact( + String number, + String uuid, + String username, + String name, + String givenName, + String familyName, + String nickName, + String nickGivenName, + String nickFamilyName, + String note, + String color, + boolean isBlocked, + boolean isHidden, + int messageExpirationTime, + boolean profileSharing, + boolean unregistered, + JsonProfile profile, + @JsonInclude(JsonInclude.Include.NON_NULL) JsonInternal internal +) { + + public record JsonProfile( + long lastUpdateTimestamp, + String givenName, + String familyName, + String about, + String aboutEmoji, + boolean hasAvatar, + String mobileCoinAddress + ) {} + + public record JsonInternal( + List capabilities, + String unidentifiedAccessMode, + Boolean sharesPhoneNumber, + Boolean discoverableByPhonenumber + ) {} +}