Commit ce2cc0eb authored by Ted Morse's avatar Ted Morse

Allow sending "read" receipts for messages

More cosmetic than strictly necessary, but messages sent to a
signald-based bot are never marked as read. This commit allows the
messages to be marked as read.
parent 5b97db31
......@@ -121,6 +121,18 @@ Completes the registration process, by providing a verification code sent after
| `code` | string | yes | The verification code. The `-` in the middle code is optional.
### `mark_read`
Mark a received message as "read" by sending a receipt message.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `username` | `string` | yes | The local account to use to send the read receipt. |
| `recipientNumber` | `string` | yes | The full number that sent the original message. |
| `timestamps` | `list of numbers` | yes | The timestamps of the messages to mark as read. |
| `when` | `number` | no | The timestamp of when the message was read. If omitted, defaults to the current time. |
### `add_device`
Adds another device to a signal account that signald controls the master device on. Possible values are:
......
......@@ -44,6 +44,8 @@ class JsonRequest {
public ContactInfo contact;
public String captcha;
public String name;
public List<Long> timestamps;
public long when;
JsonRequest() {}
}
......@@ -1094,6 +1094,28 @@ class Manager {
}
}
public SendMessageResult sendReceipt(SignalServiceReceiptMessage message, String recipient) throws IOException {
SignalServiceAddress address = getSignalServiceAddress(recipient);
if (address == null) {
save();
return null;
}
try {
SignalServiceMessageSender messageSender = new SignalServiceMessageSender(serviceConfiguration, username, password, deviceId, signalProtocolStore, USER_AGENT, true, Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.<SignalServiceMessageSender.EventListener>absent());
try {
messageSender.sendReceipt(address, getAccessFor(address), message);
return null;
} catch (UntrustedIdentityException e) {
signalProtocolStore.saveIdentity(e.getE164Number(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
return SendMessageResult.identityFailure(address, e.getIdentityKey());
}
} finally {
save();
}
}
private List<SendMessageResult> sendMessage(SignalServiceDataMessage.Builder messageBuilder, Collection<String> recipients) throws IOException {
Set<SignalServiceAddress> recipientsTS = getSignalServiceAddresses(recipients);
if (recipientsTS == null) {
......@@ -1170,17 +1192,24 @@ class Manager {
}
}
private SignalServiceAddress getSignalServiceAddress(String recipient) {
try {
return getPushAddress(recipient);
} catch (InvalidNumberException e) {
logger.warn("Failed to add recipient \"" + recipient + "\": " + e.getMessage());
logger.warn("Aborting sending.");
save();
return null;
}
}
private Set<SignalServiceAddress> getSignalServiceAddresses(Collection<String> recipients) {
Set<SignalServiceAddress> recipientsTS = new HashSet<>(recipients.size());
for (String recipient : recipients) {
try {
recipientsTS.add(getPushAddress(recipient));
} catch (InvalidNumberException e) {
logger.warn("Failed to add recipient \"" + recipient + "\": " + e.getMessage());
logger.warn("Aborting sending.");
save();
SignalServiceAddress addr = getSignalServiceAddress(recipient);
if (addr == null)
return null;
}
recipientsTS.add(addr);
}
return recipientsTS;
}
......
......@@ -26,6 +26,7 @@ import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
import org.whispersystems.signalservice.api.push.exceptions.NetworkFailureException;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
......@@ -135,6 +136,9 @@ public class SocketHandler implements Runnable {
case "send":
send(request);
break;
case "mark_read":
markRead(request);
break;
case "subscribe":
subscribe(request);
break;
......@@ -287,6 +291,28 @@ public class SocketHandler implements Runnable {
}
}
private void markRead(JsonRequest request) throws IOException, NoSuchAccountException {
logger.info("Mark as Read");
Manager m = Manager.get(request.username);
if(request.when == 0) {
request.when = System.currentTimeMillis();
}
SignalServiceReceiptMessage message = new SignalServiceReceiptMessage(
SignalServiceReceiptMessage.Type.READ,
request.timestamps,
request.when);
SendMessageResult result = m.sendReceipt(message, request.recipientNumber);
if(result != null) {
SendMessageResult.IdentityFailure identityFailure = result.getIdentityFailure();
if(identityFailure != null) {
this.reply("untrusted_identity", new JsonUntrustedIdentityException(identityFailure.getIdentityKey(), result.getAddress().getNumber(), m, request), request.id);
}
}
}
private void listAccounts(JsonRequest request) throws IOException {
JsonAccountList accounts = new JsonAccountList(subscribedAccounts);
this.reply("account_list", accounts, request.id);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment