1 package org
.asamk
.signal
.commands
;
3 import net
.sourceforge
.argparse4j
.impl
.Arguments
;
4 import net
.sourceforge
.argparse4j
.inf
.Namespace
;
5 import net
.sourceforge
.argparse4j
.inf
.Subparser
;
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
;
18 import java
.util
.Base64
;
19 import java
.util
.Optional
;
20 import java
.util
.UUID
;
22 public class ListContactsCommand
implements JsonRpcLocalCommand
{
25 public String
getName() {
26 return "listContacts";
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")
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");
49 public void handleCommand(
52 final OutputWriter outputWriter
53 ) throws CommandException
{
54 final var allRecipients
= Boolean
.TRUE
.equals(ns
.getBoolean("all-recipients"));
55 final var blocked
= ns
.getBoolean("blocked");
56 final var recipientStrings
= ns
.<String
>getList("recipient");
57 final var recipientIdentifiers
= CommandUtil
.getSingleRecipientIdentifiers(recipientStrings
, m
.getSelfNumber());
58 final var name
= ns
.getString("name");
59 final var recipients
= m
.getRecipients(!allRecipients
,
60 Optional
.ofNullable(blocked
),
62 Optional
.ofNullable(name
));
64 final var detailed
= Boolean
.TRUE
.equals(ns
.getBoolean("detailed"));
65 final var internal
= Boolean
.TRUE
.equals(ns
.getBoolean("internal"));
67 switch (outputWriter
) {
68 case PlainTextWriter writer
-> {
69 for (var r
: recipients
) {
70 final var contact
= r
.getContact() == null ? Contact
.newBuilder().build() : r
.getContact();
71 final var profile
= r
.getProfile() == null ? Profile
.newBuilder().build() : r
.getProfile();
73 "Number: {} ACI: {} Name: {} Profile name: {} Username: {} Color: {} Blocked: {} Message expiration: {}",
74 r
.getAddress().number().orElse(""),
75 r
.getAddress().aci().orElse(""),
77 profile
.getDisplayName(),
78 r
.getAddress().username().orElse(""),
79 Optional
.ofNullable(contact
.color()).orElse(""),
81 contact
.messageExpirationTime() == 0 ?
"disabled" : contact
.messageExpirationTime() + "s");
83 writer
.indentedWriter()
85 "PNI: {} Given name: {} Family name: {}, Nick name: {} Nick given name: {} Nick family name {} Note: {} Archived: {} Hidden: {} Profile sharing: {} About: {} About Emoji: {} Unregistered: {}",
86 r
.getAddress().pni().orElse(""),
87 Optional
.ofNullable(r
.getContact().givenName()).orElse(""),
88 Optional
.ofNullable(r
.getContact().familyName()).orElse(""),
89 Optional
.ofNullable(r
.getContact().nickName()).orElse(""),
90 Optional
.ofNullable(r
.getContact().nickNameGivenName()).orElse(""),
91 Optional
.ofNullable(r
.getContact().nickNameFamilyName()).orElse(""),
92 Optional
.ofNullable(r
.getContact().note()).orElse(""),
93 r
.getContact().isArchived(),
94 r
.getContact().isHidden(),
95 r
.getContact().isProfileSharingEnabled(),
96 Optional
.ofNullable(r
.getProfile().getAbout()).orElse(""),
97 Optional
.ofNullable(r
.getProfile().getAboutEmoji()).orElse(""),
98 r
.getContact().unregisteredTimestamp() != null);
101 writer
.indentedWriter()
103 "Capabilities: {} Unidentified access mode: {} Shares number: {} Discoverable by number: {}",
104 r
.getProfile().getCapabilities().stream().map(Enum
::name
).toList(),
105 Optional
.ofNullable(r
.getProfile().getUnidentifiedAccessMode()
106 == Profile
.UnidentifiedAccessMode
.UNKNOWN
108 : r
.getProfile().getUnidentifiedAccessMode().name()).orElse(""),
109 r
.getProfile().getPhoneNumberSharingMode() == null
111 : String
.valueOf(r
.getProfile().getPhoneNumberSharingMode()
112 == PhoneNumberSharingMode
.EVERYBODY
),
113 r
.getDiscoverable() == null ?
"" : String
.valueOf(r
.getDiscoverable()));
117 case JsonWriter writer
-> {
118 final var jsonContacts
= recipients
.stream().map(r
-> {
119 final var address
= r
.getAddress();
120 final var contact
= r
.getContact() == null ? Contact
.newBuilder().build() : r
.getContact();
121 final var jsonInternal
= !internal
123 : new JsonContact
.JsonInternal(r
.getProfile()
128 r
.getProfile().getUnidentifiedAccessMode() == Profile
.UnidentifiedAccessMode
.UNKNOWN
130 : r
.getProfile().getUnidentifiedAccessMode().name(),
131 r
.getProfile().getPhoneNumberSharingMode() == null
133 : r
.getProfile().getPhoneNumberSharingMode()
134 == PhoneNumberSharingMode
.EVERYBODY
,
135 r
.getDiscoverable());
136 return new JsonContact(address
.number().orElse(null),
137 address
.uuid().map(UUID
::toString
).orElse(null),
138 address
.username().orElse(null),
141 contact
.familyName(),
143 contact
.nickNameGivenName(),
144 contact
.nickNameFamilyName(),
149 contact
.messageExpirationTime(),
150 r
.getContact().isProfileSharingEnabled(),
151 r
.getContact().unregisteredTimestamp() != null,
152 r
.getProfile() == null
154 : new JsonContact
.JsonProfile(r
.getProfile().getLastUpdateTimestamp(),
155 r
.getProfile().getGivenName(),
156 r
.getProfile().getFamilyName(),
157 r
.getProfile().getAbout(),
158 r
.getProfile().getAboutEmoji(),
159 r
.getProfile().getAvatarUrlPath() != null,
160 r
.getProfile().getMobileCoinAddress() == null
162 : Base64
.getEncoder()
163 .encodeToString(r
.getProfile().getMobileCoinAddress())),
166 writer
.write(jsonContacts
);