From dd7faebb33e1cfb618637f05694b38cf208ccb3f Mon Sep 17 00:00:00 2001 From: Alessandro Autiero Date: Sun, 7 Jan 2024 23:22:45 +0100 Subject: [PATCH] Working on protobuf serialization instead of relying on smile --- pom.xml | 11 +- proto/whatsapp.proto | 2 +- .../it/auties/whatsapp/api/ClientType.java | 14 +- .../whatsapp/api/TextPreviewSetting.java | 21 ++- .../auties/whatsapp/api/WebHistoryLength.java | 8 +- .../java/it/auties/whatsapp/api/Whatsapp.java | 8 +- .../whatsapp/controller/Controller.java | 12 +- .../it/auties/whatsapp/controller/Keys.java | 138 +++++++++----- .../it/auties/whatsapp/controller/Store.java | 177 +++++++++++------- .../auties/whatsapp/crypto/GroupBuilder.java | 6 +- .../auties/whatsapp/crypto/GroupCipher.java | 4 +- .../whatsapp/listener/OnWhatsappChats.java | 14 +- .../model/action/QuickReplyAction.java | 2 +- .../action/RecentEmojiWeightsAction.java | 2 +- .../model/business/BusinessCategory.java | 10 +- .../business/BusinessVerifiedNameDetails.java | 2 +- .../InteractiveLocationAnnotation.java | 2 +- .../interactive/InteractiveNativeFlow.java | 2 +- .../model/button/misc/ButtonOpaqueData.java | 2 +- .../model/button/misc/ButtonSection.java | 2 +- .../HighlyStructuredMessage.java | 4 +- .../hsm/HighlyStructuredFourRowTemplate.java | 2 +- .../hydrated/HydratedFourRowTemplate.java | 2 +- .../it/auties/whatsapp/model/call/Call.java | 22 ++- .../whatsapp/model/call/CallStatus.java | 23 ++- .../it/auties/whatsapp/model/chat/Chat.java | 153 ++++++--------- .../model/chat/ChatEphemeralTimer.java | 20 +- .../model/chat/GroupPastParticipants.java | 2 +- .../model/companion/CompanionDevice.java | 14 +- .../model/companion/CompanionHashState.java | 45 +++-- .../model/companion/CompanionPatch.java | 15 ++ .../model/companion/CompanionSyncKey.java | 18 ++ .../whatsapp/model/contact/Contact.java | 31 +-- .../whatsapp/model/contact/ContactStatus.java | 23 ++- .../whatsapp/model/info/ChatMessageInfo.java | 8 +- .../whatsapp/model/info/ContextInfo.java | 2 +- .../model/info/NewsletterMessageInfo.java | 19 +- .../whatsapp/model/info/ProductListInfo.java | 2 +- .../model/info/WebNotificationsInfo.java | 2 +- .../model/message/button/ButtonsMessage.java | 2 +- .../model/message/button/ListMessage.java | 2 +- .../model/message/model/MessageReceipt.java | 4 +- .../message/server/StickerSyncRMRMessage.java | 2 +- .../message/standard/ContactsMessage.java | 2 +- .../model/message/standard/ImageMessage.java | 4 +- .../message/standard/PollCreationMessage.java | 2 +- .../message/standard/VideoOrGifMessage.java | 2 +- .../whatsapp/model/mobile/CountryLocale.java | 19 +- .../whatsapp/model/mobile/PhoneNumber.java | 3 + .../whatsapp/model/newsletter/Newsletter.java | 10 +- .../newsletter/NewsletterDescription.java | 13 +- .../model/newsletter/NewsletterMetadata.java | 13 +- .../model/newsletter/NewsletterName.java | 13 +- .../model/newsletter/NewsletterPicture.java | 12 +- .../model/newsletter/NewsletterReaction.java | 9 +- .../NewsletterReactionSettings.java | 36 +++- .../model/newsletter/NewsletterSettings.java | 9 +- .../model/newsletter/NewsletterState.java | 7 +- .../newsletter/NewsletterViewerMetadata.java | 7 +- .../newsletter/NewsletterViewerRole.java | 24 ++- .../poll/PollUpdateEncryptedOptions.java | 2 +- .../model/privacy/PrivacySettingEntry.java | 12 +- .../model/privacy/PrivacySettingType.java | 27 ++- .../model/privacy/PrivacySettingValue.java | 23 ++- .../model/product/ProductSection.java | 2 +- .../model/signal/auth/ClientPayload.java | 2 +- .../model/signal/auth/KeyIndexList.java | 2 +- .../model/signal/keypair/SignalKeyPair.java | 10 +- .../signal/keypair/SignalPreKeyPair.java | 12 +- .../signal/keypair/SignalSignedKeyPair.java | 12 +- .../model/signal/sender/SenderChainKey.java | 10 +- .../model/signal/sender/SenderKeyName.java | 3 + .../model/signal/sender/SenderKeyRecord.java | 40 ++-- .../model/signal/sender/SenderKeyState.java | 26 ++- .../model/signal/sender/SenderMessageKey.java | 14 +- .../model/signal/sender/SenderPreKeys.java | 53 ++++++ .../model/signal/session/Session.java | 42 ++++- .../model/signal/session/SessionAddress.java | 3 + .../model/signal/session/SessionChain.java | 14 +- .../model/signal/session/SessionPreKey.java | 13 +- .../model/signal/session/SessionState.java | 26 ++- .../model/sync/ActionMessageRangeSync.java | 2 +- .../AppStateFatalExceptionNotification.java | 2 +- .../whatsapp/model/sync/AppStateSyncKey.java | 7 +- .../sync/AppStateSyncKeyFingerprint.java | 2 +- .../model/sync/AppStateSyncKeyRequest.java | 2 +- .../model/sync/AppStateSyncKeyShare.java | 2 +- .../model/sync/DeviceListMetadata.java | 4 +- .../whatsapp/model/sync/HistorySync.java | 10 +- .../whatsapp/model/sync/MutationsSync.java | 2 +- .../auties/whatsapp/model/sync/PatchSync.java | 2 +- .../auties/whatsapp/model/sync/PatchType.java | 25 ++- .../whatsapp/model/sync/PrimaryFeature.java | 2 +- .../whatsapp/model/sync/SnapshotSync.java | 2 +- .../whatsapp/socket/AppStateHandler.java | 18 +- .../whatsapp/socket/MessageHandler.java | 12 +- .../auties/whatsapp/socket/StreamHandler.java | 4 +- .../util/DefaultControllerSerializer.java | 57 +++--- .../auties/whatsapp/util/FutureReference.java | 7 + .../java/it/auties/whatsapp/util/Medias.java | 6 +- .../whatsapp/util/ProtobufUriMixin.java | 19 ++ .../whatsapp/util/ProtobufUuidMixin.java | 19 ++ .../java/it/auties/whatsapp/util/Smile.java | 71 ------- src/main/java/module-info.java | 1 - .../it/auties/whatsapp/ci/ButtonsTest.java | 14 +- .../java/it/auties/whatsapp/ci/WebTest.java | 14 +- .../auties/whatsapp/local/MobileRunner.java | 4 +- .../it/auties/whatsapp/local/WebRunner.java | 6 +- .../auties/whatsapp/model/GithubSecrets.java | 29 +-- 109 files changed, 1152 insertions(+), 582 deletions(-) create mode 100644 src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java create mode 100644 src/main/java/it/auties/whatsapp/model/companion/CompanionSyncKey.java create mode 100644 src/main/java/it/auties/whatsapp/model/signal/sender/SenderPreKeys.java create mode 100644 src/main/java/it/auties/whatsapp/util/ProtobufUriMixin.java create mode 100644 src/main/java/it/auties/whatsapp/util/ProtobufUuidMixin.java delete mode 100644 src/main/java/it/auties/whatsapp/util/Smile.java diff --git a/pom.xml b/pom.xml index 4e9a35d9e..5f821cf17 100644 --- a/pom.xml +++ b/pom.xml @@ -244,6 +244,11 @@ + + com.github.auties00 + protobuf-serialization-plugin + ${protoc.version} + com.google.zxing javase @@ -279,10 +284,10 @@ ${protoc.version} - + - com.fasterxml.jackson.dataformat - jackson-dataformat-smile + com.fasterxml.jackson.core + jackson-databind ${jackson.version} diff --git a/proto/whatsapp.proto b/proto/whatsapp.proto index ecc0f1f4d..cfdc9c108 100644 --- a/proto/whatsapp.proto +++ b/proto/whatsapp.proto @@ -1,4 +1,4 @@ -syntax = "proto2"; +syntax = "proto3"; package unsupported; message ADVDeviceIdentity { diff --git a/src/main/java/it/auties/whatsapp/api/ClientType.java b/src/main/java/it/auties/whatsapp/api/ClientType.java index 64e1fcdd0..91a09cfb7 100644 --- a/src/main/java/it/auties/whatsapp/api/ClientType.java +++ b/src/main/java/it/auties/whatsapp/api/ClientType.java @@ -1,18 +1,26 @@ package it.auties.whatsapp.api; +import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.model.ProtobufEnum; + /** * The constants of this enumerated type describe the various types of API that can be used to make * {@link Whatsapp} work */ -public enum ClientType { +public enum ClientType implements ProtobufEnum { /** * A standalone client that requires the QR code to be scanned by its companion on log-in Reversed * from Whatsapp Web Client */ - WEB, + WEB(0), /** * A standalone client that requires an SMS code sent to the companion's phone number on log-in * Reversed from KaiOS Mobile App */ - MOBILE + MOBILE(1); + + final int index; + ClientType(@ProtobufEnumIndex int index) { + this.index = index; + } } diff --git a/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java b/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java index ada92176f..b815aff23 100644 --- a/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java +++ b/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java @@ -1,24 +1,37 @@ package it.auties.whatsapp.api; +import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.model.ProtobufEnum; + /** * The constants of this enumerated type describe the various types of text preview that can be * used */ -public enum TextPreviewSetting { +public enum TextPreviewSetting implements ProtobufEnum { /** * Link previews will be generated. If a message contains an url without a schema(for example * wikipedia.com), the message will be autocorrected to include it and a preview will be * generated */ - ENABLED_WITH_INFERENCE, + ENABLED_WITH_INFERENCE(0), /** * Link previews will be generated. No inference will be used. */ - ENABLED, + ENABLED(1), /** * Link previews will not be generated */ - DISABLED + DISABLED(2); + + final int index; + + TextPreviewSetting(@ProtobufEnumIndex int index) { + this.index = index; + } + + public int index() { + return index; + } } diff --git a/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java b/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java index 73d114092..b18c90e99 100644 --- a/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java +++ b/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java @@ -1,12 +1,18 @@ package it.auties.whatsapp.api; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Specification; /** * The constants of this enumerated type describe the various chat history's codeLength that Whatsapp * can send on the first login attempt */ -public record WebHistoryLength(int size) { +public record WebHistoryLength( + @ProtobufProperty(index = 1, type = ProtobufType.INT32) + int size +) implements ProtobufMessage { private static final WebHistoryLength ZERO = new WebHistoryLength(0); private static final WebHistoryLength STANDARD = new WebHistoryLength(Specification.Whatsapp.DEFAULT_HISTORY_SIZE); private static final WebHistoryLength EXTENDED = new WebHistoryLength(Integer.MAX_VALUE); diff --git a/src/main/java/it/auties/whatsapp/api/Whatsapp.java b/src/main/java/it/auties/whatsapp/api/Whatsapp.java index f4fa5c413..1ba62e16f 100644 --- a/src/main/java/it/auties/whatsapp/api/Whatsapp.java +++ b/src/main/java/it/auties/whatsapp/api/Whatsapp.java @@ -476,7 +476,6 @@ public CompletableFuture sendNewsletterMessage(JidProvide .map(NewsletterMessageInfo::serverId) .orElse(0); var info = new NewsletterMessageInfo( - newsletter.get(), ChatMessageKey.randomId(), oldServerId + 1, Clock.nowSeconds(), @@ -485,6 +484,7 @@ public CompletableFuture sendNewsletterMessage(JidProvide message, MessageStatus.PENDING ); + info.setNewsletter(newsletter.get()); return sendMessage(info); } @@ -504,7 +504,6 @@ public CompletableFuture editMessage(T oldMessage, Me return switch (oldMessage) { case NewsletterMessageInfo oldNewsletterInfo -> { var info = new NewsletterMessageInfo( - oldNewsletterInfo.newsletter(), oldNewsletterInfo.id(), oldNewsletterInfo.serverId(), Clock.nowSeconds(), @@ -513,6 +512,7 @@ public CompletableFuture editMessage(T oldMessage, Me MessageContainer.ofEditedMessage(newMessage), MessageStatus.PENDING ); + info.setNewsletter(oldNewsletterInfo.newsletter()); var request = new MessageSendRequest.Newsletter(info, Map.of("edit", getEditBit(info))); yield socketHandler.sendMessage(request) .thenApply(ignored -> oldMessage); @@ -1528,7 +1528,6 @@ public CompletableFuture unarchive(JidProvider chat) { */ public CompletableFuture deleteMessage(NewsletterMessageInfo messageInfo) { var revokeInfo = new NewsletterMessageInfo( - messageInfo.newsletter(), messageInfo.id(), messageInfo.serverId(), Clock.nowSeconds(), @@ -1537,6 +1536,7 @@ public CompletableFuture deleteMessage(NewsletterMessageInfo messageInfo) MessageContainer.empty(), MessageStatus.PENDING ); + revokeInfo.setNewsletter(messageInfo.newsletter()); var attrs = Map.of("edit", getDeleteBit(messageInfo)); var request = new MessageSendRequest.Newsletter(revokeInfo, attrs); return socketHandler.sendMessage(request); @@ -2509,7 +2509,7 @@ private CompletableFuture sendCallMessage(JidProvider provider) { } private Call onCallSent(JidProvider provider, String callId, Node result) { - var call = new Call(provider.toJid(), jidOrThrowError(), callId, ZonedDateTime.now(), false, CallStatus.RINGING, false); + var call = new Call(provider.toJid(), jidOrThrowError(), callId, Clock.nowSeconds(), false, CallStatus.RINGING, false); store().addCall(call); socketHandler.onCall(call); return call; diff --git a/src/main/java/it/auties/whatsapp/controller/Controller.java b/src/main/java/it/auties/whatsapp/controller/Controller.java index 9e01296a2..912755c75 100644 --- a/src/main/java/it/auties/whatsapp/controller/Controller.java +++ b/src/main/java/it/auties/whatsapp/controller/Controller.java @@ -1,9 +1,13 @@ package it.auties.whatsapp.controller; import com.fasterxml.jackson.annotation.JsonIgnore; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.model.mobile.PhoneNumber; import it.auties.whatsapp.util.Json; +import it.auties.whatsapp.util.ProtobufUuidMixin; import java.util.*; @@ -12,15 +16,17 @@ * way to store IDs and serialize said class. */ @SuppressWarnings("unused") -public abstract sealed class Controller> permits Store, Keys { +public abstract sealed class Controller> implements ProtobufMessage permits Store, Keys { /** * The id of this controller */ + @ProtobufProperty(index = 1, type = ProtobufType.STRING, mixin = ProtobufUuidMixin.class) protected final UUID uuid; /** * The phone number of the associated companion */ + @ProtobufProperty(index = 2, type = ProtobufType.UINT64) private PhoneNumber phoneNumber; /** @@ -32,11 +38,13 @@ public abstract sealed class Controller> permits Store, /** * The client type */ + @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) protected final ClientType clientType; /** * A list of alias for the controller, can be used in place of UUID1 */ + @ProtobufProperty(index = 4, type = ProtobufType.STRING) protected final Collection alias; public Controller(UUID uuid, PhoneNumber phoneNumber, ControllerSerializer serializer, ClientType clientType, Collection alias) { @@ -60,7 +68,7 @@ public Controller(UUID uuid, PhoneNumber phoneNumber, ControllerSerializer seria public abstract void dispose(); public UUID uuid() { - return this.uuid; + return uuid; } public ClientType clientType() { diff --git a/src/main/java/it/auties/whatsapp/controller/Keys.java b/src/main/java/it/auties/whatsapp/controller/Keys.java index 26c86aa67..401c7bf6e 100644 --- a/src/main/java/it/auties/whatsapp/controller/Keys.java +++ b/src/main/java/it/auties/whatsapp/controller/Keys.java @@ -2,8 +2,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.model.companion.CompanionHashState; +import it.auties.whatsapp.model.companion.CompanionPatch; +import it.auties.whatsapp.model.companion.CompanionSyncKey; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.mobile.PhoneNumber; import it.auties.whatsapp.model.signal.auth.SignedDeviceIdentity; @@ -13,6 +18,7 @@ import it.auties.whatsapp.model.signal.keypair.SignalSignedKeyPair; import it.auties.whatsapp.model.signal.sender.SenderKeyName; import it.auties.whatsapp.model.signal.sender.SenderKeyRecord; +import it.auties.whatsapp.model.signal.sender.SenderPreKeys; import it.auties.whatsapp.model.signal.session.Session; import it.auties.whatsapp.model.signal.session.SessionAddress; import it.auties.whatsapp.model.sync.AppStateSyncKey; @@ -31,131 +37,154 @@ * This controller holds the cryptographic-related data regarding a WhatsappWeb session */ @SuppressWarnings({"unused", "UnusedReturnValue"}) -public final class Keys extends Controller { +public final class Keys extends Controller implements ProtobufMessage { /** * The client id */ - private final int registrationId; + @ProtobufProperty(index = 5, type = ProtobufType.INT32) + final int registrationId; /** * The secret key pair used for buffer messages */ - private final SignalKeyPair noiseKeyPair; + @ProtobufProperty(index = 6, type = ProtobufType.OBJECT) + final SignalKeyPair noiseKeyPair; /** * The ephemeral key pair */ - private final SignalKeyPair ephemeralKeyPair; + @ProtobufProperty(index = 7, type = ProtobufType.OBJECT) + final SignalKeyPair ephemeralKeyPair; /** * The signed identity key */ - private final SignalKeyPair identityKeyPair; + @ProtobufProperty(index = 8, type = ProtobufType.OBJECT) + final SignalKeyPair identityKeyPair; /** * The companion secret key */ - private SignalKeyPair companionKeyPair; + @ProtobufProperty(index = 9, type = ProtobufType.OBJECT) + SignalKeyPair companionKeyPair; /** * The signed pre key */ - private final SignalSignedKeyPair signedKeyPair; + @ProtobufProperty(index = 10, type = ProtobufType.OBJECT) + final SignalSignedKeyPair signedKeyPair; /** * The signed key of the companion's device * This value will be null until it gets synced by whatsapp */ - private byte[] signedKeyIndex; + @ProtobufProperty(index = 11, type = ProtobufType.BYTES) + byte[] signedKeyIndex; /** * The timestampSeconds of the signed key companion's device */ - private Long signedKeyIndexTimestamp; + @ProtobufProperty(index = 12, type = ProtobufType.UINT64) + Long signedKeyIndexTimestamp; /** * Whether these keys have generated pre keys assigned to them */ - private final List preKeys; + @ProtobufProperty(index = 13, type = ProtobufType.OBJECT) + final List preKeys; /** * The phone id for the mobile api */ - private final String phoneId; + @ProtobufProperty(index = 14, type = ProtobufType.STRING) + final String phoneId; /** * The device id for the mobile api */ - private final byte[] deviceId; + @ProtobufProperty(index = 15, type = ProtobufType.BYTES) + final byte[] deviceId; /** * The recovery token for the mobile api */ - private final byte[] identityId; + @ProtobufProperty(index = 16, type = ProtobufType.BYTES) + final byte[] identityId; /** * The bytes of the encoded {@link SignedDeviceIdentityHMAC} received during the auth process */ - private SignedDeviceIdentity companionIdentity; + @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) + SignedDeviceIdentity companionIdentity; /** * Sender keys for signal implementation */ - private final Map senderKeys; + @ProtobufProperty(index = 18, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final Map senderKeys; /** * App state keys */ - private final Map> appStateKeys; + @ProtobufProperty(index = 19, type = ProtobufType.OBJECT) + final List appStateKeys; /** * Sessions map */ - private final Map sessions; + @ProtobufProperty(index = 20, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final Map sessions; /** * Hash state */ - private final Map> hashStates; + @ProtobufProperty(index = 21, type = ProtobufType.OBJECT) + final List hashStates; - private final Map> groupsPreKeys; + + @ProtobufProperty(index = 22, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final Map groupsPreKeys; /** * Whether the client was registered */ - private boolean registered; + @ProtobufProperty(index = 23, type = ProtobufType.BOOL) + boolean registered; /** * Whether the client has already sent its business certificate (mobile api only) */ - private boolean businessCertificate; + @ProtobufProperty(index = 24, type = ProtobufType.BOOL) + boolean businessCertificate; /** * Whether the client received the initial app sync (web api only) */ - private boolean initialAppSync; + @ProtobufProperty(index = 25, type = ProtobufType.BOOL) + boolean initialAppSync; /** * Write counter for IV */ @JsonIgnore - private final AtomicLong writeCounter; + final AtomicLong writeCounter; /** * Read counter for IV */ @JsonIgnore - private final AtomicLong readCounter; + final AtomicLong readCounter; /** * Session dependent keys to write and read cyphered messages */ @JsonIgnore - private byte[] writeKey, readKey; + byte[] writeKey, readKey; + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public Keys(UUID uuid, PhoneNumber phoneNumber, ControllerSerializer serializer, ClientType clientType, Collection alias, int registrationId, SignalKeyPair noiseKeyPair, SignalKeyPair ephemeralKeyPair, SignalKeyPair identityKeyPair, SignalKeyPair companionKeyPair, SignalSignedKeyPair signedKeyPair, byte[] signedKeyIndex, Long signedKeyIndexTimestamp, List preKeys, String phoneId, byte[] deviceId, byte[] identityId, SignedDeviceIdentity companionIdentity, Map senderKeys, Map> appStateKeys, Map sessions, Map> hashStates, Map> groupsPreKeys, boolean registered, boolean businessCertificate, boolean initialAppSync) { - super(uuid, phoneNumber, serializer, clientType, alias); + public Keys(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collection alias, int registrationId, SignalKeyPair noiseKeyPair, SignalKeyPair ephemeralKeyPair, SignalKeyPair identityKeyPair, SignalKeyPair companionKeyPair, SignalSignedKeyPair signedKeyPair, byte[] signedKeyIndex, Long signedKeyIndexTimestamp, List preKeys, String phoneId, byte[] deviceId, byte[] identityId, SignedDeviceIdentity companionIdentity, Map senderKeys, List appStateKeys, Map sessions, List hashStates, Map groupsPreKeys, boolean registered, boolean businessCertificate, boolean initialAppSync) { + super(uuid, phoneNumber, null, clientType, alias); this.registrationId = registrationId; this.noiseKeyPair = noiseKeyPair; this.ephemeralKeyPair = ephemeralKeyPair; @@ -261,8 +290,10 @@ public Optional findPreKeyById(Integer id) { * @return a non-null Optional app state dataSync key */ public Optional findAppKeyById(Jid jid, byte[] id) { - return Objects.requireNonNull(appStateKeys.get(jid), "Missing keys") - .stream() + return appStateKeys.stream() + .filter(preKey -> Objects.equals(preKey.companion(), jid)) + .map(CompanionSyncKey::keys) + .flatMap(Collection::stream) .filter(preKey -> preKey.keyId() != null && Arrays.equals(preKey.keyId().keyId(), id)) .findFirst(); } @@ -275,8 +306,10 @@ public Optional findAppKeyById(Jid jid, byte[] id) { * @return a non-null hash state */ public Optional findHashStateByName(Jid device, PatchType patchType) { - return Optional.ofNullable(hashStates.get(device)) - .map(entry -> entry.get(patchType)); + return hashStates.stream() + .filter(hashState -> Objects.equals(hashState.companion(), device) && hashState.state().type() == patchType) + .findFirst() + .map(CompanionPatch::state); } /** @@ -320,9 +353,8 @@ public Keys putSession(SessionAddress address, Session record) { * @return this */ public Keys putState(Jid device, CompanionHashState state) { - var oldData = Objects.requireNonNullElseGet(hashStates.get(device), HashMap::new); - oldData.put(state.name(), state); - hashStates.put(device, oldData); + var hashState = new CompanionPatch(device, state); + hashStates.add(hashState); return this; } @@ -334,7 +366,13 @@ public Keys putState(Jid device, CompanionHashState state) { * @return this */ public Keys addAppKeys(Jid jid, Collection keys) { - appStateKeys.put(jid, new LinkedList<>(keys)); + appStateKeys.stream() + .filter(preKey -> Objects.equals(preKey.companion(), jid)) + .findFirst() + .ifPresentOrElse(key -> key.keys().addAll(keys), () -> { + var syncKey = new CompanionSyncKey(jid, new LinkedList<>(keys)); + appStateKeys.add(syncKey); + }); return this; } @@ -344,8 +382,7 @@ public Keys addAppKeys(Jid jid, Collection keys) { * @return a non-null app key */ public AppStateSyncKey getLatestAppKey(Jid jid) { - var keys = Objects.requireNonNull(appStateKeys.get(jid), "Missing keys"); - return keys.getLast(); + return getAppKeys(jid).getLast(); } /** @@ -354,7 +391,11 @@ public AppStateSyncKey getLatestAppKey(Jid jid) { * @return a non-null app key */ public LinkedList getAppKeys(Jid jid) { - return Objects.requireNonNullElseGet(appStateKeys.get(jid), LinkedList::new); + return appStateKeys.stream() + .filter(preKey -> Objects.equals(preKey.companion(), jid)) + .findFirst() + .orElseThrow(() -> new NoSuchElementException("Missing keys")) + .keys(); } /** @@ -431,23 +472,24 @@ public Collection preKeys() { public void addRecipientWithPreKeys(Jid group, Jid recipient) { var preKeys = groupsPreKeys.get(group); if (preKeys != null) { - preKeys.add(recipient); + preKeys.addPreKey(recipient); return; } - var newPreKeys = new ArrayList(); - newPreKeys.add(recipient); + var newPreKeys = new SenderPreKeys(); + newPreKeys.addPreKey(recipient); groupsPreKeys.put(group, newPreKeys); } public void addRecipientsWithPreKeys(Jid group, Collection recipients) { var preKeys = groupsPreKeys.get(group); if (preKeys != null) { - preKeys.addAll(recipients); + preKeys.addPreKeys(recipients); return; } - var newPreKeys = new ArrayList<>(recipients); + var newPreKeys = new SenderPreKeys(); + newPreKeys.addPreKeys(recipients); groupsPreKeys.put(group, newPreKeys); } @@ -514,19 +556,19 @@ public Map senderKeys() { return this.senderKeys; } - public Map> appStateKeys() { - return this.appStateKeys; + public List appStateKeys() { + return appStateKeys; } public Map sessions() { return this.sessions; } - public Map> hashStates() { - return this.hashStates; + public List hashStates() { + return hashStates; } - public Map> groupsPreKeys() { + public Map groupsPreKeys() { return this.groupsPreKeys; } diff --git a/src/main/java/it/auties/whatsapp/controller/Store.java b/src/main/java/it/auties/whatsapp/controller/Store.java index 2a4ff3711..59a47723f 100644 --- a/src/main/java/it/auties/whatsapp/controller/Store.java +++ b/src/main/java/it/auties/whatsapp/controller/Store.java @@ -3,6 +3,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnore; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.api.TextPreviewSetting; import it.auties.whatsapp.api.WebHistoryLength; @@ -38,6 +41,7 @@ import it.auties.whatsapp.socket.SocketRequest; import it.auties.whatsapp.util.BytesHelper; import it.auties.whatsapp.util.FutureReference; +import it.auties.whatsapp.util.ProtobufUriMixin; import it.auties.whatsapp.util.ProxyAuthenticator; import java.net.URI; @@ -56,246 +60,281 @@ * This controller holds the user-related data regarding a WhatsappWeb session */ @SuppressWarnings({"unused", "UnusedReturnValue"}) -public final class Store extends Controller { +public final class Store extends Controller implements ProtobufMessage { /** * The version used by this session */ - private URI proxy; + @ProtobufProperty(index = 5, type = ProtobufType.STRING, mixin = ProtobufUriMixin.class) + URI proxy; /** * The version used by this session */ - private FutureReference version; + @ProtobufProperty(index = 6, type = ProtobufType.OBJECT, overrideType = Version.class) + FutureReference version; /** * Whether this account is online for other users */ - private boolean online; + @ProtobufProperty(index = 7, type = ProtobufType.BOOL) + boolean online; /** * The locale of the user linked to this account */ - private CountryLocale locale; + @ProtobufProperty(index = 8, type = ProtobufType.OBJECT) + CountryLocale locale; /** * The name of the user linked to this account. This field will be null while the user hasn't * logged in yet. Assumed to be non-null otherwise. */ - private String name; + @ProtobufProperty(index = 9, type = ProtobufType.STRING) + String name; /** * The address of this account, if it's a business account */ - private String businessAddress; + @ProtobufProperty(index = 10, type = ProtobufType.STRING) + String businessAddress; /** * The longitude of this account's location, if it's a business account */ - private Double businessLongitude; + @ProtobufProperty(index = 11, type = ProtobufType.DOUBLE) + Double businessLongitude; /** * The latitude of this account's location, if it's a business account */ - private Double businessLatitude; + @ProtobufProperty(index = 12, type = ProtobufType.DOUBLE) + Double businessLatitude; /** * The description of this account, if it's a business account */ - private String businessDescription; + @ProtobufProperty(index = 13, type = ProtobufType.STRING) + String businessDescription; /** * The website of this account, if it's a business account */ - private String businessWebsite; + @ProtobufProperty(index = 14, type = ProtobufType.STRING) + String businessWebsite; /** * The email of this account, if it's a business account */ - private String businessEmail; + @ProtobufProperty(index = 15, type = ProtobufType.STRING) + String businessEmail; /** * The category of this account, if it's a business account */ - private BusinessCategory businessCategory; + @ProtobufProperty(index = 16, type = ProtobufType.OBJECT) + BusinessCategory businessCategory; /** * The hash of the companion associated with this session */ - private String deviceHash; + @ProtobufProperty(index = 17, type = ProtobufType.STRING) + String deviceHash; /** * A map of all the devices that the companion has associated using WhatsappWeb * The key here is the index of the device's key * The value is the device's companion jid */ - private LinkedHashMap linkedDevicesKeys; + @ProtobufProperty(index = 18, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.INT32) + LinkedHashMap linkedDevicesKeys; /** * The profile picture of the user linked to this account. This field will be null while the user * hasn't logged in yet. This field can also be null if no image was set. */ - private URI profilePicture; + @ProtobufProperty(index = 19, type = ProtobufType.STRING, mixin = ProtobufUriMixin.class) + URI profilePicture; /** * The status of the user linked to this account. * This field will be null while the user hasn't logged in yet. * Assumed to be non-null otherwise. */ - private String about; + @ProtobufProperty(index = 20, type = ProtobufType.STRING) + String about; /** * The user linked to this account. This field will be null while the user hasn't logged in yet. */ - private Jid jid; + @ProtobufProperty(index = 21, type = ProtobufType.STRING) + Jid jid; /** * The lid user linked to this account. This field will be null while the user hasn't logged in yet. */ - private Jid lid; + @ProtobufProperty(index = 22, type = ProtobufType.STRING) + Jid lid; /** * The non-null map of properties received by whatsapp */ - private final ConcurrentHashMap properties; + @ProtobufProperty(index = 23, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.STRING) + final ConcurrentHashMap properties; /** * The non-null map of chats */ @JsonIgnore - private final ConcurrentHashMap chats; + final ConcurrentHashMap chats; /** * The non-null map of contacts */ - private final ConcurrentHashMap contacts; + @ProtobufProperty(index = 24, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final ConcurrentHashMap contacts; /** * The non-null list of status messages */ - private final ConcurrentHashMap> status; + @ProtobufProperty(index = 25, type = ProtobufType.OBJECT) + final KeySetView status; /** * The non-null map of newsletters */ - private final ConcurrentHashMap newsletters; - + @JsonIgnore + final ConcurrentHashMap newsletters; /** * The non-null map of privacy settings */ - private final ConcurrentHashMap privacySettings; + @ProtobufProperty(index = 26, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final ConcurrentHashMap privacySettings; /** * The non-null map of calls */ - private final ConcurrentHashMap calls; + @ProtobufProperty(index = 27, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final ConcurrentHashMap calls; /** * Whether chats should be unarchived if a new message arrives */ - private boolean unarchiveChats; + @ProtobufProperty(index = 28, type = ProtobufType.BOOL) + boolean unarchiveChats; /** * Whether the twenty-hours format is being used by the client */ - private boolean twentyFourHourFormat; + @ProtobufProperty(index = 29, type = ProtobufType.BOOL) + boolean twentyFourHourFormat; /** * The non-null list of requests that were sent to Whatsapp. They might or might not be waiting * for a newsletters */ @JsonIgnore - private final ConcurrentHashMap requests; + final ConcurrentHashMap requests; /** * The non-null list of replies waiting to be fulfilled */ @JsonIgnore - private final ConcurrentHashMap> replyHandlers; + final ConcurrentHashMap> replyHandlers; /** * The non-null list of listeners */ @JsonIgnore - private final KeySetView listeners; + final KeySetView listeners; /** * The request tag, used to create messages */ @JsonIgnore - private final String tag; + final String tag; /** * The timestampSeconds in seconds for the initialization of this object */ - private final long initializationTimeStamp; + @ProtobufProperty(index = 30, type = ProtobufType.UINT64) + final long initializationTimeStamp; /** * The media connection associated with this store */ @JsonIgnore - private MediaConnection mediaConnection; + MediaConnection mediaConnection; /** * The media connection latch associated with this store */ @JsonIgnore - private final CountDownLatch mediaConnectionLatch; + final CountDownLatch mediaConnectionLatch; /** * The request tag, used to create messages */ - private ChatEphemeralTimer newChatsEphemeralTimer; + @ProtobufProperty(index = 31, type = ProtobufType.OBJECT) + ChatEphemeralTimer newChatsEphemeralTimer; /** * The setting to use when generating previews for text messages that contain links */ - private TextPreviewSetting textPreviewSetting; + @ProtobufProperty(index = 32, type = ProtobufType.OBJECT) + TextPreviewSetting textPreviewSetting; /** * Describes how much chat history Whatsapp should send */ - private WebHistoryLength historyLength; + @ProtobufProperty(index = 33, type = ProtobufType.OBJECT) + WebHistoryLength historyLength; /** * Whether listeners should be automatically scanned and registered or not */ - private boolean autodetectListeners; + @ProtobufProperty(index = 34, type = ProtobufType.BOOL) + boolean autodetectListeners; /** * Whether the listeners that were automatically scanned should be cached */ - private boolean cacheDetectedListeners; + @ProtobufProperty(index = 35, type = ProtobufType.BOOL) + boolean cacheDetectedListeners; /** * Whether updates about the presence of the session should be sent automatically to Whatsapp * For example, when the bot is started, the status of the companion is changed to available if this option is enabled * If this option is enabled, the companion will not receive notifications because the bot will instantly read them */ - private boolean automaticPresenceUpdates; + @ProtobufProperty(index = 36, type = ProtobufType.BOOL) + boolean automaticPresenceUpdates; /** * The release channel to use when connecting to Whatsapp * This should allow the use of beta features */ - private ReleaseChannel releaseChannel; + @ProtobufProperty(index = 37, type = ProtobufType.OBJECT) + ReleaseChannel releaseChannel; /** * Metadata about the device that is being simulated for Whatsapp */ - private CompanionDevice device; + @ProtobufProperty(index = 38, type = ProtobufType.OBJECT) + CompanionDevice device; /** * Whether the mac of every app state request should be checked */ - private boolean checkPatchMacs; + @ProtobufProperty(index = 39, type = ProtobufType.BOOL) + boolean checkPatchMacs; - /** - * All args constructor - */ + /** + * All args constructor + */ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public Store(UUID uuid, PhoneNumber phoneNumber, ControllerSerializer serializer, ClientType clientType, Collection alias, URI proxy, FutureReference version, boolean online, CountryLocale locale, String name, String businessAddress, Double businessLongitude, Double businessLatitude, String businessDescription, String businessWebsite, String businessEmail, BusinessCategory businessCategory, String deviceHash, LinkedHashMap linkedDevicesKeys, URI profilePicture, String about, Jid jid, Jid lid, ConcurrentHashMap properties, ConcurrentHashMap contacts, ConcurrentHashMap> status, ConcurrentHashMap newsletters, ConcurrentHashMap privacySettings, ConcurrentHashMap calls, boolean unarchiveChats, boolean twentyFourHourFormat, long initializationTimeStamp, ChatEphemeralTimer newChatsEphemeralTimer, TextPreviewSetting textPreviewSetting, WebHistoryLength historyLength, boolean autodetectListeners, boolean cacheDetectedListeners, boolean automaticPresenceUpdates, ReleaseChannel releaseChannel, CompanionDevice device, boolean checkPatchMacs) { - super(uuid, phoneNumber, serializer, clientType, alias); + public Store(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collection alias, URI proxy, FutureReference version, boolean online, CountryLocale locale, String name, String businessAddress, Double businessLongitude, Double businessLatitude, String businessDescription, String businessWebsite, String businessEmail, BusinessCategory businessCategory, String deviceHash, LinkedHashMap linkedDevicesKeys, URI profilePicture, String about, Jid jid, Jid lid, ConcurrentHashMap properties, ConcurrentHashMap contacts, KeySetView status, ConcurrentHashMap privacySettings, ConcurrentHashMap calls, boolean unarchiveChats, boolean twentyFourHourFormat, long initializationTimeStamp, ChatEphemeralTimer newChatsEphemeralTimer, TextPreviewSetting textPreviewSetting, WebHistoryLength historyLength, boolean autodetectListeners, boolean cacheDetectedListeners, boolean automaticPresenceUpdates, ReleaseChannel releaseChannel, CompanionDevice device, boolean checkPatchMacs) { + super(uuid, phoneNumber, null, clientType, alias); if (proxy != null) { ProxyAuthenticator.register(proxy); } @@ -322,7 +361,7 @@ public Store(UUID uuid, PhoneNumber phoneNumber, ControllerSerializer serializer this.chats = new ConcurrentHashMap<>(); this.contacts = contacts; this.status = status; - this.newsletters = newsletters; + this.newsletters = new ConcurrentHashMap<>(); this.privacySettings = privacySettings; this.calls = calls; this.unarchiveChats = unarchiveChats; @@ -429,14 +468,9 @@ public Optional> findMessageById(JidProvider prov case Jid contactJid -> switch (contactJid.type()) { case NEWSLETTER -> findNewsletterByJid(contactJid) .flatMap(newsletter -> findMessageById(newsletter, id)); - case STATUS -> { - var messages = status.get(contactJid); - if (messages == null) { - yield Optional.empty(); - } - - yield Optional.ofNullable(messages.get(id)); - } + case STATUS -> status.stream() + .filter(entry -> Objects.equals(entry.chatJid(), provider.toJid()) && Objects.equals(entry.id(), id)) + .findFirst(); default -> findChatByJid(contactJid) .flatMap(chat -> findMessageById(chat, id)); }; @@ -608,10 +642,7 @@ public Set findChatsBy(Function function) { * @return an immutable collection */ public Collection status() { - return status.values() - .stream() - .flatMap(entry -> entry.values().stream()) - .collect(Collectors.toUnmodifiableSet()); + return Collections.unmodifiableCollection(status); } /** @@ -630,9 +661,9 @@ public Collection newsletters() { * @return a non-null immutable list */ public Collection findStatusBySender(JidProvider jid) { - return Optional.ofNullable(status.get(jid.toJid())) - .map(entry -> Collections.unmodifiableCollection(entry.values())) - .orElseGet(Set::of); + return status.stream() + .filter(entry -> Objects.equals(entry.chatJid(), jid)) + .toList(); } /** @@ -930,9 +961,7 @@ public Collection blockedContacts() { * @return the same instance */ public Store addStatus(ChatMessageInfo info) { - var wrapper = Objects.requireNonNullElseGet(status.get(info.senderJid()), ConcurrentHashMap::new); - wrapper.put(info.id(), info); - status.put(info.senderJid(), wrapper); + status.add(info); return this; } @@ -989,7 +1018,7 @@ public Collection privacySettings() { * @return a non-null entry */ public PrivacySettingEntry findPrivacySetting(PrivacySettingType type) { - return privacySettings.get(type); + return privacySettings.get(type.name()); } /** @@ -1000,7 +1029,7 @@ public PrivacySettingEntry findPrivacySetting(PrivacySettingType type) { * @return the old privacy setting entry */ public PrivacySettingEntry addPrivacySetting(PrivacySettingType type, PrivacySettingEntry entry) { - return privacySettings.put(type, entry); + return privacySettings.put(type.name(), entry); } /** @@ -1112,7 +1141,7 @@ public Store removeListener() { public Store setProxy(URI proxy) { if (proxy != null && proxy.getUserInfo() != null) { ProxyAuthenticator.register(proxy); - } else if (proxy == null && this.proxy != null && this.proxy.getUserInfo() != null) { + } else if (proxy == null && this.proxy != null) { ProxyAuthenticator.unregister(this.proxy); } @@ -1312,6 +1341,10 @@ public boolean checkPatchMacs() { return this.checkPatchMacs; } + public Map calls() { + return Collections.unmodifiableMap(calls); + } + public Store setOnline(boolean online) { this.online = online; return this; diff --git a/src/main/java/it/auties/whatsapp/crypto/GroupBuilder.java b/src/main/java/it/auties/whatsapp/crypto/GroupBuilder.java index ad51d54b6..9eaddd466 100644 --- a/src/main/java/it/auties/whatsapp/crypto/GroupBuilder.java +++ b/src/main/java/it/auties/whatsapp/crypto/GroupBuilder.java @@ -10,15 +10,15 @@ public record GroupBuilder(Keys keys) { public byte[] createOutgoing(SenderKeyName name) { var record = keys.findSenderKeyByName(name); if (record.isEmpty()) { - record.addState(KeyHelper.senderKeyId(), 0, KeyHelper.senderKey(), SignalKeyPair.random()); + record.addState(KeyHelper.senderKeyId(), SignalKeyPair.random(), 0, KeyHelper.senderKey()); } - var state = record.findState(); + var state = record.firstState(); var message = new SignalDistributionMessage(state.id(), state.chainKey().iteration(), state.chainKey().seed(), state.signingKey().encodedPublicKey()); return message.serialized(); } public void createIncoming(SenderKeyName name, SignalDistributionMessage message) { var record = keys.findSenderKeyByName(name); - record.addState(message.id(), message.iteration(), message.chainKey(), message.signingKey()); + record.addState(message.id(), message.signingKey(), message.iteration(), message.chainKey()); } } diff --git a/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java b/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java index e1c9b2d86..3f09bd0c8 100644 --- a/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java +++ b/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java @@ -15,7 +15,7 @@ public CipheredMessageResult encrypt(byte[] data) { return new CipheredMessageResult(null, Signal.UNAVAILABLE); } - var currentState = keys.findSenderKeyByName(name).findState(); + var currentState = keys.findSenderKeyByName(name).firstState(); var messageKey = currentState.chainKey().toMessageKey(); var ciphertext = AesCbc.encrypt(messageKey.iv(), data, messageKey.cipherKey()); var senderKeyMessage = new SenderKeyMessage(currentState.id(), messageKey.iteration(), ciphertext, currentState.signingKey().privateKey()); @@ -27,7 +27,7 @@ public CipheredMessageResult encrypt(byte[] data) { public byte[] decrypt(byte[] data) { var record = keys.findSenderKeyByName(name); var senderKeyMessage = SenderKeyMessage.ofSerialized(data); - var senderKeyStates = record.findStateById(senderKeyMessage.id()); + var senderKeyStates = record.findStatesById(senderKeyMessage.id()); for (var senderKeyState : senderKeyStates) { try { var senderKey = getSenderKey(senderKeyState, senderKeyMessage.iteration()); diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java index 6de128916..670afdc8c 100644 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java +++ b/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java @@ -1,17 +1,21 @@ package it.auties.whatsapp.listener; import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.newsletter.Newsletter; +import it.auties.whatsapp.model.chat.Chat; import java.util.Collection; public interface OnWhatsappChats extends Listener { /** - * Called when the socket receives all the newsletters from WhatsappWeb's Socket + * Called when the socket receives all the chats from WhatsappWeb's Socket. When this event is + * fired, it is guaranteed that all metadata excluding messages will be present. If you also need + * the messages to be loaded, please refer to {@link Listener#onChatMessagesSync(Chat, boolean)}. + * Particularly old chats may come later through + * {@link Listener#onChatMessagesSync(Chat, boolean)} * - * @param whatsapp an instance to the calling api - * @param newsletters the newsletters + * @param whatsapp an instance to the calling api + * @param chats the chats */ @Override - void onNewsletters(Whatsapp whatsapp, Collection newsletters); + void onChats(Whatsapp whatsapp, Collection chats); } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/action/QuickReplyAction.java b/src/main/java/it/auties/whatsapp/model/action/QuickReplyAction.java index 63bab7ba9..c69d53310 100644 --- a/src/main/java/it/auties/whatsapp/model/action/QuickReplyAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/QuickReplyAction.java @@ -16,7 +16,7 @@ public record QuickReplyAction( String shortcut, @ProtobufProperty(index = 2, type = ProtobufType.STRING) String message, - @ProtobufProperty(index = 3, type = ProtobufType.STRING, repeated = true) + @ProtobufProperty(index = 3, type = ProtobufType.STRING) List keywords, @ProtobufProperty(index = 4, type = ProtobufType.INT32) int count, diff --git a/src/main/java/it/auties/whatsapp/model/action/RecentEmojiWeightsAction.java b/src/main/java/it/auties/whatsapp/model/action/RecentEmojiWeightsAction.java index 87f21b883..918ff52f1 100644 --- a/src/main/java/it/auties/whatsapp/model/action/RecentEmojiWeightsAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/RecentEmojiWeightsAction.java @@ -13,7 +13,7 @@ */ @ProtobufMessageName("SyncActionValue.RecentEmojiWeightsAction") public record RecentEmojiWeightsAction( - @ProtobufProperty(index = 1, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List weights ) implements Action { /** diff --git a/src/main/java/it/auties/whatsapp/model/business/BusinessCategory.java b/src/main/java/it/auties/whatsapp/model/business/BusinessCategory.java index a9e11b7b6..7b46ae062 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessCategory.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessCategory.java @@ -1,5 +1,8 @@ package it.auties.whatsapp.model.business; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.node.Node; import java.net.URLDecoder; @@ -11,7 +14,12 @@ * @param id the non-null id * @param name the non-null display name */ -public record BusinessCategory(String id, String name) { +public record BusinessCategory( + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + String id, + @ProtobufProperty(index = 2, type = ProtobufType.STRING) + String name +) implements ProtobufMessage { /** * Constructs a category from a node * diff --git a/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameDetails.java b/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameDetails.java index c0edeaadb..5720447c4 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameDetails.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameDetails.java @@ -22,7 +22,7 @@ public record BusinessVerifiedNameDetails( String issuer, @ProtobufProperty(index = 4, type = ProtobufType.STRING) String name, - @ProtobufProperty(index = 8, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 8, type = ProtobufType.OBJECT) List localizedNames, @ProtobufProperty(index = 10, type = ProtobufType.UINT64) long issueTimeSeconds diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocationAnnotation.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocationAnnotation.java index 7a96f0442..935a9d1d1 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocationAnnotation.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocationAnnotation.java @@ -14,7 +14,7 @@ */ @ProtobufMessageName("InteractiveAnnotation") public record InteractiveLocationAnnotation( - @ProtobufProperty(index = 1, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List polygonVertices, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) InterativeLocation location diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveNativeFlow.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveNativeFlow.java index d7f5b88b4..4ff9296a6 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveNativeFlow.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveNativeFlow.java @@ -14,7 +14,7 @@ */ @ProtobufMessageName("Message.InteractiveMessage.NativeFlowMessage") public record InteractiveNativeFlow( - @ProtobufProperty(index = 1, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List buttons, @ProtobufProperty(index = 2, type = ProtobufType.STRING) String parameters, diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java b/src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java index 5a64add3e..5194eee08 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java +++ b/src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java @@ -46,7 +46,7 @@ public record ButtonOpaqueData( Optional loc, @ProtobufProperty(index = 17, type = ProtobufType.STRING) Optional pollName, - @ProtobufProperty(index = 18, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 18, type = ProtobufType.OBJECT) List pollOptions, @ProtobufProperty(index = 20, type = ProtobufType.UINT32) int pollSelectableOptionsCount, diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java b/src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java index 319f1f3b9..f783d710d 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java +++ b/src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java @@ -14,7 +14,7 @@ public record ButtonSection( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String title, - @ProtobufProperty(index = 2, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) List rows ) implements ProtobufMessage { diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredMessage.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredMessage.java index 7f699d416..8084d7aaa 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredMessage.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredMessage.java @@ -21,13 +21,13 @@ public record HighlyStructuredMessage( String namespace, @ProtobufProperty(index = 2, type = ProtobufType.STRING) String elementName, - @ProtobufProperty(index = 3, type = ProtobufType.STRING, repeated = true) + @ProtobufProperty(index = 3, type = ProtobufType.STRING) List params, @ProtobufProperty(index = 4, type = ProtobufType.STRING) Optional fallbackLg, @ProtobufProperty(index = 5, type = ProtobufType.STRING) Optional fallbackLc, - @ProtobufProperty(index = 6, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 6, type = ProtobufType.OBJECT) List localizableParameters, @ProtobufProperty(index = 7, type = ProtobufType.STRING) Optional deterministicLg, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplate.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplate.java index e5a39826d..1518b0a5b 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplate.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplate.java @@ -34,7 +34,7 @@ public record HighlyStructuredFourRowTemplate( HighlyStructuredMessage content, @ProtobufProperty(index = 7, type = ProtobufType.OBJECT) Optional footer, - @ProtobufProperty(index = 8, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 8, type = ProtobufType.OBJECT) List buttons ) implements TemplateFormatter { @ProtobufBuilder(className = "HighlyStructuredFourRowTemplateSimpleBuilder") diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplate.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplate.java index 4d5916194..a7fe75c25 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplate.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplate.java @@ -33,7 +33,7 @@ public record HydratedFourRowTemplate( String body, @ProtobufProperty(index = 7, type = ProtobufType.STRING) Optional footer, - @ProtobufProperty(index = 8, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 8, type = ProtobufType.OBJECT) List hydratedButtons, @ProtobufProperty(index = 9, type = ProtobufType.STRING) String templateId diff --git a/src/main/java/it/auties/whatsapp/model/call/Call.java b/src/main/java/it/auties/whatsapp/model/call/Call.java index b9044df44..e592e4566 100644 --- a/src/main/java/it/auties/whatsapp/model/call/Call.java +++ b/src/main/java/it/auties/whatsapp/model/call/Call.java @@ -1,9 +1,25 @@ package it.auties.whatsapp.model.call; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; -import java.time.ZonedDateTime; +public record Call( + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + Jid chat, + @ProtobufProperty(index = 2, type = ProtobufType.STRING) + Jid caller, + @ProtobufProperty(index = 3, type = ProtobufType.STRING) + String id, + @ProtobufProperty(index = 4, type = ProtobufType.UINT64) + long timestampSeconds, + @ProtobufProperty(index = 5, type = ProtobufType.BOOL) + boolean video, + @ProtobufProperty(index = 6, type = ProtobufType.OBJECT) + CallStatus status, + @ProtobufProperty(index = 7, type = ProtobufType.BOOL) + boolean offline +) implements ProtobufMessage { -public record Call(Jid chat, Jid caller, String id, ZonedDateTime time, - boolean video, CallStatus status, boolean offline) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/call/CallStatus.java b/src/main/java/it/auties/whatsapp/model/call/CallStatus.java index 7bf5ca86a..fd4cc1553 100644 --- a/src/main/java/it/auties/whatsapp/model/call/CallStatus.java +++ b/src/main/java/it/auties/whatsapp/model/call/CallStatus.java @@ -1,8 +1,21 @@ package it.auties.whatsapp.model.call; -public enum CallStatus { - RINGING, - ACCEPTED, - REJECTED, - TIMED_OUT +import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.model.ProtobufEnum; + +public enum CallStatus implements ProtobufEnum { + RINGING(0), + ACCEPTED(1), + REJECTED(2), + TIMED_OUT(3); + + final int index; + + CallStatus(@ProtobufEnumIndex int index) { + this.index = index; + } + + public int index() { + return index; + } } diff --git a/src/main/java/it/auties/whatsapp/model/chat/Chat.java b/src/main/java/it/auties/whatsapp/model/chat/Chat.java index e4b3eb124..62f8fe314 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/Chat.java +++ b/src/main/java/it/auties/whatsapp/model/chat/Chat.java @@ -34,125 +34,131 @@ @ProtobufMessageName("Conversation") public final class Chat implements ProtobufMessage, JidProvider { @ProtobufProperty(index = 1, type = ProtobufType.STRING) - private final Jid jid; + final Jid jid; - @ProtobufProperty(index = 2, type = ProtobufType.OBJECT, repeated = true) - private final ConcurrentLinkedHashedDequeue historySyncMessages; + @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) + final ConcurrentLinkedHashedDequeue historySyncMessages; @ProtobufProperty(index = 3, type = ProtobufType.STRING) - private final Jid newJid; + final Jid newJid; @ProtobufProperty(index = 4, type = ProtobufType.STRING) - private final Jid oldJid; + final Jid oldJid; @ProtobufProperty(index = 6, type = ProtobufType.UINT32) - private int unreadMessagesCount; + int unreadMessagesCount; @ProtobufProperty(index = 7, type = ProtobufType.BOOL) - private boolean readOnly; + boolean readOnly; @ProtobufProperty(index = 8, type = ProtobufType.BOOL) - private boolean endOfHistoryTransfer; + boolean endOfHistoryTransfer; @ProtobufProperty(index = 9, type = ProtobufType.UINT32) - private ChatEphemeralTimer ephemeralMessageDuration; + ChatEphemeralTimer ephemeralMessageDuration; @ProtobufProperty(index = 10, type = ProtobufType.INT64) - private long ephemeralMessagesToggleTimeSeconds; + long ephemeralMessagesToggleTimeSeconds; @ProtobufProperty(index = 11, type = ProtobufType.OBJECT) - private EndOfHistoryTransferType endOfHistoryTransferType; + EndOfHistoryTransferType endOfHistoryTransferType; @ProtobufProperty(index = 12, type = ProtobufType.UINT64) - private long timestampSeconds; + long timestampSeconds; @ProtobufProperty(index = 13, type = ProtobufType.STRING) - private String name; + String name; @ProtobufProperty(index = 15, type = ProtobufType.BOOL) - private boolean notSpam; + boolean notSpam; @ProtobufProperty(index = 16, type = ProtobufType.BOOL) - private boolean archived; + boolean archived; @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) - private ChatDisappear disappearInitiator; + ChatDisappear disappearInitiator; @ProtobufProperty(index = 19, type = ProtobufType.BOOL) - private boolean markedAsUnread; + boolean markedAsUnread; - @ProtobufProperty(index = 20, type = ProtobufType.OBJECT, repeated = true) - private final List participants; + @ProtobufProperty(index = 20, type = ProtobufType.OBJECT) + final List participants; @ProtobufProperty(index = 21, type = ProtobufType.BYTES) - private byte[] token; + byte[] token; @ProtobufProperty(index = 22, type = ProtobufType.UINT64) - private long tokenTimestampSeconds; + long tokenTimestampSeconds; @ProtobufProperty(index = 23, type = ProtobufType.BYTES) - private byte[] identityKey; - + byte[] identityKey; + @ProtobufProperty(index = 24, type = ProtobufType.UINT32) - private int pinnedTimestampSeconds; + int pinnedTimestampSeconds; @ProtobufProperty(index = 25, type = ProtobufType.UINT64) - private ChatMute mute; + ChatMute mute; @ProtobufProperty(index = 26, type = ProtobufType.OBJECT) - private ChatWallpaper wallpaper; + ChatWallpaper wallpaper; @ProtobufProperty(index = 27, type = ProtobufType.OBJECT) - private MediaVisibility mediaVisibility; + MediaVisibility mediaVisibility; @ProtobufProperty(index = 28, type = ProtobufType.UINT64) - private long tokenSenderTimestampSeconds; + long tokenSenderTimestampSeconds; @ProtobufProperty(index = 29, type = ProtobufType.BOOL) - private boolean suspended; + boolean suspended; @ProtobufProperty(index = 30, type = ProtobufType.BOOL) - private boolean terminated; + boolean terminated; @ProtobufProperty(index = 31, type = ProtobufType.UINT64) - private long foundationTimestampSeconds; + long foundationTimestampSeconds; @ProtobufProperty(index = 32, type = ProtobufType.STRING) - private Jid founder; + Jid founder; @ProtobufProperty(index = 33, type = ProtobufType.STRING) - private String description; + String description; @ProtobufProperty(index = 34, type = ProtobufType.BOOL) - private boolean support; + boolean support; @ProtobufProperty(index = 35, type = ProtobufType.BOOL) - private boolean parentGroup; + boolean parentGroup; @ProtobufProperty(index = 36, type = ProtobufType.BOOL) - private boolean defaultSubGroup; + boolean defaultSubGroup; @ProtobufProperty(index = 37, type = ProtobufType.STRING) - private final Jid parentGroupJid; + final Jid parentGroupJid; @ProtobufProperty(index = 38, type = ProtobufType.STRING) - private String displayName; + String displayName; @ProtobufProperty(index = 39, type = ProtobufType.STRING) - private Jid phoneJid; + Jid phoneJid; + @ProtobufProperty(index = 40, type = ProtobufType.BOOL) - private boolean shareOwnPhoneNumber; + boolean shareOwnPhoneNumber; + @ProtobufProperty(index = 41, type = ProtobufType.BOOL) - private boolean pnhDuplicateLidThread; + boolean pnhDuplicateLidThread; + @ProtobufProperty(index = 42, type = ProtobufType.STRING) - private Jid lidJid; + Jid lidJid; + + @ProtobufProperty(index = 999, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final ConcurrentHashMap presences; + + @ProtobufProperty(index = 1000, type = ProtobufType.STRING) + final Set participantsPreKeys; + + @ProtobufProperty(index = 1001, type = ProtobufType.OBJECT) + final Set pastParticipants; private boolean update; - private final ConcurrentHashMap presences; - - private final Set participantsPreKeys; - - private final Set pastParticipants; - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) public Chat(Jid jid, ConcurrentLinkedHashedDequeue historySyncMessages, Jid newJid, Jid oldJid, int unreadMessagesCount, boolean readOnly, boolean endOfHistoryTransfer, ChatEphemeralTimer ephemeralMessageDuration, long ephemeralMessagesToggleTimeSeconds, EndOfHistoryTransferType endOfHistoryTransferType, long timestampSeconds, String name, boolean notSpam, boolean archived, ChatDisappear disappearInitiator, boolean markedAsUnread, List participants, byte[] token, long tokenTimestampSeconds, byte[] identityKey, int pinnedTimestampSeconds, ChatMute mute, ChatWallpaper wallpaper, MediaVisibility mediaVisibility, long tokenSenderTimestampSeconds, boolean suspended, boolean terminated, long foundationTimestampSeconds, Jid founder, String description, boolean support, boolean parentGroup, boolean defaultSubGroup, Jid parentGroupJid, String displayName, Jid phoneJid, boolean shareOwnPhoneNumber, boolean pnhDuplicateLidThread, Jid lidJid, ConcurrentHashMap presences, Set participantsPreKeys, Set pastParticipants) { this.jid = jid; @@ -198,52 +204,7 @@ public Chat(Jid jid, ConcurrentLinkedHashedDequeue historySy this.participantsPreKeys = participantsPreKeys; this.pastParticipants = pastParticipants; } - - public Chat(Jid jid, ConcurrentLinkedHashedDequeue historySyncMessages, Jid newJid, Jid oldJid, int unreadMessagesCount, boolean readOnly, boolean endOfHistoryTransfer, ChatEphemeralTimer ephemeralMessageDuration, long ephemeralMessagesToggleTimeSeconds, EndOfHistoryTransferType endOfHistoryTransferType, long timestampSeconds, String name, boolean notSpam, boolean archived, ChatDisappear disappearInitiator, boolean markedAsUnread, List participants, byte[] token, long tokenTimestampSeconds, byte[] identityKey, int pinnedTimestampSeconds, ChatMute mute, ChatWallpaper wallpaper, MediaVisibility mediaVisibility, long tokenSenderTimestampSeconds, boolean suspended, boolean terminated, long foundationTimestampSeconds, Jid founder, String description, boolean support, boolean parentGroup, boolean defaultSubGroup, Jid parentGroupJid, String displayName, Jid phoneJid, boolean shareOwnPhoneNumber, boolean pnhDuplicateLidThread, Jid lidJid) { - this.jid = jid; - this.historySyncMessages = historySyncMessages; - this.newJid = newJid; - this.oldJid = oldJid; - this.unreadMessagesCount = unreadMessagesCount; - this.readOnly = readOnly; - this.endOfHistoryTransfer = endOfHistoryTransfer; - this.ephemeralMessageDuration = ephemeralMessageDuration; - this.ephemeralMessagesToggleTimeSeconds = ephemeralMessagesToggleTimeSeconds; - this.endOfHistoryTransferType = endOfHistoryTransferType; - this.timestampSeconds = timestampSeconds; - this.name = name; - this.notSpam = notSpam; - this.archived = archived; - this.disappearInitiator = disappearInitiator; - this.markedAsUnread = markedAsUnread; - this.participants = participants; - this.token = token; - this.tokenTimestampSeconds = tokenTimestampSeconds; - this.identityKey = identityKey; - this.pinnedTimestampSeconds = pinnedTimestampSeconds; - this.mute = Objects.requireNonNullElseGet(mute, ChatMute::notMuted); - this.wallpaper = wallpaper; - this.mediaVisibility = Objects.requireNonNullElse(mediaVisibility, MediaVisibility.ON); - this.tokenSenderTimestampSeconds = tokenSenderTimestampSeconds; - this.suspended = suspended; - this.terminated = terminated; - this.foundationTimestampSeconds = foundationTimestampSeconds; - this.founder = founder; - this.description = description; - this.support = support; - this.parentGroup = parentGroup; - this.defaultSubGroup = defaultSubGroup; - this.parentGroupJid = parentGroupJid; - this.displayName = displayName; - this.phoneJid = phoneJid; - this.shareOwnPhoneNumber = shareOwnPhoneNumber; - this.pnhDuplicateLidThread = pnhDuplicateLidThread; - this.lidJid = lidJid; - this.presences = new ConcurrentHashMap<>(); - this.participantsPreKeys = ConcurrentHashMap.newKeySet(); - this.pastParticipants = ConcurrentHashMap.newKeySet(); - } - + /** * Returns the name of this chat * @@ -894,7 +855,7 @@ public Optional founder() { } public Optional description() { - return description.describeConstable(); + return Optional.ofNullable(description); } public boolean support() { @@ -914,7 +875,7 @@ public Optional parentGroupJid() { } public Optional displayName() { - return displayName.describeConstable(); + return Optional.ofNullable(displayName); } public Optional phoneJid() { diff --git a/src/main/java/it/auties/whatsapp/model/chat/ChatEphemeralTimer.java b/src/main/java/it/auties/whatsapp/model/chat/ChatEphemeralTimer.java index 89182d9a5..5df95501b 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/ChatEphemeralTimer.java +++ b/src/main/java/it/auties/whatsapp/model/chat/ChatEphemeralTimer.java @@ -1,6 +1,8 @@ package it.auties.whatsapp.model.chat; import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.model.ProtobufEnum; import java.time.Duration; import java.util.Arrays; @@ -9,33 +11,39 @@ * Enum representing the ChatEphemeralTimer period. Each constant is associated with a specific * duration period. */ -public enum ChatEphemeralTimer { +public enum ChatEphemeralTimer implements ProtobufEnum { /** * ChatEphemeralTimer with duration of 0 days. */ - OFF(Duration.ofDays(0)), + OFF(0, Duration.ofDays(0)), /** * ChatEphemeralTimer with duration of 1 day. */ - ONE_DAY(Duration.ofDays(1)), + ONE_DAY(1, Duration.ofDays(1)), /** * ChatEphemeralTimer with duration of 7 days. */ - ONE_WEEK(Duration.ofDays(7)), + ONE_WEEK(2, Duration.ofDays(7)), /** * ChatEphemeralTimer with duration of 90 days. */ - THREE_MONTHS(Duration.ofDays(90)); + THREE_MONTHS(3, Duration.ofDays(90)); private final Duration period; + final int index; - ChatEphemeralTimer(Duration period) { + ChatEphemeralTimer(@ProtobufEnumIndex int index, Duration period) { + this.index = index; this.period = period; } + public int index() { + return index; + } + public Duration period() { return period; } diff --git a/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipants.java b/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipants.java index 4169e605a..b60753758 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipants.java +++ b/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipants.java @@ -15,7 +15,7 @@ public record GroupPastParticipants( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid groupJid, - @ProtobufProperty(index = 2, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) List pastParticipants ) implements ProtobufMessage { diff --git a/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java b/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java index 296cae5dc..1d665908c 100644 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java @@ -1,5 +1,8 @@ package it.auties.whatsapp.model.companion; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.signal.auth.UserAgent.PlatformType; import it.auties.whatsapp.model.signal.auth.Version; @@ -11,7 +14,16 @@ * @param platform the non-null os of the device * @param version the non-null os version of the device */ -public record CompanionDevice(String model, String manufacturer, PlatformType platform, String version) { +public record CompanionDevice( + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + String model, + @ProtobufProperty(index = 2, type = ProtobufType.STRING) + String manufacturer, + @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) + PlatformType platform, + @ProtobufProperty(index = 4, type = ProtobufType.STRING) + String version +) implements ProtobufMessage { public static CompanionDevice web() { return new CompanionDevice("Chrome", "Google", PlatformType.WEB,"1.0"); } diff --git a/src/main/java/it/auties/whatsapp/model/companion/CompanionHashState.java b/src/main/java/it/auties/whatsapp/model/companion/CompanionHashState.java index b8f552e03..edd440f4b 100644 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionHashState.java +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionHashState.java @@ -1,6 +1,9 @@ package it.auties.whatsapp.model.companion; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.node.Attributes; import it.auties.whatsapp.model.node.Node; import it.auties.whatsapp.model.sync.PatchType; @@ -12,29 +15,33 @@ import static it.auties.whatsapp.model.node.Node.of; -public final class CompanionHashState { - private PatchType name; +public final class CompanionHashState implements ProtobufMessage { + @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) + private PatchType type; + @ProtobufProperty(index = 2, type = ProtobufType.INT64) private long version; + @ProtobufProperty(index = 3, type = ProtobufType.BYTES) private byte[] hash; + @ProtobufProperty(index = 4, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.BYTES) private Map indexValueMap; - public CompanionHashState(PatchType name) { - this(name, 0); + public CompanionHashState(PatchType type) { + this(type, 0); } - public CompanionHashState(PatchType name, long version) { - this.name = name; + public CompanionHashState(PatchType type, long version) { + this.type = type; this.version = version; this.hash = new byte[128]; this.indexValueMap = new HashMap<>(); } @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public CompanionHashState(PatchType name, long version, byte[] hash, Map indexValueMap) { - this.name = name; + public CompanionHashState(PatchType type, long version, byte[] hash, Map indexValueMap) { + this.type = type; this.version = version; this.hash = hash; this.indexValueMap = indexValueMap; @@ -42,7 +49,7 @@ public CompanionHashState(PatchType name, long version, byte[] hash, Map(indexValueMap)); + return new CompanionHashState(type, version, Arrays.copyOf(hash, hash.length), new HashMap<>(indexValueMap)); } private boolean checkIndexEquality(CompanionHashState that) { @@ -67,8 +74,8 @@ private static boolean checkIndexEntryEquality(CompanionHashState that, String t return thatValue != null && Arrays.equals(thatValue, thisValue); } - public PatchType name() { - return this.name; + public PatchType type() { + return this.type; } public long version() { @@ -83,22 +90,22 @@ public Map indexValueMap() { return this.indexValueMap; } - public CompanionHashState name(PatchType name) { - this.name = name; + public CompanionHashState setType(PatchType name) { + this.type = name; return this; } - public CompanionHashState version(long version) { + public CompanionHashState setVersion(long version) { this.version = version; return this; } - public CompanionHashState hash(byte[] hash) { + public CompanionHashState setHash(byte[] hash) { this.hash = hash; return this; } - public CompanionHashState indexValueMap(Map indexValueMap) { + public CompanionHashState setIndexValueMap(Map indexValueMap) { this.indexValueMap = indexValueMap; return this; } @@ -108,13 +115,13 @@ public CompanionHashState indexValueMap(Map indexValueMap) { public boolean equals(Object o) { return o instanceof CompanionHashState that && this.version == that.version() - && this.name == that.name() + && this.type == that.type() && Arrays.equals(this.hash, that.hash()) && checkIndexEquality(that); } @Override public int hashCode() { - var result = Objects.hash(name, version, indexValueMap); + var result = Objects.hash(type, version, indexValueMap); result = 31 * result + Arrays.hashCode(hash); return result; } diff --git a/src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java b/src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java new file mode 100644 index 000000000..24fd3cb78 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java @@ -0,0 +1,15 @@ +package it.auties.whatsapp.model.companion; + +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; +import it.auties.whatsapp.model.jid.Jid; + +public record CompanionPatch( + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + Jid companion, + @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) + CompanionHashState state +) implements ProtobufMessage { + +} diff --git a/src/main/java/it/auties/whatsapp/model/companion/CompanionSyncKey.java b/src/main/java/it/auties/whatsapp/model/companion/CompanionSyncKey.java new file mode 100644 index 000000000..e9c74f38b --- /dev/null +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionSyncKey.java @@ -0,0 +1,18 @@ +package it.auties.whatsapp.model.companion; + +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; +import it.auties.whatsapp.model.jid.Jid; +import it.auties.whatsapp.model.sync.AppStateSyncKey; + +import java.util.LinkedList; + +public record CompanionSyncKey( + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + Jid companion, + @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) + LinkedList keys +) implements ProtobufMessage { + +} diff --git a/src/main/java/it/auties/whatsapp/model/contact/Contact.java b/src/main/java/it/auties/whatsapp/model/contact/Contact.java index 4c093d417..b0ba77f1b 100644 --- a/src/main/java/it/auties/whatsapp/model/contact/Contact.java +++ b/src/main/java/it/auties/whatsapp/model/contact/Contact.java @@ -1,7 +1,9 @@ package it.auties.whatsapp.model.contact; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonGetter; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.Whatsapp; import it.auties.whatsapp.model.chat.Chat; import it.auties.whatsapp.model.jid.Jid; @@ -11,15 +13,17 @@ import java.time.ZonedDateTime; import java.util.Objects; import java.util.Optional; +import java.util.OptionalLong; /** * A model class that represents a Contact. This class is only a model, this means that changing its * values will have no real effect on WhatsappWeb's servers. */ -public final class Contact implements JidProvider { +public final class Contact implements JidProvider, ProtobufMessage { /** * The non-null unique jid used to identify this contact */ + @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final Jid jid; /** @@ -27,17 +31,20 @@ public final class Contact implements JidProvider { * it should not be possible for this field to be null as it's required when registering for * Whatsapp. Though it looks that it can be removed later, so it's nullable. */ + @ProtobufProperty(index = 2, type = ProtobufType.STRING) private String chosenName; /** * The nullable name associated with this contact on the phone connected with Whatsapp */ + @ProtobufProperty(index = 3, type = ProtobufType.STRING) private String fullName; /** * The nullable short name associated with this contact on the phone connected with Whatsapp If a * name is available, theoretically, also a short name should be */ + @ProtobufProperty(index = 4, type = ProtobufType.STRING) private String shortName; /** @@ -48,17 +55,20 @@ public final class Contact implements JidProvider { * contact's status unless they send a message or are in the recent contacts. To force Whatsapp to * send updates, use {@link Whatsapp#subscribeToPresence(JidProvider)}. */ + @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) private ContactStatus lastKnownPresence; /** * The nullable last seconds this contact was seen available. Any contact can decide to hide this * information in their privacy settings. */ - private ZonedDateTime lastSeen; + @ProtobufProperty(index = 6, type = ProtobufType.UINT64) + private Long lastSeenSeconds; /** * Whether this contact is blocked */ + @ProtobufProperty(index = 7, type = ProtobufType.BOOL) private boolean blocked; public Contact(Jid jid) { @@ -73,7 +83,7 @@ public Contact(Jid jid, String chosenName, String fullName, String shortName, Co this.fullName = fullName; this.shortName = shortName; this.lastKnownPresence = lastKnownPresence; - this.lastSeen = Clock.parseSeconds(lastSeenSeconds).orElse(null); + this.lastSeenSeconds = lastSeenSeconds; this.blocked = blocked; } @@ -97,8 +107,12 @@ public String name() { return jid().user(); } + public OptionalLong lastSeenSeconds() { + return Clock.parseTimestamp(lastSeenSeconds); + } + public Optional lastSeen() { - return Optional.ofNullable(lastSeen); + return Clock.parseSeconds(lastSeenSeconds); } public Optional chosenName() { @@ -142,7 +156,7 @@ public Contact setLastKnownPresence(ContactStatus lastKnownPresence) { } public Contact setLastSeen(ZonedDateTime lastSeen) { - this.lastSeen = lastSeen; + this.lastSeenSeconds = lastSeen.toEpochSecond(); return this; } @@ -151,11 +165,6 @@ public Contact setBlocked(boolean blocked) { return this; } - @JsonGetter("lastSeen") - Long lastSeenValue() { - return lastSeen != null ? lastSeen.toEpochSecond() : null; - } - @Override public int hashCode() { return Objects.hashCode(this.jid()); diff --git a/src/main/java/it/auties/whatsapp/model/contact/ContactStatus.java b/src/main/java/it/auties/whatsapp/model/contact/ContactStatus.java index fc7bcae04..caf4c3944 100644 --- a/src/main/java/it/auties/whatsapp/model/contact/ContactStatus.java +++ b/src/main/java/it/auties/whatsapp/model/contact/ContactStatus.java @@ -1,6 +1,9 @@ package it.auties.whatsapp.model.contact; +import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.model.ProtobufEnum; + import java.util.Arrays; import java.util.Optional; @@ -8,23 +11,33 @@ * The constants of this enumerated type describe the various status that a {@link Contact} can be * in */ -public enum ContactStatus { +public enum ContactStatus implements ProtobufEnum { /** * When the contact is online */ - AVAILABLE, + AVAILABLE(0), /** * When the contact is offline */ - UNAVAILABLE, + UNAVAILABLE(1), /** * When the contact is writing a text message */ - COMPOSING, + COMPOSING(2), /** * When the contact is recording an audio message */ - RECORDING; + RECORDING(3); + + final int index; + + ContactStatus(@ProtobufEnumIndex int index) { + this.index = index; + } + + public int index() { + return index; + } public static Optional of(String name) { return Arrays.stream(values()) diff --git a/src/main/java/it/auties/whatsapp/model/info/ChatMessageInfo.java b/src/main/java/it/auties/whatsapp/model/info/ChatMessageInfo.java index 8d5a9803f..c73ee00a9 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ChatMessageInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ChatMessageInfo.java @@ -63,11 +63,11 @@ public final class ChatMessageInfo implements MessageInfo, MessageStatusInfo stubParameters; @ProtobufProperty(index = 27, type = ProtobufType.UINT32) private final int duration; - @ProtobufProperty(index = 28, type = ProtobufType.STRING, repeated = true) + @ProtobufProperty(index = 28, type = ProtobufType.STRING) private final List labels; @ProtobufProperty(index = 29, type = ProtobufType.OBJECT) private final PaymentInfo paymentInfo; @@ -93,7 +93,7 @@ public final class ChatMessageInfo implements MessageInfo, MessageStatusInfo reactions; @ProtobufProperty(index = 42, type = ProtobufType.OBJECT) private final MediaData quotedStickerData; @@ -101,7 +101,7 @@ public final class ChatMessageInfo implements MessageInfo, MessageStatusInfo pollUpdates; @ProtobufProperty(index = 46, type = ProtobufType.OBJECT) private PollAdditionalMetadata pollAdditionalMetadata; diff --git a/src/main/java/it/auties/whatsapp/model/info/ContextInfo.java b/src/main/java/it/auties/whatsapp/model/info/ContextInfo.java index 6464ae797..09f194377 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ContextInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ContextInfo.java @@ -50,7 +50,7 @@ public final class ContextInfo implements Info, ProtobufMessage { /** * A list of the contacts' jids mentioned in this ContextualMessage */ - @ProtobufProperty(index = 15, type = ProtobufType.STRING, repeated = true) + @ProtobufProperty(index = 15, type = ProtobufType.STRING) private final List mentions; /** diff --git a/src/main/java/it/auties/whatsapp/model/info/NewsletterMessageInfo.java b/src/main/java/it/auties/whatsapp/model/info/NewsletterMessageInfo.java index 0af5a2882..54980624e 100644 --- a/src/main/java/it/auties/whatsapp/model/info/NewsletterMessageInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/NewsletterMessageInfo.java @@ -1,6 +1,10 @@ package it.auties.whatsapp.model.info; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufProperty; +import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.message.model.MessageContainer; import it.auties.whatsapp.model.message.model.MessageStatus; @@ -11,19 +15,26 @@ import java.time.ZonedDateTime; import java.util.*; -public final class NewsletterMessageInfo implements MessageInfo, MessageStatusInfo { +public final class NewsletterMessageInfo implements MessageInfo, MessageStatusInfo, ProtobufMessage { @JsonBackReference private Newsletter newsletter; + @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String id; + @ProtobufProperty(index = 2, type = ProtobufType.INT32) private final int serverId; + @ProtobufProperty(index = 3, type = ProtobufType.UINT64) private final Long timestampSeconds; + @ProtobufProperty(index = 4, type = ProtobufType.UINT64) private final Long views; - private final Map reactions; + @ProtobufProperty(index = 5, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + final Map reactions; + @ProtobufProperty(index = 6, type = ProtobufType.OBJECT) private final MessageContainer message; + @ProtobufProperty(index = 7, type = ProtobufType.OBJECT) private MessageStatus status; - public NewsletterMessageInfo(Newsletter newsletter, String id, int serverId, Long timestampSeconds, Long views, Map reactions, MessageContainer message, MessageStatus status) { - this.newsletter = newsletter; + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public NewsletterMessageInfo(String id, int serverId, Long timestampSeconds, Long views, Map reactions, MessageContainer message, MessageStatus status) { this.id = id; this.serverId = serverId; this.timestampSeconds = timestampSeconds; diff --git a/src/main/java/it/auties/whatsapp/model/info/ProductListInfo.java b/src/main/java/it/auties/whatsapp/model/info/ProductListInfo.java index d739435ea..a2aff998d 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ProductListInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ProductListInfo.java @@ -15,7 +15,7 @@ */ @ProtobufMessageName("Message.ListMessage.ProductListInfo") public record ProductListInfo( - @ProtobufProperty(index = 1, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List productSections, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) ProductListHeaderImage headerImage, diff --git a/src/main/java/it/auties/whatsapp/model/info/WebNotificationsInfo.java b/src/main/java/it/auties/whatsapp/model/info/WebNotificationsInfo.java index 78c5f77a1..8201b6ef4 100644 --- a/src/main/java/it/auties/whatsapp/model/info/WebNotificationsInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/WebNotificationsInfo.java @@ -18,7 +18,7 @@ public record WebNotificationsInfo( int unreadChats, @ProtobufProperty(index = 4, type = ProtobufType.UINT32) int notifyMessageCount, - @ProtobufProperty(index = 5, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) List notifyMessages ) implements Info, ProtobufMessage { /** diff --git a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessage.java index c5e39426d..ed0dc311f 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessage.java @@ -39,7 +39,7 @@ public record ButtonsMessage( Optional footer, @ProtobufProperty(index = 8, type = ProtobufType.OBJECT) Optional contextInfo, - @ProtobufProperty(index = 9, type = ProtobufType.OBJECT, repeated = true) + @ProtobufProperty(index = 9, type = ProtobufType.OBJECT) List