1 package org
.asamk
.signal
.util
;
3 import org
.asamk
.signal
.commands
.exceptions
.CommandException
;
4 import org
.asamk
.signal
.commands
.exceptions
.RateLimitErrorException
;
5 import org
.asamk
.signal
.commands
.exceptions
.UntrustedKeyErrorException
;
6 import org
.asamk
.signal
.commands
.exceptions
.UserErrorException
;
7 import org
.asamk
.signal
.json
.JsonSendMessageResult
;
8 import org
.asamk
.signal
.manager
.api
.ProofRequiredException
;
9 import org
.asamk
.signal
.manager
.api
.RateLimitException
;
10 import org
.asamk
.signal
.manager
.api
.RecipientIdentifier
;
11 import org
.asamk
.signal
.manager
.api
.SendGroupMessageResults
;
12 import org
.asamk
.signal
.manager
.api
.SendMessageResult
;
13 import org
.asamk
.signal
.manager
.api
.SendMessageResults
;
14 import org
.asamk
.signal
.output
.JsonWriter
;
15 import org
.asamk
.signal
.output
.OutputWriter
;
16 import org
.asamk
.signal
.output
.PlainTextWriter
;
18 import java
.util
.Collection
;
19 import java
.util
.List
;
21 import java
.util
.Objects
;
22 import java
.util
.stream
.Collectors
;
24 public class SendMessageResultUtils
{
26 private SendMessageResultUtils() {
29 public static void outputResult(final OutputWriter outputWriter
, final SendGroupMessageResults sendMessageResults
) {
30 switch (outputWriter
) {
31 case PlainTextWriter writer
-> {
32 var errors
= getErrorMessagesFromSendMessageResults(sendMessageResults
.results());
33 printSendMessageResultErrors(writer
, errors
);
34 writer
.println("{}", sendMessageResults
.timestamp());
36 case JsonWriter writer
-> {
37 var results
= getJsonSendMessageResults(sendMessageResults
.results());
38 writer
.write(Map
.of("timestamp", sendMessageResults
.timestamp(), "results", results
));
43 public static void outputResult(
44 final OutputWriter outputWriter
,
45 final SendMessageResults sendMessageResults
46 ) throws CommandException
{
47 switch (outputWriter
) {
48 case PlainTextWriter writer
-> {
49 var errors
= getErrorMessagesFromSendMessageResults(sendMessageResults
.results());
50 printSendMessageResultErrors(writer
, errors
);
51 writer
.println("{}", sendMessageResults
.timestamp());
53 case JsonWriter writer
-> {
54 var results
= getJsonSendMessageResults(sendMessageResults
.results());
55 writer
.write(Map
.of("timestamp", sendMessageResults
.timestamp(), "results", results
));
58 if (!sendMessageResults
.hasSuccess()) {
59 if (sendMessageResults
.hasOnlyUntrustedIdentity()) {
60 throw new UntrustedKeyErrorException("Failed to send message due to untrusted identities");
61 } else if (sendMessageResults
.hasOnlyRateLimitFailure()) {
62 throw new RateLimitErrorException("Failed to send message due to rate limiting",
63 new RateLimitException(0));
65 throw new UserErrorException("Failed to send message");
70 public static List
<String
> getErrorMessagesFromSendMessageResults(final Map
<RecipientIdentifier
, List
<SendMessageResult
>> mapResults
) {
71 return mapResults
.entrySet()
73 .flatMap(entry
-> entry
.getValue()
75 .map(SendMessageResultUtils
::getErrorMessageFromSendMessageResult
)
76 .filter(Objects
::nonNull
)
77 .map(error
-> entry
.getKey().getIdentifier() + ": " + error
))
81 public static List
<String
> getErrorMessagesFromSendMessageResults(Collection
<SendMessageResult
> results
) {
82 return results
.stream()
83 .map(SendMessageResultUtils
::getErrorMessageFromSendMessageResult
)
84 .filter(Objects
::nonNull
)
88 public static String
getErrorMessageFromSendMessageResult(SendMessageResult result
) {
89 var identifier
= result
.address().getLegacyIdentifier();
90 if (result
.proofRequiredFailure() != null) {
91 final var failure
= result
.proofRequiredFailure();
93 "CAPTCHA proof required for sending to \"%s\", available options \"%s\" with challenge token \"%s\", or wait \"%d\" seconds.\n"
95 failure
.getOptions().contains(ProofRequiredException
.Option
.CAPTCHA
)
97 To get the captcha token, go to https://signalcaptchas.org/challenge/generate.html
98 After solving the captcha, right-click on the "Open Signal" link and copy the link.
99 Use the following command to submit the captcha token:
100 signal-cli submitRateLimitChallenge --challenge CHALLENGE_TOKEN --captcha CAPTCHA_TOKEN"""
106 .map(ProofRequiredException
.Option
::toString
)
107 .collect(Collectors
.joining(", ")),
109 failure
.getRetryAfterSeconds());
110 } else if (result
.isNetworkFailure()) {
111 return String
.format("Network failure for \"%s\"", identifier
);
112 } else if (result
.isRateLimitFailure()) {
113 return String
.format("Rate limit failure for \"%s\"", identifier
);
114 } else if (result
.isUnregisteredFailure()) {
115 return String
.format("Unregistered user \"%s\"", identifier
);
116 } else if (result
.isIdentityFailure()) {
117 return String
.format("Untrusted Identity for \"%s\"", identifier
);
122 public static void printSendMessageResultErrors(PlainTextWriter writer
, List
<String
> errors
) {
123 if (errors
.isEmpty()) {
126 writer
.println("Failed to send (some) messages:");
127 for (var error
: errors
) {
128 writer
.println(error
);
132 private static List
<JsonSendMessageResult
> getJsonSendMessageResults(final Map
<RecipientIdentifier
, List
<SendMessageResult
>> mapResults
) {
133 return mapResults
.entrySet().stream().flatMap(entry
-> {
134 final var groupId
= entry
.getKey() instanceof RecipientIdentifier
.Group g ? g
.groupId() : null;
135 return entry
.getValue().stream().map(r
-> JsonSendMessageResult
.from(r
, groupId
));
139 public static List
<JsonSendMessageResult
> getJsonSendMessageResults(Collection
<SendMessageResult
> results
) {
140 return results
.stream().map(JsonSendMessageResult
::from
).toList();