mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 18:40:39 +00:00
Cache profiles for 24h before retrieving them again
This commit is contained in:
parent
0f3aa22519
commit
b94c1e50e6
6 changed files with 168 additions and 4 deletions
|
@ -22,6 +22,8 @@ import org.asamk.signal.storage.SignalAccount;
|
|||
import org.asamk.signal.storage.contacts.ContactInfo;
|
||||
import org.asamk.signal.storage.groups.GroupInfo;
|
||||
import org.asamk.signal.storage.groups.JsonGroupStore;
|
||||
import org.asamk.signal.storage.profiles.SignalProfile;
|
||||
import org.asamk.signal.storage.profiles.SignalProfileEntry;
|
||||
import org.asamk.signal.storage.protocol.JsonIdentityKeyStore;
|
||||
import org.asamk.signal.util.IOUtils;
|
||||
import org.asamk.signal.util.Util;
|
||||
|
@ -442,6 +444,18 @@ public class Manager implements Closeable {
|
|||
}
|
||||
|
||||
private SignalProfile getRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess, ProfileKey profileKey) throws IOException {
|
||||
SignalProfileEntry profileEntry = account.getProfileStore().getProfile(address);
|
||||
long now = new Date().getTime();
|
||||
// Profiles are cache for 24h before retrieving them again
|
||||
if (profileEntry == null || profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000) {
|
||||
SignalProfile profile = retrieveRecipientProfile(address, unidentifiedAccess, profileKey);
|
||||
profileEntry = new SignalProfileEntry(profileKey, now, profile);
|
||||
account.getProfileStore().updateProfile(address, profileEntry);
|
||||
}
|
||||
return profileEntry.getProfile();
|
||||
}
|
||||
|
||||
private SignalProfile retrieveRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess, ProfileKey profileKey) throws IOException {
|
||||
final SignalServiceProfile encryptedProfile = getEncryptedRecipientProfile(address, unidentifiedAccess);
|
||||
|
||||
File avatarFile = null;
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.asamk.signal.storage.contacts.ContactInfo;
|
|||
import org.asamk.signal.storage.contacts.JsonContactsStore;
|
||||
import org.asamk.signal.storage.groups.GroupInfo;
|
||||
import org.asamk.signal.storage.groups.JsonGroupStore;
|
||||
import org.asamk.signal.storage.profiles.ProfileStore;
|
||||
import org.asamk.signal.storage.protocol.JsonIdentityKeyStore;
|
||||
import org.asamk.signal.storage.protocol.JsonSignalProtocolStore;
|
||||
import org.asamk.signal.storage.protocol.RecipientStore;
|
||||
|
@ -67,6 +68,7 @@ public class SignalAccount implements Closeable {
|
|||
private JsonGroupStore groupStore;
|
||||
private JsonContactsStore contactStore;
|
||||
private RecipientStore recipientStore;
|
||||
private ProfileStore profileStore;
|
||||
|
||||
private SignalAccount(final FileChannel fileChannel, final FileLock lock) {
|
||||
this.fileChannel = fileChannel;
|
||||
|
@ -109,6 +111,7 @@ public class SignalAccount implements Closeable {
|
|||
account.groupStore = new JsonGroupStore();
|
||||
account.contactStore = new JsonContactsStore();
|
||||
account.recipientStore = new RecipientStore();
|
||||
account.profileStore = new ProfileStore();
|
||||
account.registered = false;
|
||||
|
||||
return account;
|
||||
|
@ -134,6 +137,7 @@ public class SignalAccount implements Closeable {
|
|||
account.groupStore = new JsonGroupStore();
|
||||
account.contactStore = new JsonContactsStore();
|
||||
account.recipientStore = new RecipientStore();
|
||||
account.profileStore = new ProfileStore();
|
||||
account.registered = true;
|
||||
account.isMultiDevice = true;
|
||||
|
||||
|
@ -245,6 +249,14 @@ public class SignalAccount implements Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
JsonNode profileStoreNode = rootNode.get("profileStore");
|
||||
if (profileStoreNode != null) {
|
||||
profileStore = jsonProcessor.convertValue(profileStoreNode, ProfileStore.class);
|
||||
}
|
||||
if (profileStore == null) {
|
||||
profileStore = new ProfileStore();
|
||||
}
|
||||
|
||||
JsonNode threadStoreNode = rootNode.get("threadStore");
|
||||
if (threadStoreNode != null) {
|
||||
LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class);
|
||||
|
@ -291,6 +303,7 @@ public class SignalAccount implements Closeable {
|
|||
.putPOJO("groupStore", groupStore)
|
||||
.putPOJO("contactStore", contactStore)
|
||||
.putPOJO("recipientStore", recipientStore)
|
||||
.putPOJO("profileStore", profileStore)
|
||||
;
|
||||
try {
|
||||
synchronized (fileChannel) {
|
||||
|
@ -347,6 +360,10 @@ public class SignalAccount implements Closeable {
|
|||
return recipientStore;
|
||||
}
|
||||
|
||||
public ProfileStore getProfileStore() {
|
||||
return profileStore;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package org.asamk.signal.storage.profiles;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import org.whispersystems.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ProfileStore {
|
||||
|
||||
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
||||
|
||||
@JsonProperty("profiles")
|
||||
@JsonDeserialize(using = ProfileStoreDeserializer.class)
|
||||
@JsonSerialize(using = ProfileStoreSerializer.class)
|
||||
private final Map<SignalServiceAddress, SignalProfileEntry> profiles = new HashMap<>();
|
||||
|
||||
public SignalProfileEntry getProfile(SignalServiceAddress serviceAddress) {
|
||||
return profiles.get(serviceAddress);
|
||||
}
|
||||
|
||||
public SignalProfileEntry updateProfile(SignalServiceAddress serviceAddress, SignalProfileEntry profile) {
|
||||
return profiles.put(serviceAddress, profile);
|
||||
}
|
||||
|
||||
public static class ProfileStoreDeserializer extends JsonDeserializer<Map<SignalServiceAddress, SignalProfileEntry>> {
|
||||
|
||||
@Override
|
||||
public Map<SignalServiceAddress, SignalProfileEntry> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
||||
|
||||
Map<SignalServiceAddress, SignalProfileEntry> addresses = new HashMap<>();
|
||||
|
||||
if (node.isArray()) {
|
||||
for (JsonNode recipient : node) {
|
||||
String recipientName = recipient.get("name").asText();
|
||||
UUID uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText());
|
||||
final SignalServiceAddress serviceAddress = new SignalServiceAddress(uuid, recipientName);
|
||||
ProfileKey profileKey = null;
|
||||
try {
|
||||
profileKey = new ProfileKey(Base64.decode(recipient.get("profileKey").asText()));
|
||||
} catch (InvalidInputException ignored) {
|
||||
}
|
||||
long lastUpdateTimestamp = recipient.get("lastUpdateTimestamp").asLong();
|
||||
SignalProfile profile = jsonProcessor.treeToValue(recipient.get("profile"), SignalProfile.class);
|
||||
addresses.put(serviceAddress, new SignalProfileEntry(profileKey, lastUpdateTimestamp, profile));
|
||||
}
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProfileStoreSerializer extends JsonSerializer<Map<SignalServiceAddress, SignalProfileEntry>> {
|
||||
|
||||
@Override
|
||||
public void serialize(Map<SignalServiceAddress, SignalProfileEntry> profiles, JsonGenerator json, SerializerProvider serializerProvider) throws IOException {
|
||||
json.writeStartArray();
|
||||
for (Map.Entry<SignalServiceAddress, SignalProfileEntry> entry : profiles.entrySet()) {
|
||||
final SignalServiceAddress address = entry.getKey();
|
||||
final SignalProfileEntry profileEntry = entry.getValue();
|
||||
json.writeStartObject();
|
||||
json.writeStringField("name", address.getNumber().get());
|
||||
json.writeStringField("uuid", address.getUuid().get().toString());
|
||||
json.writeStringField("profileKey", Base64.encodeBytes(profileEntry.getProfileKey().serialize()));
|
||||
json.writeNumberField("lastUpdateTimestamp", profileEntry.getLastUpdateTimestamp());
|
||||
json.writeObjectField("profile", profileEntry.getProfile());
|
||||
json.writeEndObject();
|
||||
}
|
||||
json.writeEndArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
package org.asamk.signal.manager;
|
||||
package org.asamk.signal.storage.profiles;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
|
||||
|
@ -6,16 +8,21 @@ import java.io.File;
|
|||
|
||||
public class SignalProfile {
|
||||
|
||||
@JsonProperty
|
||||
private final String identityKey;
|
||||
|
||||
@JsonProperty
|
||||
private final String name;
|
||||
|
||||
private final File avatarFile;
|
||||
|
||||
@JsonProperty
|
||||
private final String unidentifiedAccess;
|
||||
|
||||
@JsonProperty
|
||||
private final boolean unrestrictedUnidentifiedAccess;
|
||||
|
||||
@JsonProperty
|
||||
private final SignalServiceProfile.Capabilities capabilities;
|
||||
|
||||
public SignalProfile(final String identityKey, final String name, final File avatarFile, final String unidentifiedAccess, final boolean unrestrictedUnidentifiedAccess, final SignalServiceProfile.Capabilities capabilities) {
|
||||
|
@ -27,6 +34,15 @@ public class SignalProfile {
|
|||
this.capabilities = capabilities;
|
||||
}
|
||||
|
||||
public SignalProfile(@JsonProperty("identityKey") final String identityKey, @JsonProperty("name") final String name, @JsonProperty("unidentifiedAccess") final String unidentifiedAccess, @JsonProperty("unrestrictedUnidentifiedAccess") final boolean unrestrictedUnidentifiedAccess, @JsonProperty("capabilities") final SignalServiceProfile.Capabilities capabilities) {
|
||||
this.identityKey = identityKey;
|
||||
this.name = name;
|
||||
this.avatarFile = null;
|
||||
this.unidentifiedAccess = unidentifiedAccess;
|
||||
this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess;
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
|
||||
public String getIdentityKey() {
|
||||
return identityKey;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.asamk.signal.storage.profiles;
|
||||
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
|
||||
public class SignalProfileEntry {
|
||||
|
||||
private ProfileKey profileKey;
|
||||
|
||||
private long lastUpdateTimestamp;
|
||||
|
||||
private SignalProfile profile;
|
||||
|
||||
public SignalProfileEntry(final ProfileKey profileKey, final long lastUpdateTimestamp, final SignalProfile profile) {
|
||||
this.profileKey = profileKey;
|
||||
this.lastUpdateTimestamp = lastUpdateTimestamp;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public ProfileKey getProfileKey() {
|
||||
return profileKey;
|
||||
}
|
||||
|
||||
public long getLastUpdateTimestamp() {
|
||||
return lastUpdateTimestamp;
|
||||
}
|
||||
|
||||
public SignalProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
}
|
|
@ -26,9 +26,6 @@ public class RecipientStore {
|
|||
@JsonSerialize(using = RecipientStoreSerializer.class)
|
||||
private final Set<SignalServiceAddress> addresses = new HashSet<>();
|
||||
|
||||
public RecipientStore() {
|
||||
}
|
||||
|
||||
public SignalServiceAddress resolveServiceAddress(SignalServiceAddress serviceAddress) {
|
||||
if (addresses.contains(serviceAddress)) {
|
||||
// If the Set already contains the exact address with UUID and Number,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue