]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/commands/ListContactsCommand.java
Add more details to listContacts command
[signal-cli] / src / main / java / org / asamk / signal / commands / ListContactsCommand.java
1 package org.asamk.signal.commands;
2
3 import net.sourceforge.argparse4j.impl.Arguments;
4 import net.sourceforge.argparse4j.inf.Namespace;
5 import net.sourceforge.argparse4j.inf.Subparser;
6
7 import org.asamk.signal.commands.exceptions.CommandException;
8 import org.asamk.signal.json.JsonContact;
9 import org.asamk.signal.manager.Manager;
10 import org.asamk.signal.manager.api.Contact;
11 import org.asamk.signal.manager.api.PhoneNumberSharingMode;
12 import org.asamk.signal.manager.api.Profile;
13 import org.asamk.signal.output.JsonWriter;
14 import org.asamk.signal.output.OutputWriter;
15 import org.asamk.signal.output.PlainTextWriter;
16 import org.asamk.signal.util.CommandUtil;
17
18 import java.util.Base64;
19 import java.util.Optional;
20 import java.util.UUID;
21
22 public class ListContactsCommand implements JsonRpcLocalCommand {
23
24 @Override
25 public String getName() {
26 return "listContacts";
27 }
28
29 @Override
30 public void attachToSubparser(final Subparser subparser) {
31 subparser.help("Show a list of known contacts with names and profiles.");
32 subparser.addArgument("recipient").help("Specify one ore more phone numbers to show.").nargs("*");
33 subparser.addArgument("-a", "--all-recipients")
34 .action(Arguments.storeTrue())
35 .help("Include all known recipients, not only contacts.");
36 subparser.addArgument("--blocked")
37 .type(Boolean.class)
38 .help("Specify if only blocked or unblocked contacts should be shown (default: all contacts)");
39 subparser.addArgument("--name").help("Find contacts with the given contact or profile name.");
40 subparser.addArgument("--detailed")
41 .action(Arguments.storeTrue())
42 .help("List the contacts with more details. If output=json, then this is always set");
43 subparser.addArgument("--internal")
44 .action(Arguments.storeTrue())
45 .help("Include internal information that's normally not user visible");
46 }
47
48 @Override
49 public void handleCommand(
50 final Namespace ns, final Manager m, final OutputWriter outputWriter
51 ) throws CommandException {
52 final var allRecipients = Boolean.TRUE.equals(ns.getBoolean("all-recipients"));
53 final var blocked = ns.getBoolean("blocked");
54 final var recipientStrings = ns.<String>getList("recipient");
55 final var recipientIdentifiers = CommandUtil.getSingleRecipientIdentifiers(recipientStrings, m.getSelfNumber());
56 final var name = ns.getString("name");
57 final var recipients = m.getRecipients(!allRecipients,
58 Optional.ofNullable(blocked),
59 recipientIdentifiers,
60 Optional.ofNullable(name));
61
62 final var detailed = Boolean.TRUE.equals(ns.getBoolean("detailed"));
63 final var internal = Boolean.TRUE.equals(ns.getBoolean("internal"));
64
65 switch (outputWriter) {
66 case PlainTextWriter writer -> {
67 for (var r : recipients) {
68 final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact();
69 final var profile = r.getProfile() == null ? Profile.newBuilder().build() : r.getProfile();
70 writer.println(
71 "Number: {} ACI: {} Name: {} Profile name: {} Username: {} Color: {} Blocked: {} Message expiration: {}",
72 r.getAddress().number().orElse(""),
73 r.getAddress().aci().orElse(""),
74 contact.getName(),
75 profile.getDisplayName(),
76 r.getAddress().username().orElse(""),
77 Optional.ofNullable(contact.color()).orElse(""),
78 contact.isBlocked(),
79 contact.messageExpirationTime() == 0 ? "disabled" : contact.messageExpirationTime() + "s");
80 if (detailed) {
81 writer.indentedWriter()
82 .println(
83 "PNI: {} Given name: {} Family name: {}, Nick name: {} Nick given name: {} Nick family name {} Note: {} Archived: {} Hidden: {} Profile sharing: {} About: {} About Emoji: {} Unregistered: {}",
84 r.getAddress().pni().orElse(""),
85 Optional.ofNullable(r.getContact().givenName()).orElse(""),
86 Optional.ofNullable(r.getContact().familyName()).orElse(""),
87 Optional.ofNullable(r.getContact().nickName()).orElse(""),
88 Optional.ofNullable(r.getContact().nickNameGivenName()).orElse(""),
89 Optional.ofNullable(r.getContact().nickNameFamilyName()).orElse(""),
90 Optional.ofNullable(r.getContact().note()).orElse(""),
91 r.getContact().isArchived(),
92 r.getContact().isHidden(),
93 r.getContact().isProfileSharingEnabled(),
94 Optional.ofNullable(r.getProfile().getAbout()).orElse(""),
95 Optional.ofNullable(r.getProfile().getAboutEmoji()).orElse(""),
96 r.getContact().unregisteredTimestamp() != null);
97 }
98 if (internal) {
99 writer.indentedWriter()
100 .println(
101 "Capabilities: {} Unidentified access mode: {} Shares number: {} Discoverable by number: {}",
102 r.getProfile().getCapabilities().stream().map(Enum::name).toList(),
103 Optional.ofNullable(r.getProfile().getUnidentifiedAccessMode()
104 == Profile.UnidentifiedAccessMode.UNKNOWN
105 ? null
106 : r.getProfile().getUnidentifiedAccessMode().name()).orElse(""),
107 r.getProfile().getPhoneNumberSharingMode() == null
108 ? ""
109 : String.valueOf(r.getProfile().getPhoneNumberSharingMode()
110 == PhoneNumberSharingMode.EVERYBODY),
111 r.getDiscoverable() == null ? "" : String.valueOf(r.getDiscoverable()));
112 }
113 }
114 }
115 case JsonWriter writer -> {
116 final var jsonContacts = recipients.stream().map(r -> {
117 final var address = r.getAddress();
118 final var contact = r.getContact() == null ? Contact.newBuilder().build() : r.getContact();
119 final var jsonInternal = !internal
120 ? null
121 : new JsonContact.JsonInternal(r.getProfile()
122 .getCapabilities()
123 .stream()
124 .map(Enum::name)
125 .toList(),
126 r.getProfile().getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNKNOWN
127 ? null
128 : r.getProfile().getUnidentifiedAccessMode().name(),
129 r.getProfile().getPhoneNumberSharingMode() == null
130 ? null
131 : r.getProfile().getPhoneNumberSharingMode()
132 == PhoneNumberSharingMode.EVERYBODY,
133 r.getDiscoverable());
134 return new JsonContact(address.number().orElse(null),
135 address.uuid().map(UUID::toString).orElse(null),
136 address.username().orElse(null),
137 contact.getName(),
138 contact.givenName(),
139 contact.familyName(),
140 contact.nickName(),
141 contact.nickNameGivenName(),
142 contact.nickNameFamilyName(),
143 contact.note(),
144 contact.color(),
145 contact.isBlocked(),
146 contact.isHidden(),
147 contact.messageExpirationTime(),
148 r.getContact().isProfileSharingEnabled(),
149 r.getContact().unregisteredTimestamp() != null,
150 r.getProfile() == null
151 ? null
152 : new JsonContact.JsonProfile(r.getProfile().getLastUpdateTimestamp(),
153 r.getProfile().getGivenName(),
154 r.getProfile().getFamilyName(),
155 r.getProfile().getAbout(),
156 r.getProfile().getAboutEmoji(),
157 r.getProfile().getAvatarUrlPath() != null,
158 r.getProfile().getMobileCoinAddress() == null
159 ? null
160 : Base64.getEncoder()
161 .encodeToString(r.getProfile().getMobileCoinAddress())),
162 jsonInternal);
163 }).toList();
164 writer.write(jsonContacts);
165 }
166 }
167 }
168 }