From 7b0744ec758b1ccbe3c9ce5a1e6dea5d7e2313da Mon Sep 17 00:00:00 2001 From: AsamK Date: Sat, 11 Nov 2023 11:35:23 +0100 Subject: [PATCH] Add --hide parameter to removeContact command --- .../org/asamk/signal/manager/Manager.java | 2 + .../org/asamk/signal/manager/api/Contact.java | 45 ++++++++++++------- .../signal/manager/helper/ContactHelper.java | 11 ++++- .../signal/manager/helper/SendHelper.java | 4 +- .../signal/manager/helper/StorageHelper.java | 9 ++-- .../signal/manager/helper/SyncHelper.java | 4 +- .../signal/manager/internal/ManagerImpl.java | 9 ++++ .../manager/storage/AccountDatabase.java | 10 ++++- .../signal/manager/storage/SignalAccount.java | 1 + .../recipients/LegacyRecipientStore2.java | 3 +- .../storage/recipients/RecipientStore.java | 13 +++--- man/signal-cli.1.adoc | 3 ++ .../signal/commands/RemoveContactCommand.java | 11 ++++- .../asamk/signal/dbus/DbusManagerImpl.java | 6 ++- 14 files changed, 96 insertions(+), 35 deletions(-) diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index da69a8da..6153000b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -193,6 +193,8 @@ public interface Manager extends Closeable { SendMessageResults sendEndSessionMessage(Set recipients) throws IOException; + void hideRecipient(RecipientIdentifier.Single recipient); + void deleteRecipient(RecipientIdentifier.Single recipient); void deleteContact(RecipientIdentifier.Single recipient); diff --git a/lib/src/main/java/org/asamk/signal/manager/api/Contact.java b/lib/src/main/java/org/asamk/signal/manager/api/Contact.java index e6dd5f93..f84c667b 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/Contact.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/Contact.java @@ -9,7 +9,8 @@ public record Contact( int messageExpirationTime, boolean isBlocked, boolean isArchived, - boolean isProfileSharingEnabled + boolean isProfileSharingEnabled, + boolean isHidden ) { private Contact(final Builder builder) { @@ -17,9 +18,10 @@ public record Contact( builder.familyName, builder.color, builder.messageExpirationTime, - builder.blocked, - builder.archived, - builder.profileSharingEnabled); + builder.isBlocked, + builder.isArchived, + builder.isProfileSharingEnabled, + builder.isHidden); } public static Builder newBuilder() { @@ -32,9 +34,10 @@ public record Contact( builder.familyName = copy.familyName(); builder.color = copy.color(); builder.messageExpirationTime = copy.messageExpirationTime(); - builder.blocked = copy.isBlocked(); - builder.archived = copy.isArchived(); - builder.profileSharingEnabled = copy.isProfileSharingEnabled(); + builder.isBlocked = copy.isBlocked(); + builder.isArchived = copy.isArchived(); + builder.isProfileSharingEnabled = copy.isProfileSharingEnabled(); + builder.isHidden = copy.isHidden(); return builder; } @@ -59,13 +62,18 @@ public record Contact( private String familyName; private String color; private int messageExpirationTime; - private boolean blocked; - private boolean archived; - private boolean profileSharingEnabled; + private boolean isBlocked; + private boolean isArchived; + private boolean isProfileSharingEnabled; + private boolean isHidden; private Builder() { } + public static Builder newBuilder() { + return new Builder(); + } + public Builder withGivenName(final String val) { givenName = val; return this; @@ -86,18 +94,23 @@ public record Contact( return this; } - public Builder withBlocked(final boolean val) { - blocked = val; + public Builder withIsBlocked(final boolean val) { + isBlocked = val; + return this; + } + + public Builder withIsArchived(final boolean val) { + isArchived = val; return this; } - public Builder withArchived(final boolean val) { - archived = val; + public Builder withIsProfileSharingEnabled(final boolean val) { + isProfileSharingEnabled = val; return this; } - public Builder withProfileSharingEnabled(final boolean val) { - profileSharingEnabled = val; + public Builder withIsHidden(final boolean val) { + isHidden = val; return this; } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java index 4e96bacc..609c2bcf 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ContactHelper.java @@ -20,6 +20,7 @@ public class ContactHelper { public void setContactName(final RecipientId recipientId, final String givenName, final String familyName) { var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); + builder.withIsHidden(false); if (givenName != null) { builder.withGivenName(givenName); } @@ -43,8 +44,14 @@ public class ContactHelper { var contact = account.getContactStore().getContact(recipientId); final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); if (blocked) { - builder.withProfileSharingEnabled(false); + builder.withIsProfileSharingEnabled(false); } - account.getContactStore().storeContact(recipientId, builder.withBlocked(blocked).build()); + account.getContactStore().storeContact(recipientId, builder.withIsBlocked(blocked).build()); + } + + public void setContactHidden(RecipientId recipientId, boolean hidden) { + var contact = account.getContactStore().getContact(recipientId); + final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); + account.getContactStore().storeContact(recipientId, builder.withIsHidden(hidden).build()); } } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index 23607815..5ffb9646 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -78,9 +78,9 @@ public class SendHelper { Optional editTargetTimestamp ) { var contact = account.getContactStore().getContact(recipientId); - if (contact == null || !contact.isProfileSharingEnabled()) { + if (contact == null || !contact.isProfileSharingEnabled() || contact.isHidden()) { final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); - contact = contactBuilder.withProfileSharingEnabled(true).build(); + contact = contactBuilder.withIsProfileSharingEnabled(true).withIsHidden(false).build(); account.getContactStore().storeContact(recipientId, contact); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 35821c5b..f2518568 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -116,11 +116,13 @@ public class StorageHelper { final var blocked = contact != null && contact.isBlocked(); final var profileShared = contact != null && contact.isProfileSharingEnabled(); final var archived = contact != null && contact.isArchived(); + final var hidden = contact != null && contact.isHidden(); final var contactGivenName = contact == null ? null : contact.givenName(); final var contactFamilyName = contact == null ? null : contact.familyName(); if (blocked != contactRecord.isBlocked() || profileShared != contactRecord.isProfileSharingEnabled() || archived != contactRecord.isArchived() + || hidden != contactRecord.isHidden() || ( contactRecord.getSystemGivenName().isPresent() && !contactRecord.getSystemGivenName() .get() @@ -133,9 +135,10 @@ public class StorageHelper { )) { logger.debug("Storing new or updated contact {}", recipientId); final var contactBuilder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact); - final var newContact = contactBuilder.withBlocked(contactRecord.isBlocked()) - .withProfileSharingEnabled(contactRecord.isProfileSharingEnabled()) - .withArchived(contactRecord.isArchived()); + final var newContact = contactBuilder.withIsBlocked(contactRecord.isBlocked()) + .withIsProfileSharingEnabled(contactRecord.isProfileSharingEnabled()) + .withIsArchived(contactRecord.isArchived()) + .withIsHidden(contactRecord.isHidden()); if (contactRecord.getSystemGivenName().isPresent() || contactRecord.getSystemFamilyName().isPresent()) { newContact.withGivenName(contactRecord.getSystemGivenName().orElse(null)) .withFamilyName(contactRecord.getSystemFamilyName().orElse(null)); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java index 0badc544..f6392ea9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java @@ -339,8 +339,8 @@ public class SyncHelper { if (c.getExpirationTimer().isPresent()) { builder.withMessageExpirationTime(c.getExpirationTimer().get()); } - builder.withBlocked(c.isBlocked()); - builder.withArchived(c.isArchived()); + builder.withIsBlocked(c.isBlocked()); + builder.withIsArchived(c.isArchived()); account.getContactStore().storeContact(recipientId, builder.build()); if (c.getAvatar().isPresent()) { diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java index af2c6558..cc67229a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/ManagerImpl.java @@ -840,6 +840,15 @@ public class ManagerImpl implements Manager { } } + @Override + public void hideRecipient(final RecipientIdentifier.Single recipient) { + final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient); + if (recipientIdOptional.isPresent()) { + context.getContactHelper().setContactHidden(recipientIdOptional.get(), true); + account.removeRecipient(recipientIdOptional.get()); + } + } + @Override public void deleteRecipient(final RecipientIdentifier.Single recipient) { final var recipientIdOptional = context.getRecipientHelper().resolveRecipientOptional(recipient); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java b/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java index 94dff8fc..43a146a7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java @@ -32,7 +32,7 @@ import java.util.UUID; public class AccountDatabase extends Database { private static final Logger logger = LoggerFactory.getLogger(AccountDatabase.class); - private static final long DATABASE_VERSION = 18; + private static final long DATABASE_VERSION = 19; private AccountDatabase(final HikariDataSource dataSource) { super(logger, DATABASE_VERSION, dataSource); @@ -531,5 +531,13 @@ public class AccountDatabase extends Database { """); } } + if (oldVersion < 19) { + logger.debug("Updating database: Adding contact hidden column"); + try (final var statement = connection.createStatement()) { + statement.executeUpdate(""" + ALTER TABLE recipient ADD COLUMN hidden INTEGER NOT NULL DEFAULT FALSE; + """); + } + } } } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index dee71275..4b2e6403 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -826,6 +826,7 @@ public class SignalAccount implements Closeable { contact.messageExpirationTime, contact.blocked, contact.archived, + false, false)); // Store profile keys only in profile store diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore2.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore2.java index 2f714c6f..55231cb7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore2.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/LegacyRecipientStore2.java @@ -44,7 +44,8 @@ public class LegacyRecipientStore2 { r.contact.messageExpirationTime, r.contact.blocked, r.contact.archived, - r.contact.profileSharingEnabled); + r.contact.profileSharingEnabled, + false); } ProfileKey profileKey = null; diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java index c446e468..bc7475b7 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientStore.java @@ -70,6 +70,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re blocked INTEGER NOT NULL DEFAULT FALSE, archived INTEGER NOT NULL DEFAULT FALSE, profile_sharing INTEGER NOT NULL DEFAULT FALSE, + hidden INTEGER NOT NULL DEFAULT FALSE, profile_last_update_timestamp INTEGER NOT NULL DEFAULT 0, profile_given_name TEXT, @@ -318,9 +319,9 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re public List> getContacts() { final var sql = ( """ - SELECT r._id, r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived + SELECT r._id, r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden FROM %s r - WHERE (r.number IS NOT NULL OR r.uuid IS NOT NULL) AND %s + WHERE (r.number IS NOT NULL OR r.uuid IS NOT NULL) AND %s AND r.hidden = FALSE """ ).formatted(TABLE_RECIPIENT, SQL_IS_CONTACT); try (final var connection = database.getConnection()) { @@ -342,6 +343,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re final var sqlWhere = new ArrayList(); if (onlyContacts) { sqlWhere.add("(" + SQL_IS_CONTACT + ")"); + sqlWhere.add("r.hidden = FALSE"); } if (blocked.isPresent()) { sqlWhere.add("r.blocked = ?"); @@ -357,7 +359,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re SELECT r._id, r.number, r.uuid, r.pni, r.username, r.profile_key, r.profile_key_credential, - r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, + r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden, r.profile_last_update_timestamp, r.profile_given_name, r.profile_family_name, r.profile_about, r.profile_about_emoji, r.profile_avatar_url_path, r.profile_mobile_coin_address, r.profile_unidentified_access_mode, r.profile_capabilities FROM %s r WHERE (r.number IS NOT NULL OR r.uuid IS NOT NULL) AND %s @@ -962,7 +964,7 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re private Contact getContact(final Connection connection, final RecipientId recipientId) throws SQLException { final var sql = ( """ - SELECT r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived + SELECT r.given_name, r.family_name, r.expiration_time, r.profile_sharing, r.color, r.blocked, r.archived, r.hidden FROM %s r WHERE r._id = ? AND (%s) """ @@ -1053,7 +1055,8 @@ public class RecipientStore implements RecipientIdCreator, RecipientResolver, Re resultSet.getInt("expiration_time"), resultSet.getBoolean("blocked"), resultSet.getBoolean("archived"), - resultSet.getBoolean("profile_sharing")); + resultSet.getBoolean("profile_sharing"), + resultSet.getBoolean("hidden")); } private Profile getProfileFromResultSet(ResultSet resultSet) throws SQLException { diff --git a/man/signal-cli.1.adoc b/man/signal-cli.1.adoc index 3a28219a..4814d28a 100644 --- a/man/signal-cli.1.adoc +++ b/man/signal-cli.1.adoc @@ -613,6 +613,9 @@ Remove the info of a given contact NUMBER:: Specify the contact phone number. +*--hide*:: +Hide the contact in the contact list, but keep the data. + *--forget*:: Delete all data associated with this contact, including identity keys and sessions. diff --git a/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java b/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java index c4a3d7d8..aca52778 100644 --- a/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java +++ b/src/main/java/org/asamk/signal/commands/RemoveContactCommand.java @@ -20,7 +20,11 @@ public class RemoveContactCommand implements JsonRpcLocalCommand { public void attachToSubparser(final Subparser subparser) { subparser.help("Remove the details of a given contact"); subparser.addArgument("recipient").help("Contact number"); - subparser.addArgument("--forget") + final var mut = subparser.addMutuallyExclusiveGroup(); + mut.addArgument("--hide") + .action(Arguments.storeTrue()) + .help("Hide the contact in the contact list, but keep the data."); + mut.addArgument("--forget") .action(Arguments.storeTrue()) .help("Delete all data associated with this contact, including identity keys and sessions."); } @@ -32,8 +36,11 @@ public class RemoveContactCommand implements JsonRpcLocalCommand { var recipientString = ns.getString("recipient"); var recipient = CommandUtil.getSingleRecipientIdentifier(recipientString, m.getSelfNumber()); + var hide = Boolean.TRUE == ns.getBoolean("hide"); var forget = Boolean.TRUE == ns.getBoolean("forget"); - if (forget) { + if (hide) { + m.hideRecipient(recipient); + } else if (forget) { m.deleteRecipient(recipient); } else { m.deleteContact(recipient); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index ea09dba4..a3f18393 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -452,6 +452,10 @@ public class DbusManagerImpl implements Manager { return new SendMessageResults(0, Map.of()); } + public void hideRecipient(final RecipientIdentifier.Single recipient) { + throw new UnsupportedOperationException(); + } + @Override public void deleteRecipient(final RecipientIdentifier.Single recipient) { signal.deleteRecipient(recipient.getIdentifier()); @@ -653,7 +657,7 @@ public class DbusManagerImpl implements Manager { } return Recipient.newBuilder() .withAddress(new RecipientAddress(null, n)) - .withContact(new Contact(contactName, null, null, 0, contactBlocked, false, false)) + .withContact(new Contact(contactName, null, null, 0, contactBlocked, false, false, false)) .build(); }).filter(Objects::nonNull).toList(); } -- 2.50.1