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(
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
),
60 Optional
.ofNullable(name
));
62 final var detailed
= Boolean
.TRUE
.equals(ns
.getBoolean("detailed"));
63 final var internal
= Boolean
.TRUE
.equals(ns
.getBoolean("internal"));
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();
71 "Number: {} ACI: {} Name: {} Profile name: {} Username: {} Color: {} Blocked: {} Message expiration: {}",
72 r
.getAddress().number().orElse(""),
73 r
.getAddress().aci().orElse(""),
75 profile
.getDisplayName(),
76 r
.getAddress().username().orElse(""),
77 Optional
.ofNullable(contact
.color()).orElse(""),
79 contact
.messageExpirationTime() == 0 ?
"disabled" : contact
.messageExpirationTime() + "s");
81 writer
.indentedWriter()
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);
99 writer
.indentedWriter()
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
106 : r
.getProfile().getUnidentifiedAccessMode().name()).orElse(""),
107 r
.getProfile().getPhoneNumberSharingMode() == null
109 : String
.valueOf(r
.getProfile().getPhoneNumberSharingMode()
110 == PhoneNumberSharingMode
.EVERYBODY
),
111 r
.getDiscoverable() == null ?
"" : String
.valueOf(r
.getDiscoverable()));
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
121 : new JsonContact
.JsonInternal(r
.getProfile()
126 r
.getProfile().getUnidentifiedAccessMode() == Profile
.UnidentifiedAccessMode
.UNKNOWN
128 : r
.getProfile().getUnidentifiedAccessMode().name(),
129 r
.getProfile().getPhoneNumberSharingMode() == 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),
139 contact
.familyName(),
141 contact
.nickNameGivenName(),
142 contact
.nickNameFamilyName(),
147 contact
.messageExpirationTime(),
148 r
.getContact().isProfileSharingEnabled(),
149 r
.getContact().unregisteredTimestamp() != null,
150 r
.getProfile() == 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
160 : Base64
.getEncoder()
161 .encodeToString(r
.getProfile().getMobileCoinAddress())),
164 writer
.write(jsonContacts
);