mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
parent
c9f5550d18
commit
6f5e72119e
11 changed files with 274 additions and 3 deletions
|
@ -98,6 +98,13 @@ public interface Manager extends Closeable {
|
|||
|
||||
void updateAccountAttributes(String deviceName) throws IOException;
|
||||
|
||||
void updateConfiguration(
|
||||
final Boolean readReceipts,
|
||||
final Boolean unidentifiedDeliveryIndicators,
|
||||
final Boolean typingIndicators,
|
||||
final Boolean linkPreviews
|
||||
) throws IOException, NotMasterDeviceException;
|
||||
|
||||
void setProfile(
|
||||
String givenName, String familyName, String about, String aboutEmoji, Optional<File> avatar
|
||||
) throws IOException;
|
||||
|
|
|
@ -320,6 +320,31 @@ public class ManagerImpl implements Manager {
|
|||
account.isDiscoverableByPhoneNumber());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConfiguration(
|
||||
final Boolean readReceipts,
|
||||
final Boolean unidentifiedDeliveryIndicators,
|
||||
final Boolean typingIndicators,
|
||||
final Boolean linkPreviews
|
||||
) throws IOException, NotMasterDeviceException {
|
||||
if (!account.isMasterDevice()) {
|
||||
throw new NotMasterDeviceException();
|
||||
}
|
||||
if (readReceipts != null) {
|
||||
account.getConfigurationStore().setReadReceipts(readReceipts);
|
||||
}
|
||||
if (unidentifiedDeliveryIndicators != null) {
|
||||
account.getConfigurationStore().setUnidentifiedDeliveryIndicators(unidentifiedDeliveryIndicators);
|
||||
}
|
||||
if (typingIndicators != null) {
|
||||
account.getConfigurationStore().setTypingIndicators(typingIndicators);
|
||||
}
|
||||
if (linkPreviews != null) {
|
||||
account.getConfigurationStore().setLinkPreviews(linkPreviews);
|
||||
}
|
||||
syncHelper.sendConfigurationMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param givenName if null, the previous givenName will be kept
|
||||
* @param familyName if null, the previous familyName will be kept
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package org.asamk.signal.manager.actions;
|
||||
|
||||
import org.asamk.signal.manager.jobs.Context;
|
||||
|
||||
public class SendSyncConfigurationAction implements HandleAction {
|
||||
|
||||
private static final SendSyncConfigurationAction INSTANCE = new SendSyncConfigurationAction();
|
||||
|
||||
private SendSyncConfigurationAction() {
|
||||
}
|
||||
|
||||
public static SendSyncConfigurationAction create() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Context context) throws Throwable {
|
||||
context.getSyncHelper().sendConfigurationMessage();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package org.asamk.signal.manager.configuration;
|
||||
|
||||
public class ConfigurationStore {
|
||||
|
||||
private final Saver saver;
|
||||
|
||||
private Boolean readReceipts;
|
||||
private Boolean unidentifiedDeliveryIndicators;
|
||||
private Boolean typingIndicators;
|
||||
private Boolean linkPreviews;
|
||||
|
||||
public ConfigurationStore(final Saver saver) {
|
||||
this.saver = saver;
|
||||
}
|
||||
|
||||
public static ConfigurationStore fromStorage(Storage storage, Saver saver) {
|
||||
final var store = new ConfigurationStore(saver);
|
||||
store.readReceipts = storage.readReceipts;
|
||||
store.unidentifiedDeliveryIndicators = storage.unidentifiedDeliveryIndicators;
|
||||
store.typingIndicators = storage.typingIndicators;
|
||||
store.linkPreviews = storage.linkPreviews;
|
||||
return store;
|
||||
}
|
||||
|
||||
public Boolean getReadReceipts() {
|
||||
return readReceipts;
|
||||
}
|
||||
|
||||
public void setReadReceipts(final boolean readReceipts) {
|
||||
this.readReceipts = readReceipts;
|
||||
saver.save(toStorage());
|
||||
}
|
||||
|
||||
public Boolean getUnidentifiedDeliveryIndicators() {
|
||||
return unidentifiedDeliveryIndicators;
|
||||
}
|
||||
|
||||
public void setUnidentifiedDeliveryIndicators(final boolean unidentifiedDeliveryIndicators) {
|
||||
this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
|
||||
saver.save(toStorage());
|
||||
}
|
||||
|
||||
public Boolean getTypingIndicators() {
|
||||
return typingIndicators;
|
||||
}
|
||||
|
||||
public void setTypingIndicators(final boolean typingIndicators) {
|
||||
this.typingIndicators = typingIndicators;
|
||||
saver.save(toStorage());
|
||||
}
|
||||
|
||||
public Boolean getLinkPreviews() {
|
||||
return linkPreviews;
|
||||
}
|
||||
|
||||
public void setLinkPreviews(final boolean linkPreviews) {
|
||||
this.linkPreviews = linkPreviews;
|
||||
saver.save(toStorage());
|
||||
}
|
||||
|
||||
private Storage toStorage() {
|
||||
return new Storage(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
|
||||
}
|
||||
|
||||
public static final class Storage {
|
||||
|
||||
public Boolean readReceipts;
|
||||
public Boolean unidentifiedDeliveryIndicators;
|
||||
public Boolean typingIndicators;
|
||||
public Boolean linkPreviews;
|
||||
|
||||
// For deserialization
|
||||
private Storage() {
|
||||
}
|
||||
|
||||
public Storage(
|
||||
final Boolean readReceipts,
|
||||
final Boolean unidentifiedDeliveryIndicators,
|
||||
final Boolean typingIndicators,
|
||||
final Boolean linkPreviews
|
||||
) {
|
||||
this.readReceipts = readReceipts;
|
||||
this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
|
||||
this.typingIndicators = typingIndicators;
|
||||
this.linkPreviews = linkPreviews;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Saver {
|
||||
|
||||
void save(Storage storage);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
|
|||
import org.asamk.signal.manager.actions.SendReceiptAction;
|
||||
import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncConfigurationAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncContactsAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncGroupsAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncKeysAction;
|
||||
|
@ -271,7 +272,9 @@ public final class IncomingMessageHandler {
|
|||
if (rm.isKeysRequest()) {
|
||||
actions.add(SendSyncKeysAction.create());
|
||||
}
|
||||
// TODO Handle rm.isConfigurationRequest();
|
||||
if (rm.isConfigurationRequest()) {
|
||||
actions.add(SendSyncConfigurationAction.create());
|
||||
}
|
||||
}
|
||||
if (syncMessage.getGroups().isPresent()) {
|
||||
logger.warn("Received a group v1 sync message, that can't be handled anymore, ignoring.");
|
||||
|
@ -353,7 +356,13 @@ public final class IncomingMessageHandler {
|
|||
}
|
||||
}
|
||||
if (syncMessage.getConfiguration().isPresent()) {
|
||||
// TODO
|
||||
final var configurationMessage = syncMessage.getConfiguration().get();
|
||||
account.getConfigurationStore().setReadReceipts(configurationMessage.getReadReceipts().orNull());
|
||||
account.getConfigurationStore().setLinkPreviews(configurationMessage.getLinkPreviews().orNull());
|
||||
account.getConfigurationStore().setTypingIndicators(configurationMessage.getTypingIndicators().orNull());
|
||||
account.getConfigurationStore()
|
||||
.setUnidentifiedDeliveryIndicators(configurationMessage.getUnidentifiedDeliveryIndicators()
|
||||
.orNull());
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
|||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStream;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream;
|
||||
|
@ -221,6 +222,15 @@ public class SyncHelper {
|
|||
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forKeys(keysMessage));
|
||||
}
|
||||
|
||||
public void sendConfigurationMessage() throws IOException {
|
||||
final var config = account.getConfigurationStore();
|
||||
var configurationMessage = new ConfigurationMessage(Optional.fromNullable(config.getReadReceipts()),
|
||||
Optional.fromNullable(config.getUnidentifiedDeliveryIndicators()),
|
||||
Optional.fromNullable(config.getTypingIndicators()),
|
||||
Optional.fromNullable(config.getLinkPreviews()));
|
||||
sendHelper.sendSyncMessage(SignalServiceSyncMessage.forConfiguration(configurationMessage));
|
||||
}
|
||||
|
||||
public void handleSyncDeviceContacts(final InputStream input) throws IOException {
|
||||
final var s = new DeviceContactsInputStream(input);
|
||||
DeviceContact c;
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.asamk.signal.manager.TrustLevel;
|
||||
import org.asamk.signal.manager.configuration.ConfigurationStore;
|
||||
import org.asamk.signal.manager.groups.GroupId;
|
||||
import org.asamk.signal.manager.storage.contacts.ContactsStore;
|
||||
import org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore;
|
||||
|
@ -103,6 +104,8 @@ public class SignalAccount implements Closeable {
|
|||
private RecipientStore recipientStore;
|
||||
private StickerStore stickerStore;
|
||||
private StickerStore.Storage stickerStoreStorage;
|
||||
private ConfigurationStore configurationStore;
|
||||
private ConfigurationStore.Storage configurationStoreStorage;
|
||||
|
||||
private MessageCache messageCache;
|
||||
|
||||
|
@ -159,6 +162,7 @@ public class SignalAccount implements Closeable {
|
|||
account.recipientStore,
|
||||
account::saveGroupStore);
|
||||
account.stickerStore = new StickerStore(account::saveStickerStore);
|
||||
account.configurationStore = new ConfigurationStore(account::saveConfigurationStore);
|
||||
|
||||
account.registered = false;
|
||||
|
||||
|
@ -267,6 +271,7 @@ public class SignalAccount implements Closeable {
|
|||
account.recipientStore,
|
||||
account::saveGroupStore);
|
||||
account.stickerStore = new StickerStore(account::saveStickerStore);
|
||||
account.configurationStore = new ConfigurationStore(account::saveConfigurationStore);
|
||||
|
||||
account.recipientStore.resolveRecipientTrusted(account.getSelfAddress());
|
||||
account.migrateLegacyConfigs();
|
||||
|
@ -491,6 +496,15 @@ public class SignalAccount implements Closeable {
|
|||
stickerStore = new StickerStore(this::saveStickerStore);
|
||||
}
|
||||
|
||||
if (rootNode.hasNonNull("configurationStore")) {
|
||||
configurationStoreStorage = jsonProcessor.convertValue(rootNode.get("configurationStore"),
|
||||
ConfigurationStore.Storage.class);
|
||||
configurationStore = ConfigurationStore.fromStorage(configurationStoreStorage,
|
||||
this::saveConfigurationStore);
|
||||
} else {
|
||||
configurationStore = new ConfigurationStore(this::saveConfigurationStore);
|
||||
}
|
||||
|
||||
migratedLegacyConfig = loadLegacyThreadStore(rootNode) || migratedLegacyConfig;
|
||||
|
||||
if (migratedLegacyConfig) {
|
||||
|
@ -677,6 +691,11 @@ public class SignalAccount implements Closeable {
|
|||
save();
|
||||
}
|
||||
|
||||
private void saveConfigurationStore(ConfigurationStore.Storage storage) {
|
||||
this.configurationStoreStorage = storage;
|
||||
save();
|
||||
}
|
||||
|
||||
private void save() {
|
||||
synchronized (fileChannel) {
|
||||
var rootNode = jsonProcessor.createObjectNode();
|
||||
|
@ -707,7 +726,8 @@ public class SignalAccount implements Closeable {
|
|||
profileKey == null ? null : Base64.getEncoder().encodeToString(profileKey.serialize()))
|
||||
.put("registered", registered)
|
||||
.putPOJO("groupStore", groupStoreStorage)
|
||||
.putPOJO("stickerStore", stickerStoreStorage);
|
||||
.putPOJO("stickerStore", stickerStoreStorage)
|
||||
.putPOJO("configurationStore", configurationStoreStorage);
|
||||
try {
|
||||
try (var output = new ByteArrayOutputStream()) {
|
||||
// Write to memory first to prevent corrupting the file in case of serialization errors
|
||||
|
@ -797,6 +817,10 @@ public class SignalAccount implements Closeable {
|
|||
return senderKeyStore;
|
||||
}
|
||||
|
||||
public ConfigurationStore getConfigurationStore() {
|
||||
return configurationStore;
|
||||
}
|
||||
|
||||
public MessageCache getMessageCache() {
|
||||
return messageCache;
|
||||
}
|
||||
|
|
|
@ -113,6 +113,23 @@ Can fix problems with receiving messages.
|
|||
*-n* NAME, *--device-name* NAME::
|
||||
Set a new device name for the main or linked device
|
||||
|
||||
=== updateConfiguration
|
||||
|
||||
Update signal configs and sync them to linked devices.
|
||||
This command only works on the main devices.
|
||||
|
||||
*--read-receipts* {true,false}::
|
||||
Indicates if Signal should send read receipts.
|
||||
|
||||
*--unidentified-delivery-indicators* {true,false}::
|
||||
Indicates if Signal should show unidentified delivery indicators.
|
||||
|
||||
*--typing-indicators* {true,false}::
|
||||
Indicates if Signal should send/show typing indicators.
|
||||
|
||||
*--link-previews* {true,false}::
|
||||
Indicates if Signal should generate link previews.
|
||||
|
||||
=== setPin
|
||||
|
||||
Set a registration lock pin, to prevent others from registering this number.
|
||||
|
|
|
@ -39,6 +39,7 @@ public class Commands {
|
|||
addCommand(new UnblockCommand());
|
||||
addCommand(new UnregisterCommand());
|
||||
addCommand(new UpdateAccountCommand());
|
||||
addCommand(new UpdateConfigurationCommand());
|
||||
addCommand(new UpdateContactCommand());
|
||||
addCommand(new UpdateGroupCommand());
|
||||
addCommand(new UpdateProfileCommand());
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package org.asamk.signal.commands;
|
||||
|
||||
import net.sourceforge.argparse4j.inf.Namespace;
|
||||
import net.sourceforge.argparse4j.inf.Subparser;
|
||||
|
||||
import org.asamk.signal.OutputWriter;
|
||||
import org.asamk.signal.commands.exceptions.CommandException;
|
||||
import org.asamk.signal.commands.exceptions.IOErrorException;
|
||||
import org.asamk.signal.commands.exceptions.UserErrorException;
|
||||
import org.asamk.signal.manager.Manager;
|
||||
import org.asamk.signal.manager.NotMasterDeviceException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UpdateConfigurationCommand implements JsonRpcLocalCommand {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "updateConfiguration";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachToSubparser(final Subparser subparser) {
|
||||
subparser.help("Update signal configs and sync them to linked devices.");
|
||||
subparser.addArgument("--read-receipts")
|
||||
.type(Boolean.class)
|
||||
.help("Indicates if Signal should send read receipts.");
|
||||
subparser.addArgument("--unidentified-delivery-indicators")
|
||||
.type(Boolean.class)
|
||||
.help("Indicates if Signal should show unidentified delivery indicators.");
|
||||
subparser.addArgument("--typing-indicators")
|
||||
.type(Boolean.class)
|
||||
.help("Indicates if Signal should send/show typing indicators.");
|
||||
subparser.addArgument("--link-previews")
|
||||
.type(Boolean.class)
|
||||
.help("Indicates if Signal should generate link previews.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(
|
||||
final Namespace ns, final Manager m, final OutputWriter outputWriter
|
||||
) throws CommandException {
|
||||
final var readReceipts = ns.getBoolean("read-receipts");
|
||||
final var unidentifiedDeliveryIndicators = ns.getBoolean("unidentified-delivery-indicators");
|
||||
final var typingIndicators = ns.getBoolean("typing-indicators");
|
||||
final var linkPreviews = ns.getBoolean("link-previews");
|
||||
try {
|
||||
m.updateConfiguration(readReceipts, unidentifiedDeliveryIndicators, typingIndicators, linkPreviews);
|
||||
} catch (IOException e) {
|
||||
throw new IOErrorException("UpdateAccount error: " + e.getMessage(), e);
|
||||
} catch (NotMasterDeviceException e) {
|
||||
throw new UserErrorException("This command doesn't work on linked devices.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,6 +93,16 @@ public class DbusManagerImpl implements Manager {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConfiguration(
|
||||
final Boolean readReceipts,
|
||||
final Boolean unidentifiedDeliveryIndicators,
|
||||
final Boolean typingIndicators,
|
||||
final Boolean linkPreviews
|
||||
) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProfile(
|
||||
final String givenName,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue