Use record classes
This commit is contained in:
		
							parent
							
								
									ce70a623c2
								
							
						
					
					
						commit
						ce7aa580b6
					
				
							
								
								
									
										7
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
								
							| @ -26,14 +26,14 @@ | ||||
|           <emptyLine /> | ||||
|         </value> | ||||
|       </option> | ||||
|       <option name="RECORD_COMPONENTS_WRAP" value="5" /> | ||||
|       <option name="NEW_LINE_AFTER_LPAREN_IN_RECORD_HEADER" value="true" /> | ||||
|       <option name="RPAREN_ON_NEW_LINE_IN_RECORD_HEADER" value="true" /> | ||||
|       <option name="JD_P_AT_EMPTY_LINES" value="false" /> | ||||
|     </JavaCodeStyleSettings> | ||||
|     <JetCodeStyleSettings> | ||||
|       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> | ||||
|     </JetCodeStyleSettings> | ||||
|     <XML> | ||||
|       <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> | ||||
|     </XML> | ||||
|     <codeStyleSettings language="JAVA"> | ||||
|       <option name="RIGHT_MARGIN" value="120" /> | ||||
|       <option name="KEEP_LINE_BREAKS" value="false" /> | ||||
| @ -52,6 +52,7 @@ | ||||
|       <option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" /> | ||||
|       <option name="TERNARY_OPERATION_WRAP" value="5" /> | ||||
|       <option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" /> | ||||
|       <option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" /> | ||||
|       <option name="ENUM_CONSTANTS_WRAP" value="2" /> | ||||
|     </codeStyleSettings> | ||||
|     <codeStyleSettings language="XML"> | ||||
|  | ||||
| @ -15,6 +15,7 @@ repositories { | ||||
| 
 | ||||
| dependencies { | ||||
|     api("com.github.turasa:signal-service-java:2.15.3_unofficial_31") | ||||
|     api("com.fasterxml.jackson.core", "jackson-databind", "2.13.0") | ||||
|     implementation("com.google.protobuf:protobuf-javalite:3.10.0") | ||||
|     implementation("org.bouncycastle:bcprov-jdk15on:1.69") | ||||
|     implementation("org.slf4j:slf4j-api:1.7.32") | ||||
|  | ||||
| @ -15,10 +15,7 @@ import java.util.Map; | ||||
| 
 | ||||
| import static org.whispersystems.signalservice.internal.util.Util.isEmpty; | ||||
| 
 | ||||
| public class DeviceLinkInfo { | ||||
| 
 | ||||
|     final String deviceIdentifier; | ||||
|     final ECPublicKey deviceKey; | ||||
| public record DeviceLinkInfo(String deviceIdentifier, ECPublicKey deviceKey) { | ||||
| 
 | ||||
|     public static DeviceLinkInfo parseDeviceLinkUri(URI linkUri) throws InvalidKeyException { | ||||
|         final var rawQuery = linkUri.getRawQuery(); | ||||
| @ -57,11 +54,6 @@ public class DeviceLinkInfo { | ||||
|         return map; | ||||
|     } | ||||
| 
 | ||||
|     public DeviceLinkInfo(final String deviceIdentifier, final ECPublicKey deviceKey) { | ||||
|         this.deviceIdentifier = deviceIdentifier; | ||||
|         this.deviceKey = deviceKey; | ||||
|     } | ||||
| 
 | ||||
|     public URI createDeviceLinkUri() { | ||||
|         final var deviceKeyString = Base64.getEncoder().encodeToString(deviceKey.serialize()).replace("=", ""); | ||||
|         try { | ||||
|  | ||||
| @ -1,55 +1,8 @@ | ||||
| package org.asamk.signal.manager; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class JsonStickerPack { | ||||
| public record JsonStickerPack(String title, String author, JsonSticker cover, List<JsonSticker> stickers) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     public String title; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     public String author; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     public JsonSticker cover; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     public List<JsonSticker> stickers; | ||||
| 
 | ||||
|     // For deserialization | ||||
|     private JsonStickerPack() { | ||||
|     } | ||||
| 
 | ||||
|     public JsonStickerPack( | ||||
|             final String title, final String author, final JsonSticker cover, final List<JsonSticker> stickers | ||||
|     ) { | ||||
|         this.title = title; | ||||
|         this.author = author; | ||||
|         this.cover = cover; | ||||
|         this.stickers = stickers; | ||||
|     } | ||||
| 
 | ||||
|     public static class JsonSticker { | ||||
| 
 | ||||
|         @JsonProperty | ||||
|         public String emoji; | ||||
| 
 | ||||
|         @JsonProperty | ||||
|         public String file; | ||||
| 
 | ||||
|         @JsonProperty | ||||
|         public String contentType; | ||||
| 
 | ||||
|         // For deserialization | ||||
|         private JsonSticker() { | ||||
|         } | ||||
| 
 | ||||
|         public JsonSticker(final String emoji, final String file, final String contentType) { | ||||
|             this.emoji = emoji; | ||||
|             this.file = file; | ||||
|             this.contentType = contentType; | ||||
|         } | ||||
|     } | ||||
|     public record JsonSticker(String emoji, String file, String contentType) {} | ||||
| } | ||||
|  | ||||
| @ -57,11 +57,11 @@ public interface Manager extends Closeable { | ||||
|     ) throws IOException, NotRegisteredException { | ||||
|         var pathConfig = PathConfig.createDefault(settingsPath); | ||||
| 
 | ||||
|         if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) { | ||||
|         if (!SignalAccount.userExists(pathConfig.dataPath(), number)) { | ||||
|             throw new NotRegisteredException(); | ||||
|         } | ||||
| 
 | ||||
|         var account = SignalAccount.load(pathConfig.getDataPath(), number, true, trustNewIdentity); | ||||
|         var account = SignalAccount.load(pathConfig.dataPath(), number, true, trustNewIdentity); | ||||
| 
 | ||||
|         if (!account.isRegistered()) { | ||||
|             throw new NotRegisteredException(); | ||||
| @ -74,7 +74,7 @@ public interface Manager extends Closeable { | ||||
| 
 | ||||
|     static List<String> getAllLocalNumbers(File settingsPath) { | ||||
|         var pathConfig = PathConfig.createDefault(settingsPath); | ||||
|         final var dataPath = pathConfig.getDataPath(); | ||||
|         final var dataPath = pathConfig.dataPath(); | ||||
|         final var files = dataPath.listFiles(); | ||||
| 
 | ||||
|         if (files == null) { | ||||
|  | ||||
| @ -169,9 +169,9 @@ public class ManagerImpl implements Manager { | ||||
|                 account.getSignalProtocolStore(), | ||||
|                 executor, | ||||
|                 sessionLock); | ||||
|         final var avatarStore = new AvatarStore(pathConfig.getAvatarsPath()); | ||||
|         final var attachmentStore = new AttachmentStore(pathConfig.getAttachmentsPath()); | ||||
|         final var stickerPackStore = new StickerPackStore(pathConfig.getStickerPacksPath()); | ||||
|         final var avatarStore = new AvatarStore(pathConfig.avatarsPath()); | ||||
|         final var attachmentStore = new AttachmentStore(pathConfig.attachmentsPath()); | ||||
|         final var stickerPackStore = new StickerPackStore(pathConfig.stickerPacksPath()); | ||||
| 
 | ||||
|         this.attachmentHelper = new AttachmentHelper(dependencies, attachmentStore); | ||||
|         this.pinHelper = new PinHelper(dependencies.getKeyBackupService()); | ||||
| @ -426,7 +426,7 @@ public class ManagerImpl implements Manager { | ||||
|     public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException { | ||||
|         var info = DeviceLinkInfo.parseDeviceLinkUri(linkUri); | ||||
| 
 | ||||
|         addDevice(info.deviceIdentifier, info.deviceKey); | ||||
|         addDevice(info.deviceIdentifier(), info.deviceKey()); | ||||
|     } | ||||
| 
 | ||||
|     private void addDevice(String deviceIdentifier, ECPublicKey deviceKey) throws IOException, InvalidKeyException { | ||||
| @ -642,8 +642,8 @@ public class ManagerImpl implements Manager { | ||||
|     private void applyMessage( | ||||
|             final SignalServiceDataMessage.Builder messageBuilder, final Message message | ||||
|     ) throws AttachmentInvalidException, IOException { | ||||
|         messageBuilder.withBody(message.getMessageText()); | ||||
|         final var attachments = message.getAttachments(); | ||||
|         messageBuilder.withBody(message.messageText()); | ||||
|         final var attachments = message.attachments(); | ||||
|         if (attachments != null) { | ||||
|             messageBuilder.withAttachments(attachmentHelper.uploadAttachments(attachments)); | ||||
|         } | ||||
|  | ||||
| @ -2,12 +2,9 @@ package org.asamk.signal.manager; | ||||
| 
 | ||||
| import java.io.File; | ||||
| 
 | ||||
| public class PathConfig { | ||||
| 
 | ||||
|     private final File dataPath; | ||||
|     private final File attachmentsPath; | ||||
|     private final File avatarsPath; | ||||
|     private final File stickerPacksPath; | ||||
| public record PathConfig( | ||||
|         File dataPath, File attachmentsPath, File avatarsPath, File stickerPacksPath | ||||
| ) { | ||||
| 
 | ||||
|     public static PathConfig createDefault(final File settingsPath) { | ||||
|         return new PathConfig(new File(settingsPath, "data"), | ||||
| @ -15,29 +12,4 @@ public class PathConfig { | ||||
|                 new File(settingsPath, "avatars"), | ||||
|                 new File(settingsPath, "stickers")); | ||||
|     } | ||||
| 
 | ||||
|     private PathConfig( | ||||
|             final File dataPath, final File attachmentsPath, final File avatarsPath, final File stickerPacksPath | ||||
|     ) { | ||||
|         this.dataPath = dataPath; | ||||
|         this.attachmentsPath = attachmentsPath; | ||||
|         this.avatarsPath = avatarsPath; | ||||
|         this.stickerPacksPath = stickerPacksPath; | ||||
|     } | ||||
| 
 | ||||
|     public File getDataPath() { | ||||
|         return dataPath; | ||||
|     } | ||||
| 
 | ||||
|     public File getAttachmentsPath() { | ||||
|         return attachmentsPath; | ||||
|     } | ||||
| 
 | ||||
|     public File getAvatarsPath() { | ||||
|         return avatarsPath; | ||||
|     } | ||||
| 
 | ||||
|     public File getStickerPacksPath() { | ||||
|         return stickerPacksPath; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -95,8 +95,8 @@ public class ProvisioningManager { | ||||
| 
 | ||||
|         logger.info("Received link information from {}, linking in progress ...", number); | ||||
| 
 | ||||
|         if (SignalAccount.userExists(pathConfig.getDataPath(), number) && !canRelinkExistingAccount(number)) { | ||||
|             throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.getDataPath(), number)); | ||||
|         if (SignalAccount.userExists(pathConfig.dataPath(), number) && !canRelinkExistingAccount(number)) { | ||||
|             throw new UserAlreadyExists(number, SignalAccount.getFileName(pathConfig.dataPath(), number)); | ||||
|         } | ||||
| 
 | ||||
|         var encryptedDeviceName = deviceName == null | ||||
| @ -115,7 +115,7 @@ public class ProvisioningManager { | ||||
| 
 | ||||
|         SignalAccount account = null; | ||||
|         try { | ||||
|             account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.getDataPath(), | ||||
|             account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(), | ||||
|                     number, | ||||
|                     ret.getUuid(), | ||||
|                     password, | ||||
| @ -165,7 +165,7 @@ public class ProvisioningManager { | ||||
|     private boolean canRelinkExistingAccount(final String number) throws IOException { | ||||
|         final SignalAccount signalAccount; | ||||
|         try { | ||||
|             signalAccount = SignalAccount.load(pathConfig.getDataPath(), number, false, TrustNewIdentity.ON_FIRST_USE); | ||||
|             signalAccount = SignalAccount.load(pathConfig.dataPath(), number, false, TrustNewIdentity.ON_FIRST_USE); | ||||
|         } catch (IOException e) { | ||||
|             logger.debug("Account in use or failed to load.", e); | ||||
|             return false; | ||||
|  | ||||
| @ -96,12 +96,12 @@ public class RegistrationManager implements Closeable { | ||||
|         var pathConfig = PathConfig.createDefault(settingsPath); | ||||
| 
 | ||||
|         final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent); | ||||
|         if (!SignalAccount.userExists(pathConfig.getDataPath(), number)) { | ||||
|         if (!SignalAccount.userExists(pathConfig.dataPath(), number)) { | ||||
|             var identityKey = KeyUtils.generateIdentityKeyPair(); | ||||
|             var registrationId = KeyHelper.generateRegistrationId(false); | ||||
| 
 | ||||
|             var profileKey = KeyUtils.createProfileKey(); | ||||
|             var account = SignalAccount.create(pathConfig.getDataPath(), | ||||
|             var account = SignalAccount.create(pathConfig.dataPath(), | ||||
|                     number, | ||||
|                     identityKey, | ||||
|                     registrationId, | ||||
| @ -111,7 +111,7 @@ public class RegistrationManager implements Closeable { | ||||
|             return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); | ||||
|         } | ||||
| 
 | ||||
|         var account = SignalAccount.load(pathConfig.getDataPath(), number, true, TrustNewIdentity.ON_FIRST_USE); | ||||
|         var account = SignalAccount.load(pathConfig.dataPath(), number, true, TrustNewIdentity.ON_FIRST_USE); | ||||
| 
 | ||||
|         return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent); | ||||
|     } | ||||
|  | ||||
| @ -1,38 +1,3 @@ | ||||
| package org.asamk.signal.manager.api; | ||||
| 
 | ||||
| public class Device { | ||||
| 
 | ||||
|     private final long id; | ||||
|     private final String name; | ||||
|     private final long created; | ||||
|     private final long lastSeen; | ||||
|     private final boolean thisDevice; | ||||
| 
 | ||||
|     public Device(long id, String name, long created, long lastSeen, final boolean thisDevice) { | ||||
|         this.id = id; | ||||
|         this.name = name; | ||||
|         this.created = created; | ||||
|         this.lastSeen = lastSeen; | ||||
|         this.thisDevice = thisDevice; | ||||
|     } | ||||
| 
 | ||||
|     public long getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     public long getCreated() { | ||||
|         return created; | ||||
|     } | ||||
| 
 | ||||
|     public long getLastSeen() { | ||||
|         return lastSeen; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isThisDevice() { | ||||
|         return thisDevice; | ||||
|     } | ||||
| } | ||||
| public record Device(long id, String name, long created, long lastSeen, boolean isThisDevice) {} | ||||
|  | ||||
| @ -7,116 +7,20 @@ import org.asamk.signal.manager.storage.recipients.RecipientAddress; | ||||
| 
 | ||||
| import java.util.Set; | ||||
| 
 | ||||
| public class Group { | ||||
| 
 | ||||
|     private final GroupId groupId; | ||||
|     private final String title; | ||||
|     private final String description; | ||||
|     private final GroupInviteLinkUrl groupInviteLinkUrl; | ||||
|     private final Set<RecipientAddress> members; | ||||
|     private final Set<RecipientAddress> pendingMembers; | ||||
|     private final Set<RecipientAddress> requestingMembers; | ||||
|     private final Set<RecipientAddress> adminMembers; | ||||
|     private final boolean isBlocked; | ||||
|     private final int messageExpirationTimer; | ||||
| 
 | ||||
|     private final GroupPermission permissionAddMember; | ||||
|     private final GroupPermission permissionEditDetails; | ||||
|     private final GroupPermission permissionSendMessage; | ||||
|     private final boolean isMember; | ||||
|     private final boolean isAdmin; | ||||
| 
 | ||||
|     public Group( | ||||
|             final GroupId groupId, | ||||
|             final String title, | ||||
|             final String description, | ||||
|             final GroupInviteLinkUrl groupInviteLinkUrl, | ||||
|             final Set<RecipientAddress> members, | ||||
|             final Set<RecipientAddress> pendingMembers, | ||||
|             final Set<RecipientAddress> requestingMembers, | ||||
|             final Set<RecipientAddress> adminMembers, | ||||
|             final boolean isBlocked, | ||||
|             final int messageExpirationTimer, | ||||
|             final GroupPermission permissionAddMember, | ||||
|             final GroupPermission permissionEditDetails, | ||||
|             final GroupPermission permissionSendMessage, | ||||
|             final boolean isMember, | ||||
|             final boolean isAdmin | ||||
|     ) { | ||||
|         this.groupId = groupId; | ||||
|         this.title = title; | ||||
|         this.description = description; | ||||
|         this.groupInviteLinkUrl = groupInviteLinkUrl; | ||||
|         this.members = members; | ||||
|         this.pendingMembers = pendingMembers; | ||||
|         this.requestingMembers = requestingMembers; | ||||
|         this.adminMembers = adminMembers; | ||||
|         this.isBlocked = isBlocked; | ||||
|         this.messageExpirationTimer = messageExpirationTimer; | ||||
|         this.permissionAddMember = permissionAddMember; | ||||
|         this.permissionEditDetails = permissionEditDetails; | ||||
|         this.permissionSendMessage = permissionSendMessage; | ||||
|         this.isMember = isMember; | ||||
|         this.isAdmin = isAdmin; | ||||
|     } | ||||
| 
 | ||||
|     public GroupId getGroupId() { | ||||
|         return groupId; | ||||
|     } | ||||
| 
 | ||||
|     public String getTitle() { | ||||
|         return title; | ||||
|     } | ||||
| 
 | ||||
|     public String getDescription() { | ||||
|         return description; | ||||
|     } | ||||
| 
 | ||||
|     public GroupInviteLinkUrl getGroupInviteLinkUrl() { | ||||
|         return groupInviteLinkUrl; | ||||
|     } | ||||
| 
 | ||||
|     public Set<RecipientAddress> getMembers() { | ||||
|         return members; | ||||
|     } | ||||
| 
 | ||||
|     public Set<RecipientAddress> getPendingMembers() { | ||||
|         return pendingMembers; | ||||
|     } | ||||
| 
 | ||||
|     public Set<RecipientAddress> getRequestingMembers() { | ||||
|         return requestingMembers; | ||||
|     } | ||||
| 
 | ||||
|     public Set<RecipientAddress> getAdminMembers() { | ||||
|         return adminMembers; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isBlocked() { | ||||
|         return isBlocked; | ||||
|     } | ||||
| 
 | ||||
|     public int getMessageExpirationTimer() { | ||||
|         return messageExpirationTimer; | ||||
|     } | ||||
| 
 | ||||
|     public GroupPermission getPermissionAddMember() { | ||||
|         return permissionAddMember; | ||||
|     } | ||||
| 
 | ||||
|     public GroupPermission getPermissionEditDetails() { | ||||
|         return permissionEditDetails; | ||||
|     } | ||||
| 
 | ||||
|     public GroupPermission getPermissionSendMessage() { | ||||
|         return permissionSendMessage; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isMember() { | ||||
|         return isMember; | ||||
|     } | ||||
| 
 | ||||
|     public boolean isAdmin() { | ||||
|         return isAdmin; | ||||
|     } | ||||
| } | ||||
| public record Group( | ||||
|         GroupId groupId, | ||||
|         String title, | ||||
|         String description, | ||||
|         GroupInviteLinkUrl groupInviteLinkUrl, | ||||
|         Set<RecipientAddress> members, | ||||
|         Set<RecipientAddress> pendingMembers, | ||||
|         Set<RecipientAddress> requestingMembers, | ||||
|         Set<RecipientAddress> adminMembers, | ||||
|         boolean isBlocked, | ||||
|         int messageExpirationTimer, | ||||
|         GroupPermission permissionAddMember, | ||||
|         GroupPermission permissionEditDetails, | ||||
|         GroupPermission permissionSendMessage, | ||||
|         boolean isMember, | ||||
|         boolean isAdmin | ||||
| ) {} | ||||
|  | ||||
| @ -6,60 +6,16 @@ import org.whispersystems.libsignal.IdentityKey; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| 
 | ||||
| public class Identity { | ||||
| 
 | ||||
|     private final RecipientAddress recipient; | ||||
|     private final IdentityKey identityKey; | ||||
|     private final String safetyNumber; | ||||
|     private final byte[] scannableSafetyNumber; | ||||
|     private final TrustLevel trustLevel; | ||||
|     private final Date dateAdded; | ||||
| 
 | ||||
|     public Identity( | ||||
|             final RecipientAddress recipient, | ||||
|             final IdentityKey identityKey, | ||||
|             final String safetyNumber, | ||||
|             final byte[] scannableSafetyNumber, | ||||
|             final TrustLevel trustLevel, | ||||
|             final Date dateAdded | ||||
|     ) { | ||||
|         this.recipient = recipient; | ||||
|         this.identityKey = identityKey; | ||||
|         this.safetyNumber = safetyNumber; | ||||
|         this.scannableSafetyNumber = scannableSafetyNumber; | ||||
|         this.trustLevel = trustLevel; | ||||
|         this.dateAdded = dateAdded; | ||||
|     } | ||||
| 
 | ||||
|     public RecipientAddress getRecipient() { | ||||
|         return recipient; | ||||
|     } | ||||
| 
 | ||||
|     public IdentityKey getIdentityKey() { | ||||
|         return this.identityKey; | ||||
|     } | ||||
| 
 | ||||
|     public TrustLevel getTrustLevel() { | ||||
|         return this.trustLevel; | ||||
|     } | ||||
| 
 | ||||
|     boolean isTrusted() { | ||||
|         return trustLevel == TrustLevel.TRUSTED_UNVERIFIED || trustLevel == TrustLevel.TRUSTED_VERIFIED; | ||||
|     } | ||||
| 
 | ||||
|     public Date getDateAdded() { | ||||
|         return this.dateAdded; | ||||
|     } | ||||
| public record Identity( | ||||
|         RecipientAddress recipient, | ||||
|         IdentityKey identityKey, | ||||
|         String safetyNumber, | ||||
|         byte[] scannableSafetyNumber, | ||||
|         TrustLevel trustLevel, | ||||
|         Date dateAdded | ||||
| ) { | ||||
| 
 | ||||
|     public byte[] getFingerprint() { | ||||
|         return identityKey.getPublicKey().serialize(); | ||||
|     } | ||||
| 
 | ||||
|     public String getSafetyNumber() { | ||||
|         return safetyNumber; | ||||
|     } | ||||
| 
 | ||||
|     public byte[] getScannableSafetyNumber() { | ||||
|         return scannableSafetyNumber; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,21 +2,4 @@ package org.asamk.signal.manager.api; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class Message { | ||||
| 
 | ||||
|     private final String messageText; | ||||
|     private final List<String> attachments; | ||||
| 
 | ||||
|     public Message(final String messageText, final List<String> attachments) { | ||||
|         this.messageText = messageText; | ||||
|         this.attachments = attachments; | ||||
|     } | ||||
| 
 | ||||
|     public String getMessageText() { | ||||
|         return messageText; | ||||
|     } | ||||
| 
 | ||||
|     public List<String> getAttachments() { | ||||
|         return attachments; | ||||
|     } | ||||
| } | ||||
| public record Message(String messageText, List<String> attachments) {} | ||||
|  | ||||
| @ -4,23 +4,4 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class SendGroupMessageResults { | ||||
| 
 | ||||
|     private final long timestamp; | ||||
|     private final List<SendMessageResult> results; | ||||
| 
 | ||||
|     public SendGroupMessageResults( | ||||
|             final long timestamp, final List<SendMessageResult> results | ||||
|     ) { | ||||
|         this.timestamp = timestamp; | ||||
|         this.results = results; | ||||
|     } | ||||
| 
 | ||||
|     public long getTimestamp() { | ||||
|         return timestamp; | ||||
|     } | ||||
| 
 | ||||
|     public List<SendMessageResult> getResults() { | ||||
|         return results; | ||||
|     } | ||||
| } | ||||
| public record SendGroupMessageResults(long timestamp, List<SendMessageResult> results) {} | ||||
|  | ||||
| @ -5,23 +5,4 @@ import org.whispersystems.signalservice.api.messages.SendMessageResult; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| public class SendMessageResults { | ||||
| 
 | ||||
|     private final long timestamp; | ||||
|     private final Map<RecipientIdentifier, List<SendMessageResult>> results; | ||||
| 
 | ||||
|     public SendMessageResults( | ||||
|             final long timestamp, final Map<RecipientIdentifier, List<SendMessageResult>> results | ||||
|     ) { | ||||
|         this.timestamp = timestamp; | ||||
|         this.results = results; | ||||
|     } | ||||
| 
 | ||||
|     public long getTimestamp() { | ||||
|         return timestamp; | ||||
|     } | ||||
| 
 | ||||
|     public Map<RecipientIdentifier, List<SendMessageResult>> getResults() { | ||||
|         return results; | ||||
|     } | ||||
| } | ||||
| public record SendMessageResults(long timestamp, Map<RecipientIdentifier, List<SendMessageResult>> results) {} | ||||
|  | ||||
| @ -62,29 +62,9 @@ public class ConfigurationStore { | ||||
|         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 record Storage( | ||||
|             Boolean readReceipts, Boolean unidentifiedDeliveryIndicators, Boolean typingIndicators, Boolean linkPreviews | ||||
|     ) {} | ||||
| 
 | ||||
|     public interface Saver { | ||||
| 
 | ||||
|  | ||||
| @ -262,7 +262,7 @@ public class GroupStore { | ||||
|                         g1.blocked, | ||||
|                         g1.archived, | ||||
|                         g1.members.stream() | ||||
|                                 .map(m -> new Storage.GroupV1.Member(m.getId(), null, null)) | ||||
|                                 .map(m -> new Storage.GroupV1.Member(m.id(), null, null)) | ||||
|                                 .collect(Collectors.toList())); | ||||
|             } | ||||
| 
 | ||||
| @ -274,91 +274,22 @@ public class GroupStore { | ||||
|         }).collect(Collectors.toList())); | ||||
|     } | ||||
| 
 | ||||
|     public static class Storage { | ||||
|     public record Storage(@JsonDeserialize(using = GroupsDeserializer.class) List<Object> groups) { | ||||
| 
 | ||||
|         @JsonDeserialize(using = GroupsDeserializer.class) | ||||
|         public List<Storage.Group> groups; | ||||
|         private record GroupV1( | ||||
|                 String groupId, | ||||
|                 String expectedV2Id, | ||||
|                 String name, | ||||
|                 String color, | ||||
|                 int messageExpirationTime, | ||||
|                 boolean blocked, | ||||
|                 boolean archived, | ||||
|                 @JsonDeserialize(using = MembersDeserializer.class) @JsonSerialize(using = MembersSerializer.class) List<Member> members | ||||
|         ) { | ||||
| 
 | ||||
|         // For deserialization | ||||
|         public Storage() { | ||||
|         } | ||||
|             private record Member(Long recipientId, String uuid, String number) {} | ||||
| 
 | ||||
|         public Storage(final List<Storage.Group> groups) { | ||||
|             this.groups = groups; | ||||
|         } | ||||
| 
 | ||||
|         private abstract static class Group { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         private static class GroupV1 extends Group { | ||||
| 
 | ||||
|             public String groupId; | ||||
|             public String expectedV2Id; | ||||
|             public String name; | ||||
|             public String color; | ||||
|             public int messageExpirationTime; | ||||
|             public boolean blocked; | ||||
|             public boolean archived; | ||||
| 
 | ||||
|             @JsonDeserialize(using = MembersDeserializer.class) | ||||
|             @JsonSerialize(using = MembersSerializer.class) | ||||
|             public List<Member> members; | ||||
| 
 | ||||
|             // For deserialization | ||||
|             public GroupV1() { | ||||
|             } | ||||
| 
 | ||||
|             public GroupV1( | ||||
|                     final String groupId, | ||||
|                     final String expectedV2Id, | ||||
|                     final String name, | ||||
|                     final String color, | ||||
|                     final int messageExpirationTime, | ||||
|                     final boolean blocked, | ||||
|                     final boolean archived, | ||||
|                     final List<Member> members | ||||
|             ) { | ||||
|                 this.groupId = groupId; | ||||
|                 this.expectedV2Id = expectedV2Id; | ||||
|                 this.name = name; | ||||
|                 this.color = color; | ||||
|                 this.messageExpirationTime = messageExpirationTime; | ||||
|                 this.blocked = blocked; | ||||
|                 this.archived = archived; | ||||
|                 this.members = members; | ||||
|             } | ||||
| 
 | ||||
|             private static final class Member { | ||||
| 
 | ||||
|                 public Long recipientId; | ||||
| 
 | ||||
|                 public String uuid; | ||||
| 
 | ||||
|                 public String number; | ||||
| 
 | ||||
|                 Member(Long recipientId, final String uuid, final String number) { | ||||
|                     this.recipientId = recipientId; | ||||
|                     this.uuid = uuid; | ||||
|                     this.number = number; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             private static final class JsonRecipientAddress { | ||||
| 
 | ||||
|                 public String uuid; | ||||
| 
 | ||||
|                 public String number; | ||||
| 
 | ||||
|                 // For deserialization | ||||
|                 public JsonRecipientAddress() { | ||||
|                 } | ||||
| 
 | ||||
|                 JsonRecipientAddress(final String uuid, final String number) { | ||||
|                     this.uuid = uuid; | ||||
|                     this.number = number; | ||||
|                 } | ||||
|             } | ||||
|             private record JsonRecipientAddress(String uuid, String number) {} | ||||
| 
 | ||||
|             private static class MembersSerializer extends JsonSerializer<List<Member>> { | ||||
| 
 | ||||
| @ -366,7 +297,7 @@ public class GroupStore { | ||||
|                 public void serialize( | ||||
|                         final List<Member> value, final JsonGenerator jgen, final SerializerProvider provider | ||||
|                 ) throws IOException { | ||||
|                     jgen.writeStartArray(value.size()); | ||||
|                     jgen.writeStartArray(null, value.size()); | ||||
|                     for (var address : value) { | ||||
|                         if (address.recipientId != null) { | ||||
|                             jgen.writeNumber(address.recipientId); | ||||
| @ -404,39 +335,19 @@ public class GroupStore { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private static class GroupV2 extends Group { | ||||
| 
 | ||||
|             public String groupId; | ||||
|             public String masterKey; | ||||
|             public boolean blocked; | ||||
|             public boolean permissionDenied; | ||||
| 
 | ||||
|             // For deserialization | ||||
|             private GroupV2() { | ||||
|             } | ||||
| 
 | ||||
|             public GroupV2( | ||||
|                     final String groupId, final String masterKey, final boolean blocked, final boolean permissionDenied | ||||
|             ) { | ||||
|                 this.groupId = groupId; | ||||
|                 this.masterKey = masterKey; | ||||
|                 this.blocked = blocked; | ||||
|                 this.permissionDenied = permissionDenied; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private record GroupV2(String groupId, String masterKey, boolean blocked, boolean permissionDenied) {} | ||||
|     } | ||||
| 
 | ||||
|     private static class GroupsDeserializer extends JsonDeserializer<List<Storage.Group>> { | ||||
|     private static class GroupsDeserializer extends JsonDeserializer<List<Object>> { | ||||
| 
 | ||||
|         @Override | ||||
|         public List<Storage.Group> deserialize( | ||||
|         public List<Object> deserialize( | ||||
|                 JsonParser jsonParser, DeserializationContext deserializationContext | ||||
|         ) throws IOException { | ||||
|             var groups = new ArrayList<Storage.Group>(); | ||||
|             var groups = new ArrayList<>(); | ||||
|             JsonNode node = jsonParser.getCodec().readTree(jsonParser); | ||||
|             for (var n : node) { | ||||
|                 Storage.Group g; | ||||
|                 Object g; | ||||
|                 if (n.hasNonNull("masterKey")) { | ||||
|                     // a v2 group | ||||
|                     g = jsonParser.getCodec().treeToValue(n, Storage.GroupV2.class); | ||||
|  | ||||
| @ -185,7 +185,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden | ||||
|         } catch (IOException e) { | ||||
|             throw new AssertionError("Failed to create identities path", e); | ||||
|         } | ||||
|         return new File(identitiesPath, String.valueOf(recipientId.getId())); | ||||
|         return new File(identitiesPath, String.valueOf(recipientId.id())); | ||||
|     } | ||||
| 
 | ||||
|     private IdentityInfo loadIdentityLocked(final RecipientId recipientId) { | ||||
| @ -203,9 +203,9 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden | ||||
|         try (var inputStream = new FileInputStream(file)) { | ||||
|             var storage = objectMapper.readValue(inputStream, IdentityStorage.class); | ||||
| 
 | ||||
|             var id = new IdentityKey(Base64.getDecoder().decode(storage.getIdentityKey())); | ||||
|             var trustLevel = TrustLevel.fromInt(storage.getTrustLevel()); | ||||
|             var added = new Date(storage.getAddedTimestamp()); | ||||
|             var id = new IdentityKey(Base64.getDecoder().decode(storage.identityKey())); | ||||
|             var trustLevel = TrustLevel.fromInt(storage.trustLevel()); | ||||
|             var added = new Date(storage.addedTimestamp()); | ||||
| 
 | ||||
|             final var identityInfo = new IdentityInfo(recipientId, id, trustLevel, added); | ||||
|             cachedIdentities.put(recipientId, identityInfo); | ||||
| @ -251,32 +251,5 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class IdentityStorage { | ||||
| 
 | ||||
|         private String identityKey; | ||||
|         private int trustLevel; | ||||
|         private long addedTimestamp; | ||||
| 
 | ||||
|         // For deserialization | ||||
|         private IdentityStorage() { | ||||
|         } | ||||
| 
 | ||||
|         private IdentityStorage(final String identityKey, final int trustLevel, final long addedTimestamp) { | ||||
|             this.identityKey = identityKey; | ||||
|             this.trustLevel = trustLevel; | ||||
|             this.addedTimestamp = addedTimestamp; | ||||
|         } | ||||
| 
 | ||||
|         public String getIdentityKey() { | ||||
|             return identityKey; | ||||
|         } | ||||
| 
 | ||||
|         public int getTrustLevel() { | ||||
|             return trustLevel; | ||||
|         } | ||||
| 
 | ||||
|         public long getAddedTimestamp() { | ||||
|             return addedTimestamp; | ||||
|         } | ||||
|     } | ||||
|     private record IdentityStorage(String identityKey, int trustLevel, long addedTimestamp) {} | ||||
| } | ||||
|  | ||||
| @ -76,7 +76,7 @@ public class MessageCache { | ||||
|             return messageCachePath; | ||||
|         } | ||||
| 
 | ||||
|         var sender = String.valueOf(recipientId.getId()); | ||||
|         var sender = String.valueOf(recipientId.id()); | ||||
|         return new File(messageCachePath, sender.replace("/", "_")); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,38 +1,8 @@ | ||||
| package org.asamk.signal.manager.storage.recipients; | ||||
| 
 | ||||
| public class RecipientId { | ||||
| 
 | ||||
|     private final long id; | ||||
| 
 | ||||
|     RecipientId(final long id) { | ||||
|         this.id = id; | ||||
|     } | ||||
| public record RecipientId(long id) { | ||||
| 
 | ||||
|     public static RecipientId of(long id) { | ||||
|         return new RecipientId(id); | ||||
|     } | ||||
| 
 | ||||
|     public long getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "RecipientId{" + "id=" + id + '}'; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean equals(final Object o) { | ||||
|         if (this == o) return true; | ||||
|         if (o == null || getClass() != o.getClass()) return false; | ||||
| 
 | ||||
|         final RecipientId that = (RecipientId) o; | ||||
| 
 | ||||
|         return id == that.id; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int hashCode() { | ||||
|         return (int) (id ^ (id >>> 32)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -453,7 +453,7 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile | ||||
|                                     .stream() | ||||
|                                     .map(Enum::name) | ||||
|                                     .collect(Collectors.toSet())); | ||||
|             return new Storage.Recipient(pair.getKey().getId(), | ||||
|             return new Storage.Recipient(pair.getKey().id(), | ||||
|                     recipient.getAddress().getNumber().orElse(null), | ||||
|                     recipient.getAddress().getUuid().map(UUID::toString).orElse(null), | ||||
|                     recipient.getProfileKey() == null | ||||
| @ -479,115 +479,32 @@ public class RecipientStore implements RecipientResolver, ContactsStore, Profile | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static class Storage { | ||||
|     private record Storage(List<Recipient> recipients, long lastId) { | ||||
| 
 | ||||
|         public List<Recipient> recipients; | ||||
|         private record Recipient( | ||||
|                 long id, | ||||
|                 String number, | ||||
|                 String uuid, | ||||
|                 String profileKey, | ||||
|                 String profileKeyCredential, | ||||
|                 Storage.Recipient.Contact contact, | ||||
|                 Storage.Recipient.Profile profile | ||||
|         ) { | ||||
| 
 | ||||
|         public long lastId; | ||||
|             private record Contact( | ||||
|                     String name, String color, int messageExpirationTime, boolean blocked, boolean archived | ||||
|             ) {} | ||||
| 
 | ||||
|         // For deserialization | ||||
|         private Storage() { | ||||
|         } | ||||
| 
 | ||||
|         public Storage(final List<Recipient> recipients, final long lastId) { | ||||
|             this.recipients = recipients; | ||||
|             this.lastId = lastId; | ||||
|         } | ||||
| 
 | ||||
|         private static class Recipient { | ||||
| 
 | ||||
|             public long id; | ||||
|             public String number; | ||||
|             public String uuid; | ||||
|             public String profileKey; | ||||
|             public String profileKeyCredential; | ||||
|             public Contact contact; | ||||
|             public Profile profile; | ||||
| 
 | ||||
|             // For deserialization | ||||
|             private Recipient() { | ||||
|             } | ||||
| 
 | ||||
|             public Recipient( | ||||
|                     final long id, | ||||
|                     final String number, | ||||
|                     final String uuid, | ||||
|                     final String profileKey, | ||||
|                     final String profileKeyCredential, | ||||
|                     final Contact contact, | ||||
|                     final Profile profile | ||||
|             ) { | ||||
|                 this.id = id; | ||||
|                 this.number = number; | ||||
|                 this.uuid = uuid; | ||||
|                 this.profileKey = profileKey; | ||||
|                 this.profileKeyCredential = profileKeyCredential; | ||||
|                 this.contact = contact; | ||||
|                 this.profile = profile; | ||||
|             } | ||||
| 
 | ||||
|             private static class Contact { | ||||
| 
 | ||||
|                 public String name; | ||||
|                 public String color; | ||||
|                 public int messageExpirationTime; | ||||
|                 public boolean blocked; | ||||
|                 public boolean archived; | ||||
| 
 | ||||
|                 // For deserialization | ||||
|                 public Contact() { | ||||
|                 } | ||||
| 
 | ||||
|                 public Contact( | ||||
|                         final String name, | ||||
|                         final String color, | ||||
|                         final int messageExpirationTime, | ||||
|                         final boolean blocked, | ||||
|                         final boolean archived | ||||
|                 ) { | ||||
|                     this.name = name; | ||||
|                     this.color = color; | ||||
|                     this.messageExpirationTime = messageExpirationTime; | ||||
|                     this.blocked = blocked; | ||||
|                     this.archived = archived; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             private static class Profile { | ||||
| 
 | ||||
|                 public long lastUpdateTimestamp; | ||||
|                 public String givenName; | ||||
|                 public String familyName; | ||||
|                 public String about; | ||||
|                 public String aboutEmoji; | ||||
|                 public String avatarUrlPath; | ||||
|                 public String unidentifiedAccessMode; | ||||
|                 public Set<String> capabilities; | ||||
| 
 | ||||
|                 // For deserialization | ||||
|                 private Profile() { | ||||
|                 } | ||||
| 
 | ||||
|                 public Profile( | ||||
|                         final long lastUpdateTimestamp, | ||||
|                         final String givenName, | ||||
|                         final String familyName, | ||||
|                         final String about, | ||||
|                         final String aboutEmoji, | ||||
|                         final String avatarUrlPath, | ||||
|                         final String unidentifiedAccessMode, | ||||
|                         final Set<String> capabilities | ||||
|                 ) { | ||||
|                     this.lastUpdateTimestamp = lastUpdateTimestamp; | ||||
|                     this.givenName = givenName; | ||||
|                     this.familyName = familyName; | ||||
|                     this.about = about; | ||||
|                     this.aboutEmoji = aboutEmoji; | ||||
|                     this.avatarUrlPath = avatarUrlPath; | ||||
|                     this.unidentifiedAccessMode = unidentifiedAccessMode; | ||||
|                     this.capabilities = capabilities; | ||||
|                 } | ||||
|             } | ||||
|             private record Profile( | ||||
|                     long lastUpdateTimestamp, | ||||
|                     String givenName, | ||||
|                     String familyName, | ||||
|                     String about, | ||||
|                     String aboutEmoji, | ||||
|                     String avatarUrlPath, | ||||
|                     String unidentifiedAccessMode, | ||||
|                     Set<String> capabilities | ||||
|             ) {} | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -103,7 +103,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 final var newKey = new Key(recipientId, key.getDeviceId(), key.distributionId); | ||||
|                 final var newKey = new Key(recipientId, key.deviceId(), key.distributionId); | ||||
|                 final var senderKeyRecord = loadSenderKeyLocked(newKey); | ||||
|                 if (senderKeyRecord != null) { | ||||
|                     continue; | ||||
| @ -126,7 +126,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups | ||||
|     } | ||||
| 
 | ||||
|     private List<Key> getKeysLocked(RecipientId recipientId) { | ||||
|         final var files = senderKeysPath.listFiles((_file, s) -> s.startsWith(recipientId.getId() + "_")); | ||||
|         final var files = senderKeysPath.listFiles((_file, s) -> s.startsWith(recipientId.id() + "_")); | ||||
|         if (files == null) { | ||||
|             return List.of(); | ||||
|         } | ||||
| @ -152,7 +152,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups | ||||
|             throw new AssertionError("Failed to create sender keys path", e); | ||||
|         } | ||||
|         return new File(senderKeysPath, | ||||
|                 key.getRecipientId().getId() + "_" + key.getDeviceId() + "_" + key.distributionId.toString()); | ||||
|                 key.recipientId().id() + "_" + key.deviceId() + "_" + key.distributionId.toString()); | ||||
|     } | ||||
| 
 | ||||
|     private SenderKeyRecord loadSenderKeyLocked(final Key key) { | ||||
| @ -212,50 +212,7 @@ public class SenderKeyRecordStore implements org.whispersystems.libsignal.groups | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class Key { | ||||
|     private record Key(RecipientId recipientId, int deviceId, UUID distributionId) { | ||||
| 
 | ||||
|         private final RecipientId recipientId; | ||||
|         private final int deviceId; | ||||
|         private final UUID distributionId; | ||||
| 
 | ||||
|         public Key( | ||||
|                 final RecipientId recipientId, final int deviceId, final UUID distributionId | ||||
|         ) { | ||||
|             this.recipientId = recipientId; | ||||
|             this.deviceId = deviceId; | ||||
|             this.distributionId = distributionId; | ||||
|         } | ||||
| 
 | ||||
|         public RecipientId getRecipientId() { | ||||
|             return recipientId; | ||||
|         } | ||||
| 
 | ||||
|         public int getDeviceId() { | ||||
|             return deviceId; | ||||
|         } | ||||
| 
 | ||||
|         public UUID getDistributionId() { | ||||
|             return distributionId; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean equals(final Object o) { | ||||
|             if (this == o) return true; | ||||
|             if (o == null || getClass() != o.getClass()) return false; | ||||
| 
 | ||||
|             final Key key = (Key) o; | ||||
| 
 | ||||
|             if (deviceId != key.deviceId) return false; | ||||
|             if (!recipientId.equals(key.recipientId)) return false; | ||||
|             return distributionId.equals(key.distributionId); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public int hashCode() { | ||||
|             int result = recipientId.hashCode(); | ||||
|             result = 31 * result + deviceId; | ||||
|             result = 31 * result + distributionId.hashCode(); | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -87,8 +87,8 @@ public class SenderKeySharedStore { | ||||
|         synchronized (sharedSenderKeys) { | ||||
|             return sharedSenderKeys.get(distributionId) | ||||
|                     .stream() | ||||
|                     .map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.getRecipientId()) | ||||
|                             .getIdentifier(), k.getDeviceId())) | ||||
|                     .map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.recipientId()) | ||||
|                             .getIdentifier(), k.deviceId())) | ||||
|                     .collect(Collectors.toSet()); | ||||
|         } | ||||
|     } | ||||
| @ -146,7 +146,7 @@ public class SenderKeySharedStore { | ||||
| 
 | ||||
|                 sharedSenderKeys.put(distributionId, new HashSet<>(entries) { | ||||
|                     { | ||||
|                         entries.removeIf(e -> e.getRecipientId().equals(recipientId)); | ||||
|                         removeIf(e -> e.recipientId().equals(recipientId)); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
| @ -163,7 +163,7 @@ public class SenderKeySharedStore { | ||||
|                         entries.stream() | ||||
|                                 .map(e -> e.recipientId.equals(toBeMergedRecipientId) ? new SenderKeySharedEntry( | ||||
|                                         recipientId, | ||||
|                                         e.getDeviceId()) : e) | ||||
|                                         e.deviceId()) : e) | ||||
|                                 .collect(Collectors.toSet())); | ||||
|             } | ||||
|             saveLocked(); | ||||
| @ -181,8 +181,8 @@ public class SenderKeySharedStore { | ||||
|         var storage = new Storage(sharedSenderKeys.entrySet().stream().flatMap(pair -> { | ||||
|             final var sharedWith = pair.getValue(); | ||||
|             return sharedWith.stream() | ||||
|                     .map(entry -> new Storage.SharedSenderKey(entry.getRecipientId().getId(), | ||||
|                             entry.getDeviceId(), | ||||
|                     .map(entry -> new Storage.SharedSenderKey(entry.recipientId().id(), | ||||
|                             entry.deviceId(), | ||||
|                             pair.getKey().asUuid().toString())); | ||||
|         }).collect(Collectors.toList())); | ||||
| 
 | ||||
| @ -199,72 +199,10 @@ public class SenderKeySharedStore { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static class Storage { | ||||
|     private record Storage(List<SharedSenderKey> sharedSenderKeys) { | ||||
| 
 | ||||
|         public List<SharedSenderKey> sharedSenderKeys; | ||||
| 
 | ||||
|         // For deserialization | ||||
|         private Storage() { | ||||
|         } | ||||
| 
 | ||||
|         public Storage(final List<SharedSenderKey> sharedSenderKeys) { | ||||
|             this.sharedSenderKeys = sharedSenderKeys; | ||||
|         } | ||||
| 
 | ||||
|         private static class SharedSenderKey { | ||||
| 
 | ||||
|             public long recipientId; | ||||
|             public int deviceId; | ||||
|             public String distributionId; | ||||
| 
 | ||||
|             // For deserialization | ||||
|             private SharedSenderKey() { | ||||
|             } | ||||
| 
 | ||||
|             public SharedSenderKey(final long recipientId, final int deviceId, final String distributionId) { | ||||
|                 this.recipientId = recipientId; | ||||
|                 this.deviceId = deviceId; | ||||
|                 this.distributionId = distributionId; | ||||
|             } | ||||
|         } | ||||
|         private record SharedSenderKey(long recipientId, int deviceId, String distributionId) {} | ||||
|     } | ||||
| 
 | ||||
|     private static final class SenderKeySharedEntry { | ||||
| 
 | ||||
|         private final RecipientId recipientId; | ||||
|         private final int deviceId; | ||||
| 
 | ||||
|         public SenderKeySharedEntry( | ||||
|                 final RecipientId recipientId, final int deviceId | ||||
|         ) { | ||||
|             this.recipientId = recipientId; | ||||
|             this.deviceId = deviceId; | ||||
|         } | ||||
| 
 | ||||
|         public RecipientId getRecipientId() { | ||||
|             return recipientId; | ||||
|         } | ||||
| 
 | ||||
|         public int getDeviceId() { | ||||
|             return deviceId; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean equals(final Object o) { | ||||
|             if (this == o) return true; | ||||
|             if (o == null || getClass() != o.getClass()) return false; | ||||
| 
 | ||||
|             final SenderKeySharedEntry that = (SenderKeySharedEntry) o; | ||||
| 
 | ||||
|             if (deviceId != that.deviceId) return false; | ||||
|             return recipientId.equals(that.recipientId); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public int hashCode() { | ||||
|             int result = recipientId.hashCode(); | ||||
|             result = 31 * result + deviceId; | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
|     private record SenderKeySharedEntry(RecipientId recipientId, int deviceId) {} | ||||
| } | ||||
|  | ||||
| @ -88,8 +88,8 @@ public class SessionStore implements SignalServiceSessionStore { | ||||
|         synchronized (cachedSessions) { | ||||
|             return getKeysLocked(recipientId).stream() | ||||
|                     // get all sessions for recipient except main device session | ||||
|                     .filter(key -> key.getDeviceId() != 1 && key.getRecipientId().equals(recipientId)) | ||||
|                     .map(Key::getDeviceId) | ||||
|                     .filter(key -> key.deviceId() != 1 && key.recipientId().equals(recipientId)) | ||||
|                     .map(Key::deviceId) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } | ||||
|     } | ||||
| @ -155,7 +155,7 @@ public class SessionStore implements SignalServiceSessionStore { | ||||
|                     .stream() | ||||
|                     .flatMap(recipientId -> getKeysLocked(recipientId).stream()) | ||||
|                     .filter(key -> isActive(this.loadSessionLocked(key))) | ||||
|                     .map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId), key.getDeviceId())) | ||||
|                     .map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId), key.deviceId())) | ||||
|                     .collect(Collectors.toSet()); | ||||
|         } | ||||
|     } | ||||
| @ -197,7 +197,7 @@ public class SessionStore implements SignalServiceSessionStore { | ||||
|                     if (session == null) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     final var newKey = new Key(recipientId, key.getDeviceId()); | ||||
|                     final var newKey = new Key(recipientId, key.deviceId()); | ||||
|                     storeSessionLocked(newKey, session); | ||||
|                 } | ||||
|             } | ||||
| @ -217,7 +217,7 @@ public class SessionStore implements SignalServiceSessionStore { | ||||
|     } | ||||
| 
 | ||||
|     private List<Key> getKeysLocked(RecipientId recipientId) { | ||||
|         final var files = sessionsPath.listFiles((_file, s) -> s.startsWith(recipientId.getId() + "_")); | ||||
|         final var files = sessionsPath.listFiles((_file, s) -> s.startsWith(recipientId.id() + "_")); | ||||
|         if (files == null) { | ||||
|             return List.of(); | ||||
|         } | ||||
| @ -249,7 +249,7 @@ public class SessionStore implements SignalServiceSessionStore { | ||||
|         } catch (IOException e) { | ||||
|             throw new AssertionError("Failed to create sessions path", e); | ||||
|         } | ||||
|         return new File(sessionsPath, key.getRecipientId().getId() + "_" + key.getDeviceId()); | ||||
|         return new File(sessionsPath, key.recipientId().id() + "_" + key.deviceId()); | ||||
|     } | ||||
| 
 | ||||
|     private SessionRecord loadSessionLocked(final Key key) { | ||||
| @ -324,40 +324,5 @@ public class SessionStore implements SignalServiceSessionStore { | ||||
|                 && record.getSessionVersion() == CiphertextMessage.CURRENT_VERSION; | ||||
|     } | ||||
| 
 | ||||
|     private static final class Key { | ||||
| 
 | ||||
|         private final RecipientId recipientId; | ||||
|         private final int deviceId; | ||||
| 
 | ||||
|         public Key(final RecipientId recipientId, final int deviceId) { | ||||
|             this.recipientId = recipientId; | ||||
|             this.deviceId = deviceId; | ||||
|         } | ||||
| 
 | ||||
|         public RecipientId getRecipientId() { | ||||
|             return recipientId; | ||||
|         } | ||||
| 
 | ||||
|         public int getDeviceId() { | ||||
|             return deviceId; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean equals(final Object o) { | ||||
|             if (this == o) return true; | ||||
|             if (o == null || getClass() != o.getClass()) return false; | ||||
| 
 | ||||
|             final var key = (Key) o; | ||||
| 
 | ||||
|             if (deviceId != key.deviceId) return false; | ||||
|             return recipientId.equals(key.recipientId); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public int hashCode() { | ||||
|             int result = recipientId.hashCode(); | ||||
|             result = 31 * result + deviceId; | ||||
|             return result; | ||||
|         } | ||||
|     } | ||||
|     private record Key(RecipientId recipientId, int deviceId) {} | ||||
| } | ||||
|  | ||||
| @ -65,33 +65,10 @@ public class StickerStore { | ||||
|                 .collect(Collectors.toList())); | ||||
|     } | ||||
| 
 | ||||
|     public static class Storage { | ||||
|     public record Storage(List<Storage.Sticker> stickers) { | ||||
| 
 | ||||
|         public List<Storage.Sticker> stickers; | ||||
|         private record Sticker(String packId, String packKey, boolean installed) { | ||||
| 
 | ||||
|         // For deserialization | ||||
|         private Storage() { | ||||
|         } | ||||
| 
 | ||||
|         public Storage(final List<Sticker> stickers) { | ||||
|             this.stickers = stickers; | ||||
|         } | ||||
| 
 | ||||
|         private static class Sticker { | ||||
| 
 | ||||
|             public String packId; | ||||
|             public String packKey; | ||||
|             public boolean installed; | ||||
| 
 | ||||
|             // For deserialization | ||||
|             private Sticker() { | ||||
|             } | ||||
| 
 | ||||
|             public Sticker(final String packId, final String packKey, final boolean installed) { | ||||
|                 this.packId = packId; | ||||
|                 this.packKey = packKey; | ||||
|                 this.installed = installed; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -35,60 +35,59 @@ public class StickerUtils { | ||||
| 
 | ||||
|         var pack = parseStickerPack(rootPath, zip); | ||||
| 
 | ||||
|         if (pack.stickers == null) { | ||||
|         if (pack.stickers() == null) { | ||||
|             throw new StickerPackInvalidException("Must set a 'stickers' field."); | ||||
|         } | ||||
| 
 | ||||
|         if (pack.stickers.isEmpty()) { | ||||
|         if (pack.stickers().isEmpty()) { | ||||
|             throw new StickerPackInvalidException("Must include stickers."); | ||||
|         } | ||||
| 
 | ||||
|         var stickers = new ArrayList<SignalServiceStickerManifestUpload.StickerInfo>(pack.stickers.size()); | ||||
|         for (var sticker : pack.stickers) { | ||||
|             if (sticker.file == null) { | ||||
|         var stickers = new ArrayList<SignalServiceStickerManifestUpload.StickerInfo>(pack.stickers().size()); | ||||
|         for (var sticker : pack.stickers()) { | ||||
|             if (sticker.file() == null) { | ||||
|                 throw new StickerPackInvalidException("Must set a 'file' field on each sticker."); | ||||
|             } | ||||
| 
 | ||||
|             Pair<InputStream, Long> data; | ||||
|             try { | ||||
|                 data = getInputStreamAndLength(rootPath, zip, sticker.file); | ||||
|                 data = getInputStreamAndLength(rootPath, zip, sticker.file()); | ||||
|             } catch (IOException ignored) { | ||||
|                 throw new StickerPackInvalidException("Could not find find " + sticker.file); | ||||
|                 throw new StickerPackInvalidException("Could not find find " + sticker.file()); | ||||
|             } | ||||
| 
 | ||||
|             var contentType = sticker.contentType != null && !sticker.contentType.isEmpty() | ||||
|                     ? sticker.contentType | ||||
|                     : getContentType(rootPath, zip, sticker.file); | ||||
|             var contentType = sticker.contentType() != null && !sticker.contentType().isEmpty() | ||||
|                     ? sticker.contentType() | ||||
|                     : getContentType(rootPath, zip, sticker.file()); | ||||
|             var stickerInfo = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), | ||||
|                     data.second(), | ||||
|                     Optional.fromNullable(sticker.emoji).or(""), | ||||
|                     Optional.fromNullable(sticker.emoji()).or(""), | ||||
|                     contentType); | ||||
|             stickers.add(stickerInfo); | ||||
|         } | ||||
| 
 | ||||
|         SignalServiceStickerManifestUpload.StickerInfo cover = null; | ||||
|         if (pack.cover != null) { | ||||
|             if (pack.cover.file == null) { | ||||
|         if (pack.cover() != null) { | ||||
|             if (pack.cover().file() == null) { | ||||
|                 throw new StickerPackInvalidException("Must set a 'file' field on the cover."); | ||||
|             } | ||||
| 
 | ||||
|             Pair<InputStream, Long> data; | ||||
|             try { | ||||
|                 data = getInputStreamAndLength(rootPath, zip, pack.cover.file); | ||||
|                 data = getInputStreamAndLength(rootPath, zip, pack.cover().file()); | ||||
|             } catch (IOException ignored) { | ||||
|                 throw new StickerPackInvalidException("Could not find find " + pack.cover.file); | ||||
|                 throw new StickerPackInvalidException("Could not find find " + pack.cover().file()); | ||||
|             } | ||||
| 
 | ||||
|             var contentType = pack.cover.contentType != null && !pack.cover.contentType.isEmpty() | ||||
|                     ? pack.cover.contentType | ||||
|                     : getContentType(rootPath, zip, pack.cover.file); | ||||
|             var contentType = pack.cover().contentType() != null && !pack.cover().contentType().isEmpty() ? pack.cover() | ||||
|                     .contentType() : getContentType(rootPath, zip, pack.cover().file()); | ||||
|             cover = new SignalServiceStickerManifestUpload.StickerInfo(data.first(), | ||||
|                     data.second(), | ||||
|                     Optional.fromNullable(pack.cover.emoji).or(""), | ||||
|                     Optional.fromNullable(pack.cover().emoji()).or(""), | ||||
|                     contentType); | ||||
|         } | ||||
| 
 | ||||
|         return new SignalServiceStickerManifestUpload(pack.title, pack.author, cover, stickers); | ||||
|         return new SignalServiceStickerManifestUpload(pack.title(), pack.author(), cover, stickers); | ||||
|     } | ||||
| 
 | ||||
|     private static JsonStickerPack parseStickerPack(String rootPath, ZipFile zip) throws IOException { | ||||
|  | ||||
| @ -26,11 +26,11 @@ public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler | ||||
|     public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { | ||||
|         final var object = new HashMap<String, Object>(); | ||||
|         if (exception != null) { | ||||
|             object.put("error", new JsonError(exception)); | ||||
|             object.put("error", JsonError.from(exception)); | ||||
|         } | ||||
| 
 | ||||
|         if (envelope != null) { | ||||
|             object.put("envelope", new JsonMessageEnvelope(envelope, content, exception, m)); | ||||
|             object.put("envelope", JsonMessageEnvelope.from(envelope, content, exception, m)); | ||||
|         } | ||||
| 
 | ||||
|         jsonWriter.write(object); | ||||
|  | ||||
| @ -643,7 +643,7 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler { | ||||
| 
 | ||||
|         var group = m.getGroup(groupId); | ||||
|         if (group != null) { | ||||
|             writer.println("Name: {}", group.getTitle()); | ||||
|             writer.println("Name: {}", group.title()); | ||||
|         } else { | ||||
|             writer.println("Name: <Unknown group>"); | ||||
|         } | ||||
|  | ||||
| @ -66,21 +66,5 @@ public class GetUserStatusCommand implements JsonRpcLocalCommand { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class JsonUserStatus { | ||||
| 
 | ||||
|         public final String recipient; | ||||
| 
 | ||||
|         public final String number; | ||||
| 
 | ||||
|         public final String uuid; | ||||
| 
 | ||||
|         public final boolean isRegistered; | ||||
| 
 | ||||
|         public JsonUserStatus(String recipient, String number, String uuid, boolean isRegistered) { | ||||
|             this.recipient = recipient; | ||||
|             this.number = number; | ||||
|             this.uuid = uuid; | ||||
|             this.isRegistered = isRegistered; | ||||
|         } | ||||
|     } | ||||
|     private record JsonUserStatus(String recipient, String number, String uuid, boolean isRegistered) {} | ||||
| } | ||||
|  | ||||
| @ -69,7 +69,7 @@ public class JoinGroupCommand implements JsonRpcLocalCommand { | ||||
|                     writer.println("Joined group \"{}\"", newGroupId.toBase64()); | ||||
|                 } | ||||
|             } | ||||
|             handleSendMessageResults(results.second().getResults()); | ||||
|             handleSendMessageResults(results.second().results()); | ||||
|         } catch (GroupPatchNotAcceptedException e) { | ||||
|             throw new UserErrorException("Failed to join group, maybe already a member"); | ||||
|         } catch (IOException e) { | ||||
|  | ||||
| @ -16,8 +16,7 @@ import java.util.Map; | ||||
| public interface JsonRpcLocalCommand extends JsonRpcCommand<Map<String, Object>>, LocalCommand { | ||||
| 
 | ||||
|     default TypeReference<Map<String, Object>> getRequestType() { | ||||
|         return new TypeReference<>() { | ||||
|         }; | ||||
|         return new TypeReference<>() {}; | ||||
|     } | ||||
| 
 | ||||
|     default void handleCommand( | ||||
|  | ||||
| @ -54,26 +54,5 @@ public class ListContactsCommand implements JsonRpcLocalCommand { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class JsonContact { | ||||
| 
 | ||||
|         public final String number; | ||||
|         public final String uuid; | ||||
|         public final String name; | ||||
|         public final boolean isBlocked; | ||||
|         public final int messageExpirationTime; | ||||
| 
 | ||||
|         private JsonContact( | ||||
|                 final String number, | ||||
|                 final String uuid, | ||||
|                 final String name, | ||||
|                 final boolean isBlocked, | ||||
|                 final int messageExpirationTime | ||||
|         ) { | ||||
|             this.number = number; | ||||
|             this.uuid = uuid; | ||||
|             this.name = name; | ||||
|             this.isBlocked = isBlocked; | ||||
|             this.messageExpirationTime = messageExpirationTime; | ||||
|         } | ||||
|     } | ||||
|     private record JsonContact(String number, String uuid, String name, boolean isBlocked, int messageExpirationTime) {} | ||||
| } | ||||
|  | ||||
| @ -45,36 +45,21 @@ public class ListDevicesCommand implements JsonRpcLocalCommand { | ||||
| 
 | ||||
|         if (outputWriter instanceof PlainTextWriter writer) { | ||||
|             for (var d : devices) { | ||||
|                 writer.println("- Device {}{}:", d.getId(), (d.isThisDevice() ? " (this device)" : "")); | ||||
|                 writer.println("- Device {}{}:", d.id(), (d.isThisDevice() ? " (this device)" : "")); | ||||
|                 writer.indent(w -> { | ||||
|                     w.println("Name: {}", d.getName()); | ||||
|                     w.println("Created: {}", DateUtils.formatTimestamp(d.getCreated())); | ||||
|                     w.println("Last seen: {}", DateUtils.formatTimestamp(d.getLastSeen())); | ||||
|                     w.println("Name: {}", d.name()); | ||||
|                     w.println("Created: {}", DateUtils.formatTimestamp(d.created())); | ||||
|                     w.println("Last seen: {}", DateUtils.formatTimestamp(d.lastSeen())); | ||||
|                 }); | ||||
|             } | ||||
|         } else { | ||||
|             final var writer = (JsonWriter) outputWriter; | ||||
|             final var jsonDevices = devices.stream() | ||||
|                     .map(d -> new JsonDevice(d.getId(), d.getName(), d.getCreated(), d.getLastSeen())) | ||||
|                     .map(d -> new JsonDevice(d.id(), d.name(), d.created(), d.lastSeen())) | ||||
|                     .collect(Collectors.toList()); | ||||
|             writer.write(jsonDevices); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class JsonDevice { | ||||
| 
 | ||||
|         public final long id; | ||||
|         public final String name; | ||||
|         public final long createdTimestamp; | ||||
|         public final long lastSeenTimestamp; | ||||
| 
 | ||||
|         private JsonDevice( | ||||
|                 final long id, final String name, final long createdTimestamp, final long lastSeenTimestamp | ||||
|         ) { | ||||
|             this.id = id; | ||||
|             this.name = name; | ||||
|             this.createdTimestamp = createdTimestamp; | ||||
|             this.lastSeenTimestamp = lastSeenTimestamp; | ||||
|         } | ||||
|     } | ||||
|     private record JsonDevice(long id, String name, long createdTimestamp, long lastSeenTimestamp) {} | ||||
| } | ||||
|  | ||||
| @ -50,25 +50,25 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { | ||||
|             PlainTextWriter writer, Group group, boolean detailed | ||||
|     ) { | ||||
|         if (detailed) { | ||||
|             final var groupInviteLink = group.getGroupInviteLinkUrl(); | ||||
|             final var groupInviteLink = group.groupInviteLinkUrl(); | ||||
| 
 | ||||
|             writer.println( | ||||
|                     "Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Message expiration: {} Link: {}", | ||||
|                     group.getGroupId().toBase64(), | ||||
|                     group.getTitle(), | ||||
|                     group.getDescription(), | ||||
|                     group.groupId().toBase64(), | ||||
|                     group.title(), | ||||
|                     group.description(), | ||||
|                     group.isMember(), | ||||
|                     group.isBlocked(), | ||||
|                     resolveMembers(group.getMembers()), | ||||
|                     resolveMembers(group.getPendingMembers()), | ||||
|                     resolveMembers(group.getRequestingMembers()), | ||||
|                     resolveMembers(group.getAdminMembers()), | ||||
|                     group.getMessageExpirationTimer() == 0 ? "disabled" : group.getMessageExpirationTimer() + "s", | ||||
|                     resolveMembers(group.members()), | ||||
|                     resolveMembers(group.pendingMembers()), | ||||
|                     resolveMembers(group.requestingMembers()), | ||||
|                     resolveMembers(group.adminMembers()), | ||||
|                     group.messageExpirationTimer() == 0 ? "disabled" : group.messageExpirationTimer() + "s", | ||||
|                     groupInviteLink == null ? '-' : groupInviteLink.getUrl()); | ||||
|         } else { | ||||
|             writer.println("Id: {} Name: {}  Active: {} Blocked: {}", | ||||
|                     group.getGroupId().toBase64(), | ||||
|                     group.getTitle(), | ||||
|                     group.groupId().toBase64(), | ||||
|                     group.title(), | ||||
|                     group.isMember(), | ||||
|                     group.isBlocked()); | ||||
|         } | ||||
| @ -83,21 +83,21 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { | ||||
|         if (outputWriter instanceof JsonWriter jsonWriter) { | ||||
| 
 | ||||
|             var jsonGroups = groups.stream().map(group -> { | ||||
|                 final var groupInviteLink = group.getGroupInviteLinkUrl(); | ||||
|                 final var groupInviteLink = group.groupInviteLinkUrl(); | ||||
| 
 | ||||
|                 return new JsonGroup(group.getGroupId().toBase64(), | ||||
|                         group.getTitle(), | ||||
|                         group.getDescription(), | ||||
|                 return new JsonGroup(group.groupId().toBase64(), | ||||
|                         group.title(), | ||||
|                         group.description(), | ||||
|                         group.isMember(), | ||||
|                         group.isBlocked(), | ||||
|                         group.getMessageExpirationTimer(), | ||||
|                         resolveJsonMembers(group.getMembers()), | ||||
|                         resolveJsonMembers(group.getPendingMembers()), | ||||
|                         resolveJsonMembers(group.getRequestingMembers()), | ||||
|                         resolveJsonMembers(group.getAdminMembers()), | ||||
|                         group.getPermissionAddMember().name(), | ||||
|                         group.getPermissionEditDetails().name(), | ||||
|                         group.getPermissionSendMessage().name(), | ||||
|                         group.messageExpirationTimer(), | ||||
|                         resolveJsonMembers(group.members()), | ||||
|                         resolveJsonMembers(group.pendingMembers()), | ||||
|                         resolveJsonMembers(group.requestingMembers()), | ||||
|                         resolveJsonMembers(group.adminMembers()), | ||||
|                         group.permissionAddMember().name(), | ||||
|                         group.permissionEditDetails().name(), | ||||
|                         group.permissionSendMessage().name(), | ||||
|                         groupInviteLink == null ? null : groupInviteLink.getUrl()); | ||||
|             }).collect(Collectors.toList()); | ||||
| 
 | ||||
| @ -111,66 +111,22 @@ public class ListGroupsCommand implements JsonRpcLocalCommand { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class JsonGroup { | ||||
|     private record JsonGroup( | ||||
|             String id, | ||||
|             String name, | ||||
|             String description, | ||||
|             boolean isMember, | ||||
|             boolean isBlocked, | ||||
|             int messageExpirationTime, | ||||
|             Set<JsonGroupMember> members, | ||||
|             Set<JsonGroupMember> pendingMembers, | ||||
|             Set<JsonGroupMember> requestingMembers, | ||||
|             Set<JsonGroupMember> admins, | ||||
|             String permissionAddMember, | ||||
|             String permissionEditDetails, | ||||
|             String permissionSendMessage, | ||||
|             String groupInviteLink | ||||
|     ) {} | ||||
| 
 | ||||
|         public final String id; | ||||
|         public final String name; | ||||
|         public final String description; | ||||
|         public final boolean isMember; | ||||
|         public final boolean isBlocked; | ||||
|         public final int messageExpirationTime; | ||||
| 
 | ||||
|         public final Set<JsonGroupMember> members; | ||||
|         public final Set<JsonGroupMember> pendingMembers; | ||||
|         public final Set<JsonGroupMember> requestingMembers; | ||||
|         public final Set<JsonGroupMember> admins; | ||||
|         public final String permissionAddMember; | ||||
|         public final String permissionEditDetails; | ||||
|         public final String permissionSendMessage; | ||||
|         public final String groupInviteLink; | ||||
| 
 | ||||
|         public JsonGroup( | ||||
|                 String id, | ||||
|                 String name, | ||||
|                 String description, | ||||
|                 boolean isMember, | ||||
|                 boolean isBlocked, | ||||
|                 final int messageExpirationTime, | ||||
|                 Set<JsonGroupMember> members, | ||||
|                 Set<JsonGroupMember> pendingMembers, | ||||
|                 Set<JsonGroupMember> requestingMembers, | ||||
|                 Set<JsonGroupMember> admins, | ||||
|                 final String permissionAddMember, | ||||
|                 final String permissionEditDetails, | ||||
|                 final String permissionSendMessage, | ||||
|                 String groupInviteLink | ||||
|         ) { | ||||
|             this.id = id; | ||||
|             this.name = name; | ||||
|             this.description = description; | ||||
|             this.isMember = isMember; | ||||
|             this.isBlocked = isBlocked; | ||||
|             this.messageExpirationTime = messageExpirationTime; | ||||
| 
 | ||||
|             this.members = members; | ||||
|             this.pendingMembers = pendingMembers; | ||||
|             this.requestingMembers = requestingMembers; | ||||
|             this.admins = admins; | ||||
|             this.permissionAddMember = permissionAddMember; | ||||
|             this.permissionEditDetails = permissionEditDetails; | ||||
|             this.permissionSendMessage = permissionSendMessage; | ||||
|             this.groupInviteLink = groupInviteLink; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class JsonGroupMember { | ||||
| 
 | ||||
|         public final String number; | ||||
|         public final String uuid; | ||||
| 
 | ||||
|         private JsonGroupMember(final String number, final String uuid) { | ||||
|             this.number = number; | ||||
|             this.uuid = uuid; | ||||
|         } | ||||
|     } | ||||
|     private record JsonGroupMember(String number, String uuid) {} | ||||
| } | ||||
|  | ||||
| @ -30,12 +30,12 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { | ||||
|     } | ||||
| 
 | ||||
|     private static void printIdentityFingerprint(PlainTextWriter writer, Manager m, Identity theirId) { | ||||
|         final SignalServiceAddress address = theirId.getRecipient().toSignalServiceAddress(); | ||||
|         var digits = Util.formatSafetyNumber(theirId.getSafetyNumber()); | ||||
|         final SignalServiceAddress address = theirId.recipient().toSignalServiceAddress(); | ||||
|         var digits = Util.formatSafetyNumber(theirId.safetyNumber()); | ||||
|         writer.println("{}: {} Added: {} Fingerprint: {} Safety Number: {}", | ||||
|                 address.getNumber().orNull(), | ||||
|                 theirId.getTrustLevel(), | ||||
|                 theirId.getDateAdded(), | ||||
|                 theirId.trustLevel(), | ||||
|                 theirId.dateAdded(), | ||||
|                 Hex.toString(theirId.getFingerprint()), | ||||
|                 digits); | ||||
|     } | ||||
| @ -66,9 +66,9 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { | ||||
|         } else { | ||||
|             final var writer = (JsonWriter) outputWriter; | ||||
|             final var jsonIdentities = identities.stream().map(id -> { | ||||
|                 final var address = id.getRecipient().toSignalServiceAddress(); | ||||
|                 var safetyNumber = Util.formatSafetyNumber(id.getSafetyNumber()); | ||||
|                 var scannableSafetyNumber = id.getScannableSafetyNumber(); | ||||
|                 final var address = id.recipient().toSignalServiceAddress(); | ||||
|                 var safetyNumber = Util.formatSafetyNumber(id.safetyNumber()); | ||||
|                 var scannableSafetyNumber = id.scannableSafetyNumber(); | ||||
|                 return new JsonIdentity(address.getNumber().orNull(), | ||||
|                         address.getUuid().toString(), | ||||
|                         Hex.toString(id.getFingerprint()), | ||||
| @ -76,40 +76,21 @@ public class ListIdentitiesCommand implements JsonRpcLocalCommand { | ||||
|                         scannableSafetyNumber == null | ||||
|                                 ? null | ||||
|                                 : Base64.getEncoder().encodeToString(scannableSafetyNumber), | ||||
|                         id.getTrustLevel().name(), | ||||
|                         id.getDateAdded().getTime()); | ||||
|                         id.trustLevel().name(), | ||||
|                         id.dateAdded().getTime()); | ||||
|             }).collect(Collectors.toList()); | ||||
| 
 | ||||
|             writer.write(jsonIdentities); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static final class JsonIdentity { | ||||
| 
 | ||||
|         public final String number; | ||||
|         public final String uuid; | ||||
|         public final String fingerprint; | ||||
|         public final String safetyNumber; | ||||
|         public final String scannableSafetyNumber; | ||||
|         public final String trustLevel; | ||||
|         public final long addedTimestamp; | ||||
| 
 | ||||
|         private JsonIdentity( | ||||
|                 final String number, | ||||
|                 final String uuid, | ||||
|                 final String fingerprint, | ||||
|                 final String safetyNumber, | ||||
|                 final String scannableSafetyNumber, | ||||
|                 final String trustLevel, | ||||
|                 final long addedTimestamp | ||||
|         ) { | ||||
|             this.number = number; | ||||
|             this.uuid = uuid; | ||||
|             this.fingerprint = fingerprint; | ||||
|             this.safetyNumber = safetyNumber; | ||||
|             this.scannableSafetyNumber = scannableSafetyNumber; | ||||
|             this.trustLevel = trustLevel; | ||||
|             this.addedTimestamp = addedTimestamp; | ||||
|         } | ||||
|     } | ||||
|     private record JsonIdentity( | ||||
|             String number, | ||||
|             String uuid, | ||||
|             String fingerprint, | ||||
|             String safetyNumber, | ||||
|             String scannableSafetyNumber, | ||||
|             String trustLevel, | ||||
|             long addedTimestamp | ||||
|     ) {} | ||||
| } | ||||
|  | ||||
| @ -55,9 +55,9 @@ public class QuitGroupCommand implements JsonRpcLocalCommand { | ||||
|         try { | ||||
|             try { | ||||
|                 final var results = m.quitGroup(groupId, groupAdmins); | ||||
|                 final var timestamp = results.getTimestamp(); | ||||
|                 final var timestamp = results.timestamp(); | ||||
|                 outputResult(outputWriter, timestamp); | ||||
|                 handleSendMessageResults(results.getResults()); | ||||
|                 handleSendMessageResults(results.results()); | ||||
|             } catch (NotAGroupMemberException e) { | ||||
|                 logger.info("User is not a group member"); | ||||
|             } | ||||
|  | ||||
| @ -61,19 +61,19 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { | ||||
|             if (outputWriter instanceof JsonWriter jsonWriter) { | ||||
| 
 | ||||
|                 dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> { | ||||
|                     var envelope = new JsonMessageEnvelope(messageReceived); | ||||
|                     var envelope = JsonMessageEnvelope.from(messageReceived); | ||||
|                     final var object = Map.of("envelope", envelope); | ||||
|                     jsonWriter.write(object); | ||||
|                 }); | ||||
| 
 | ||||
|                 dbusconnection.addSigHandler(Signal.ReceiptReceived.class, signal, receiptReceived -> { | ||||
|                     var envelope = new JsonMessageEnvelope(receiptReceived); | ||||
|                     var envelope = JsonMessageEnvelope.from(receiptReceived); | ||||
|                     final var object = Map.of("envelope", envelope); | ||||
|                     jsonWriter.write(object); | ||||
|                 }); | ||||
| 
 | ||||
|                 dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, signal, syncReceived -> { | ||||
|                     var envelope = new JsonMessageEnvelope(syncReceived); | ||||
|                     var envelope = JsonMessageEnvelope.from(syncReceived); | ||||
|                     final var object = Map.of("envelope", envelope); | ||||
|                     jsonWriter.write(object); | ||||
|                 }); | ||||
|  | ||||
| @ -56,8 +56,8 @@ public class RemoteDeleteCommand implements JsonRpcLocalCommand { | ||||
| 
 | ||||
|         try { | ||||
|             final var results = m.sendRemoteDeleteMessage(targetTimestamp, recipientIdentifiers); | ||||
|             outputResult(outputWriter, results.getTimestamp()); | ||||
|             ErrorUtils.handleSendMessageResults(results.getResults()); | ||||
|             outputResult(outputWriter, results.timestamp()); | ||||
|             ErrorUtils.handleSendMessageResults(results.results()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
|             throw new UserErrorException(e.getMessage()); | ||||
|         } catch (IOException e) { | ||||
|  | ||||
| @ -102,8 +102,8 @@ public class SendCommand implements JsonRpcLocalCommand { | ||||
| 
 | ||||
|         try { | ||||
|             var results = m.sendMessage(new Message(messageText, attachments), recipientIdentifiers); | ||||
|             outputResult(outputWriter, results.getTimestamp()); | ||||
|             ErrorUtils.handleSendMessageResults(results.getResults()); | ||||
|             outputResult(outputWriter, results.timestamp()); | ||||
|             ErrorUtils.handleSendMessageResults(results.results()); | ||||
|         } catch (AttachmentInvalidException | IOException e) { | ||||
|             throw new UnexpectedErrorException("Failed to send message: " + e.getMessage() + " (" + e.getClass() | ||||
|                     .getSimpleName() + ")", e); | ||||
|  | ||||
| @ -72,8 +72,8 @@ public class SendReactionCommand implements JsonRpcLocalCommand { | ||||
|                     CommandUtil.getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()), | ||||
|                     targetTimestamp, | ||||
|                     recipientIdentifiers); | ||||
|             outputResult(outputWriter, results.getTimestamp()); | ||||
|             ErrorUtils.handleSendMessageResults(results.getResults()); | ||||
|             outputResult(outputWriter, results.timestamp()); | ||||
|             ErrorUtils.handleSendMessageResults(results.results()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
|             throw new UserErrorException(e.getMessage()); | ||||
|         } catch (IOException e) { | ||||
|  | ||||
| @ -127,8 +127,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { | ||||
|                 var results = m.createGroup(groupName, | ||||
|                         groupMembers, | ||||
|                         groupAvatar == null ? null : new File(groupAvatar)); | ||||
|                 timestamp = results.second().getTimestamp(); | ||||
|                 ErrorUtils.handleSendMessageResults(results.second().getResults()); | ||||
|                 timestamp = results.second().timestamp(); | ||||
|                 ErrorUtils.handleSendMessageResults(results.second().results()); | ||||
|                 groupId = results.first(); | ||||
|                 groupName = null; | ||||
|                 groupMembers = null; | ||||
| @ -154,8 +154,8 @@ public class UpdateGroupCommand implements JsonRpcLocalCommand { | ||||
|                                     : groupSendMessagesPermission == GroupPermission.ONLY_ADMINS) | ||||
|                             .build()); | ||||
|             if (results != null) { | ||||
|                 timestamp = results.getTimestamp(); | ||||
|                 ErrorUtils.handleSendMessageResults(results.getResults()); | ||||
|                 timestamp = results.timestamp(); | ||||
|                 ErrorUtils.handleSendMessageResults(results.results()); | ||||
|             } | ||||
|             outputResult(outputWriter, timestamp, isNewGroup ? groupId : null); | ||||
|         } catch (AttachmentInvalidException e) { | ||||
|  | ||||
| @ -321,9 +321,9 @@ public class DbusManagerImpl implements Manager { | ||||
|             final Message message, final Set<RecipientIdentifier> recipients | ||||
|     ) throws IOException, AttachmentInvalidException, NotAGroupMemberException, GroupNotFoundException, GroupSendingNotAllowedException { | ||||
|         return handleMessage(recipients, | ||||
|                 numbers -> signal.sendMessage(message.getMessageText(), message.getAttachments(), numbers), | ||||
|                 () -> signal.sendNoteToSelfMessage(message.getMessageText(), message.getAttachments()), | ||||
|                 groupId -> signal.sendGroupMessage(message.getMessageText(), message.getAttachments(), groupId)); | ||||
|                 numbers -> signal.sendMessage(message.messageText(), message.attachments(), numbers), | ||||
|                 () -> signal.sendNoteToSelfMessage(message.messageText(), message.attachments()), | ||||
|                 groupId -> signal.sendGroupMessage(message.messageText(), message.attachments(), groupId)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | ||||
| @ -153,8 +153,8 @@ public class DbusSignalImpl implements Signal { | ||||
|                             .map(RecipientIdentifier.class::cast) | ||||
|                             .collect(Collectors.toSet())); | ||||
| 
 | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (AttachmentInvalidException e) { | ||||
|             throw new Error.AttachmentInvalid(e.getMessage()); | ||||
|         } catch (IOException e) { | ||||
| @ -182,8 +182,8 @@ public class DbusSignalImpl implements Signal { | ||||
|                     getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() | ||||
|                             .map(RecipientIdentifier.class::cast) | ||||
|                             .collect(Collectors.toSet())); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (IOException e) { | ||||
|             throw new Error.Failure(e.getMessage()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
| @ -198,8 +198,8 @@ public class DbusSignalImpl implements Signal { | ||||
|         try { | ||||
|             final var results = m.sendRemoteDeleteMessage(targetSentTimestamp, | ||||
|                     Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (IOException e) { | ||||
|             throw new Error.Failure(e.getMessage()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
| @ -236,8 +236,8 @@ public class DbusSignalImpl implements Signal { | ||||
|                     getSingleRecipientIdentifiers(recipients, m.getSelfNumber()).stream() | ||||
|                             .map(RecipientIdentifier.class::cast) | ||||
|                             .collect(Collectors.toSet())); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (IOException e) { | ||||
|             throw new Error.Failure(e.getMessage()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
| @ -303,8 +303,8 @@ public class DbusSignalImpl implements Signal { | ||||
|         try { | ||||
|             final var results = m.sendMessage(new Message(message, attachments), | ||||
|                     Set.of(RecipientIdentifier.NoteToSelf.INSTANCE)); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (AttachmentInvalidException e) { | ||||
|             throw new Error.AttachmentInvalid(e.getMessage()); | ||||
|         } catch (IOException e) { | ||||
| @ -318,7 +318,7 @@ public class DbusSignalImpl implements Signal { | ||||
|     public void sendEndSessionMessage(final List<String> recipients) { | ||||
|         try { | ||||
|             final var results = m.sendEndSessionMessage(getSingleRecipientIdentifiers(recipients, m.getSelfNumber())); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|         } catch (IOException e) { | ||||
|             throw new Error.Failure(e.getMessage()); | ||||
|         } | ||||
| @ -329,8 +329,8 @@ public class DbusSignalImpl implements Signal { | ||||
|         try { | ||||
|             var results = m.sendMessage(new Message(message, attachments), | ||||
|                     Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (IOException e) { | ||||
|             throw new Error.Failure(e.getMessage()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
| @ -354,8 +354,8 @@ public class DbusSignalImpl implements Signal { | ||||
|                     getSingleRecipientIdentifier(targetAuthor, m.getSelfNumber()), | ||||
|                     targetSentTimestamp, | ||||
|                     Set.of(new RecipientIdentifier.Group(getGroupId(groupId)))); | ||||
|             checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|             return results.getTimestamp(); | ||||
|             checkSendMessageResults(results.timestamp(), results.results()); | ||||
|             return results.timestamp(); | ||||
|         } catch (IOException e) { | ||||
|             throw new Error.Failure(e.getMessage()); | ||||
|         } catch (GroupNotFoundException | NotAGroupMemberException | GroupSendingNotAllowedException e) { | ||||
| @ -420,7 +420,7 @@ public class DbusSignalImpl implements Signal { | ||||
|         var groups = m.getGroups(); | ||||
|         var ids = new ArrayList<byte[]>(groups.size()); | ||||
|         for (var group : groups) { | ||||
|             ids.add(group.getGroupId().serialize()); | ||||
|             ids.add(group.groupId().serialize()); | ||||
|         } | ||||
|         return ids; | ||||
|     } | ||||
| @ -444,10 +444,10 @@ public class DbusSignalImpl implements Signal { | ||||
|     @Override | ||||
|     public String getGroupName(final byte[] groupId) { | ||||
|         var group = m.getGroup(getGroupId(groupId)); | ||||
|         if (group == null || group.getTitle() == null) { | ||||
|         if (group == null || group.title() == null) { | ||||
|             return ""; | ||||
|         } else { | ||||
|             return group.getTitle(); | ||||
|             return group.title(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -457,7 +457,7 @@ public class DbusSignalImpl implements Signal { | ||||
|         if (group == null) { | ||||
|             return List.of(); | ||||
|         } else { | ||||
|             final var members = group.getMembers(); | ||||
|             final var members = group.members(); | ||||
|             return getRecipientStrings(members); | ||||
|         } | ||||
|     } | ||||
| @ -478,7 +478,7 @@ public class DbusSignalImpl implements Signal { | ||||
|             final var memberIdentifiers = getSingleRecipientIdentifiers(members, m.getSelfNumber()); | ||||
|             if (groupId == null) { | ||||
|                 final var results = m.createGroup(name, memberIdentifiers, avatar == null ? null : new File(avatar)); | ||||
|                 checkSendMessageResults(results.second().getTimestamp(), results.second().getResults()); | ||||
|                 checkSendMessageResults(results.second().timestamp(), results.second().results()); | ||||
|                 return results.first().serialize(); | ||||
|             } else { | ||||
|                 final var results = m.updateGroup(getGroupId(groupId), | ||||
| @ -488,7 +488,7 @@ public class DbusSignalImpl implements Signal { | ||||
|                                 .withAvatarFile(avatar == null ? null : new File(avatar)) | ||||
|                                 .build()); | ||||
|                 if (results != null) { | ||||
|                     checkSendMessageResults(results.getTimestamp(), results.getResults()); | ||||
|                     checkSendMessageResults(results.timestamp(), results.results()); | ||||
|                 } | ||||
|                 return groupId; | ||||
|             } | ||||
| @ -600,8 +600,8 @@ public class DbusSignalImpl implements Signal { | ||||
|     // all numbers the system knows | ||||
|     @Override | ||||
|     public List<String> listNumbers() { | ||||
|         return Stream.concat(m.getIdentities().stream().map(Identity::getRecipient), | ||||
|                 m.getContacts().stream().map(Pair::first)) | ||||
|         return Stream.concat(m.getIdentities().stream().map(Identity::recipient), | ||||
|                         m.getContacts().stream().map(Pair::first)) | ||||
|                 .map(a -> a.getNumber().orElse(null)) | ||||
|                 .filter(Objects::nonNull) | ||||
|                 .distinct() | ||||
| @ -620,7 +620,7 @@ public class DbusSignalImpl implements Signal { | ||||
|         } | ||||
|         // Try profiles if no contact name was found | ||||
|         for (var identity : m.getIdentities()) { | ||||
|             final var address = identity.getRecipient(); | ||||
|             final var address = identity.recipient(); | ||||
|             var number = address.getNumber().orElse(null); | ||||
|             if (number != null) { | ||||
|                 Profile profile = null; | ||||
| @ -835,7 +835,7 @@ public class DbusSignalImpl implements Signal { | ||||
|             if (d.isThisDevice()) { | ||||
|                 thisDevice = new DBusPath(deviceObjectPath); | ||||
|             } | ||||
|             this.devices.add(new StructDevice(new DBusPath(deviceObjectPath), d.getId(), emptyIfNull(d.getName()))); | ||||
|             this.devices.add(new StructDevice(new DBusPath(deviceObjectPath), d.id(), emptyIfNull(d.name()))); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -862,15 +862,15 @@ public class DbusSignalImpl implements Signal { | ||||
|         unExportGroups(); | ||||
| 
 | ||||
|         groups.forEach(g -> { | ||||
|             final var object = new DbusSignalGroupImpl(g.getGroupId()); | ||||
|             final var object = new DbusSignalGroupImpl(g.groupId()); | ||||
|             try { | ||||
|                 connection.exportObject(object); | ||||
|             } catch (DBusException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|             this.groups.add(new StructGroup(new DBusPath(object.getObjectPath()), | ||||
|                     g.getGroupId().serialize(), | ||||
|                     emptyIfNull(g.getTitle()))); | ||||
|                     g.groupId().serialize(), | ||||
|                     emptyIfNull(g.title()))); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -885,22 +885,22 @@ public class DbusSignalImpl implements Signal { | ||||
| 
 | ||||
|         public DbusSignalDeviceImpl(final org.asamk.signal.manager.api.Device device) { | ||||
|             super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Device", | ||||
|                     List.of(new DbusProperty<>("Id", device::getId), | ||||
|                             new DbusProperty<>("Name", () -> emptyIfNull(device.getName()), this::setDeviceName), | ||||
|                             new DbusProperty<>("Created", device::getCreated), | ||||
|                             new DbusProperty<>("LastSeen", device::getLastSeen)))); | ||||
|                     List.of(new DbusProperty<>("Id", device::id), | ||||
|                             new DbusProperty<>("Name", () -> emptyIfNull(device.name()), this::setDeviceName), | ||||
|                             new DbusProperty<>("Created", device::created), | ||||
|                             new DbusProperty<>("LastSeen", device::lastSeen)))); | ||||
|             this.device = device; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String getObjectPath() { | ||||
|             return getDeviceObjectPath(objectPath, device.getId()); | ||||
|             return getDeviceObjectPath(objectPath, device.id()); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public void removeDevice() throws Error.Failure { | ||||
|             try { | ||||
|                 m.removeLinkedDevices(device.getId()); | ||||
|                 m.removeLinkedDevices(device.id()); | ||||
|                 updateDevices(); | ||||
|             } catch (IOException e) { | ||||
|                 throw new Error.Failure(e.getMessage()); | ||||
| @ -929,36 +929,36 @@ public class DbusSignalImpl implements Signal { | ||||
|             this.groupId = groupId; | ||||
|             super.addPropertiesHandler(new DbusInterfacePropertiesHandler("org.asamk.Signal.Group", | ||||
|                     List.of(new DbusProperty<>("Id", groupId::serialize), | ||||
|                             new DbusProperty<>("Name", () -> emptyIfNull(getGroup().getTitle()), this::setGroupName), | ||||
|                             new DbusProperty<>("Name", () -> emptyIfNull(getGroup().title()), this::setGroupName), | ||||
|                             new DbusProperty<>("Description", | ||||
|                                     () -> emptyIfNull(getGroup().getDescription()), | ||||
|                                     () -> emptyIfNull(getGroup().description()), | ||||
|                                     this::setGroupDescription), | ||||
|                             new DbusProperty<>("Avatar", this::setGroupAvatar), | ||||
|                             new DbusProperty<>("IsBlocked", () -> getGroup().isBlocked(), this::setIsBlocked), | ||||
|                             new DbusProperty<>("IsMember", () -> getGroup().isMember()), | ||||
|                             new DbusProperty<>("IsAdmin", () -> getGroup().isAdmin()), | ||||
|                             new DbusProperty<>("MessageExpirationTimer", | ||||
|                                     () -> getGroup().getMessageExpirationTimer(), | ||||
|                                     () -> getGroup().messageExpirationTimer(), | ||||
|                                     this::setMessageExpirationTime), | ||||
|                             new DbusProperty<>("Members", | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().getMembers()), "as")), | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().members()), "as")), | ||||
|                             new DbusProperty<>("PendingMembers", | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().getPendingMembers()), "as")), | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().pendingMembers()), "as")), | ||||
|                             new DbusProperty<>("RequestingMembers", | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().getRequestingMembers()), "as")), | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().requestingMembers()), "as")), | ||||
|                             new DbusProperty<>("Admins", | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().getAdminMembers()), "as")), | ||||
|                                     () -> new Variant<>(getRecipientStrings(getGroup().adminMembers()), "as")), | ||||
|                             new DbusProperty<>("PermissionAddMember", | ||||
|                                     () -> getGroup().getPermissionAddMember().name(), | ||||
|                                     () -> getGroup().permissionAddMember().name(), | ||||
|                                     this::setGroupPermissionAddMember), | ||||
|                             new DbusProperty<>("PermissionEditDetails", | ||||
|                                     () -> getGroup().getPermissionEditDetails().name(), | ||||
|                                     () -> getGroup().permissionEditDetails().name(), | ||||
|                                     this::setGroupPermissionEditDetails), | ||||
|                             new DbusProperty<>("PermissionSendMessage", | ||||
|                                     () -> getGroup().getPermissionSendMessage().name(), | ||||
|                                     () -> getGroup().permissionSendMessage().name(), | ||||
|                                     this::setGroupPermissionSendMessage), | ||||
|                             new DbusProperty<>("GroupInviteLink", () -> { | ||||
|                                 final var groupInviteLinkUrl = getGroup().getGroupInviteLinkUrl(); | ||||
|                                 final var groupInviteLinkUrl = getGroup().groupInviteLinkUrl(); | ||||
|                                 return groupInviteLinkUrl == null ? "" : groupInviteLinkUrl.getUrl(); | ||||
|                             })))); | ||||
|         } | ||||
|  | ||||
| @ -1,43 +1,25 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; | ||||
| 
 | ||||
| class JsonAttachment { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String contentType; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String filename; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String id; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final Long size; | ||||
| 
 | ||||
|     JsonAttachment(SignalServiceAttachment attachment) { | ||||
|         this.contentType = attachment.getContentType(); | ||||
| record JsonAttachment(String contentType, String filename, String id, Long size) { | ||||
| 
 | ||||
|     static JsonAttachment from(SignalServiceAttachment attachment) { | ||||
|         if (attachment.isPointer()) { | ||||
|             final var pointer = attachment.asPointer(); | ||||
|             this.id = pointer.getRemoteId().toString(); | ||||
|             this.filename = pointer.getFileName().orNull(); | ||||
|             this.size = pointer.getSize().transform(Integer::longValue).orNull(); | ||||
|             final var id = pointer.getRemoteId().toString(); | ||||
|             final var filename = pointer.getFileName().orNull(); | ||||
|             final var size = pointer.getSize().transform(Integer::longValue).orNull(); | ||||
|             return new JsonAttachment(attachment.getContentType(), filename, id, size); | ||||
|         } else { | ||||
|             final var stream = attachment.asStream(); | ||||
|             this.id = null; | ||||
|             this.filename = stream.getFileName().orNull(); | ||||
|             this.size = stream.getLength(); | ||||
|             final var filename = stream.getFileName().orNull(); | ||||
|             final var size = stream.getLength(); | ||||
|             return new JsonAttachment(attachment.getContentType(), filename, null, size); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     JsonAttachment(String filename) { | ||||
|         this.filename = filename; | ||||
|         this.contentType = null; | ||||
|         this.id = null; | ||||
|         this.size = null; | ||||
|     static JsonAttachment from(String filename) { | ||||
|         return new JsonAttachment(filename, null, null, null); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.calls.AnswerMessage; | ||||
| import org.whispersystems.signalservice.api.messages.calls.BusyMessage; | ||||
| @ -12,33 +11,19 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| class JsonCallMessage { | ||||
| record JsonCallMessage( | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) OfferMessage offerMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) AnswerMessage answerMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) BusyMessage busyMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) HangupMessage hangupMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<IceUpdateMessage> iceUpdateMessages | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final OfferMessage offerMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final AnswerMessage answerMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final BusyMessage busyMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final HangupMessage hangupMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<IceUpdateMessage> iceUpdateMessages; | ||||
| 
 | ||||
|     JsonCallMessage(SignalServiceCallMessage callMessage) { | ||||
|         this.offerMessage = callMessage.getOfferMessage().orNull(); | ||||
|         this.answerMessage = callMessage.getAnswerMessage().orNull(); | ||||
|         this.busyMessage = callMessage.getBusyMessage().orNull(); | ||||
|         this.hangupMessage = callMessage.getHangupMessage().orNull(); | ||||
|         this.iceUpdateMessages = callMessage.getIceUpdateMessages().orNull(); | ||||
|     static JsonCallMessage from(SignalServiceCallMessage callMessage) { | ||||
|         return new JsonCallMessage(callMessage.getOfferMessage().orNull(), | ||||
|                 callMessage.getAnswerMessage().orNull(), | ||||
|                 callMessage.getBusyMessage().orNull(), | ||||
|                 callMessage.getHangupMessage().orNull(), | ||||
|                 callMessage.getIceUpdateMessages().orNull()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,48 +1,29 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.util.Util; | ||||
| import org.whispersystems.signalservice.api.messages.shared.SharedContact; | ||||
| 
 | ||||
| public class JsonContactAddress { | ||||
| public record JsonContactAddress( | ||||
|         SharedContact.PostalAddress.Type type, | ||||
|         String label, | ||||
|         String street, | ||||
|         String pobox, | ||||
|         String neighborhood, | ||||
|         String city, | ||||
|         String region, | ||||
|         String postcode, | ||||
|         String country | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final SharedContact.PostalAddress.Type type; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String label; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String street; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String pobox; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String neighborhood; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String city; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String region; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String postcode; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String country; | ||||
| 
 | ||||
|     public JsonContactAddress(SharedContact.PostalAddress address) { | ||||
|         type = address.getType(); | ||||
|         label = Util.getStringIfNotBlank(address.getLabel()); | ||||
|         street = Util.getStringIfNotBlank(address.getStreet()); | ||||
|         pobox = Util.getStringIfNotBlank(address.getPobox()); | ||||
|         neighborhood = Util.getStringIfNotBlank(address.getNeighborhood()); | ||||
|         city = Util.getStringIfNotBlank(address.getCity()); | ||||
|         region = Util.getStringIfNotBlank(address.getRegion()); | ||||
|         postcode = Util.getStringIfNotBlank(address.getPostcode()); | ||||
|         country = Util.getStringIfNotBlank(address.getCountry()); | ||||
|     static JsonContactAddress from(SharedContact.PostalAddress address) { | ||||
|         return new JsonContactAddress(address.getType(), | ||||
|                 Util.getStringIfNotBlank(address.getLabel()), | ||||
|                 Util.getStringIfNotBlank(address.getStreet()), | ||||
|                 Util.getStringIfNotBlank(address.getPobox()), | ||||
|                 Util.getStringIfNotBlank(address.getNeighborhood()), | ||||
|                 Util.getStringIfNotBlank(address.getCity()), | ||||
|                 Util.getStringIfNotBlank(address.getRegion()), | ||||
|                 Util.getStringIfNotBlank(address.getPostcode()), | ||||
|                 Util.getStringIfNotBlank(address.getCountry())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,19 +1,10 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.shared.SharedContact; | ||||
| 
 | ||||
| public class JsonContactAvatar { | ||||
| public record JsonContactAvatar(JsonAttachment attachment, boolean isProfile) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final JsonAttachment attachment; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final boolean isProfile; | ||||
| 
 | ||||
|     public JsonContactAvatar(SharedContact.Avatar avatar) { | ||||
|         attachment = new JsonAttachment(avatar.getAttachment()); | ||||
|         isProfile = avatar.isProfile(); | ||||
|     static JsonContactAvatar from(SharedContact.Avatar avatar) { | ||||
|         return new JsonContactAvatar(JsonAttachment.from(avatar.getAttachment()), avatar.isProfile()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,11 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.util.Util; | ||||
| import org.whispersystems.signalservice.api.messages.shared.SharedContact; | ||||
| 
 | ||||
| public class JsonContactEmail { | ||||
| public record JsonContactEmail(String value, SharedContact.Email.Type type, String label) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String value; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final SharedContact.Email.Type type; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String label; | ||||
| 
 | ||||
|     public JsonContactEmail(SharedContact.Email email) { | ||||
|         value = email.getValue(); | ||||
|         type = email.getType(); | ||||
|         label = Util.getStringIfNotBlank(email.getLabel()); | ||||
|     static JsonContactEmail from(SharedContact.Email email) { | ||||
|         return new JsonContactEmail(email.getValue(), email.getType(), Util.getStringIfNotBlank(email.getLabel())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,36 +1,18 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.util.Util; | ||||
| import org.whispersystems.signalservice.api.messages.shared.SharedContact; | ||||
| 
 | ||||
| public class JsonContactName { | ||||
| public record JsonContactName( | ||||
|         String display, String given, String family, String prefix, String suffix, String middle | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String display; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String given; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String family; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String prefix; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String suffix; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String middle; | ||||
| 
 | ||||
|     public JsonContactName(SharedContact.Name name) { | ||||
|         display = Util.getStringIfNotBlank(name.getDisplay()); | ||||
|         given = Util.getStringIfNotBlank(name.getGiven()); | ||||
|         family = Util.getStringIfNotBlank(name.getFamily()); | ||||
|         prefix = Util.getStringIfNotBlank(name.getPrefix()); | ||||
|         suffix = Util.getStringIfNotBlank(name.getSuffix()); | ||||
|         middle = Util.getStringIfNotBlank(name.getMiddle()); | ||||
|     static JsonContactName from(SharedContact.Name name) { | ||||
|         return new JsonContactName(Util.getStringIfNotBlank(name.getDisplay()), | ||||
|                 Util.getStringIfNotBlank(name.getGiven()), | ||||
|                 Util.getStringIfNotBlank(name.getFamily()), | ||||
|                 Util.getStringIfNotBlank(name.getPrefix()), | ||||
|                 Util.getStringIfNotBlank(name.getSuffix()), | ||||
|                 Util.getStringIfNotBlank(name.getMiddle())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,11 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.util.Util; | ||||
| import org.whispersystems.signalservice.api.messages.shared.SharedContact; | ||||
| 
 | ||||
| public class JsonContactPhone { | ||||
| public record JsonContactPhone(String value, SharedContact.Phone.Type type, String label) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String value; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final SharedContact.Phone.Type type; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     private final String label; | ||||
| 
 | ||||
|     public JsonContactPhone(SharedContact.Phone phone) { | ||||
|         value = phone.getValue(); | ||||
|         type = phone.getType(); | ||||
|         label = Util.getStringIfNotBlank(phone.getLabel()); | ||||
|     static JsonContactPhone from(SharedContact.Phone phone) { | ||||
|         return new JsonContactPhone(phone.getValue(), phone.getType(), Util.getStringIfNotBlank(phone.getLabel())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.Signal; | ||||
| import org.asamk.signal.manager.Manager; | ||||
| @ -10,136 +9,124 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| class JsonDataMessage { | ||||
| record JsonDataMessage( | ||||
|         long timestamp, | ||||
|         String message, | ||||
|         Integer expiresInSeconds, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) Boolean viewOnce, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonReaction reaction, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonQuote quote, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonMention> mentions, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonAttachment> attachments, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSticker sticker, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonRemoteDelete remoteDelete, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSharedContact> contacts, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonGroupInfo groupInfo | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long timestamp; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String message; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final Integer expiresInSeconds; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final Boolean viewOnce; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonReaction reaction; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonQuote quote; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonMention> mentions; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonAttachment> attachments; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonSticker sticker; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonRemoteDelete remoteDelete; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonSharedContact> contacts; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonGroupInfo groupInfo; | ||||
| 
 | ||||
|     JsonDataMessage(SignalServiceDataMessage dataMessage, Manager m) { | ||||
|         this.timestamp = dataMessage.getTimestamp(); | ||||
|     static JsonDataMessage from(SignalServiceDataMessage dataMessage, Manager m) { | ||||
|         final var timestamp = dataMessage.getTimestamp(); | ||||
|         final JsonGroupInfo groupInfo; | ||||
|         if (dataMessage.getGroupContext().isPresent()) { | ||||
|             final var groupContext = dataMessage.getGroupContext().get(); | ||||
|             if (groupContext.getGroupV1().isPresent()) { | ||||
|                 var groupInfo = groupContext.getGroupV1().get(); | ||||
|                 this.groupInfo = new JsonGroupInfo(groupInfo); | ||||
|                 var group = groupContext.getGroupV1().get(); | ||||
|                 groupInfo = JsonGroupInfo.from(group); | ||||
|             } else if (groupContext.getGroupV2().isPresent()) { | ||||
|                 var groupInfo = groupContext.getGroupV2().get(); | ||||
|                 this.groupInfo = new JsonGroupInfo(groupInfo); | ||||
|                 var group = groupContext.getGroupV2().get(); | ||||
|                 groupInfo = JsonGroupInfo.from(group); | ||||
|             } else { | ||||
|                 this.groupInfo = null; | ||||
|                 groupInfo = null; | ||||
|             } | ||||
|         } else { | ||||
|             this.groupInfo = null; | ||||
|             groupInfo = null; | ||||
|         } | ||||
|         this.message = dataMessage.getBody().orNull(); | ||||
|         this.expiresInSeconds = dataMessage.getExpiresInSeconds(); | ||||
|         this.viewOnce = dataMessage.isViewOnce(); | ||||
|         this.reaction = dataMessage.getReaction().isPresent() | ||||
|                 ? new JsonReaction(dataMessage.getReaction().get(), m) | ||||
|                 : null; | ||||
|         this.quote = dataMessage.getQuote().isPresent() ? new JsonQuote(dataMessage.getQuote().get(), m) : null; | ||||
|         final var message = dataMessage.getBody().orNull(); | ||||
|         final var expiresInSeconds = dataMessage.getExpiresInSeconds(); | ||||
|         final var viewOnce = dataMessage.isViewOnce(); | ||||
|         final var reaction = dataMessage.getReaction().isPresent() ? JsonReaction.from(dataMessage.getReaction().get(), | ||||
|                 m) : null; | ||||
|         final var quote = dataMessage.getQuote().isPresent() ? JsonQuote.from(dataMessage.getQuote().get(), m) : null; | ||||
|         final List<JsonMention> mentions; | ||||
|         if (dataMessage.getMentions().isPresent()) { | ||||
|             this.mentions = dataMessage.getMentions() | ||||
|             mentions = dataMessage.getMentions() | ||||
|                     .get() | ||||
|                     .stream() | ||||
|                     .map(mention -> new JsonMention(mention, m)) | ||||
|                     .map(mention -> JsonMention.from(mention, m)) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.mentions = List.of(); | ||||
|             mentions = List.of(); | ||||
|         } | ||||
|         remoteDelete = dataMessage.getRemoteDelete().isPresent() ? new JsonRemoteDelete(dataMessage.getRemoteDelete() | ||||
|                 .get()) : null; | ||||
|         final var remoteDelete = dataMessage.getRemoteDelete().isPresent() | ||||
|                 ? JsonRemoteDelete.from(dataMessage.getRemoteDelete().get()) | ||||
|                 : null; | ||||
|         final List<JsonAttachment> attachments; | ||||
|         if (dataMessage.getAttachments().isPresent()) { | ||||
|             this.attachments = dataMessage.getAttachments() | ||||
|             attachments = dataMessage.getAttachments() | ||||
|                     .get() | ||||
|                     .stream() | ||||
|                     .map(JsonAttachment::new) | ||||
|                     .map(JsonAttachment::from) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.attachments = List.of(); | ||||
|             attachments = List.of(); | ||||
|         } | ||||
|         this.sticker = dataMessage.getSticker().isPresent() ? new JsonSticker(dataMessage.getSticker().get()) : null; | ||||
|         final var sticker = dataMessage.getSticker().isPresent() | ||||
|                 ? JsonSticker.from(dataMessage.getSticker().get()) | ||||
|                 : null; | ||||
| 
 | ||||
|         final List<JsonSharedContact> contacts; | ||||
|         if (dataMessage.getSharedContacts().isPresent()) { | ||||
|             this.contacts = dataMessage.getSharedContacts() | ||||
|             contacts = dataMessage.getSharedContacts() | ||||
|                     .get() | ||||
|                     .stream() | ||||
|                     .map(JsonSharedContact::new) | ||||
|                     .map(JsonSharedContact::from) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.contacts = List.of(); | ||||
|             contacts = List.of(); | ||||
|         } | ||||
|         return new JsonDataMessage(timestamp, | ||||
|                 message, | ||||
|                 expiresInSeconds, | ||||
|                 viewOnce, | ||||
|                 reaction, | ||||
|                 quote, | ||||
|                 mentions, | ||||
|                 attachments, | ||||
|                 sticker, | ||||
|                 remoteDelete, | ||||
|                 contacts, | ||||
|                 groupInfo); | ||||
|     } | ||||
| 
 | ||||
|     public JsonDataMessage(Signal.MessageReceived messageReceived) { | ||||
|         timestamp = messageReceived.getTimestamp(); | ||||
|         message = messageReceived.getMessage(); | ||||
|         groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; | ||||
|         expiresInSeconds = null; | ||||
|         viewOnce = null; | ||||
|         remoteDelete = null; | ||||
|         reaction = null;    // TODO Replace these 5 with the proper commands | ||||
|         quote = null; | ||||
|         mentions = null; | ||||
|         sticker = null; | ||||
|         contacts = null; | ||||
|         attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); | ||||
|     static JsonDataMessage from(Signal.MessageReceived messageReceived) { | ||||
|         return new JsonDataMessage(messageReceived.getTimestamp(), | ||||
|                 messageReceived.getMessage(), | ||||
|                 // TODO Replace these with the proper commands | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 messageReceived.getAttachments().stream().map(JsonAttachment::from).collect(Collectors.toList()), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 messageReceived.getGroupId().length > 0 ? JsonGroupInfo.from(messageReceived.getGroupId()) : null); | ||||
|     } | ||||
| 
 | ||||
|     public JsonDataMessage(Signal.SyncMessageReceived messageReceived) { | ||||
|         timestamp = messageReceived.getTimestamp(); | ||||
|         message = messageReceived.getMessage(); | ||||
|         groupInfo = messageReceived.getGroupId().length > 0 ? new JsonGroupInfo(messageReceived.getGroupId()) : null; | ||||
|         expiresInSeconds = null; | ||||
|         viewOnce = null; | ||||
|         remoteDelete = null; | ||||
|         reaction = null;    // TODO Replace these 5 with the proper commands | ||||
|         quote = null; | ||||
|         mentions = null; | ||||
|         sticker = null; | ||||
|         contacts = null; | ||||
|         attachments = messageReceived.getAttachments().stream().map(JsonAttachment::new).collect(Collectors.toList()); | ||||
|     static JsonDataMessage from(Signal.SyncMessageReceived messageReceived) { | ||||
|         return new JsonDataMessage(messageReceived.getTimestamp(), | ||||
|                 messageReceived.getMessage(), | ||||
|                 // TODO Replace these with the proper commands | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 messageReceived.getAttachments().stream().map(JsonAttachment::from).collect(Collectors.toList()), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 messageReceived.getGroupId().length > 0 ? JsonGroupInfo.from(messageReceived.getGroupId()) : null); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,8 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| public record JsonError(String message, String type) { | ||||
| 
 | ||||
| public class JsonError { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String message; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String type; | ||||
| 
 | ||||
|     public JsonError(Throwable exception) { | ||||
|         this.message = exception.getMessage(); | ||||
|         this.type = exception.getClass().getSimpleName(); | ||||
|     public static JsonError from(Throwable exception) { | ||||
|         return new JsonError(exception.getMessage(), exception.getClass().getSimpleName()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.manager.groups.GroupUtils; | ||||
| import org.asamk.signal.util.Util; | ||||
| @ -12,48 +11,32 @@ import java.util.Base64; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| class JsonGroupInfo { | ||||
| record JsonGroupInfo( | ||||
|         String groupId, | ||||
|         String type, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) String name, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> members | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String groupId; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String type; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final String name; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<String> members; | ||||
| 
 | ||||
|     JsonGroupInfo(SignalServiceGroup groupInfo) { | ||||
|         this.groupId = Base64.getEncoder().encodeToString(groupInfo.getGroupId()); | ||||
|         this.type = groupInfo.getType().toString(); | ||||
|         this.name = groupInfo.getName().orNull(); | ||||
|         if (groupInfo.getMembers().isPresent()) { | ||||
|             this.members = groupInfo.getMembers() | ||||
|                     .get() | ||||
|                     .stream() | ||||
|                     .map(Util::getLegacyIdentifier) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.members = null; | ||||
|         } | ||||
|     static JsonGroupInfo from(SignalServiceGroup groupInfo) { | ||||
|         return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupInfo.getGroupId()), | ||||
|                 groupInfo.getType().toString(), | ||||
|                 groupInfo.getName().orNull(), | ||||
|                 groupInfo.getMembers().isPresent() ? groupInfo.getMembers() | ||||
|                         .get() | ||||
|                         .stream() | ||||
|                         .map(Util::getLegacyIdentifier) | ||||
|                         .collect(Collectors.toList()) : null); | ||||
|     } | ||||
| 
 | ||||
|     JsonGroupInfo(SignalServiceGroupV2 groupInfo) { | ||||
|         this.groupId = GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64(); | ||||
|         this.type = groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER"; | ||||
|         this.members = null; | ||||
|         this.name = null; | ||||
|     static JsonGroupInfo from(SignalServiceGroupV2 groupInfo) { | ||||
|         return new JsonGroupInfo(GroupUtils.getGroupIdV2(groupInfo.getMasterKey()).toBase64(), | ||||
|                 groupInfo.hasSignedGroupChange() ? "UPDATE" : "DELIVER", | ||||
|                 null, | ||||
|                 null); | ||||
|     } | ||||
| 
 | ||||
|     JsonGroupInfo(byte[] groupId) { | ||||
|         this.groupId = Base64.getEncoder().encodeToString(groupId); | ||||
|         this.type = "DELIVER"; | ||||
|         this.members = null; | ||||
|         this.name = null; | ||||
|     static JsonGroupInfo from(byte[] groupId) { | ||||
|         return new JsonGroupInfo(Base64.getEncoder().encodeToString(groupId), "DELIVER", null, null); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,37 +1,19 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.manager.Manager; | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; | ||||
| import org.whispersystems.signalservice.api.push.SignalServiceAddress; | ||||
| 
 | ||||
| import static org.asamk.signal.util.Util.getLegacyIdentifier; | ||||
| 
 | ||||
| public class JsonMention { | ||||
| public record JsonMention(@Deprecated String name, String number, String uuid, int start, int length) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @Deprecated | ||||
|     final String name; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String number; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String uuid; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final int start; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final int length; | ||||
| 
 | ||||
|     JsonMention(SignalServiceDataMessage.Mention mention, Manager m) { | ||||
|     static JsonMention from(SignalServiceDataMessage.Mention mention, Manager m) { | ||||
|         final var address = m.resolveSignalServiceAddress(new SignalServiceAddress(mention.getUuid())); | ||||
|         this.name = getLegacyIdentifier(address); | ||||
|         this.number = address.getNumber().orNull(); | ||||
|         this.uuid = address.getUuid().toString(); | ||||
|         this.start = mention.getStart(); | ||||
|         this.length = mention.getLength(); | ||||
|         return new JsonMention(getLegacyIdentifier(address), | ||||
|                 address.getNumber().orNull(), | ||||
|                 address.getUuid().toString(), | ||||
|                 mention.getStart(), | ||||
|                 mention.getLength()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.Signal; | ||||
| import org.asamk.signal.manager.Manager; | ||||
| @ -15,143 +14,133 @@ import java.util.List; | ||||
| 
 | ||||
| import static org.asamk.signal.util.Util.getLegacyIdentifier; | ||||
| 
 | ||||
| public class JsonMessageEnvelope { | ||||
| public record JsonMessageEnvelope( | ||||
|         @Deprecated String source, | ||||
|         String sourceNumber, | ||||
|         String sourceUuid, | ||||
|         String sourceName, | ||||
|         Integer sourceDevice, | ||||
|         long timestamp, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonDataMessage dataMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessage syncMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonCallMessage callMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonReceiptMessage receiptMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonTypingMessage typingMessage | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @Deprecated | ||||
|     final String source; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String sourceNumber; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String sourceUuid; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String sourceName; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final Integer sourceDevice; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long timestamp; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonDataMessage dataMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonSyncMessage syncMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonCallMessage callMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonReceiptMessage receiptMessage; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonTypingMessage typingMessage; | ||||
| 
 | ||||
|     public JsonMessageEnvelope( | ||||
|     public static JsonMessageEnvelope from( | ||||
|             SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception, Manager m | ||||
|     ) { | ||||
|         final String source; | ||||
|         final String sourceNumber; | ||||
|         final String sourceUuid; | ||||
|         final Integer sourceDevice; | ||||
|         if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) { | ||||
|             var source = m.resolveSignalServiceAddress(envelope.getSourceAddress()); | ||||
|             this.source = getLegacyIdentifier(source); | ||||
|             this.sourceNumber = source.getNumber().orNull(); | ||||
|             this.sourceUuid = source.getUuid().toString(); | ||||
|             this.sourceDevice = envelope.getSourceDevice(); | ||||
|             final var sourceAddress = m.resolveSignalServiceAddress(envelope.getSourceAddress()); | ||||
|             source = getLegacyIdentifier(sourceAddress); | ||||
|             sourceNumber = sourceAddress.getNumber().orNull(); | ||||
|             sourceUuid = sourceAddress.getUuid().toString(); | ||||
|             sourceDevice = envelope.getSourceDevice(); | ||||
|         } else if (envelope.isUnidentifiedSender() && content != null) { | ||||
|             final var source = m.resolveSignalServiceAddress(content.getSender()); | ||||
|             this.source = getLegacyIdentifier(source); | ||||
|             this.sourceNumber = source.getNumber().orNull(); | ||||
|             this.sourceUuid = source.getUuid().toString(); | ||||
|             this.sourceDevice = content.getSenderDevice(); | ||||
|             final var sender = m.resolveSignalServiceAddress(content.getSender()); | ||||
|             source = getLegacyIdentifier(sender); | ||||
|             sourceNumber = sender.getNumber().orNull(); | ||||
|             sourceUuid = sender.getUuid().toString(); | ||||
|             sourceDevice = content.getSenderDevice(); | ||||
|         } else if (exception instanceof UntrustedIdentityException e) { | ||||
|             final var source = m.resolveSignalServiceAddress(e.getSender()); | ||||
|             this.source = getLegacyIdentifier(source); | ||||
|             this.sourceNumber = source.getNumber().orNull(); | ||||
|             this.sourceUuid = source.getUuid().toString(); | ||||
|             this.sourceDevice = e.getSenderDevice(); | ||||
|             final var sender = m.resolveSignalServiceAddress(e.getSender()); | ||||
|             source = getLegacyIdentifier(sender); | ||||
|             sourceNumber = sender.getNumber().orNull(); | ||||
|             sourceUuid = sender.getUuid().toString(); | ||||
|             sourceDevice = e.getSenderDevice(); | ||||
|         } else { | ||||
|             this.source = null; | ||||
|             this.sourceNumber = null; | ||||
|             this.sourceUuid = null; | ||||
|             this.sourceDevice = null; | ||||
|             source = null; | ||||
|             sourceNumber = null; | ||||
|             sourceUuid = null; | ||||
|             sourceDevice = null; | ||||
|         } | ||||
|         String name; | ||||
|         try { | ||||
|             name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(this.source, m.getSelfNumber())); | ||||
|             name = m.getContactOrProfileName(RecipientIdentifier.Single.fromString(source, m.getSelfNumber())); | ||||
|         } catch (InvalidNumberException | NullPointerException e) { | ||||
|             name = null; | ||||
|         } | ||||
|         this.sourceName = name; | ||||
|         this.timestamp = envelope.getTimestamp(); | ||||
|         final var sourceName = name; | ||||
|         final var timestamp = envelope.getTimestamp(); | ||||
|         final JsonReceiptMessage receiptMessage; | ||||
|         if (envelope.isReceipt()) { | ||||
|             this.receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); | ||||
|             receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); | ||||
|         } else if (content != null && content.getReceiptMessage().isPresent()) { | ||||
|             this.receiptMessage = new JsonReceiptMessage(content.getReceiptMessage().get()); | ||||
|             receiptMessage = JsonReceiptMessage.from(content.getReceiptMessage().get()); | ||||
|         } else { | ||||
|             this.receiptMessage = null; | ||||
|             receiptMessage = null; | ||||
|         } | ||||
|         this.typingMessage = content != null && content.getTypingMessage().isPresent() | ||||
|                 ? new JsonTypingMessage(content.getTypingMessage().get()) | ||||
|         final var typingMessage = content != null && content.getTypingMessage().isPresent() ? JsonTypingMessage.from( | ||||
|                 content.getTypingMessage().get()) : null; | ||||
| 
 | ||||
|         final var dataMessage = content != null && content.getDataMessage().isPresent() | ||||
|                 ? JsonDataMessage.from(content.getDataMessage().get(), m) | ||||
|                 : null; | ||||
|         final var syncMessage = content != null && content.getSyncMessage().isPresent() | ||||
|                 ? JsonSyncMessage.from(content.getSyncMessage().get(), m) | ||||
|                 : null; | ||||
|         final var callMessage = content != null && content.getCallMessage().isPresent() | ||||
|                 ? JsonCallMessage.from(content.getCallMessage().get()) | ||||
|                 : null; | ||||
| 
 | ||||
|         this.dataMessage = content != null && content.getDataMessage().isPresent() | ||||
|                 ? new JsonDataMessage(content.getDataMessage().get(), m) | ||||
|                 : null; | ||||
|         this.syncMessage = content != null && content.getSyncMessage().isPresent() | ||||
|                 ? new JsonSyncMessage(content.getSyncMessage().get(), m) | ||||
|                 : null; | ||||
|         this.callMessage = content != null && content.getCallMessage().isPresent() | ||||
|                 ? new JsonCallMessage(content.getCallMessage().get()) | ||||
|                 : null; | ||||
|         return new JsonMessageEnvelope(source, | ||||
|                 sourceNumber, | ||||
|                 sourceUuid, | ||||
|                 sourceName, | ||||
|                 sourceDevice, | ||||
|                 timestamp, | ||||
|                 dataMessage, | ||||
|                 syncMessage, | ||||
|                 callMessage, | ||||
|                 receiptMessage, | ||||
|                 typingMessage); | ||||
|     } | ||||
| 
 | ||||
|     public JsonMessageEnvelope(Signal.MessageReceived messageReceived) { | ||||
|         source = messageReceived.getSender(); | ||||
|         sourceNumber = null; | ||||
|         sourceUuid = null; | ||||
|         sourceName = null; | ||||
|         sourceDevice = null; | ||||
|         timestamp = messageReceived.getTimestamp(); | ||||
|         receiptMessage = null; | ||||
|         dataMessage = new JsonDataMessage(messageReceived); | ||||
|         syncMessage = null; | ||||
|         callMessage = null; | ||||
|         typingMessage = null; | ||||
|     public static JsonMessageEnvelope from(Signal.MessageReceived messageReceived) { | ||||
|         return new JsonMessageEnvelope(messageReceived.getSource(), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 messageReceived.getTimestamp(), | ||||
|                 JsonDataMessage.from(messageReceived), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null); | ||||
|     } | ||||
| 
 | ||||
|     public JsonMessageEnvelope(Signal.ReceiptReceived receiptReceived) { | ||||
|         source = receiptReceived.getSender(); | ||||
|         sourceNumber = null; | ||||
|         sourceUuid = null; | ||||
|         sourceName = null; | ||||
|         sourceDevice = null; | ||||
|         timestamp = receiptReceived.getTimestamp(); | ||||
|         receiptMessage = JsonReceiptMessage.deliveryReceipt(timestamp, List.of(timestamp)); | ||||
|         dataMessage = null; | ||||
|         syncMessage = null; | ||||
|         callMessage = null; | ||||
|         typingMessage = null; | ||||
|     public static JsonMessageEnvelope from(Signal.ReceiptReceived receiptReceived) { | ||||
|         return new JsonMessageEnvelope(receiptReceived.getSender(), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 receiptReceived.getTimestamp(), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 JsonReceiptMessage.deliveryReceipt(receiptReceived.getTimestamp(), | ||||
|                         List.of(receiptReceived.getTimestamp())), | ||||
|                 null); | ||||
|     } | ||||
| 
 | ||||
|     public JsonMessageEnvelope(Signal.SyncMessageReceived messageReceived) { | ||||
|         source = messageReceived.getSource(); | ||||
|         sourceNumber = null; | ||||
|         sourceUuid = null; | ||||
|         sourceName = null; | ||||
|         sourceDevice = null; | ||||
|         timestamp = messageReceived.getTimestamp(); | ||||
|         receiptMessage = null; | ||||
|         dataMessage = null; | ||||
|         syncMessage = new JsonSyncMessage(messageReceived); | ||||
|         callMessage = null; | ||||
|         typingMessage = null; | ||||
|     public static JsonMessageEnvelope from(Signal.SyncMessageReceived messageReceived) { | ||||
|         return new JsonMessageEnvelope(messageReceived.getSource(), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 messageReceived.getTimestamp(), | ||||
|                 null, | ||||
|                 JsonSyncMessage.from(messageReceived), | ||||
|                 null, | ||||
|                 null, | ||||
|                 null); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.manager.Manager; | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; | ||||
| @ -12,55 +11,41 @@ import java.util.stream.Collectors; | ||||
| 
 | ||||
| import static org.asamk.signal.util.Util.getLegacyIdentifier; | ||||
| 
 | ||||
| public class JsonQuote { | ||||
| public record JsonQuote( | ||||
|         long id, | ||||
|         @Deprecated String author, | ||||
|         String authorNumber, | ||||
|         String authorUuid, | ||||
|         String text, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonMention> mentions, | ||||
|         List<JsonQuotedAttachment> attachments | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long id; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @Deprecated | ||||
|     final String author; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String authorNumber; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String authorUuid; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String text; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonMention> mentions; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final List<JsonQuotedAttachment> attachments; | ||||
| 
 | ||||
|     JsonQuote(SignalServiceDataMessage.Quote quote, Manager m) { | ||||
|         this.id = quote.getId(); | ||||
|     static JsonQuote from(SignalServiceDataMessage.Quote quote, Manager m) { | ||||
|         final var id = quote.getId(); | ||||
|         final var address = m.resolveSignalServiceAddress(quote.getAuthor()); | ||||
|         this.author = getLegacyIdentifier(address); | ||||
|         this.authorNumber = address.getNumber().orNull(); | ||||
|         this.authorUuid = address.getUuid().toString(); | ||||
|         this.text = quote.getText(); | ||||
|         final var author = getLegacyIdentifier(address); | ||||
|         final var authorNumber = address.getNumber().orNull(); | ||||
|         final var authorUuid = address.getUuid().toString(); | ||||
|         final var text = quote.getText(); | ||||
| 
 | ||||
|         final List<JsonMention> mentions; | ||||
|         if (quote.getMentions() != null && quote.getMentions().size() > 0) { | ||||
|             this.mentions = quote.getMentions() | ||||
|             mentions = quote.getMentions() | ||||
|                     .stream() | ||||
|                     .map(quotedMention -> new JsonMention(quotedMention, m)) | ||||
|                     .map(quotedMention -> JsonMention.from(quotedMention, m)) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.mentions = null; | ||||
|             mentions = null; | ||||
|         } | ||||
| 
 | ||||
|         final List<JsonQuotedAttachment> attachments; | ||||
|         if (quote.getAttachments().size() > 0) { | ||||
|             this.attachments = quote.getAttachments() | ||||
|                     .stream() | ||||
|                     .map(JsonQuotedAttachment::new) | ||||
|                     .collect(Collectors.toList()); | ||||
|             attachments = quote.getAttachments().stream().map(JsonQuotedAttachment::from).collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.attachments = new ArrayList<>(); | ||||
|             attachments = new ArrayList<>(); | ||||
|         } | ||||
| 
 | ||||
|         return new JsonQuote(id, author, authorNumber, authorUuid, text, mentions, attachments); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,29 +1,22 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; | ||||
| 
 | ||||
| public class JsonQuotedAttachment { | ||||
| public record JsonQuotedAttachment( | ||||
|         String contentType, String filename, @JsonInclude(JsonInclude.Include.NON_NULL) JsonAttachment thumbnail | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String contentType; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String filename; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonAttachment thumbnail; | ||||
| 
 | ||||
|     JsonQuotedAttachment(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) { | ||||
|         contentType = quotedAttachment.getContentType(); | ||||
|         filename = quotedAttachment.getFileName(); | ||||
|     static JsonQuotedAttachment from(SignalServiceDataMessage.Quote.QuotedAttachment quotedAttachment) { | ||||
|         final var contentType = quotedAttachment.getContentType(); | ||||
|         final var filename = quotedAttachment.getFileName(); | ||||
|         final JsonAttachment thumbnail; | ||||
|         if (quotedAttachment.getThumbnail() != null) { | ||||
|             thumbnail = new JsonAttachment(quotedAttachment.getThumbnail()); | ||||
|             thumbnail = JsonAttachment.from(quotedAttachment.getThumbnail()); | ||||
|         } else { | ||||
|             thumbnail = null; | ||||
|         } | ||||
|         return new JsonQuotedAttachment(contentType, filename, thumbnail); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,40 +1,32 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.signal.manager.Manager; | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Reaction; | ||||
| 
 | ||||
| import static org.asamk.signal.util.Util.getLegacyIdentifier; | ||||
| 
 | ||||
| public class JsonReaction { | ||||
| public record JsonReaction( | ||||
|         String emoji, | ||||
|         @Deprecated String targetAuthor, | ||||
|         String targetAuthorNumber, | ||||
|         String targetAuthorUuid, | ||||
|         long targetSentTimestamp, | ||||
|         boolean isRemove | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String emoji; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @Deprecated | ||||
|     final String targetAuthor; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String targetAuthorNumber; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String targetAuthorUuid; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long targetSentTimestamp; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final boolean isRemove; | ||||
| 
 | ||||
|     JsonReaction(Reaction reaction, Manager m) { | ||||
|         this.emoji = reaction.getEmoji(); | ||||
|     static JsonReaction from(Reaction reaction, Manager m) { | ||||
|         final var emoji = reaction.getEmoji(); | ||||
|         final var address = m.resolveSignalServiceAddress(reaction.getTargetAuthor()); | ||||
|         this.targetAuthor = getLegacyIdentifier(address); | ||||
|         this.targetAuthorNumber = address.getNumber().orNull(); | ||||
|         this.targetAuthorUuid = address.getUuid().toString(); | ||||
|         this.targetSentTimestamp = reaction.getTargetSentTimestamp(); | ||||
|         this.isRemove = reaction.isRemove(); | ||||
|         final var targetAuthor = getLegacyIdentifier(address); | ||||
|         final var targetAuthorNumber = address.getNumber().orNull(); | ||||
|         final var targetAuthorUuid = address.getUuid().toString(); | ||||
|         final var targetSentTimestamp = reaction.getTargetSentTimestamp(); | ||||
|         final var isRemove = reaction.isRemove(); | ||||
|         return new JsonReaction(emoji, | ||||
|                 targetAuthor, | ||||
|                 targetAuthorNumber, | ||||
|                 targetAuthorUuid, | ||||
|                 targetSentTimestamp, | ||||
|                 isRemove); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,39 +1,17 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| class JsonReceiptMessage { | ||||
| record JsonReceiptMessage(long when, boolean isDelivery, boolean isRead, List<Long> timestamps) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long when; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final boolean isDelivery; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final boolean isRead; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final List<Long> timestamps; | ||||
| 
 | ||||
|     JsonReceiptMessage(SignalServiceReceiptMessage receiptMessage) { | ||||
|         this.when = receiptMessage.getWhen(); | ||||
|         this.isDelivery = receiptMessage.isDeliveryReceipt(); | ||||
|         this.isRead = receiptMessage.isReadReceipt(); | ||||
|         this.timestamps = receiptMessage.getTimestamps(); | ||||
|     } | ||||
| 
 | ||||
|     private JsonReceiptMessage( | ||||
|             final long when, final boolean isDelivery, final boolean isRead, final List<Long> timestamps | ||||
|     ) { | ||||
|         this.when = when; | ||||
|         this.isDelivery = isDelivery; | ||||
|         this.isRead = isRead; | ||||
|         this.timestamps = timestamps; | ||||
|     static JsonReceiptMessage from(SignalServiceReceiptMessage receiptMessage) { | ||||
|         final var when = receiptMessage.getWhen(); | ||||
|         final var isDelivery = receiptMessage.isDeliveryReceipt(); | ||||
|         final var isRead = receiptMessage.isReadReceipt(); | ||||
|         final var timestamps = receiptMessage.getTimestamps(); | ||||
|         return new JsonReceiptMessage(when, isDelivery, isRead, timestamps); | ||||
|     } | ||||
| 
 | ||||
|     static JsonReceiptMessage deliveryReceipt(final long when, final List<Long> timestamps) { | ||||
|  | ||||
| @ -1,15 +1,10 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; | ||||
| 
 | ||||
| class JsonRemoteDelete { | ||||
| record JsonRemoteDelete(long timestamp) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long timestamp; | ||||
| 
 | ||||
|     JsonRemoteDelete(SignalServiceDataMessage.RemoteDelete remoteDelete) { | ||||
|         this.timestamp = remoteDelete.getTargetSentTimestamp(); | ||||
|     static JsonRemoteDelete from(SignalServiceDataMessage.RemoteDelete remoteDelete) { | ||||
|         return new JsonRemoteDelete(remoteDelete.getTargetSentTimestamp()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,62 +1,45 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.shared.SharedContact; | ||||
| 
 | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| public class JsonSharedContact { | ||||
| public record JsonSharedContact( | ||||
|         JsonContactName name, | ||||
|         JsonContactAvatar avatar, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonContactPhone> phone, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonContactEmail> email, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonContactAddress> address, | ||||
|         String organization | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final JsonContactName name; | ||||
|     static JsonSharedContact from(SharedContact contact) { | ||||
|         final var name = JsonContactName.from(contact.getName()); | ||||
|         final var avatar = contact.getAvatar().isPresent() ? JsonContactAvatar.from(contact.getAvatar().get()) : null; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final JsonContactAvatar avatar; | ||||
|         final var phone = contact.getPhone().isPresent() ? contact.getPhone() | ||||
|                 .get() | ||||
|                 .stream() | ||||
|                 .map(JsonContactPhone::from) | ||||
|                 .collect(Collectors.toList()) : null; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonContactPhone> phone; | ||||
|         final var email = contact.getEmail().isPresent() ? contact.getEmail() | ||||
|                 .get() | ||||
|                 .stream() | ||||
|                 .map(JsonContactEmail::from) | ||||
|                 .collect(Collectors.toList()) : null; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonContactEmail> email; | ||||
|         final var address = contact.getAddress().isPresent() ? contact.getAddress() | ||||
|                 .get() | ||||
|                 .stream() | ||||
|                 .map(JsonContactAddress::from) | ||||
|                 .collect(Collectors.toList()) : null; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonContactAddress> address; | ||||
|         final var organization = contact.getOrganization().orNull(); | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String organization; | ||||
| 
 | ||||
|     public JsonSharedContact(SharedContact contact) { | ||||
|         name = new JsonContactName(contact.getName()); | ||||
|         if (contact.getAvatar().isPresent()) { | ||||
|             avatar = new JsonContactAvatar(contact.getAvatar().get()); | ||||
|         } else { | ||||
|             avatar = null; | ||||
|         } | ||||
| 
 | ||||
|         if (contact.getPhone().isPresent()) { | ||||
|             phone = contact.getPhone().get().stream().map(JsonContactPhone::new).collect(Collectors.toList()); | ||||
|         } else { | ||||
|             phone = null; | ||||
|         } | ||||
| 
 | ||||
|         if (contact.getEmail().isPresent()) { | ||||
|             email = contact.getEmail().get().stream().map(JsonContactEmail::new).collect(Collectors.toList()); | ||||
|         } else { | ||||
|             email = null; | ||||
|         } | ||||
| 
 | ||||
|         if (contact.getAddress().isPresent()) { | ||||
|             address = contact.getAddress().get().stream().map(JsonContactAddress::new).collect(Collectors.toList()); | ||||
|         } else { | ||||
|             address = null; | ||||
|         } | ||||
| 
 | ||||
|         organization = contact.getOrganization().orNull(); | ||||
|         return new JsonSharedContact(name, avatar, phone, email, address, organization); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,25 +1,15 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; | ||||
| 
 | ||||
| import java.util.Base64; | ||||
| 
 | ||||
| public class JsonSticker { | ||||
| public record JsonSticker(String packId, String packKey, int stickerId) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String packId; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String packKey; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final int stickerId; | ||||
| 
 | ||||
|     public JsonSticker(SignalServiceDataMessage.Sticker sticker) { | ||||
|         this.packId = Base64.getEncoder().encodeToString(sticker.getPackId()); | ||||
|         this.packKey = Base64.getEncoder().encodeToString(sticker.getPackKey()); | ||||
|         this.stickerId = sticker.getStickerId(); | ||||
|     static JsonSticker from(SignalServiceDataMessage.Sticker sticker) { | ||||
|         final var packId = Base64.getEncoder().encodeToString(sticker.getPackId()); | ||||
|         final var packKey = Base64.getEncoder().encodeToString(sticker.getPackKey()); | ||||
|         final var stickerId = sticker.getStickerId(); | ||||
|         return new JsonSticker(packId, packKey, stickerId); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| import com.fasterxml.jackson.annotation.JsonUnwrapped; | ||||
| 
 | ||||
| import org.asamk.Signal; | ||||
| import org.asamk.signal.manager.Manager; | ||||
| @ -8,37 +8,30 @@ import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptM | ||||
| 
 | ||||
| import static org.asamk.signal.util.Util.getLegacyIdentifier; | ||||
| 
 | ||||
| class JsonSyncDataMessage extends JsonDataMessage { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @Deprecated | ||||
|     final String destination; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String destinationNumber; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String destinationUuid; | ||||
| 
 | ||||
|     JsonSyncDataMessage(SentTranscriptMessage transcriptMessage, Manager m) { | ||||
|         super(transcriptMessage.getMessage(), m); | ||||
| record JsonSyncDataMessage( | ||||
|         @Deprecated String destination, | ||||
|         String destinationNumber, | ||||
|         String destinationUuid, | ||||
|         @JsonUnwrapped JsonDataMessage dataMessage | ||||
| ) { | ||||
| 
 | ||||
|     static JsonSyncDataMessage from(SentTranscriptMessage transcriptMessage, Manager m) { | ||||
|         if (transcriptMessage.getDestination().isPresent()) { | ||||
|             final var address = transcriptMessage.getDestination().get(); | ||||
|             this.destination = getLegacyIdentifier(address); | ||||
|             this.destinationNumber = address.getNumber().orNull(); | ||||
|             this.destinationUuid = address.getUuid().toString(); | ||||
|             return new JsonSyncDataMessage(getLegacyIdentifier(address), | ||||
|                     address.getNumber().orNull(), | ||||
|                     address.getUuid().toString(), | ||||
|                     JsonDataMessage.from(transcriptMessage.getMessage(), m)); | ||||
| 
 | ||||
|         } else { | ||||
|             this.destination = null; | ||||
|             this.destinationNumber = null; | ||||
|             this.destinationUuid = null; | ||||
|             return new JsonSyncDataMessage(null, null, null, JsonDataMessage.from(transcriptMessage.getMessage(), m)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     JsonSyncDataMessage(Signal.SyncMessageReceived messageReceived) { | ||||
|         super(messageReceived); | ||||
|         this.destination = messageReceived.getDestination(); | ||||
|         this.destinationNumber = null; | ||||
|         this.destinationUuid = null; | ||||
|     static JsonSyncDataMessage from(Signal.SyncMessageReceived messageReceived) { | ||||
|         return new JsonSyncDataMessage(messageReceived.getDestination(), | ||||
|                 null, | ||||
|                 null, | ||||
|                 JsonDataMessage.from(messageReceived)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.asamk.Signal; | ||||
| import org.asamk.signal.manager.Manager; | ||||
| @ -18,76 +17,72 @@ enum JsonSyncMessageType { | ||||
|     REQUEST_SYNC | ||||
| } | ||||
| 
 | ||||
| class JsonSyncMessage { | ||||
| record JsonSyncMessage( | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncDataMessage sentMessage, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedNumbers, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<String> blockedGroupIds, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) List<JsonSyncReadMessage> readMessages, | ||||
|         @JsonInclude(JsonInclude.Include.NON_NULL) JsonSyncMessageType type | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonSyncDataMessage sentMessage; | ||||
|     JsonSyncMessage( | ||||
|             final JsonSyncDataMessage sentMessage, | ||||
|             final List<String> blockedNumbers, | ||||
|             final List<String> blockedGroupIds, | ||||
|             final List<JsonSyncReadMessage> readMessages, | ||||
|             final JsonSyncMessageType type | ||||
|     ) { | ||||
|         this.sentMessage = sentMessage; | ||||
|         this.blockedNumbers = blockedNumbers; | ||||
|         this.blockedGroupIds = blockedGroupIds; | ||||
|         this.readMessages = readMessages; | ||||
|         this.type = type; | ||||
|     } | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<String> blockedNumbers; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<String> blockedGroupIds; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final List<JsonSyncReadMessage> readMessages; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final JsonSyncMessageType type; | ||||
| 
 | ||||
|     JsonSyncMessage(SignalServiceSyncMessage syncMessage, Manager m) { | ||||
|         this.sentMessage = syncMessage.getSent().isPresent() | ||||
|                 ? new JsonSyncDataMessage(syncMessage.getSent().get(), m) | ||||
|                 : null; | ||||
|     static JsonSyncMessage from(SignalServiceSyncMessage syncMessage, Manager m) { | ||||
|         final var sentMessage = syncMessage.getSent().isPresent() ? JsonSyncDataMessage.from(syncMessage.getSent() | ||||
|                 .get(), m) : null; | ||||
|         final List<String> blockedNumbers; | ||||
|         final List<String> blockedGroupIds; | ||||
|         if (syncMessage.getBlockedList().isPresent()) { | ||||
|             final var base64 = Base64.getEncoder(); | ||||
|             this.blockedNumbers = syncMessage.getBlockedList() | ||||
|             blockedNumbers = syncMessage.getBlockedList() | ||||
|                     .get() | ||||
|                     .getAddresses() | ||||
|                     .stream() | ||||
|                     .map(Util::getLegacyIdentifier) | ||||
|                     .collect(Collectors.toList()); | ||||
|             this.blockedGroupIds = syncMessage.getBlockedList() | ||||
|             blockedGroupIds = syncMessage.getBlockedList() | ||||
|                     .get() | ||||
|                     .getGroupIds() | ||||
|                     .stream() | ||||
|                     .map(base64::encodeToString) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.blockedNumbers = null; | ||||
|             this.blockedGroupIds = null; | ||||
|         } | ||||
|         if (syncMessage.getRead().isPresent()) { | ||||
|             this.readMessages = syncMessage.getRead() | ||||
|                     .get() | ||||
|                     .stream() | ||||
|                     .map(JsonSyncReadMessage::new) | ||||
|                     .collect(Collectors.toList()); | ||||
|         } else { | ||||
|             this.readMessages = null; | ||||
|             blockedNumbers = null; | ||||
|             blockedGroupIds = null; | ||||
|         } | ||||
| 
 | ||||
|         final var readMessages = syncMessage.getRead().isPresent() ? syncMessage.getRead() | ||||
|                 .get() | ||||
|                 .stream() | ||||
|                 .map(JsonSyncReadMessage::from) | ||||
|                 .collect(Collectors.toList()) : null; | ||||
| 
 | ||||
|         final JsonSyncMessageType type; | ||||
|         if (syncMessage.getContacts().isPresent()) { | ||||
|             this.type = JsonSyncMessageType.CONTACTS_SYNC; | ||||
|             type = JsonSyncMessageType.CONTACTS_SYNC; | ||||
|         } else if (syncMessage.getGroups().isPresent()) { | ||||
|             this.type = JsonSyncMessageType.GROUPS_SYNC; | ||||
|             type = JsonSyncMessageType.GROUPS_SYNC; | ||||
|         } else if (syncMessage.getRequest().isPresent()) { | ||||
|             this.type = JsonSyncMessageType.REQUEST_SYNC; | ||||
|             type = JsonSyncMessageType.REQUEST_SYNC; | ||||
|         } else { | ||||
|             this.type = null; | ||||
|             type = null; | ||||
|         } | ||||
|         return new JsonSyncMessage(sentMessage, blockedNumbers, blockedGroupIds, readMessages, type); | ||||
|     } | ||||
| 
 | ||||
|     JsonSyncMessage(Signal.SyncMessageReceived messageReceived) { | ||||
|         this.sentMessage = new JsonSyncDataMessage(messageReceived); | ||||
|         this.blockedNumbers = null; | ||||
|         this.blockedGroupIds = null; | ||||
|         this.readMessages = null; | ||||
|         this.type = null; | ||||
|     static JsonSyncMessage from(Signal.SyncMessageReceived messageReceived) { | ||||
|         return new JsonSyncMessage(JsonSyncDataMessage.from(messageReceived), null, null, null, null); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,31 +1,19 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage; | ||||
| 
 | ||||
| import static org.asamk.signal.util.Util.getLegacyIdentifier; | ||||
| 
 | ||||
| class JsonSyncReadMessage { | ||||
| record JsonSyncReadMessage( | ||||
|         @Deprecated String sender, String senderNumber, String senderUuid, long timestamp | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @Deprecated | ||||
|     final String sender; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String senderNumber; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String senderUuid; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long timestamp; | ||||
| 
 | ||||
|     public JsonSyncReadMessage(final ReadMessage readMessage) { | ||||
|         final var sender = readMessage.getSender(); | ||||
|         this.sender = getLegacyIdentifier(sender); | ||||
|         this.senderNumber = sender.getNumber().orNull(); | ||||
|         this.senderUuid = sender.getUuid().toString(); | ||||
|         this.timestamp = readMessage.getTimestamp(); | ||||
|     static JsonSyncReadMessage from(final ReadMessage readMessage) { | ||||
|         final var senderAddress = readMessage.getSender(); | ||||
|         final var sender = getLegacyIdentifier(senderAddress); | ||||
|         final var senderNumber = senderAddress.getNumber().orNull(); | ||||
|         final var senderUuid = senderAddress.getUuid().toString(); | ||||
|         final var timestamp = readMessage.getTimestamp(); | ||||
|         return new JsonSyncReadMessage(sender, senderNumber, senderUuid, timestamp); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,28 +1,26 @@ | ||||
| package org.asamk.signal.json; | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| 
 | ||||
| import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; | ||||
| 
 | ||||
| import java.util.Base64; | ||||
| 
 | ||||
| class JsonTypingMessage { | ||||
| record JsonTypingMessage( | ||||
|         String action, long timestamp, @JsonInclude(JsonInclude.Include.NON_NULL) String groupId | ||||
| ) { | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final String action; | ||||
|     JsonTypingMessage(final String action, final long timestamp, final String groupId) { | ||||
|         this.action = action; | ||||
|         this.timestamp = timestamp; | ||||
|         this.groupId = groupId; | ||||
|     } | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     final long timestamp; | ||||
| 
 | ||||
|     @JsonProperty | ||||
|     @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
|     final String groupId; | ||||
| 
 | ||||
|     JsonTypingMessage(SignalServiceTypingMessage typingMessage) { | ||||
|         this.action = typingMessage.getAction().name(); | ||||
|         this.timestamp = typingMessage.getTimestamp(); | ||||
|     static JsonTypingMessage from(SignalServiceTypingMessage typingMessage) { | ||||
|         final var action = typingMessage.getAction().name(); | ||||
|         final var timestamp = typingMessage.getTimestamp(); | ||||
|         final var encoder = Base64.getEncoder(); | ||||
|         this.groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull(); | ||||
|         final var groupId = typingMessage.getGroupId().transform(encoder::encodeToString).orNull(); | ||||
|         return new JsonTypingMessage(action, timestamp, groupId); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 AsamK
						AsamK